ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

深入理解 AI Agent |从上下文到工具调用,系统讲透 Agent 工程原理的中文开源书

2026/10/8 12:34:28 拓冰建站 浏览量
深入理解 AI Agent |从上下文到工具调用,系统讲透 Agent 工程原理的中文开源书 1. 为什么“Agent LLM 上下文 工具”值得你亲手跑一遍AI Agent 这个词在 2025 到 2026 年被说得太多但真正落到工程层面很多人还是停留在“调个 API 加个 while 循环”的阶段。我最近在翻一本中文开源书《深入理解 AI Agent》作者李博杰把 Agent 拆成一个非常干净的公式Agent LLM 上下文 工具。这个公式看起来简单但它把“怎么造 Agent”从玄学拉回了工程。LLM 负责推理和生成上下文负责让模型知道当前状态和历史工具负责让模型能对外部世界产生动作。三者缺一不可而且每一块都有明确的职责边界。如果你正在做 AI Agent 相关的开发或者想从“会用框架”进阶到“理解框架内部发生了什么”那这篇文章就是为你写的。我不会只讲概念而是带你从零跑通一个可观测的 Agent 循环你能看到每一轮上下文是怎么拼出来的、工具调用是怎么被解析的、结果是怎么回填的。整个过程你可以在本地复现不需要 GPU只需要一个能调用的 LLM API。这里会用到 TaoToken 作为模型接入层因为它提供了兼容 OpenAI 协议的接口配置简单适合做这种原理验证型的实验。你可以在 https://taotoken.net/api 拿到 API 地址然后在控制台创建 Key。注意这不是广告而是因为我们要跑通一个真实的 Agent 循环必须有一个稳定的模型入口。下面我会把每一步都写清楚包括配置文件、请求体、预期输出和常见报错。先明确一下我们要构建的东西一个最小可观测 Agent它有一个系统提示词、一个用户输入、两个工具比如“查天气”和“算加法”然后循环执行“模型推理 → 解析工具调用 → 执行工具 → 把结果写回上下文 → 再次推理”直到模型不再调用工具。这个循环就是 ReAct 范式的核心。你会看到上下文窗口里每轮多了什么、少了什么以及为什么“上下文工程”是 Agent 真正的竞争力所在。2. 前置准备TaoToken 接入与本地环境搭建在开始写 Agent 循环之前你需要先准备好模型接入。TaoToken 的 API 兼容 OpenAI 的/v1/chat/completions接口所以你可以用任何 OpenAI SDK 来调用。先去 https://taotoken.net/api 了解接口说明然后到控制台创建一个 API Key。创建 Key 的页面在 https://taotoken.net/console/api-keys 记得把 Key 保存到环境变量里不要硬编码在代码中。本地环境只需要 Python 3.10 和openai包。如果你还没有安装执行pip install openai然后设置环境变量。Linux/macOS 下export TAOTOKEN_API_KEY你的Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell$env:TAOTOKEN_API_KEY你的Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api接下来验证一下模型能不能通。写一个最简单的脚本check_model.pyimport os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 只回复两个字通了}], ) print(resp.choices[0].message.content)运行python check_model.py如果输出“通了”说明接入正常。如果报 401检查 Key 是否复制完整如果报连接错误检查 base_url 是否写成了https://taotoken.net/api而不是带/v1的路径。TaoToken 的接口路径已经包含了版本前缀所以 base_url 写https://taotoken.net/api即可SDK 会自动拼接/chat/completions。这里要强调一个细节很多人在配置 OpenAI SDK 时习惯把 base_url 写成https://taotoken.net/api/v1然后发现请求 404。原因是 SDK 内部会再拼一次/chat/completions导致路径变成/api/v1/chat/completions而实际接口是/api/chat/completions。所以 base_url 只写到/api。这个坑我在第一次配置时也踩过后来用 curl 直接测才定位到。另外如果你要用 Claude Code 或者 Cline 这类工具它们的配置方式略有不同。Claude Code 需要设置ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY但 TaoToken 的 Anthropic 兼容端点需要单独确认。对于本文的 Agent 循环实验用 OpenAI SDK 就够了。如果你后续想接入 Coding Plan 做长期编码任务可以到 https://taotoken.net/coding-plan 看具体的套餐和接入方式。环境准备好之后我们进入核心部分手写一个可观测的 Agent 循环。3. 可复制配置手写最小 Agent 循环与上下文窗口验证这一节是全文的核心。我会给你一份完整的 Python 代码它实现了一个带工具调用的 Agent 循环并且每一步都会打印上下文长度和消息列表让你直观看到上下文是怎么增长的。代码可以直接复制运行只需要你替换模型名称如果你用的不是gpt-4o-mini改成你实际可用的模型 ID。先定义两个工具一个查天气一个做加法。工具的描述要写成 JSON Schema因为模型需要根据 schema 来决定是否调用以及传什么参数。import os import json from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) # 工具实现 def get_weather(city: str) - str: fake_db {北京: 晴25°C, 上海: 多云28°C, 深圳: 阵雨30°C} return fake_db.get(city, f{city}暂无数据) def add(a: float, b: float) - str: return str(a b) # 工具 schema tools [ { type: function, function: { name: get_weather, description: 查询指定城市的天气, parameters: { type: object, properties: { city: {type: string, description: 城市名如北京} }, required: [city], }, }, }, { type: function, function: { name: add, description: 计算两个数字之和, parameters: { type: object, properties: { a: {type: number}, b: {type: number}, }, required: [a, b], }, }, }, ] TOOL_MAP {get_weather: get_weather, add: add} def run_agent(user_input: str, max_turns: int 5): messages [ {role: system, content: 你是一个助手可以调用工具。需要天气或加法时请调用对应工具。}, {role: user, content: user_input}, ] for turn in range(max_turns): print(f\n 第 {turn 1} 轮 ) print(f当前上下文消息数{len(messages)}) print(f当前上下文字符数{sum(len(str(m)) for m in messages)}) resp client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, tool_choiceauto, ) msg resp.choices[0].message messages.append(msg) if not msg.tool_calls: print(模型不再调用工具最终回复) print(msg.content) break for call in msg.tool_calls: fn_name call.function.name args json.loads(call.function.arguments) print(f调用工具{fn_name}参数{args}) result TOOL_MAP[fn_name](**args) print(f工具返回{result}) messages.append({ role: tool, tool_call_id: call.id, content: result, }) return messages if __name__ __main__: run_agent(北京天气怎么样另外帮我算 12.5 7.3)运行这段代码你会看到类似下面的输出 第 1 轮 当前上下文消息数2 当前上下文字符数约 120 调用工具get_weather参数{city: 北京} 工具返回晴25°C 调用工具add参数{a: 12.5, b: 7.3} 工具返回19.8 第 2 轮 当前上下文消息数5 当前上下文字符数约 320 模型不再调用工具最终回复 北京今天晴气温 25°C。12.5 7.3 19.8。这个循环就是 Agent 的最小运行单元。你可以清楚地看到第一轮上下文只有系统提示和用户输入模型返回工具调用后我们把 assistant 消息和 tool 结果都追加进 messages第二轮模型基于这些新上下文生成最终回复。上下文窗口从 2 条消息涨到 5 条字符数从 120 涨到 320。这就是“上下文工程”要管理的东西——如果对话轮数很多上下文会迅速膨胀你需要做压缩、摘要或者只保留最近 N 轮。如果你想验证上下文窗口的边界可以把max_turns调大然后让模型反复调用工具观察字符数增长曲线。更严谨的做法是用 tiktoken 计算 token 数但字符数已经能给你直观感受。这里的关键认知是LLM 本身是无状态的它每次只能看到你传给它的 messages 列表。所谓“记忆”不过是你把历史消息重新拼进上下文而已。工具调用也不是模型真的执行了函数而是模型输出了一个结构化的 JSON你的代码解析后去执行再把结果写回上下文。理解这一点你就理解了 Agent 工程的一半。另外如果你用的是 Claude 系列模型工具调用的格式略有不同但思路完全一致。TaoToken 的模型对话页面 https://taotoken.net/models 可以让你快速切换不同模型做对比。我实测下来不同模型对工具 schema 的遵循程度差异很大有些模型会在参数里多塞字段有些会漏掉 required 字段所以生产环境一定要做参数校验。4. 验证请求与成功结果观察上下文增长与工具回填上一节的代码已经能跑通但我想再带你做一次更细致的验证把每一轮的完整 messages 打印成 JSON观察 assistant 消息里的tool_calls字段和 tool 消息里的tool_call_id是怎么对应的。这个对应关系是 Agent 循环里最容易出错的地方之一。修改run_agent在每轮结束后打印json.dumps(messages, ensure_asciiFalse, indent2)。你会看到 assistant 消息长这样{ role: assistant, content: null, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\: \北京\} } } ] }而 tool 消息长这样{ role: tool, tool_call_id: call_abc123, content: 晴25°C }tool_call_id必须和 assistant 消息里的id完全一致否则模型会报错说找不到对应的工具结果。这个错误在并发工具调用时尤其常见——如果你同时调用了两个工具必须为每个tool_call_id都追加一条 tool 消息顺序可以任意但 id 不能错。成功跑通后你可以做一个消融实验把系统提示词删掉看看模型还会不会调用工具。我试过删掉系统提示后模型有时会直接用自己的知识回答天气而不是调用工具。这说明系统提示在“引导工具使用”上有实际作用。另一个实验是只保留最近一轮的 tool 结果把之前的删掉观察模型是否还能正确回答。这些实验能帮你量化上下文各组件的贡献而不是凭感觉说“上下文很重要”。如果你在验证过程中遇到reading choices相关的报错通常是因为响应结构和你预期的不一样。比如某些兼容接口返回的choices为空或者message字段缺失。这时候先打印完整的resp对象确认返回结构。TaoToken 的接口返回格式和 OpenAI 一致所以如果你用 OpenAI SDKresp.choices[0].message是标准路径。如果报local proxy failed检查你的网络环境是否能直连taotoken.net以及 base_url 是否写错。还有一个常见问题是模型返回的arguments不是合法 JSON。有些模型会在 JSON 外面包一层 markdown 代码块比如json ... 。这时候json.loads会失败。解决办法是在解析前做一次清洗去掉代码块标记。这个坑在开源模型上更常见闭源模型一般不会。验证成功后你应该能看到一个完整的 Agent 循环日志用户输入 → 模型推理 → 工具调用 → 工具执行 → 结果回填 → 模型再推理 → 最终回复。这个日志就是可观测性的基础。生产级 Agent 还需要记录每轮的 token 消耗、延迟、工具成功率但原理是一样的。5. 本篇常见错误排查401、工具调用失败与上下文溢出跑 Agent 循环时报错集中在几个地方。我把最常见的列出来并给出排查路径。401 Unauthorized这是 Key 的问题。先确认环境变量TAOTOKEN_API_KEY是否设置成功可以在 Python 里print(os.environ.get(TAOTOKEN_API_KEY)[:8])看前几位。如果 Key 正确检查 base_url 是否写成了https://taotoken.net/api。如果 base_url 写成https://taotoken.net/api/v1有些 SDK 会拼出错误路径导致 401 或 404。另外Key 如果被删除或过期也会 401去控制台重新创建一个即可。local proxy failed这个报错通常出现在你本地设置了 HTTP 代理但代理不可用。检查环境变量HTTP_PROXY和HTTPS_PROXY临时取消它们再试。如果你在公司网络下可能需要配置正确的代理但本文不展开网络配置细节。另一个可能是 DNS 解析问题试试curl https://taotoken.net/api看能否返回。reading choices 报错AttributeError: NoneType object has no attribute choices或者KeyError: choices。这说明响应对象结构不对。先打印resp的原始内容。如果是流式请求choices在 chunk 里不是完整响应。如果你用了streamTrue需要逐块处理。本文的代码没有用流式所以如果你改了流式记得调整解析逻辑。工具调用参数解析失败json.decoder.JSONDecodeError。前面说过模型可能在 arguments 里包 markdown。加一个清洗函数def clean_json(s: str) - str: s s.strip() if s.startswith(): s s.split(\n, 1)[1] s s.rsplit(, 1)[0] return s.strip()然后在json.loads前调用clean_json(call.function.arguments)。上下文溢出当 messages 太长超过模型的最大上下文窗口时接口会返回错误通常是 400 或者提示 token 超限。解决办法是限制历史消息数量或者做摘要压缩。最简单的策略是只保留系统提示 最近 N 轮对话。更复杂的做法是用一个小模型对历史做摘要把摘要作为系统提示的一部分。这就是“上下文工程”要解决的问题。OAuth 相关报错如果你用 Claude Code 或者某些 CLI 工具可能会遇到 OAuth token 过期。这类工具通常有自己的认证流程和 API Key 不同。如果你只是跑本文的 Python 脚本不会遇到 OAuth 问题。但如果你要接入 Claude Code需要确认它的认证方式。TaoToken 的文档页面 https://taotoken.net/doc 有各工具的接入说明遇到 OAuth 报错先去那里对照配置。模型不调用工具有时候模型会忽略 tools 参数直接用自己的知识回答。这通常是因为系统提示不够明确或者tool_choice设置成了none。把tool_choice设为auto或required并在系统提示里明确说“需要天气信息时必须调用 get_weather 工具”。不同模型对工具调用的积极性不同实测下来指令遵循能力强的模型更可靠。排查的核心思路是先确认网络和认证没问题再确认请求体结构正确最后确认响应解析逻辑匹配。每一步都打印中间结果不要靠猜。6. 从最小循环到工程化下一步该看什么跑通这个最小循环后你已经理解了 Agent 的三个核心模块LLM 负责推理上下文负责状态工具负责动作。但真实生产环境还有很多工程问题上下文怎么压缩、工具怎么编排、失败怎么重试、多轮对话怎么管理记忆、怎么评估 Agent 的效果。这些正是《深入理解 AI Agent》这本书覆盖的内容从上下文工程到记忆系统从工具协议到评估体系再到多 Agent 协作。如果你想继续深入我建议你先把这个最小循环改造成一个可配置的版本把模型名称、系统提示、工具列表都放到一个 JSON 或 TOML 配置文件里这样你可以快速切换不同模型和工具组合做对比实验。配置文件示例{ model: gpt-4o-mini, system_prompt: 你是一个助手可以调用工具。, max_turns: 5, tools: [get_weather, add] }然后写一个加载器读取配置动态注册工具。这个改造过程会让你更清楚各模块的边界。另外如果你要做长期编码任务或者 Agent 工作流可以了解 TaoToken 的 Coding Plan它提供了更适合持续调用的接入方式。模型对话页面可以用来快速测试不同模型对工具调用的支持程度。接入文档里有各语言 SDK 的配置示例遇到问题先查文档。最后说一个我踩过的坑不要在生产环境直接把工具执行结果无脑拼进上下文。如果工具返回的内容很长比如网页抓取结果上下文会迅速爆炸。正确的做法是在工具层做截断或摘要只把关键信息回填给模型。这个细节在书里的上下文工程章节有详细讨论也是“Harness Engineering”要解决的核心问题之一。Agent 的竞争力不在模型本身而在模型之外的那套工程体系。