Claude opus4.6生成的AI Agent 深度学习指南
从”会调 SDK”到”真正理解 Agent”的完整学习路径
目录
- Agent 的本质
- 手写 Agent 循环
- Prompt Engineering for Agents
- 工具设计的学问
- 核心论文与思维模式
- 记忆与上下文管理
- 多 Agent 编排
- 可靠性工程
- 评估 (Evals)
- 实战项目建议
- 推荐资源
1. Agent 的本质
一句话定义
Agent = LLM + 工具调用 + 循环
没了。所有框架(Vercel AI SDK、LangChain、CrewAI)封装的都是这个东西。
伪代码
function agent(userMessage, tools, maxSteps): messages = [systemPrompt, userMessage]
for step in 1..maxSteps: response = LLM(messages, tools)
if response.hasToolCalls: for toolCall in response.toolCalls: result = execute(toolCall.name, toolCall.input) messages.append(toolCall) // 记录模型想调什么 messages.append(toolResult) // 记录工具返回了什么 else: return response.text // 模型不再调工具,输出最终回答
return "达到最大步数,停止"关键洞察
模型并不”知道”自己在当 agent。 它只是在做一件事:根据当前的 messages 数组,预测下一个 token。当 messages 里包含工具定义时,模型可能生成工具调用格式的输出;当工具结果被追加到 messages 后,模型基于新的上下文继续生成。
所谓的”自主决策”,其实是:
- 模型看到了工具的 description,知道有哪些能力可用
- 模型根据用户问题和已有的工具结果,判断还需不需要调工具
- 如果不需要了,就直接生成文字回答
没有任何魔法。
2. 手写 Agent 循环(不用 SDK)
这是最重要的一步。不依赖任何框架,直接用 HTTP 请求实现 agent。
2.1 基础版:单次工具调用
// 手写 agent 循环 - 理解 SDK 背后在做什么
import "dotenv/config";
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;const BASE_URL = "https://api.deepseek.com/v1";
// ----- 定义工具 -----const toolDefinitions = [ { type: "function" as const, function: { name: "getWeather", description: "获取指定城市的当前天气信息", parameters: { type: "object", properties: { city: { type: "string", description: "城市名称" }, }, required: ["city"], }, }, }, { type: "function" as const, function: { name: "calculate", description: "执行数学计算", parameters: { type: "object", properties: { expression: { type: "string", description: "数学表达式" }, }, required: ["expression"], }, }, },];
// ----- 工具实现 -----const toolImplementations: Record<string, (args: any) => any> = { getWeather: ({ city }: { city: string }) => { const data: Record<string, any> = { 北京: { temp: 5, condition: "晴", humidity: 30 }, 东京: { temp: 8, condition: "小雨", humidity: 75 }, }; return data[city] ?? { temp: 20, condition: "未知", humidity: 50 }; }, calculate: ({ expression }: { expression: string }) => { const sanitized = expression.replace(/[^0-9+\-*/().% ]/g, ""); return { result: new Function(`return (${sanitized})`)() }; },};
// ----- 核心:Agent 循环 -----async function callLLM(messages: any[]) { const response = await fetch(`${BASE_URL}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${DEEPSEEK_API_KEY}`, }, body: JSON.stringify({ model: "deepseek-chat", messages, tools: toolDefinitions, }), }); const data = await response.json(); return data.choices[0].message;}
async function runAgent(userMessage: string, maxSteps = 10) { console.log(`\n🧑 用户: ${userMessage}\n`);
// 这就是 agent 的全部状态 —— 一个 messages 数组 const messages: any[] = [ { role: "system", content: "你是一个有用的助手,请用中文回答。" }, { role: "user", content: userMessage }, ];
for (let step = 1; step <= maxSteps; step++) { // 第一步:调用 LLM const assistantMessage = await callLLM(messages); messages.push(assistantMessage);
// 第二步:检查是否有工具调用 if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) { // 没有工具调用 → 模型认为可以直接回答了 console.log(`🤖 助手: ${assistantMessage.content}`); return; }
// 第三步:执行每个工具调用 for (const toolCall of assistantMessage.tool_calls) { const fnName = toolCall.function.name; const fnArgs = JSON.parse(toolCall.function.arguments);
console.log(`🔧 [Step ${step}] ${fnName}(${JSON.stringify(fnArgs)})`);
const result = toolImplementations[fnName](fnArgs); console.log(` → ${JSON.stringify(result)}`);
// 第四步:把工具结果追加到 messages(这是关键!) messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result), }); }
// 然后回到循环顶部,带着工具结果再次调用 LLM }
console.log("⚠️ 达到最大步数限制");}
// 运行runAgent("查一下北京和东京的天气,算一下温差");运行这段代码,你会清楚看到:
- 每次 LLM 调用的输入是整个 messages 数组(包括之前的工具调用和结果)
- **模型的”记忆”**就是这个数组,没有任何其他状态
- SDK 做的事情就是帮你管理这个数组 + 执行这个循环
2.2 动手练习
练习 1: 运行上面的代码,在每次 callLLM 前打印完整的 messages 数组,观察它是如何增长的。
练习 2: 故意把某个工具的 description 写得很模糊,观察模型是否还能正确选择工具。
练习 3: 把 maxSteps 设为 1,观察模型在只能执行一步时会怎么做。
3. Prompt Engineering for Agents
普通聊天的 prompt engineering 和 agent 场景有本质区别。聊天只需要模型回答好,agent 还需要模型正确地选择和调用工具。
3.1 System Prompt 的要素
你是一个 [角色描述]。
## 能力你可以使用以下工具:- getWeather:查询天气。当用户问到天气相关问题时使用。- calculate:数学计算。当需要精确计算时使用,不要自己心算。- searchKnowledge:搜索知识库。当用户问到你不确定的事实时使用。
## 行为准则1. 先思考是否需要使用工具,不要盲目调用2. 如果一个工具的返回结果不够,可以再调用其他工具补充3. 得到所有需要的信息后,给出完整的中文回答4. 如果工具返回了错误或空结果,告诉用户而不是编造答案
## 限制- 不要编造工具不存在的功能- 不要在一次回答中调用超过 5 次工具3.2 常见问题与解法
| 问题 | 现象 | 解法 |
|---|---|---|
| 模型不调工具 | 用户问天气,模型直接编一个回答 | 在 prompt 里强调”必须使用工具获取实时信息,不要猜测” |
| 模型乱调工具 | 用户随便聊天,模型也去调工具 | 在 prompt 里说明”只在需要时使用工具” |
| 参数错误 | 模型传了错误的参数格式 | 改善工具的 description 和参数 description |
| 无限循环 | 模型反复调用同一个工具 | 设置 maxSteps + 在 prompt 里说”不要重复调用相同工具” |
| 忽略工具结果 | 工具返回了数据但模型没用 | 在 prompt 里强调”基于工具返回的实际数据回答” |
3.3 高级技巧:思维链 (Chain of Thought)
## 回答流程在每次决定是否调用工具前,先用 <thinking> 标签思考:1. 用户想知道什么?2. 我目前已经知道哪些信息?3. 还缺少什么信息?需要调用哪个工具?4. 如果信息足够了,直接回答。这会让模型的工具选择更加可控和可解释。
4. 工具设计的学问
工具设计直接决定 agent 的能力上限。模型是通过工具的 description 和 schema 来理解工具的,而不是通过代码。
4.1 好工具 vs 差工具
差的设计:
tool({ description: "数据库操作", // 太模糊,模型不知道什么时候该用 inputSchema: z.object({ sql: z.string(), // 让模型写 SQL?灾难 }),})好的设计:
tool({ description: "根据用户ID查询用户信息,返回姓名、邮箱、注册时间", inputSchema: z.object({ userId: z.string().describe("用户的唯一标识符,格式如 user_123"), }),})4.2 工具设计原则
原则 1:单一职责
❌ processData(action: "create" | "read" | "update" | "delete", ...)✅ createUser(name, email)✅ getUser(userId)✅ updateUser(userId, fields)✅ deleteUser(userId)原则 2:description 是给模型看的文档
- 说明什么时候应该用这个工具
- 说明输入参数的含义和格式
- 说明返回值包含什么信息
原则 3:参数设计要降低模型犯错概率
❌ date: z.string() // 模型可能给出 "明天"、"2024-01-01"、"Jan 1" 等各种格式✅ year: z.number(), month: z.number(), day: z.number() // 结构化,不容易出错原则 4:返回值要有足够的上下文
// 差:模型不知道单位,可能误解❌ return { temp: 5 }
// 好:自包含,模型能正确理解和转述✅ return { temp: 5, unit: "°C", city: "北京", condition: "晴" }4.3 工具粒度的权衡
| 粒度 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 细粒度(多个小工具) | 灵活组合,容易测试 | 模型需要更多步骤,可能选错 | 通用 agent |
| 粗粒度(少量大工具) | 步骤少,不容易出错 | 灵活性差,功能写死 | 特定业务流程 |
5. 核心论文与思维模式
5.1 ReAct (Reasoning + Acting)
论文: ReAct: Synergizing Reasoning and Acting in Language Models (2022)
这是当前 agent 工具调用模式的理论基础。
核心思想:让模型交替进行「推理」和「行动」。
用户:北京和东京哪个更热?
Thought: 我需要知道两个城市的温度才能比较。先查北京。Action: getWeather(city="北京")Observation: {temp: 5, condition: "晴"}
Thought: 北京是 5°C。现在查东京。Action: getWeather(city="东京")Observation: {temp: 8, condition: "小雨"}
Thought: 北京 5°C,东京 8°C,东京更热。我有足够信息了。Answer: 东京(8°C)比北京(5°C)更热,温差 3°C。为什么重要: 目前所有主流 agent 框架的工具调用循环,本质上都是 ReAct 模式的实现。
5.2 Plan-and-Execute
与 ReAct 的「走一步看一步」不同,Plan-and-Execute 是「先规划完,再执行」。
用户:帮我调研 React 和 Vue 的区别,写一份对比报告
Plan: 1. 搜索 React 的核心特点 2. 搜索 Vue 的核心特点 3. 搜索两者的性能对比数据 4. 搜索社区生态对比 5. 综合以上信息撰写报告
Execute: Step 1: search("React 核心特点 2024") → ... Step 2: search("Vue 核心特点 2024") → ... ...适用场景: 复杂任务、多步骤任务、需要全局规划的任务。
5.3 Reflexion(自我反思)
让 agent 在失败后反思原因,并改进下一次尝试。
Attempt 1: Action: search("Python 排序") → 结果不相关 Reflection: 搜索太宽泛,应该具体到算法名称
Attempt 2: Action: search("Python quicksort 实现") → 得到有效结果5.4 必读论文清单
| 论文 | 年份 | 核心贡献 |
|---|---|---|
| ReAct | 2022 | 推理+行动交替,奠定 agent 基础模式 |
| Toolformer | 2023 | 让模型自己学会什么时候调工具 |
| Reflexion | 2023 | 自我反思与迭代改进 |
| Plan-and-Execute | 2023 | 先规划后执行的 agent 架构 |
| LATS (Language Agent Tree Search) | 2023 | 将树搜索引入 agent 决策 |
| Voyager | 2023 | Minecraft 里的自主 agent,展示终身学习能力 |
6. 记忆与上下文管理
6.1 问题:Context Window 是有限的
DeepSeek 的 context window 大约 64K-128K tokens。看似很大,但在 agent 场景下:
System Prompt: ~500 tokens工具定义 (3 个工具): ~800 tokens用户消息: ~100 tokens每轮工具调用+结果: ~300-1000 tokens─────────────────────────10 轮交互后: ~5000-10000 tokens如果是长期运行的 agent(比如编程助手),几十轮交互后就会逼近上限。
6.2 解法一:对话压缩
// 当 messages 太长时,用另一个 LLM 调用来压缩历史async function compressHistory(messages: Message[]): Promise<Message[]> { const summary = await generateText({ model: deepseek("deepseek-chat"), prompt: `总结以下对话的关键信息,保留所有重要的事实和决策: ${JSON.stringify(messages)}`, });
return [ messages[0], // 保留 system prompt { role: "system", content: `之前的对话摘要:${summary.text}` }, ...messages.slice(-4), // 保留最近 4 条消息 ];}6.3 解法二:RAG (检索增强生成)
不把所有信息塞进 context,而是用向量数据库存储,需要时检索。
用户提问 → 向量搜索相关文档 → 把相关片段塞进 context → LLM 生成回答// 概念示意const relevantDocs = await vectorDB.search(userQuery, { topK: 5 });const context = relevantDocs.map((d) => d.content).join("\n");
const response = await generateText({ model: deepseek("deepseek-chat"), system: `基于以下参考资料回答问题:\n${context}`, prompt: userQuery,});6.4 记忆的三个层次
| 层次 | 实现方式 | 生命周期 | 示例 |
|---|---|---|---|
| 工作记忆 | messages 数组 | 单次对话 | 当前对话的上下文 |
| 短期记忆 | 数据库/文件 | 跨对话(天/周) | 用户偏好、最近的任务 |
| 长期记忆 | 向量数据库 | 永久 | 知识库、历史决策 |
7. 多 Agent 编排
单个 agent 有能力上限。当任务复杂时,需要多个 agent 协作。
7.1 模式一:顺序链 (Pipeline)
Planner Agent → Executor Agent → Reviewer Agent 规划 执行 检验// 概念示意const plan = await plannerAgent.generateText({ prompt: "用户想要一个 Todo API,请制定实现计划",});
const code = await executorAgent.generateText({ prompt: `按照以下计划实现代码:${plan.text}`,});
const review = await reviewerAgent.generateText({ prompt: `审查以下代码是否符合计划:\n计划:${plan.text}\n代码:${code.text}`,});7.2 模式二:分层委托 (Delegation)
一个 “管理者” agent 把子任务分配给专门的 agent。
Manager Agent / | \ Search Agent Code Agent Test Agent实现方式:把「调用其他 agent」作为管理者的工具。
const managerTools = { delegateToSearchAgent: tool({ description: "将搜索任务委托给搜索专家", inputSchema: z.object({ query: z.string() }), execute: async ({ query }) => { const result = await searchAgent.generateText({ prompt: query }); return result.text; }, }), delegateToCodeAgent: tool({ description: "将编码任务委托给编码专家", inputSchema: z.object({ task: z.string() }), execute: async ({ task }) => { const result = await codeAgent.generateText({ prompt: task }); return result.text; }, }),};7.3 模式三:辩论/共识 (Debate)
多个 agent 从不同角度分析同一个问题,最后综合。
Agent A (乐观视角) ──┐ Agent B (悲观视角) ──┤→ Synthesizer Agent → 最终结论 Agent C (技术视角) ──┘7.4 模式四:自主协作 (Swarm)
多个 agent 共享一个任务池,自主领取和完成任务。
Task Pool: [task1, task2, task3, task4, task5] ↑ Agent A (领 task1) | Agent B (领 task2) | Agent C (领 task3)7.5 选择哪种模式?
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 线性流程(写代码→测试→部署) | Pipeline | 每步依赖上一步结果 |
| 复杂项目(多模块并行开发) | Delegation / Swarm | 可并行,需要协调 |
| 需要深思熟虑的决策 | Debate | 多角度避免盲点 |
| 明确的子任务分工 | Delegation | 管理者统一调度 |
8. 可靠性工程
这是 agent 从 demo 到生产的最大鸿沟。
8.1 模型会犯的错
| 错误类型 | 示例 | 防御措施 |
|---|---|---|
| 幻觉 | 编造工具不存在的参数 | Zod schema 验证输入 |
| 格式错误 | 工具参数不是合法 JSON | try-catch + 重试 |
| 选错工具 | 该搜索时去算数 | 改善工具 description |
| 忽略错误 | 工具报错但模型当没看见 | 在 prompt 里强调错误处理 |
| 无限循环 | 反复调同一个工具 | maxSteps + 重复检测 |
| 过度调用 | 简单问题也调一堆工具 | 在 prompt 里引导”先思考是否需要” |
8.2 防御性编程
// 工具执行的 wrapperasync function safeExecute( toolName: string, args: unknown, impl: Function): Promise<string> { try { const result = await Promise.race([ impl(args), new Promise((_, reject) => setTimeout(() => reject(new Error("工具执行超时")), 10000) ), ]); return JSON.stringify(result); } catch (error) { // 不要让工具错误崩掉整个 agent // 而是把错误信息返回给模型,让它决定怎么办 return JSON.stringify({ error: true, message: `工具 ${toolName} 执行失败: ${error}`, }); }}8.3 输出校验
// 用 Zod 校验模型的最终输出import { z } from "zod/v4";import { generateObject } from "ai";
// generateObject 强制模型输出符合 schema 的结构化数据const { object } = await generateObject({ model: deepseek("deepseek-chat"), schema: z.object({ answer: z.string(), confidence: z.number().min(0).max(1), sources: z.array(z.string()), }), prompt: "...",});// object 一定是 { answer: string, confidence: number, sources: string[] }// 不符合 schema 的输出会被拒绝并重试8.4 可靠性 Checklist
- 所有工具都有超时处理
- 所有工具都有 try-catch
- 设置了合理的 maxSteps
- 关键输出用 schema 校验
- 有重试机制(API 偶尔会 500)
- 有日志记录每一步的输入输出
- 敏感操作(删除、支付)需要人工确认
9. 评估 (Evals)
Agent 的输出是不确定的。同一个问题跑两次可能得到不同的工具调用顺序和最终回答。怎么评估好不好?
9.1 评估维度
| 维度 | 衡量什么 | 评估方法 |
|---|---|---|
| 正确性 | 最终答案是否正确 | 人工标注 + 自动对比 |
| 工具效率 | 是否用最少的步骤完成 | 统计平均步骤数 |
| 工具准确性 | 是否选了正确的工具 | 与预期工具序列对比 |
| 鲁棒性 | 工具出错时能否恢复 | 故意注入错误,观察表现 |
| 延迟 | 端到端耗时 | 计时 |
| 成本 | token 消耗量 | 统计 token 用量 |
9.2 简单的评估框架
interface TestCase { input: string; expectedToolCalls?: string[]; // 期望调用的工具名列表 expectedOutputContains?: string[]; // 期望输出包含的关键词 maxStepsAllowed?: number; // 期望的最大步骤数}
const testCases: TestCase[] = [ { input: "北京天气怎么样", expectedToolCalls: ["getWeather"], expectedOutputContains: ["北京", "°C"], maxStepsAllowed: 2, }, { input: "北京和东京的温差", expectedToolCalls: ["getWeather", "getWeather", "calculate"], expectedOutputContains: ["温差", "3"], maxStepsAllowed: 5, },];
async function runEval(testCases: TestCase[]) { let passed = 0; for (const tc of testCases) { const { text, steps } = await runAgent(tc.input); const toolsCalled = steps.flatMap((s) => s.toolCalls.map((c) => c.toolName));
const toolsMatch = tc.expectedToolCalls ? JSON.stringify(toolsCalled) === JSON.stringify(tc.expectedToolCalls) : true; const outputMatch = tc.expectedOutputContains ? tc.expectedOutputContains.every((kw) => text.includes(kw)) : true; const stepsOk = tc.maxStepsAllowed ? steps.length <= tc.maxStepsAllowed : true;
if (toolsMatch && outputMatch && stepsOk) { passed++; console.log(`✅ PASS: "${tc.input}"`); } else { console.log(`❌ FAIL: "${tc.input}"`); if (!toolsMatch) console.log(` 工具: 期望 ${tc.expectedToolCalls}, 实际 ${toolsCalled}`); if (!outputMatch) console.log(` 输出缺少关键词`); if (!stepsOk) console.log(` 步骤数: ${steps.length} > ${tc.maxStepsAllowed}`); } } console.log(`\n结果: ${passed}/${testCases.length} 通过`);}9.3 LLM-as-Judge
用另一个 LLM 来评判 agent 的输出质量。
const judgment = await generateText({ model: deepseek("deepseek-chat"), prompt: `你是一个评判者。请评估以下 AI 助手的回答质量。
用户问题:${userQuestion}助手回答:${agentAnswer}
评分标准(1-5 分):1. 准确性:回答是否基于工具返回的真实数据?2. 完整性:是否回答了用户的所有问题?3. 简洁性:是否有多余的废话?
请给出评分和理由。`,});10. 实战项目建议
按难度递增排列。每个项目都会遇到不同的真实问题。
项目 1:个人知识库问答 Agent
做什么: 读取本地 Markdown 文件,用 RAG 回答关于这些文件的问题。
你会学到:
- 文本分割 (chunking)
- 向量嵌入 (embedding)
- 相似度搜索
- 如何把检索结果融入 prompt
技术栈建议: AI SDK + 本地向量库(如 vectra 或 orama)
项目 2:命令行编程助手
做什么: 一个能读写文件、执行命令的 CLI agent(类似简化版 Claude Code)。
你会学到:
- 危险操作的权限控制(不能让 agent 随便 rm -rf)
- 文件系统工具的设计
- 命令执行的沙箱
- 错误恢复
工具示例:
readFile(path) → 读文件writeFile(path, content) → 写文件runCommand(cmd) → 执行命令(需要确认)listFiles(dir) → 列目录项目 3:多 Agent 研究助手
做什么: 给定一个主题,自动搜索、阅读、总结、生成研究报告。
你会学到:
- 多 agent 编排
- 真实 API 调用(搜索引擎 API)
- 长文本处理
- 输出结构化
架构:
Planner → [Searcher, Searcher, Searcher] (并行) → Synthesizer → Writer项目 4:自我改进的代码生成 Agent
做什么: 给需求 → 生成代码 → 运行测试 → 如果失败则分析错误 → 修改代码 → 重试。
你会学到:
- Reflexion 模式
- 代码执行沙箱
- 测试驱动的 agent 循环
- 失败分析与恢复
11. 推荐资源
论文
| 论文 | 链接 |
|---|---|
| ReAct | https://arxiv.org/abs/2210.03629 |
| Toolformer | https://arxiv.org/abs/2302.04761 |
| Reflexion | https://arxiv.org/abs/2303.11366 |
| Voyager | https://arxiv.org/abs/2305.16291 |
| LATS | https://arxiv.org/abs/2310.04406 |
| A Survey on LLM-based Agents | https://arxiv.org/abs/2308.11432 |
文档
| 资源 | 链接 |
|---|---|
| Vercel AI SDK 官方文档 | https://ai-sdk.dev |
| Vercel AI SDK - Agents 章节 | https://ai-sdk.dev/docs/foundations/agents |
| OpenAI Function Calling 指南 | https://platform.openai.com/docs/guides/function-calling |
| DeepSeek API 文档 | https://platform.deepseek.com/api-docs |
| Anthropic Tool Use 指南 | https://docs.anthropic.com/en/docs/build-with-claude/tool-use |
开源项目(值得阅读源码)
| 项目 | 为什么读它 |
|---|---|
| Vercel AI SDK (vercel/ai) | 理解 agent 循环的工业级实现 |
| LangChain.js | 理解 chain/agent/memory 的抽象设计 |
| AutoGPT | 早期自主 agent 的经典尝试,学习它的问题和局限 |
| OpenDevin | 开源编程 agent,学习工具设计和沙箱 |
| CrewAI | 多 agent 协作框架,学习编排模式 |
课程与博客
| 资源 | 说明 |
|---|---|
| DeepLearning.AI - AI Agents 系列课程 | Andrew Ng 的短课程,免费 |
| Lilian Weng 的博客 | OpenAI 研究员,写了很多 agent 综述文章 |
| Simon Willison 的博客 | LLM 实践经验,大量真实案例 |
总结
SDK 调用 → 入门(你已经在这里了)手写 agent 循环 → 理解本质工具设计 → 决定 agent 能力上限Prompt 工程 → 决定 agent 稳定性多 agent 编排 → 处理复杂任务可靠性工程 → 从 demo 到生产评估体系 → 量化改进方向最重要的一条建议:做一个解决你自己真实需求的 agent。 在 demo 里一切都很美好,只有真实场景才会暴露真正的挑战。
发现错误或想要改进这篇文章?
在 GitHub 上编辑此页