本系列定位:零基础可上手、工程向、可落地实操的 AI Agent 进阶教程。拒绝空洞理论,只讲看得懂、画得出图、写得出代码、跑得出效果、能直接用到项目里的内容。
1. 开篇导读
这章解决什么问题?
很多开发者第一次听到 AI Agent 这个词时,脑子里会蹦出一堆概念:大模型、Function Call、RAG、ReAct、Tool Use、AutoGPT......看着很高级,但一问:
- Agent 到底和 ChatGPT 对话框有什么区别?
- 为什么我的大模型回答不了实时问题?
- 企业项目里到底什么时候该用 Agent,什么时候不该用?
这章就是要把这些迷雾拨开。我们不讲论文,不堆术语,就用你日常写代码的经验,把 AI Agent 的本质 一次性讲清楚。
学完这章你能掌握什么?
- 用一个生活化类比,彻底理解 Agent 和传统程序、大模型对话的区别。
- 画出 Agent 的核心执行链路图。
- 写出一个可运行的最小 Agent Demo(TypeScript,几十行代码)。
- 知道企业生产环境中,Agent 最容易踩的几个坑。
适合什么场景?
- 企业内部的智能客服、智能表单填写助手。
- 能调用内部 API 的私有部署 Agent。
- 本地运行的代码助手、数据分析 Agent。
- 网页端集成大模型 + 工具调用的前端 Agent。
2. 核心原理通俗讲解
2.1 一句话定义:Agent 是个能自己动手的"智能员工"
你可以把 AI 系统分成三个段位,越往后越"像人":
| 段位 | 角色 | 特点 | 例子 |
|---|---|---|---|
| 青铜 | 传统程序 | 你写死所有 if/else,它按部就班执行 | 一个查询数据库的 REST 接口 |
| 白银 | 大模型对话 | 能聊天、能总结、能写代码,但只动口不动手 | ChatGPT 网页聊天 |
| 黄金 | AI Agent | 有大模型的脑子,还能自己查资料、调接口、用工具、做判断 | 能自动查天气、算数据、填表单的智能助手 |
一句话:Agent = 大模型(脑子)+ 工具(手脚)+ 记忆(经验)+ 循环判断(思考)。
2.2 类比:传统程序、大模型、Agent 的区别
场景:用户问"今天北京天气怎么样,适合穿短袖吗?"
传统程序版:
js
function answer(city) {
const weather = queryWeatherAPI(city); // 必须提前写好
return weather.temperature > 25 ? "适合短袖" : "不适合";
}
你提前定义了城市、温度、规则。用户换个问法,比如"北京今天冷不冷",程序可能就傻了,因为它只认识固定的输入格式。
大模型版:
大模型可以回答"一般 25℃以上适合短袖",但它不知道今天的真实天气。它的知识有截止日期,也没法主动联网查数据。
Agent 版:
Agent 接收到问题后,会先思考:"用户问的是北京今天天气,我需要用天气工具查一下。"
然后它自动调用天气 API,拿到"27℃,晴天",再结合自己的常识判断:"适合穿短袖。"
你看到区别了吗?
- 传统程序:人把所有分支都写死。
- 大模型:脑子聪明,但没有手脚。
- Agent:有脑子,有手脚,能自己决定什么时候动手。
2.3 Agent 的四大核心零件
所有 Agent,不管名字叫 LangChain、AutoGPT、Claude Code 还是 Codex,底层都逃不开这四个零件:
零件 1:大脑(LLM)
这就是大模型,比如 GPT-4、Claude、Qwen、Llama。它的作用是理解用户意图、做推理、决定下一步。
你可以把它理解成团队里的"产品助理":它很聪明,但不会写代码、不会查数据库,只会告诉你"该干嘛"。
零件 2:工具(Tools)
工具就是 Agent 的"手脚"。常见的工具有:
- 查询天气 API
- 查数据库
- 调用搜索引擎
- 执行代码
- 调用企业内部接口
- 读写文件
每个工具都要告诉模型:我能干什么、需要什么参数、返回什么结果。
零件 3:记忆(Memory)
人不能聊一句忘一句,Agent 也一样。记忆分两种:
- 短期记忆:当前这轮对话的上下文,比如用户前面说"查北京天气",后面问"那上海呢",Agent 要知道"那"指的是天气。
- 长期记忆:跨会话保存的用户偏好、历史记录,比如"这个用户习惯用摄氏度"。
零件 4:执行循环(Loop)
这是 Agent 的灵魂。它的工作流程不是一问一答,而是一个思考 → 行动 → 观察 → 再思考的循环:
text
1. 用户输入问题
2. Agent 把问题和工具说明一起喂给大模型
3. 大模型决定:直接回答 / 调用某个工具
4. 如果调用工具,Agent 执行工具,拿到结果(Observation)
5. 把结果再喂给大模型,让它继续判断
6. 直到得出最终答案,输出给用户
2.4 不这么做会有什么坑?
很多开发者第一次做 Agent,会直接问大模型:"帮我查一下北京天气。"
然后大模型回答:"北京今天多云,25℃。"
但这个答案是编出来的。因为大模型没有实时数据,它只是在"猜测"。
这就是幻觉问题的一个缩影。Agent 的核心价值,就是给大模型装上"手脚",让它在需要真实数据的时候,主动出去查,而不是瞎编。
2.5 传统代码 VS Agent 智能代码
| 对比项 | 传统代码 | Agent 智能代码 |
|---|---|---|
| 决策逻辑 | if/else 写死 | 大模型动态推理 |
| 输入理解 | 必须固定格式 | 支持自然语言, Loose Coupling |
| 外部数据 | 程序自己调 | 大模型决定什么时候调 |
| 应对变化 | 改代码 | 改工具描述或提示词 |
| 出错处理 | 抛异常 | 大模型可重试、可解释 |
注意:Agent 不是替代传统代码,而是在传统代码外面包一层智能决策层。底层该调 API 还是调 API,该查数据库还是查数据库,只是把"什么时候调、调哪个"的决定权交给大模型。
3. 架构流程图 / 原理图
3.1 Agent 整体执行链路图
3.2 核心模块依赖关系
3.3 用户输入 → 模型 → 工具 → 结果全流程
4. 手把手实操代码(可直接运行)
下面是一个最简 Agent Demo,纯 TypeScript,不依赖任何第三方库(除了 Node.js 本身)。复制即可跑通,用来理解 Agent 的"思考-行动-观察"循环。
真实项目中,你只需要把
mockLLM替换成 OpenAI / Claude / 通义千问的 API 调用即可。
4.1 代码:simple-agent.ts
typescript
// simple-agent.ts
// 一个最简 AI Agent:能查天气、做计算,或直接聊天
// ================== 1. 定义工具类型 ==================
type Tool = {
name: string; // 工具英文名,模型靠这个识别
description: string; // 工具是干嘛的,告诉模型
parameters: Record<string, string>; // 需要哪些参数
execute: (args: any) => Promise<string>; // 执行逻辑
};
// ================== 2. 注册工具(Agent 的手脚) ==================
const tools: Tool[] = [
{
name: "get_weather",
description: "查询指定城市的当前天气,返回天气描述和温度",
parameters: { city: "城市名称,比如北京、上海" },
execute: async (args: { city: string }) => {
// 真实环境里这里调用天气 API;demo 用 hardcode 数据
const mockDB: Record<string, string> = {
北京: "晴天,27℃",
上海: "多云,29℃",
广州: "雷阵雨,31℃",
};
return mockDB[args.city] ?? "暂不支持该城市";
},
},
{
name: "calculate",
description: "执行数学计算表达式,比如 12 * 34 + 5",
parameters: { expression: "合法数学表达式字符串" },
execute: async (args: { expression: string }) => {
try {
// 真实生产环境千万别用 eval,要用安全表达式解析器
return String(eval(args.expression));
} catch {
return "表达式格式错误";
}
},
},
];
// ================== 3. 模拟大模型(实际项目替换为真实 API) ==================
/**
* mockLLM 只做一件事:根据用户输入,决定是直接回答,还是调用某个工具。
* 真实项目里,这一段应替换为 OpenAI/Claude/千问等 SDK 的调用。
*/
async function mockLLM(prompt: string): Promise<
| { type: "answer"; content: string }
| { type: "tool"; tool: string; args: Record<string, any> }
> {
const lower = prompt.toLowerCase();
// 识别天气意图
if (lower.includes("天气")) {
const match = prompt.match(/([\u4e00-\u9fa5]+?)(?:的|今天)?天气/);
const city = match?.[1]?.trim() ?? "北京";
return { type: "tool", tool: "get_weather", args: { city } };
}
// 识别计算意图
if (lower.includes("计算") || /[\d\+\-\*\/\(\)]/.test(prompt)) {
// 简单提取算式里的数字和运算符
const expr = prompt.replace(/.*计算\s*/, "").replace(/[^0-9+\-*/().]/g, "");
if (expr) {
return { type: "tool", tool: "calculate", args: { expression: expr } };
}
}
// 默认直接聊天回答
return {
type: "answer",
content: "我是你的 Agent 助手,可以查天气、做数学计算。试试问我:北京天气怎么样?",
};
}
// ================== 4. Agent 执行引擎 ==================
async function runAgent(userInput: string, maxLoops = 3) {
console.log("\n👤 用户:", userInput);
let loopCount = 0;
// 核心循环:思考 -> 行动 -> 观察 -> 再思考
while (loopCount < maxLoops) {
loopCount++;
const decision = await mockLLM(userInput);
if (decision.type === "answer") {
console.log("🤖 Agent:", decision.content);
return;
}
// 需要调用工具
const tool = tools.find((t) => t.name === decision.tool);
if (!tool) {
console.log(`❌ 找不到工具:${decision.tool}`);
return;
}
console.log(`🔧 调用工具:${decision.tool}(${JSON.stringify(decision.args)})`);
const observation = await tool.execute(decision.args);
console.log(`📥 工具返回:${observation}`);
// 把工具结果再喂给模型,让它生成最终回复
userInput = `工具 "${decision.tool}" 返回结果是:"${observation}"。请用中文回答用户的原始问题:"${userInput}"`;
}
console.log("⚠️ 超过最大循环次数,强制结束");
}
// ================== 5. 跑起来 ==================
(async () => {
await runAgent("北京今天天气怎么样?");
await runAgent("帮我计算 12 * 34 + 5");
await runAgent("你好");
})();
4.2 怎么运行?
- 确保你装了 Node.js(建议 v18+)。
- 把上面代码保存为
simple-agent.ts。 - 在命令行执行:
bash
npx tsx simple-agent.ts
如果没有 tsx,也可以先编译:
bash
npx tsc simple-agent.ts --target es2020 --module commonjs
node simple-agent.js
4.3 运行结果长什么样?
text
👤 用户: 北京今天天气怎么样?
🔧 调用工具:get_weather({"city":"北京"})
📥 工具返回:晴天,27℃
🤖 Agent: 北京今天天气晴朗,温度 27℃,比较舒适。
👤 用户: 帮我计算 12 * 34 + 5
🔧 调用工具:calculate({"expression":"12*34+5"})
📥 工具返回:413
🤖 Agent: 12 * 34 + 5 的结果是 413。
👤 用户: 你好
🤖 Agent: 我是你的 Agent 助手,可以查天气、做数学计算。试试问我:北京天气怎么样?
5. 源码核心底层剖析
5.1 这段代码为什么这样设计?
别看代码短,它已经把一个 Agent 的底层骨架都搭好了:
工具注册表(Tool Registry)
typescript
const tools: Tool[] = [...];
这是所有工具的清单。每个工具必须有的三件套:
name:给模型识别的"函数名"。description:告诉模型这个工具干嘛用,越好模型越知道该调谁。parameters:告诉模型我需要什么参数,避免它乱传。
生产中,这个表通常是一个 Map<string, Tool>,方便 O(1) 查找。
决策层(Planning)
typescript
const decision = await mockLLM(userInput);
这一步是 Agent 的"大脑"。大模型根据用户输入 + 工具说明书,决定是回答还是调工具。
真实项目里,这一段要把工具描述拼进提示词,格式类似:
text
你是一个智能助手,可以使用以下工具:
工具名:get_weather
参数:{ "city": "string" }
说明:查询指定城市天气
请判断:用户的输入是否需要调用工具?如果需要,输出 JSON:
{ "type": "tool", "tool": "get_weather", "args": { "city": "北京" } }
如果不需要,输出 JSON:
{ "type": "answer", "content": "你的回答" }
这就是Function Call / Tool Use的雏形,下一章会展开讲。
执行循环(Execution Loop)
typescript
while (loopCount < maxLoops) {
const decision = await mockLLM(userInput);
if (decision.type === "answer") return;
const observation = await tool.execute(decision.args);
userInput = `工具返回...请回答...`;
}
这个循环是 Agent 能"多步推理"的关键。
- 第一次进循环:问题是用户的原始问题。
- 第二次进循环:问题是"工具已经返回了结果,请你组织语言回答"。
- 如果结果还不够,还可以继续调下一个工具。
maxLoops 是防止死循环的安全阀,生产中必须加。
5.2 企业级框架的同源设计思想
你现在看到的这个骨架,和 Claude Code、OpenAI Codex、LangChain 这些框架的底层思路是一模一样的:
| 组件 | 我们这个 Demo | Claude Code / Codex / LangChain |
|---|---|---|
| 大脑 | mockLLM |
Claude / GPT / 自定义模型 |
| 工具 | tools: Tool[] |
tools / mcp 工具集 |
| 执行循环 | while 循环 |
AgentExecutor / Runner |
| 记忆 | 还没加 | ConversationBufferMemory |
| 安全阀 | maxLoops |
maxIterations |
区别只在于:
- 大厂框架把工具调用、错误处理、流式输出、上下文管理都封装好了。
- 我们这个 Demo 把这些暴露了,方便你理解"原来框架底层就这样"。
5.3 真实坑点解析
坑点 1:无限循环
如果模型一直判断"还需要再查一个工具",Agent 就会死循环。解决方案:
- 设置
maxLoops上限。 - 记录已经调用过的工具,避免重复调用同一个工具。
- 给模型明确的终止条件,比如"当你觉得可以直接回答时,必须返回 type=answer"。
坑点 2:工具找不到
模型可能返回一个你根本没注册的工具名。解决方案:
- strict 校验:工具名必须在
tools列表里。 - 捕获异常后,把"没有这个工具"的信息反馈给模型,让它重试。
坑点 3:参数格式错误
模型可能传 { city: " Beijing " } 或漏传参数。解决方案:
- 每个工具做参数校验(Zod / JSON Schema)。
- 校验失败后,把错误信息送回模型,让它修正。
坑点 4:上下文丢失
Demo 里我们只把 userInput 替换成工具结果,没有把原始问题保留下来。复杂场景下,模型会忘记用户最初想问什么。解决方案:
- 维护一个
messages数组,把用户问题、模型决策、工具结果全部放进去。 - 每次调用模型都带上完整历史。
6. 常见问题 & 生产踩坑总结
Q1:Agent 和 Chatbot 到底有什么区别?
Chatbot 只能"说",Agent 能"做"。Chatbot 大公司模型回答基于训练数据;Agent 可以通过工具拿到实时数据、执行实际动作。
Q2:我的 Agent 总是乱调用工具怎么办?
很可能是工具描述写得不清楚。模型是靠 description 判断的,描述要像 API 文档一样明确:"这个工具什么时候用、需要什么参数、返回什么"。
Q3:工具调用失败了,Agent 直接崩溃?
不要把工具执行异常直接抛给用户。应该:
- try/catch 捕获异常。
- 把错误信息格式化后喂给模型。
- 让模型决定重试、换工具、还是向用户道歉。
Q4:Agent 回答慢,每次都要等很久?
这是 LLM 推理 + 工具调用串行导致的。优化方向:
- 使用流式输出(SSE),先把"我在查天气"的提示给用户。
- 多个独立工具 parallel 执行。
- 给工具加超时控制。
Q5:模型编造数据怎么办?
让模型在需要准确数据时必须调用工具,而不是凭记忆回答。系统提示词里明确:"如果问题涉及实时信息,你必须调用工具,不能直接回答。"
Q6:Agent 应该放在前端还是后端?
强烈建议放后端 或独立的 Agent 服务:
- 安全性:API Key 不能暴露给前端。
- 可控性:工具调用、超时、限流、日志都在服务端好管理。
- 可扩展性:多个前端可以共用同一个 Agent 服务。