#AI Agent#TypeScript

源码解读:手写一个最小 ReAct Agent

理解 Agent 最好的方式,就是亲手写一个最小的。本文用一个不到 100 行的 TypeScript 实现,把 ReAct(Thought → Action → Observation 循环)讲透:核心循环、工具调用、停止条件、错误恢复。读完你就知道 LangChain/LangGraph 那些 Agent 框架内部在做什么。

本文要点

  • 主题聚焦:AI Agent、TypeScript
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年1月13日·更新于 2026年7月23日

用框架(LangChain、LangGraph、AI SDK)搭 Agent 很方便,但也容易"会用而不懂"。理解 Agent 最彻底的方式,是自己从零写一个最小的——你会发现 ReAct Agent 的核心,其实就是一个不到 100 行的循环。

这篇带你手写一个,并讲清每一步。

一、ReAct 的本质:一个循环

ReAct(Reason + Act)的核心就这么点东西:

text
循环 {
  向模型发送: system(规则+工具) + 历史(thought/action/observation)
  模型返回: 下一步的 thought 和 action(调哪个工具、传什么参数)
  如果模型说"完成了" → 结束
  否则: 执行 action(工具),把结果作为 observation 加进历史
  继续
}

就这。所有 Agent 框架的复杂度,都是在这个循环外面包"状态管理、并行、人在回路、可观测"等工程能力。核心循环本身极简。

二、定义工具

Agent 的能力来自工具。先定义两个最简单的:

ts
const tools = [
  {
    type: "function" as const,
    function: {
      name: "search",
      description: "Search the web for factual information. Use when you don't know something.",
      parameters: {
        type: "object",
        properties: { query: { type: "string" } },
        required: ["query"],
      },
    },
  },
  {
    type: "function" as const,
    function: {
      name: "calculate",
      description: "Evaluate a math expression, e.g. '12 * (3+4)'. Use for exact arithmetic.",
      parameters: {
        type: "object",
        properties: { expr: { type: "string" } },
        required: ["expr"],
      },
    },
  },
];

// 工具的实际实现
const handlers: Record<string, (args: any) => Promise<string>> = {
  search: async ({ query }) => `[模拟搜索结果] 关于 "${query}" 的信息...`,
  calculate: async ({ expr }) => String(Function(`return (${expr})`)()),
};

三、核心循环(这就是 Agent 本体)

ts
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();

async function reactAgent(userMessage: string, maxSteps = 10) {
  const messages: any[] = [{ role: "user", content: userMessage }];

  for (let step = 0; step < maxSteps; step++) {
    // 1) 让模型想下一步
    const response = await client.messages.create({
      model: "claude-sonnet-5",
      max_tokens: 1024,
      system: "你是一个会使用工具的助手。一步步推理。需要信息就 search,需要计算就 calculate。完成就给出最终答案。",
      tools,
      messages,
    });

    // 2) 把模型这轮的输出加入历史
    messages.push({ role: "assistant", content: response.content });

    // 3) 模型没调工具 → 它给出最终答案,结束
    if (response.stop_reason === "end_turn") {
      const text = response.content.find((c: any) => c.type === "text");
      return text?.text ?? "(no answer)";
    }

    // 4) 模型调了工具 → 执行,把结果作为 observation 回灌
    if (response.stop_reason === "tool_use") {
      const toolUses = response.content.filter((c: any) => c.type === "tool_use");
      const toolResults = [];
      for (const tu of toolUses) {
        const handler = handlers[tu.name];
        const result = handler
          ? await handler(tu.input).catch((e) => `tool error: ${e.message}`)
          : `unknown tool: ${tu.name}`;
        toolResults.push({ type: "tool_result", tool_use_id: tu.id, content: result });
      }
      messages.push({ role: "user", content: toolResults });
    }
  }

  return "(达到最大步数,未完成)";
}

这就是一个能跑的 ReAct Agent,核心不到 50 行。 跑一下:

ts
await reactAgent("2024 年中国 GDP 总量是多少万亿美元?换算成人民币是多少(按 7.2 汇率)?");
// 模型会: search(GDP) → observation → calculate(换算) → observation → 给最终答案

四、看懂这几十行,就懂了所有 Agent 框架

对照这个实现,去理解框架:

  • LangGraph 的 StateGraph:把这个循环画成图,nodes 是"模型一步",edge 是循环回来,state 是 messages。
  • AI SDK 的 generateText({ tools, maxSteps }):就是把这个循环封装成 maxSteps 参数。
  • 各种 Agent 类:本质都是这个循环 + 不同程度的状态/并行/可观测封装。

框架的价值在外围工程(并行、人在回路、trace、状态持久化),不在核心循环。一旦你看穿"Agent 就是个带工具调用的循环",框架就不再是黑盒,你能在合适的时候绕过框架自己写。

五、必须自己加的工程护栏

这个最小实现是为了讲原理,生产用还差几道关:

  1. maxSteps(已加):防死循环。没有上限的 Agent 是烧钱炸弹。
  2. 错误恢复:工具失败时,catch 把可读错误回灌(tool error: ...),模型能自行换路——而不是抛异常中断。
  3. 工具参数校验:执行前用 schema 校验 tu.input,不合法的回灌错误让模型重试。
  4. 超时:每个工具调用设超时,别卡死循环。
  5. trace:每一步的 thought/action/observation 落日志(调试和生产都靠它)。

加上这五样,这个 50 行的 Agent 就具备生产雏形了。

六、从这个起点扩展

理解了核心,向上扩展都是叠加:

  • 多工具 / 并行调用:模型一次返回多个 tool_use,循环里并行执行、一起回灌(代码里已经支持)。
  • 记忆:在 messages 外加长期记忆,每轮检索注入。
  • RAG:加一个 retrieve_docs 工具,让 Agent 自主决定何时检索。
  • 多 Agent:把工具换成"调用另一个 Agent",就是多 Agent 协作。
  • Plan-Execute:循环前先让模型出计划,按计划执行,就是 Plan-Execute 范式。

这些扩展,本质上都还是在这个核心循环上加东西。

小结

ReAct Agent 的核心,是一个"模型决定下一步 → 执行工具 → 把结果喂回去"的循环,本体不到 50 行。所有 Agent 框架的复杂度都在外围(状态、并行、可观测、护栏),核心极其简单。

亲手写一遍这个最小实现,是理解 Agent 最快的路径。你会明白:Agent 不神秘,它就是"LLM + 一个循环 + 工具"。带着这个认知去用 LangGraph、AI SDK,你不再是被框架牵着走,而是知道每一步在干什么、什么时候该用框架、什么时候自己写更清爽。

Related

相关推荐