← blog AI Agent · 2026-04-10

TypeScript 在 AI Agent 上的应用

深入探讨 TypeScript 与 Python 双语言架构在 AI Agent 项目中的应用,分析 LangChain.js、LangGraph.js、Vercel AI SDK 等核心库的使用,涵盖类型安全的工具调用、多 Agent 编排、内存与状态管理等最佳实践。

12 min read
#AI

在 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 支持良好,重构和调试体验出色
层职责语言
展现层前端界面、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 Agent
const 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 各有优劣,选择取决于具体场景。

方面PythonTypeScript
复杂状态管理运行时错误编译时错误
工具 Schema 变更静默失败编译器警告
多 Agent 路由手动处理类型保护
前端集成额外 HTTP 层直接调用
IDE 与重构一般支持VS Code 强大支持
并发工具调用asyncio 繁琐Promise.all 简洁
运行时性能优秀良好
启动速度较慢快速

Python 的适用场景

  • 模型训练和微调相关的实验性工作
  • 需要深度定制 LangChain/LangGraph 内部逻辑
  • 数据处理和特征工程为主的任务
  • 团队成员以 Python 为主,没有前端背景

TypeScript 的适用场景

  • 需要前后端类型统一的场景
  • 复杂的多 Agent 系统,需要编译时检查
  • 生产环境,需要长期维护和重构
  • 需要与 React/Vue 等前端框架集成
  • 强调开发体验和代码可靠性

8. 前后端统一不等于耦合

TypeScript 的”统一”常被误解为前后端耦合,这是一个需要澄清的误区。

正确的理解

前后端统一指的是类型定义的共享,而非业务逻辑的耦合:

传统架构:

graph LR
    F1["Frontend<br/>(TS/React)"] -- HTTP --> B1["Backend<br/>(Python)"]
    B1 -- HTTP --> F1
    B1 --> D1["类型定义需要重复维护"]

TypeScript 统一架构:

graph LR
    F2["Frontend<br/>(TS/React)"] -- HTTP --> B2["Backend<br/>(TS/Node)"]
    B2 -- HTTP --> F2
    S["共享类型 @shared/types<br/>(仅 npm 包,无业务逻辑)"]
    F2 --> S
    B2 --> S

实施方式

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 协调

最佳实践

  1. 类型安全优先:使用 Zod 定义所有工具 Schema,充分利用 TypeScript 类型推断

  2. 模块化设计:按职责分离 Agent、工具、记忆、编排逻辑,便于测试和维护

  3. 记忆分层:短期记忆处理当前会话,长期记忆支持跨会话知识积累

  4. 错误边界:为每个工具调用和 Agent 执行添加错误处理,避免级联失败

  5. 可观测性:集成日志和追踪,便于调试 Agent 行为和问题定位

未来趋势

随着 AI Agent 应用的成熟,TypeScript 在这个领域的优势将更加明显:

  • 微软等大厂推动 TypeScript 在 AI 领域的应用
  • 更多 AI 原生的 TypeScript 库涌现
  • 前端与 Agent 的深度集成需求增长
  • 类型安全的工具调用成为行业标准

TypeScript 不仅是前端开发的选择,更是构建可靠、可维护 AI Agent 应用的重要基础设施。

Sources

No external sources for this entry.

Related