TypeScript 在 AI Agent 上的应用
在 AI Agent 开发领域,TypeScript 正在成为应用层开发的首选语言。本文将深入探讨 TypeScript 在 AI Agent 应用中的优势、核心库使用、类型安全的工具调用机制,以及实际的架构设计。
1. 为什么 AI Agent 项目需要 TypeScript + Python
现代 AI Agent 项目通常采用双语言架构,这种分工充分发挥了各自的优势。
Python 的角色:模型层
Python 在 AI Agent 的模型层占据主导地位,这得益于其丰富的生态系统:
- AI/ML 生态:PyTorch、TensorFlow、transformers、LangChain 等框架为模型训练和推理提供了坚实基础
- 数据处理能力:列表推导式、NumPy、Pandas 等工具使数据处理变得简洁高效
- 胶水语言特性:易于与 C/C++、Rust 等底层语言集成,能够调用高性能计算库
- 低学习曲线:对于 AI 研究人员来说,Python 的入门门槛较低
- 最完整的 API SDK:OpenAI、HuggingFace 等主流平台的 SDK 首先支持 Python
TypeScript 的角色:应用层
TypeScript 在应用层发挥着不可替代的作用:
- 类型安全:静态类型检查和 IDE 自动补全能够在编译时发现错误,减少运行时异常
- npm 生态:Vercel AI SDK、LangChain.js 等库提供了完善的工具链支持
- 前后端统一:共享类型定义而非业务逻辑耦合,既保证类型一致性又不增加复杂度
- Node.js 运行时:成熟的异步 I/O 模型,非常适合并发工具调用场景
- VS Code 生态:微软背书,Copilot 支持良好,重构和调试体验出色
┌─────────────────────────────────────────────────────────────┐│ AI Agent 架构 │├─────────────────────────────────────────────────────────────┤│ 展现层 │ 前端界面、Web 服务 │ TypeScript │├─────────────────────────────────────────────────────────────┤│ 应用层 │ 工具调用、状态管理、类型安全 │ TypeScript │├─────────────────────────────────────────────────────────────┤│ 控制层 │ Agent 逻辑、工具编排 │ Python/TypeScript │├─────────────────────────────────────────────────────────────┤│ 模型层 │ LLM 调用、RAG、向量检索 │ Python │└─────────────────────────────────────────────────────────────┘2. 核心 TypeScript 库
2.1 LangChain.js / LangGraph.js
LangChain.js 是 LangChain 的 TypeScript 版本,为构建 Agent 提供了丰富的抽象。
import { createReactAgent } from '@langchain/langgraph/prebuilt';import { ChatAnthropic } from '@langchain/anthropic';
// 创建 ReAct Agentconst agent = createReactAgent({ llm: new ChatAnthropic({ model: 'claude-3-5-sonnet' }), tools: [webSearchTool, calculatorTool],});
// 流式输出const stream = await agent.stream({ messages: [{ role: 'user', content: '帮我查今天天气' }],});
for await (const chunk of stream) { console.log(chunk);}LangGraph.js 则提供了更细粒度的状态管理能力,适合复杂的多步骤 Agent 流程。
2.2 Vercel AI SDK
Vercel AI SDK 是专为现代 AI 应用设计的 TypeScript 优先框架,提供了优雅的工具调用抽象。
import { anthropic } from '@ai-sdk/anthropic';import { generateText, tool } from 'ai';import { z } from 'zod';
const { text, toolCalls } = await generateText({ model: anthropic('claude-3-5-sonnet'), prompt: '帮我查北京天气', tools: { getWeather: tool({ description: '获取天气信息', parameters: z.object({ city: z.string(), unit: z.enum(['celsius', 'fahrenheit']).optional().default('celsius'), }), execute: async ({ city, unit }) => { // 实际调用天气 API return { temp: 22, weather: '晴', unit }; }, }), },});
console.log(text);console.log(toolCalls);Vercel AI SDK 的核心优势在于其简洁的 API 设计和开箱即用的流式支持。
3. 类型安全的工具调用
工具调用是 Agent 与外部世界交互的桥梁,类型安全在这个环节至关重要。
3.1 使用 Zod 定义工具 Schema
Zod 是一个 TypeScript 优先的模式验证库,与 TypeScript 的类型推断完美配合。
import { z } from 'zod';
// 定义工具 schema - 类型自动推断const tools = { search: { description: '搜索网页', parameters: z.object({ query: z.string(), limit: z.number().optional().default(10), site: z.string().optional(), }), }, calculator: { description: '计算数学表达式', parameters: z.object({ expression: z.string().describe('要计算的数学表达式'), }), }, weather: { description: '获取城市天气', parameters: z.object({ city: z.string(), days: z.number().min(1).max(7).optional().default(1), }), },} as const;
// 从 schema 推断类型type ToolName = keyof typeof tools;type ToolParams<T extends ToolName> = z.infer<typeof tools[T]['parameters']>;
// 执行工具 - IDE 自动补全和类型检查async function executeTool(name: ToolName, params: ToolParams<typeof name>) { const tool = tools[name];
// 验证参数 const validated = tool.parameters.parse(params);
// 执行对应的工具逻辑 switch (name) { case 'search': return await performSearch(validated); case 'calculator': return await evaluateExpression(validated.expression); case 'weather': return await fetchWeather(validated.city, validated.days); }}3.2 LLM 工具调用的类型化处理
当 LLM 返回工具调用请求时,我们需要将原始输出转换为类型安全的结构。
interface ToolCallRequest { name: ToolName; params: ToolParams<ToolName>;}
interface ToolCallResult { request: ToolCallRequest; result: unknown; error?: string;}
// 处理 LLM 返回的工具调用async function handleToolCalls( llmOutput: unknown): Promise<ToolCallResult[]> { const results: ToolCallResult[] = [];
// 假设 llmOutput 包含 tool_calls 数组 const toolCalls = extractToolCalls(llmOutput);
for (const call of toolCalls) { try { // 类型守卫:确保工具名称有效 if (!isValidToolName(call.name)) { throw new Error(`Unknown tool: ${call.name}`); }
const result = await executeTool(call.name, call.params); results.push({ request: call, result }); } catch (error) { results.push({ request: call, result: null, error: error instanceof Error ? error.message : 'Unknown error', }); } }
return results;}
// 类型守卫函数function isValidToolName(name: string): name is ToolName { return name in tools;}这种类型安全的设计带来三大好处:编译期检查减少运行时错误、IDE 自动补全提升开发效率、重构时能够自动发现所有引用点。
4. 多 Agent 编排
复杂的 AI Agent 系统通常需要多个专门化的 Agent 协同工作,这就涉及到多 Agent 编排问题。
4.1 Agent 定义与注册
interface Tool { name: string; description: string; parameters: z.ZodSchema; execute: (params: unknown) => Promise<unknown>;}
interface SubAgent { name: string; description: string; systemPrompt: string; tools: Tool[]; model?: string;}
class AgentRegistry { private agents: Map<string, SubAgent> = new Map();
register(agent: SubAgent): void { if (this.agents.has(agent.name)) { throw new Error(`Agent ${agent.name} already registered`); } this.agents.set(agent.name, agent); }
get(name: string): SubAgent | undefined { return this.agents.get(name); }
list(): SubAgent[] { return Array.from(this.agents.values()); }
findBestMatch(userInput: string): SubAgent[] { // 基于描述相似度或 LLM 判断选择合适的 Agent return this.list().sort((a, b) => calculateRelevance(userInput, b.description) - calculateRelevance(userInput, a.description) ); }}4.2 编排器实现
interface OrchestrationResult { agentName: string; response: string; toolCalls: ToolCallResult[]; reasoning: string;}
class AgentOrchestrator { private registry: AgentRegistry; private supervisor: LLMChain;
constructor(registry: AgentRegistry, supervisor: LLMChain) { this.registry = registry; this.supervisor = supervisor; }
async route(userInput: string): Promise<OrchestrationResult> { // 1. 使用 supervisor 决定使用哪个 Agent const decision = await this.supervisor.predictMessages([{ role: 'user', content: `用户输入: ${userInput}\n可用 Agent: ${this.listAgents()}`, }]);
// 2. 解析决策结果 const targetAgent = this.parseDecision(decision);
// 3. 调用目标 Agent const response = await this.callAgent(targetAgent, userInput);
return { agentName: targetAgent.name, response: response.content, toolCalls: response.toolCalls, reasoning: decision.reasoning, }; }
private listAgents(): string { return this.registry.list() .map(a => `- ${a.name}: ${a.description}`) .join('\n'); }
private parseDecision(decision: unknown): SubAgent { // 解析 LLM 返回的决策,提取目标 Agent 名称 const agentName = extractAgentName(decision); const agent = this.registry.get(agentName);
if (!agent) { throw new Error(`Agent not found: ${agentName}`); }
return agent; }
private async callAgent( agent: SubAgent, userInput: string ): Promise<AgentResponse> { // 实现 Agent 调用逻辑 const context = await this.buildContext(agent, userInput); return this.executeAgent(agent, context); }}4.3 并发 Agent 调用
在某些场景下,我们可能需要同时调用多个 Agent 并综合它们的结果。
async function concurrentAgents( userInput: string, agentNames: string[]): Promise<OrchestrationResult[]> { const agents = agentNames .map(name => registry.get(name)) .filter((a): a is SubAgent => a !== undefined);
// 并发执行所有 Agent const results = await Promise.all( agents.map(agent => orchestrator.callAgent(agent, userInput)) );
// 综合结果 return results.map((response, index) => ({ agentName: agents[index].name, response: response.content, toolCalls: response.toolCalls, reasoning: 'Concurrent execution', }));}5. 记忆与状态管理
Agent 的记忆系统是实现连续对话和上下文理解的关键。
5.1 记忆类型定义
type MemoryType = 'short_term' | 'long_term' | 'vector';
interface MemoryMetadata { timestamp: Date; importance: number; // 0-1, 记忆重要性评分 accessCount: number; // 访问次数 source: 'user' | 'agent' | 'tool'; tags?: string[];}
interface MemoryEntry { id: string; type: MemoryType; content: string; embedding?: number[]; // 用于向量检索 metadata: MemoryMetadata;}
interface ConversationContext { sessionId: string; messages: Message[]; memories: MemoryEntry[]; createdAt: Date; updatedAt: Date;}5.2 分层记忆实现
class AgentMemory { private shortTerm: MemoryBuffer; private longTerm: VectorStore; private workingMemory: Map<string, unknown>;
constructor(config: MemoryConfig) { this.shortTerm = new MemoryBuffer(config.shortTermLimit); this.longTerm = new VectorStore(config.vectorDimensions); this.workingMemory = new Map(); }
// 添加记忆 async add( content: string, type: MemoryType, metadata: Partial<MemoryMetadata> = {} ): Promise<MemoryEntry> { const entry: MemoryEntry = { id: generateId(), type, content, embedding: type === 'vector' ? await this.embed(content) : undefined, metadata: { timestamp: new Date(), importance: metadata.importance ?? 0.5, accessCount: 0, source: metadata.source ?? 'agent', tags: metadata.tags, }, };
if (type === 'short_term') { this.shortTerm.add(entry); } else if (type === 'long_term' || type === 'vector') { await this.longTerm.add(entry); }
return entry; }
// 检索记忆 async retrieve( query: string, limit: number = 5, type?: MemoryType ): Promise<MemoryEntry[]> { if (type === 'short_term') { return this.shortTerm.getRecent(limit); }
if (type === 'vector' || !type) { const queryEmbedding = await this.embed(query); return this.longTerm.similaritySearch(queryEmbedding, limit); }
return []; }
// 遗忘低重要性记忆 async prune(minImportance: number = 0.3): Promise<number> { const toRemove = await this.longTerm.query({ filter: { importance: { $lt: minImportance } }, });
for (const entry of toRemove) { await this.longTerm.delete(entry.id); }
return toRemove.length; }
// 总结短期记忆为长期记忆 async summarize(): Promise<void> { const recent = this.shortTerm.getAll(); const summary = await this.summarizeContent(recent.map(e => e.content));
await this.add(summary, 'long_term', { importance: 0.8, source: 'agent', tags: ['summary'], });
this.shortTerm.clear(); }}5.3 状态持久化
interface PersistedState { version: number; sessions: Record<string, ConversationContext>; memories: MemoryEntry[]; lastUpdated: string;}
class PersistentAgentState { private storage: StateStorage; private currentSession: ConversationContext | null = null;
async saveSession(session: ConversationContext): Promise<void> { const state = await this.loadState(); state.sessions[session.sessionId] = session; state.lastUpdated = new Date().toISOString(); await this.storage.save(state); }
async loadSession(sessionId: string): Promise<ConversationContext | null> { const state = await this.loadState(); return state.sessions[sessionId] ?? null; }
async createSession(): Promise<ConversationContext> { const session: ConversationContext = { sessionId: generateId(), messages: [], memories: [], createdAt: new Date(), updatedAt: new Date(), };
await this.saveSession(session); this.currentSession = session; return session; }}6. 项目架构结构
一个典型的 TypeScript AI Agent 项目应该遵循清晰的模块化架构。
src/├── agents/│ ├── base.ts # Agent 基类,定义通用接口│ ├── react-agent.ts # ReAct 模式实现│ ├── planner-agent.ts # 规划器 Agent│ ├── executor-agent.ts # 执行器 Agent│ └── index.ts # 统一导出├── tools/│ ├── registry.ts # 工具注册表│ ├── web-search.ts # 网页搜索工具│ ├── calculator.ts # 计算器工具│ ├── weather.ts # 天气查询工具│ └── types.ts # 工具类型定义├── memory/│ ├── buffer.ts # 短期记忆缓冲区│ ├── vector-store.ts # 向量存储实现│ ├── persistent.ts # 持久化存储│ └── index.ts├── orchestration/│ ├── router.ts # Agent 路由逻辑│ ├── supervisor.ts # 监督器│ └── concurrent.ts # 并发编排├── types/│ ├── messages.ts # 消息类型定义│ ├── tools.ts # 工具类型定义│ └── agent.ts # Agent 相关类型├── utils/│ ├── logger.ts # 日志工具│ ├── embedding.ts # 向量化工具│ └── validation.ts # 验证工具├── config/│ └── index.ts # 配置管理├── index.ts # 入口文件└── app.ts # 应用启动核心模块说明
agents/base.ts 提供了所有 Agent 的抽象基类:
abstract class BaseAgent { protected name: string; protected description: string; protected tools: Tool[]; protected memory: AgentMemory;
abstract execute(input: string, context?: Context): Promise<AgentResponse>;
protected async think(input: string): Promise<string> { // Agent 的思考逻辑 throw new Error('Not implemented'); }
protected async act(input: string): Promise<Action> { // Agent 的行动逻辑 throw new Error('Not implemented'); }}tools/registry.ts 实现类型安全的工具注册:
class ToolRegistry { private tools: Map<string, Tool> = new Map();
register<T extends z.ZodSchema>( name: string, description: string, schema: T, execute: (params: z.infer<T>) => Promise<unknown> ): void { this.tools.set(name, { name, description, parameters: schema, execute, }); }
get(name: string): Tool | undefined { return this.tools.get(name); }
list(): Tool[] { return Array.from(this.tools.values()); }
getSchema(name: string): z.ZodSchema | undefined { return this.tools.get(name)?.parameters; }}7. Python 与 TypeScript 对比:Agent 控制层
在 Agent 控制层,Python 和 TypeScript 各有优劣,选择取决于具体场景。
| 方面 | Python | TypeScript |
|---|---|---|
| 复杂状态管理 | 运行时错误 | 编译时错误 |
| 工具 Schema 变更 | 静默失败 | 编译器警告 |
| 多 Agent 路由 | 手动处理 | 类型保护 |
| 前端集成 | 额外 HTTP 层 | 直接调用 |
| IDE 与重构 | 一般支持 | VS Code 强大支持 |
| 并发工具调用 | asyncio 繁琐 | Promise.all 简洁 |
| 运行时性能 | 优秀 | 良好 |
| 启动速度 | 较慢 | 快速 |
Python 的适用场景
- 模型训练和微调相关的实验性工作
- 需要深度定制 LangChain/LangGraph 内部逻辑
- 数据处理和特征工程为主的任务
- 团队成员以 Python 为主,没有前端背景
TypeScript 的适用场景
- 需要前后端类型统一的场景
- 复杂的多 Agent 系统,需要编译时检查
- 生产环境,需要长期维护和重构
- 需要与 React/Vue 等前端框架集成
- 强调开发体验和代码可靠性
8. 前后端统一不等于耦合
TypeScript 的”统一”常被误解为前后端耦合,这是一个需要澄清的误区。
正确的理解
前后端统一指的是类型定义的共享,而非业务逻辑的耦合:
传统架构:┌─────────────┐ HTTP ┌─────────────┐│ Frontend │ ───────────────→ │ Backend ││ (TS/React) │ ←─────────────── │ (Python) │└─────────────┘ └─────────────┘ ↓ 类型定义需要重复维护
TypeScript 统一架构:┌─────────────┐ HTTP ┌─────────────┐│ Frontend │ ───────────────→ │ Backend ││ (TS/React) │ ←─────────────── │ (TS/Node) │└─────────────┘ └─────────────┘ │ │ └──── 共享类型 @shared/types ←───┘ (仅 npm 包,无业务逻辑)实施方式
packages/└── @shared/ └── types/ ├── agent.ts # Agent 相关类型 ├── tool.ts # 工具类型定义 ├── message.ts # 消息类型定义 └── index.ts # 统一导出
# frontend 和 backend 分别独立部署# 仅通过 HTTP/API 通信# 共享类型包确保接口一致性前后端仍然是独立的服务、独立部署,只是都使用 TypeScript 并通过 npm 包共享类型定义。
9. 总结与建议
技术选型建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 全 Python 团队 | Python (LangGraph) | 无学习成本,生态完整 |
| 有前端参与,需类型安全 | TypeScript | 前后端统一,编译期检查 |
| 快速原型,验证想法 | Python | 开发速度快,迭代敏捷 |
| 生产环境,需重构安全 | TypeScript | 编译时检查,重构信心 |
| 复杂多 Agent,强类型 | TypeScript | 类型保护,多 Agent 协调 |
最佳实践
-
类型安全优先:使用 Zod 定义所有工具 Schema,充分利用 TypeScript 类型推断
-
模块化设计:按职责分离 Agent、工具、记忆、编排逻辑,便于测试和维护
-
记忆分层:短期记忆处理当前会话,长期记忆支持跨会话知识积累
-
错误边界:为每个工具调用和 Agent 执行添加错误处理,避免级联失败
-
可观测性:集成日志和追踪,便于调试 Agent 行为和问题定位
未来趋势
随着 AI Agent 应用的成熟,TypeScript 在这个领域的优势将更加明显:
- 微软等大厂推动 TypeScript 在 AI 领域的应用
- 更多 AI 原生的 TypeScript 库涌现
- 前端与 Agent 的深度集成需求增长
- 类型安全的工具调用成为行业标准
TypeScript 不仅是前端开发的选择,更是构建可靠、可维护 AI Agent 应用的重要基础设施。