news 2026/8/31 13:00:14

TypeScript手写AI Agent:100行代码搭建智能体核心框架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TypeScript手写AI Agent:100行代码搭建智能体核心框架

最近在做 AI 应用落地时,一个很深的体会是:真正难的不是调用大模型 API,而是怎么让模型在真实任务里稳定地“干活”。网上很多 Agent 教程要么贴了一堆概念图,要么只给出 Python 代码。对于 TypeScript 技术栈的同学来说,想找一个能直接跑起来的 Agent 最小框架并不容易。

这篇文章就用 TypeScript 手写一个通用智能体,核心逻辑控制在 100 行左右,完整呈现企业级 Agent 的主干架构。读完你不仅能跑通代码,还能理解 Agent 设计里最重要的模块划分和职责边界。代码全部放在一个最小项目中,可以复制到本地直接运行。

1. 为什么用 TypeScript 写 AI Agent

1.1 AI Agent 到底是什么

很多人把 Agent 理解成“调一次大模型接口,拿到回答”,这是不完整的。AI Agent 的核心特征是目标驱动和自主决策:你给它一个目标,它能自己拆解任务、选择工具、执行操作、观察结果,再决定下一步怎么做,直到完成目标。

举个最简单的例子:

  • 普通 API 调用:用户问“北京天气怎么样”,程序直接把问题发给模型,模型返回一段话。
  • Agent 方式:模型先判断需要调用天气查询工具,然后程序执行工具,把工具返回的数据回传给模型,模型基于真实天气数据生成最终回答。

区别在于“工具调用”和“多轮循环”。Agent 不是一个单次接口调用,而是一个循环决策系统。大模型在这个循环里充当“大脑”,工具系统充当“手脚”,消息列表充当“工作记忆”。

1.2 为什么选择 TypeScript

目前社区里 Agent 框架确实以 Python 为主,但 TypeScript 在工程化方面有不可替代的优势。

第一,类型安全。Agent 涉及大量的数据结构:消息、工具定义、参数校验、工具返回值。用 TypeScript 写,编译阶段就能发现字段名错误,而不是等运行时才报错。第二,生态复用。Node.js 环境下可以直接使用数据库驱动、Redis 客户端、消息队列 SDK,前端团队也能无缝参与开发和调试。第三,部署成本低。TypeScript 编译后的产物可以打包成 Serverless 函数、Docker 镜像,或者直接跑在 Node 服务里。

如果你的团队本身就是 React/Node 技术栈,用 TypeScript 写 Agent 意味着不需要引入一套全新的 Python 工程体系,学习成本和维护成本都更低。

1.3 类 PI-Agent 架构带来了什么启发

标题里的“PI-Agent”并不是某个固定开源项目的名称,而是业界对 Agent 分层组织方式的一种通俗叫法。大家参考的其实是同一套核心思路:让大模型在一个受控循环中自主决策,而不是一次性生成答案

类 PI-Agent 架构给开发者最大的启发,是把 Agent 拆成几个边界清晰的部分:

  • 模型调用层:负责和大模型 API 通信。
  • 工具系统:负责声明、注册、执行外部能力。
  • 记忆模块:负责保存对话历史、工具结果、关键中间状态。
  • 主循环:负责指挥模型“决策—执行—观察—再决策”。

这种拆分方式的好处是:每一层都能独立扩展。你可以替换底层模型,可以新增工具,可以调整记忆策略,而不会把整个系统搅成一团。下面我们就按这个思路,从零搭建一个 TypeScript 版本的 Agent 骨架。

2. 环境准备与 TypeScript 项目初始化

2.1 开发环境要求

本文示例所需的运行环境如下:

依赖版本建议说明
Node.js18+需要原生 fetch,Node 18 之前需要额外安装 polyfill
npm / yarn / pnpm任意本文以 npm 为例
TypeScript5.x使用现代模块配置
大模型 APIOpenAI 兼容接口例如 OpenAI、DeepSeek、通义千问等兼容服务

版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。如果你本地 Node 版本较低,建议先升级到 18 以上,否则后面运行时会遇到 fetch 未定义的报错。

2.2 初始化项目与安装依赖

先创建一个空目录,然后初始化 npm 项目:

mkdir typescript-agent-demo cd typescript-agent-demo npm init -y

接着安装开发依赖:

npm install -D typescript tsx @types/node

这里用到了两个工具:

  • typescript:TypeScript 编译器。
  • tsx:一个基于 esbuild 的 TypeScript 运行工具,可以直接运行.ts文件,省去先编译再运行的步骤,非常适合开发调试。
  • @types/node:提供 Node.js 的类型定义,否则process.env等内置 API 会报类型错误。

如果安装时报 peer dependencies 冲突,可以检查一下 npm 版本,或者使用npm install -D指定版本安装。

2.3 tsconfig.json 配置

在项目根目录创建tsconfig.json,内容如下:

{ "compilerOptions": { "target": "ES2022", "module": "CommonJS", "moduleResolution": "Node", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": false }, "include": ["src"] }

重点说明几个配置项:

  • target: ES2022:保证可以使用较新的 JavaScript 语法,比如async/awaitfor...ofRecord类型。
  • module: CommonJS:Node.js 服务端最常用的模块规范。
  • strict: true:开启严格类型检查,这是 TypeScript 的核心价值所在。
  • esModuleInterop: true:方便在 CommonJS 环境下使用默认导入写法。

这里额外提醒一个细节:新版 TypeScript 中baseUrl选项已经开始弃用,并计划在未来版本停止支持。如果你的项目里还在使用baseUrl,建议改为paths配合相对路径的方式,避免升级 TypeScript 后出现编译警告。

3. Agent 核心架构模块拆解

3.1 模型调用层(LLM Adapter)

模型调用层是整个 Agent 的大门,负责屏蔽不同厂商 API 的差异。无论是 OpenAI、DeepSeek 还是其他兼容服务,它们的请求格式和响应格式都不完全一样。如果没有这一层抽象,Agent 主循环里会到处散落着fetch调用和响应解析逻辑,后期想换模型会非常痛苦。

一个设计良好的 LLM Adapter 需要暴露两个核心能力:

  • 接收一组消息,返回模型生成的内容。
  • 支持工具调用:把当前可用的工具列表传给模型,模型返回“要调用哪些工具、参数是什么”。

在 TypeScript 里,这可以抽象成一个通用接口,不同厂商提供不同实现。本文示例采用 OpenAI 兼容接口实现,因为它目前是事实上的行业标准,很多厂商都提供了兼容端点。

3.2 工具注册与执行层(Tool Registry)

工具系统是 Agent 的“手脚”。模型本身不能查询天气、不能读写数据库、不能调用内部 API,但这些能力都可以通过工具暴露给模型。

在实现上,工具系统至少要负责三件事:

  1. 声明:告诉模型这个工具叫什么、是干什么的、需要哪些参数。
  2. 注册:把工具放到一个可管理的注册表里,统一管理。
  3. 执行:根据模型返回的工具名和参数,真正调用对应的函数,并返回结构化结果。

设计时要注意:模型传入的参数是不可信的,工具执行前必须做参数校验。比如模型传了一个city: null,代码里不能直接报错,而是要返回一个结构化的错误结果,让模型知道“参数不对,请调整后重试”。

3.3 记忆与上下文管理(Message Buffer)

Agent 的记忆本质上就是聊天消息列表。每次循环都会向消息列表追加新内容:用户输入、模型的工具调用意图、工具执行结果、模型的最终回答。

在 TypeScript 实现中,消息需要区分几种角色:

  • system:系统提示词,定义 Agent 的角色和行为边界。
  • user:用户的输入。
  • assistant:模型的输出。
  • tool:工具执行后的结果。

上下文管理有两个实际问题需要处理。一是消息量膨胀:工具调用轮次越多,消息越长,最终会超过模型上下文窗口。二是工具结果格式:工具返回的可能是一个大型对象,直接塞进消息里会浪费 token,需要做摘要或截断。

本文的最小实现聚焦前者,使用一个简单的消息数组。生产环境则需要引入滑动窗口、摘要压缩、向量检索等机制。

3.4 任务规划与主循环(Agent Loop)

主循环是 Agent 的灵魂,所有模块在这里被串联起来。循环的逻辑非常直观:

  1. 把消息列表和工具列表发给模型。
  2. 判断模型返回结果:如果需要调用工具,就执行工具并把结果追加到消息列表,然后进入下一轮循环;如果返回的是最终回答,就结束循环。
  3. 如果超过最大迭代次数,强制终止,避免模型陷入死循环。

这个循环看似简单,却是很多 Agent 框架的基石。它把“大模型自主决策”和“程序确定性执行”结合在了一起:模型负责判断“做什么”,程序负责“怎么做”以及“结果反馈”。

4. 用 TypeScript 实现通用智能体核心骨架

4.1 项目结构规划

代码虽然不多,但我们按模块拆分,方便后续扩展。完整项目结构如下:

typescript-agent-demo/ ├── package.json ├── tsconfig.json └── src/ ├── index.ts # 入口,组装工具和 Agent ├── types.ts # 类型定义 ├── tools.ts # 工具注册表 ├── llm.ts # LLM 适配器 └── agent.ts # Agent 主循环

这样的分层结构在企业项目中很重要。如果所有代码都堆在index.ts里,也许 100 行能跑通,但后续加一个工具、换一个模型、调整记忆策略,都会互相影响。

4.2 类型定义:先想清楚消息模型

先创建src/types.ts,定义整个项目的基础类型:

export type Role = 'system' | 'user' | 'assistant' | 'tool'; export interface ToolCallParam { id: string; name: string; arguments: Record<string, unknown>; } export interface Message { role: Role; content: string; toolCalls?: ToolCallParam[]; toolCallId?: string; name?: string; } export interface ToolDefinition { name: string; description: string; parameters: Record<string, unknown>; } export interface ToolResult { success: boolean; data?: unknown; error?: string; } export interface Tool { definition: ToolDefinition; execute(args: Record<string, unknown>): Promise<ToolResult>; } export interface AgentResponse { content?: string; toolCalls?: ToolCallParam[]; }

这里面有几个字段需要重点解释。

Message.toolCalls表示模型在某一轮次里决定调用的工具列表,只有当roleassistant时才需要存在。Message.toolCallId用于关联工具调用和工具结果:模型发出多个工具调用,工具系统执行后,每个结果必须对应到具体的那一次调用,否则模型无法区分哪个结果属于哪次调用。nameroletool时使用,表示是哪个工具返回的结果。

parameters使用 JSON Schema 风格的结构,虽然不是完整规范,但已经足够模型理解参数格式。大多数兼容 OpenAI 格式的接口都支持这种描述结构。

4.3 实现工具注册表

创建src/tools.ts,实现工具注册、列表、调用的完整闭环:

import { Tool, ToolResult } from './types'; export class ToolRegistry { private tools = new Map<string, Tool>(); register(tool: Tool): void { this.tools.set(tool.definition.name, tool); } list(): Tool[] { return Array.from(this.tools.values()); } async call(name: string, args: Record<string, unknown>): Promise<ToolResult> { const tool = this.tools.get(name); if (!tool) { return { success: false, error: `未找到工具: ${name}` }; } try { return await tool.execute(args); } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error), }; } } }

这个类虽然短,但体现了几个重要的工程决策:

  • 使用Map存储工具,注册时以工具名为 key,避免同名工具重复注册。
  • list()返回的是当前所有工具,Agent 主循环每次都会把最新工具列表传给模型。
  • call()内部做了异常兜底,任何工具执行抛错都会转成ToolResult返回,而不是直接中断整个 Agent 流程。这一点很关键,因为 Agent 的容错逻辑依赖“错误也要变成模型能理解的反馈”。

如果工具执行失败后直接抛出异常,Agent 主循环就感知不到失败原因,模型也没法根据原因调整策略。把错误包装成{ success: false, error: '...' },模型读到后就知道刚才的调用出了问题。

4.4 实现 LLM 适配器

创建src/llm.ts,以 OpenAI 兼容接口为例实现模型调用:

import { AgentResponse, Message, Tool } from './types'; interface LLMConfig { baseUrl: string; apiKey: string; model: string; } interface OpenAIResponse { choices?: Array<{ message?: { content?: string | null; tool_calls?: Array<{ id: string; type: string; function: { name: string; arguments: string; }; }>; }; }>; } export class OpenAICompatibleAdapter { constructor(private config: LLMConfig) {} async chat(messages: Message[], tools: Tool[]): Promise<AgentResponse> { const url = `${this.config.baseUrl.replace(/\/$/, '')}/chat/completions`; const body: Record<string, unknown> = { model: this.config.model, messages: messages.map((m) => { if (m.role === 'tool') { return { role: 'tool', tool_call_id: m.toolCallId, content: m.content, }; } const msg: Record<string, unknown> = { role: m.role, content: m.content, }; if (m.toolCalls && m.toolCalls.length > 0) { msg.tool_calls = m.toolCalls.map((tc) => ({ id: tc.id, type: 'function', function: { name: tc.name, arguments: JSON.stringify(tc.arguments), }, })); } return msg; }), }; if (tools.length > 0) { body.tools = tools.map((tool) => ({ type: 'function', function: { name: tool.definition.name, description: tool.definition.description, parameters: tool.definition.parameters, }, })); } const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.config.apiKey}`, }, body: JSON.stringify(body), }); if (!response.ok) { const text = await response.text(); throw new Error(`LLM 接口调用失败: ${response.status} ${text}`); } const data = (await response.json()) as OpenAIResponse; const choice = data.choices?.[0]; if (!choice) { return {}; } const toolCalls = choice.message?.tool_calls?.map((tc) => ({ id: tc.id, name: tc.function?.name ?? '', arguments: safeParse(tc.function?.arguments), })); return { content: choice.message?.content ?? '', toolCalls, }; } } function safeParse(json: string | undefined): Record<string, unknown> { if (!json) return {}; try { return JSON.parse(json) as Record<string, unknown>; } catch { return {}; } }

这个适配器做了几件核心事情。

第一,消息格式转换。OpenAI 接口对tool消息有特殊要求:必须带上tool_call_id,否则接口会报错。我们内部统一的Message结构需要在发送前转换成厂商要求的格式。

第二,工具声明格式。OpenAI 要求工具以 JSON Schema 形式放在tools字段里,本文的ToolDefinition.parameters正好可以直接透传。

第三,响应容忍度。模型接口偶尔会出现tool_calls缺失或arguments不是合法 JSON 的情况。代码里用safeParse做了兜底,解析失败时返回空对象,避免因为一个非法 JSON 直接崩溃。

第四,错误处理。HTTP 非 2xx 状态码会直接抛出异常,由上一层的 Agent 主循环决定如何处理。

各厂商的兼容接口在请求体上会有细微差异。如果你接入的是 DeepSeek、通义千问等平台,建议先查看对应文档确认字段命名,核心的 adapter 结构通常不需要大改。

4.5 实现 Agent 主循环

创建src/agent.ts,这是整个 Agent 的核心逻辑:

import { OpenAICompatibleAdapter } from './llm'; import { ToolRegistry } from './tools'; import { Message } from './types'; interface AgentOptions { systemPrompt: string; maxIterations?: number; } export class Agent { private messages: Message[] = []; private maxIterations: number; constructor( private llm: OpenAICompatibleAdapter, private tools: ToolRegistry, private options: AgentOptions ) { this.maxIterations = options.maxIterations ?? 5; this.messages.push({ role: 'system', content: options.systemPrompt }); } async run(userInput: string): Promise<string> { this.messages.push({ role: 'user', content: userInput }); for (let i = 1; i <= this.maxIterations; i++) { console.log(`[Agent] 第 ${i} 轮决策开始`); const response = await this.llm.chat(this.messages, this.tools.list()); if (response.toolCalls && response.toolCalls.length > 0) { this.messages.push({ role: 'assistant', content: response.content ?? '', toolCalls: response.toolCalls, }); for (const toolCall of response.toolCalls) { console.log(`[Agent] 调用工具: ${toolCall.name}`, toolCall.arguments); const result = await this.tools.call(toolCall.name, toolCall.arguments); console.log(`[Agent] 工具结果:`, result); this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: toolCall.id, }); } continue; } if (response.content) { this.messages.push({ role: 'assistant', content: response.content }); return response.content; } throw new Error(`第 ${i} 轮迭代未产生有效输出`); } throw new Error(`超过最大迭代次数 ${this.maxIterations},任务未完成`); } }

主循环的逻辑只有十几行,却把 Agent 的核心机制完整实现了。

maxIterations是安全阀。模型在工具调用过程中偶尔会陷入“反复调用同一个工具、参数不变”的死循环。如果没有最大迭代次数保护,会一直消耗 token 直到超时。工程实践中一般设置 5 到 10 次,具体根据任务复杂度调整。

还有一个容易被忽略的点:assistant消息保存了contenttoolCalls。即使内容为空,这条assistant消息也必须追加到历史中,否则模型会丢失“自己已经做出过哪些决策”的信息,导致重复发起相同的工具调用。

工具执行结果用JSON.stringify序列化后保存为字符串。这样做的好处是消息结构简单,坏处是大对象会占用大量 token。本文示例保持了最小实现,生产环境可以根据工具类型对结果做摘要。

4.6 组装工具与运行入口

创建src/index.ts,把前面所有模块组装起来:

import { Agent } from './agent'; import { OpenAICompatibleAdapter } from './llm'; import { ToolRegistry } from './tools'; import { Tool } from './types'; async function main() { // 定义天气查询工具 const weatherTool: Tool = { definition: { name: 'get_weather', description: '查询指定城市的当前天气', parameters: { type: 'object', properties: { city: { type: 'string', description: '城市名称' }, }, required: ['city'], }, }, async execute(args) { const city = String(args.city ?? ''); if (!city) { return { success: false, error: '缺少 city 参数' }; } // 演示数据,生产环境请接入真实天气服务 return { success: true, data: { city, weather: '晴', temperature: 26 }, }; }, }; // 定义计算器工具 const calculatorTool: Tool = { definition: { name: 'calculator', description: '执行简单的四则运算', parameters: { type: 'object', properties: { expression: { type: 'string', description: '数学表达式,例如 25 * 4 + 16' }, }, required: ['expression'], }, }, async execute(args) { const expr = String(args.expression ?? ''); // 注意:生产环境请使用 mathjs 等表达式解析库,不要直接使用 Function const result = Function(`'use strict'; return (${expr})`)(); return { success: true, data: { expression: expr, result } }; }, }; // 注册工具 const tools = new ToolRegistry(); tools.register(weatherTool); tools.register(calculatorTool); // 初始化 LLM const llm = new OpenAICompatibleAdapter({ baseUrl: process.env.LLM_BASE_URL ?? 'https://api.openai.com/v1', apiKey: process.env.LLM_API_KEY ?? '', model: process.env.LLM_MODEL ?? 'gpt-4o-mini', }); // 构建 Agent const agent = new Agent(llm, tools, { systemPrompt: '你是一个通用智能体。请根据用户问题选择合适的工具,并在得到工具结果后给出最终回答。', maxIterations: 5, }); // 运行 const answer = await agent.run('北京今天天气怎么样?顺便帮我算一下 25 * 4 + 16'); console.log('\n=== 最终回答 ==='); console.log(answer); } main().catch((err) => { console.error(err); process.exit(1); });

从工具箱到 Agent,一共不到 100 行核心逻辑。读者可以在这个基础上任意扩展新工具,只需要实现Tool接口并注册即可,不需要改 Agent 主循环。

5. 运行验证与效果演示

5.1 配置模型环境变量

在项目根目录创建.env文件(注意不要提交到代码仓库),内容如下:

LLM_BASE_URL=https://api.openai.com/v1 LLM_API_KEY=你的_API_Key LLM_MODEL=gpt-4o-mini

如果使用国内兼容平台,把LLM_BASE_URL换成平台提供的 base URL 即可。API Key 也可以通过环境变量设置,但要确保密钥不暴露在前端代码中。

5.2 编译与启动

开发调试阶段,直接使用tsx运行:

npx tsx src/index.ts

也可以把启动命令写入package.json的 scripts 中:

{ "scripts": { "dev": "tsx src/index.ts", "build": "tsc", "start": "node dist/index.js" } }

之后再运行:

npm run dev

如果要部署到生产环境,先执行npm run build编译到dist目录,再用node dist/index.js启动。

5.3 预期执行流程

假设模型决定调用两个工具,运行日志大致如下:

[Agent] 第 1 轮决策开始 [Agent] 调用工具: get_weather { city: '北京' } [Agent] 工具结果: { success: true, data: { city: '北京', weather: '晴', temperature: 26 } } [Agent] 调用工具: calculator { expression: '25 * 4 + 16' } [Agent] 工具结果: { success: true, data: { expression: '25 * 4 + 16', result: 116 } } [Agent] 第 2 轮决策开始 === 最终回答 === 北京今天天气晴,气温 26 摄氏度。25 * 4 + 16 的计算结果是 116。

注意,模型不一定总是在第一轮就选择调用所有工具,也可能先查天气再计算,这取决于模型本身和系统提示词的引导。只要 Agent 循环正常结束并返回最终回答,就说明整个架构已经跑通了。

6. 常见问题与排查思路

在实际运行过程中,最常见的问题集中在网络、类型和环境变量三个方面。

问题现象常见原因解决思路
fetch is not definedNode.js 版本低于 18升级 Node.js 到 18+
403 Forbidden401 UnauthorizedAPI Key 错误或缺失检查LLM_API_KEY环境变量
baseUrl弃用警告TypeScript 高版本对旧配置提示改用paths并移除baseUrl
模型始终不调用工具工具描述不清晰或模型能力不足优化工具 description,使用支持 function calling 的模型
模型重复调用同一个工具缺少历史消息回填检查assistant消息是否正确追加追问
上下文超长工具结果和消息过多压缩工具结果、限制消息轮次

这里重点说一下模型不调用工具的问题。很多人遇到“Agent 就是不用工具”的情况,第一反应是换模型,其实更常见的原因是工具描述写得太模糊。比如description: '天气工具'description: '查询指定城市的当前天气,参数 city 为城市名称',后者的调用成功率会高很多。此外,系统提示词里应明确说明“你需要调用工具来完成任务”,而不是让模型自由发挥。

另一个常见问题是工具参数解析失败。模型返回的arguments偶尔会是格式不完整的 JSON 字符串,safeParse兜底后返回空对象,但工具拿到空对象就会报参数缺失。遇到这种情况,可以在提示词里强调“严格按照 JSON 格式输出参数”,同时在工具执行端做好默认值处理。

7. 企业级 Agent 设计最佳实践

7.1 工具执行的安全边界

工具系统是 Agent 与外部世界交互的入口,也是风险最高的地方。

首先,不要把未经校验的模型输出直接拼接到命令行、SQL 或代码片段中。本文示例里的calculator工具使用了Function执行表达式,这是高风险写法,生产环境必须替换为mathjs这类成熟的表达式解析库。任何工具如果需要执行远程命令或访问数据库,都必须先做参数白名单校验、加上权限限制,并开启审计日志。

其次,为工具设置最小权限。一个查询天气的工具不应该拥有访问订单数据库的权限。Agent 能调用的工具集合越小,攻击面就越小。有些企业级系统还会为不同的 Agent 分配不同的工具集,做到“工具级最小化”和“模型级最小化”。

7.2 可观测性与运行日志

Agent 的决策过程不是一次调用,而是一个多轮循环。线上排查问题时,如果看不到“模型为什么这么决策”,排错会非常困难。因此必须把关键信息记录下来:

  • 每一轮的系统提示词、用户输入、模型输出。
  • 模型请求了哪些工具、传入了什么参数。
  • 工具执行结果、耗时、是否成功。
  • 最终回答内容和总轮数。

生产环境建议把这些日志接入链路追踪系统,给每轮 Agent 运行分配一个 traceId,方便按任务维度检索。如果你用的是云厂商的日志服务,也可以把console.log替换为结构化 JSON 日志,实现更精细的检索。

7.3 超时、重试与降级策略

大模型接口的延迟和稳定性通常比普通 HTTP 服务更不可控。企业级 Agent 至少需要考虑以下策略:

  • 超时控制:给fetch设置合理的超时时间,比如 30 秒,避免模型接口卡死导致整个 Agent 进程挂起。
  • 重试机制:对于 429 限流、5xx 等瞬时错误,可以指数退避重试 2 到 3 次。
  • 降级方案:如果主模型不可用,可以切换到备用模型。如果 Agent 循环超限,应该返回一个友好的兜底回答,而不是把异常直接抛给用户。

在代码层面,建议把重试逻辑放在 LLM Adapter 内部,而不是散落在 Agent 主循环里,这样主循环只关注决策流程,不需要关心网络细节。

7.4 从单 Agent 走向多 Agent 协作

本文实现的是单 Agent 最小架构,但企业级场景往往需要多个 Agent 分工协作。常见做法是设置一个“主管 Agent”负责拆解目标,再把子任务分配给不同的“执行 Agent”,最后汇总结果。

多 Agent 协作在架构上并不是推翻单 Agent,而是在单 Agent 基础上增加两层能力:

  • 任务队列:子任务需要排队、调度、状态管理。
  • 结果汇总:多个子 Agent 执行完后,需要合并结果并做冲突处理。

如果你的系统复杂度还没到多 Agent 的程度,建议先把单 Agent 的稳定性做扎实:工具调用可靠、上下文管理合理、调度日志完整。这些能力在多 Agent 阶段同样不可省略。

8. 总结与下一步学习路线

这篇文章的核心内容可以浓缩成一句话:Agent 的本质是一个“决策—执行—反馈”的循环,TypeScript 完全可以实现一套结构清晰的企业级 Agent 骨架。

你掌握了以下几个关键点:

  • Agent 基础概念与核心模块划分。
  • TypeScript 项目初始化和类型设计。
  • 工具注册表、LLM 适配器、主循环的实现思路。
  • 常见运行问题的排查方法。
  • 企业级 Agent 在安全、可观测性、稳定性方面的最佳实践。

如果你打算继续深入,可以按照下面的路线逐步进阶:

  1. 为 Agent 增加持久化记忆,把关键历史写入 Redis 或 SQLite。
  2. 接入 RAG 能力,让 Agent 能检索私有知识库。
  3. 实现流式输出,把模型生成过程实时推送给前端。
  4. 引入多 Agent 编排框架,让不同 Agent 协作完成复杂任务。
  5. 建立 Agent 评测集,用自动化测试评估工具调用准确率和任务完成率。

先跑通这个最小框架,再逐步加记忆、加 RAG、加多 Agent 协作。动手试一下,比看十篇架构图都有用。遇到问题可以在评论区交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 13:00:13

turbovec的warning_hook机制:自定义警告钩子的用法与场景

turbovec的warning_hook机制&#xff1a;自定义警告钩子的用法与场景 【免费下载链接】turbovec A vector index built on TurboQuant, written in Rust with Python bindings 项目地址: https://gitcode.com/GitHub_Trending/tu/turbovec turbovec 是一个基于 TurboQua…

作者头像 李华
网站建设 2026/8/31 12:58:38

C++11跨平台异步网络库:从Reactor到游戏服务器的高性能实践

简介&#xff1a;这是一套面向网络游戏开发者的C11异步多线程跨平台网络库&#xff0c;专为Linux与Windows双环境设计&#xff0c;适用于毕设、课程设计、工程实训及中小型网游服务端原型开发&#xff0c;兼顾初学者入门与进阶者架构实践。资源共135个文件&#xff0c;涵盖58个…

作者头像 李华
网站建设 2026/8/31 12:58:26

HAMP-LIC:Hessian感知的混合精度量化,解决图像压缩模型部署难题

图像压缩模型近年来在率失真性能上已经明显超越传统编码标准&#xff0c;但“能跑实验”和“能落地部署”之间还存在一条不小的鸿沟。端到端模型的编码端、解码端都包含大量浮点算子&#xff0c;即使训练好的模型精度很高&#xff0c;一旦直接做后训练量化&#xff0c;潜在特征…

作者头像 李华
网站建设 2026/8/31 12:56:33

基于MATLAB的四自由度齿轮动力学振动模型仿真与实现

简介&#xff1a;本资源是一套完整的四自由度齿轮系统动力学振动建模与仿真解决方案&#xff0c;面向机械工程、车辆工程及自动化等专业的本科生毕业设计、课程设计与科研项目开发需求&#xff0c;聚焦齿轮啮合过程中的非线性振动特性分析。压缩包共20个文件&#xff08;63KB&a…

作者头像 李华
网站建设 2026/8/31 12:53:56

Umi-OCR:免费离线 OCR 工具,批量识别截图、图片与 PDF 扫描件

Umi-OCR&#xff1a;免费离线 OCR 工具&#xff0c;批量识别截图、图片与 PDF 扫描件 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片&#xff0c;PDF文档识别&#xff0c;排除水印/页眉页脚&#xff0c;扫描/生成二…

作者头像 李华