Agent 不是工具调用器——理解 Agent 的工作机制

副标题: 从一段 150 行的 Python 代码,看清 Agent 循环的每一环


一、引子:你问了一个问题,但背后跑了三遍大模型

"查一下 KV Cache 的优化方法。"

当你在 ChatGPT 里输入这样一句话,它可能直接回复你一段文字。但当我们自己写了一个最小的 Agent 系统后,才发现背后远没有那么简单。

打开我们的 Agent 循环(agent_loop.py,约 150 行 Python),你会看到这个回答的实际过程:

复制代码
Round 1: 发给大模型 → 它决定"我需要查资料"
         → 输出 JSON: {"tool": "search_kb", "args": {"query": "KV Cache 优化"}}
         → 执行 search_kb → 得到结果

Round 2: 把搜索结果追加到对话 → 再发给大模型
         → 它觉得信息够了 → 输出纯文字回答
         → 返回给用户

一次回答,背后跑了 2 轮 LLM 推理1 次工具调用。如果任务更复杂,可能会循环 3-5 轮,每轮都消耗 token 和时间。

这篇文章就从我们的代码出发,拆开 Agent 循环的每一环------不讲框架、不堆概念,只看一个最简单、能工作的 Agent 系统是怎么搭建起来的


二、System Prompt:Agent 的"出厂设置"

AI 模型本质上是一个"聊天机器人"------你问它答。它不知道什么"工具"、"调用"、"JSON 输出"。

让它变成 Agent 的关键,是 System Prompt(系统提示)。看我们的代码:

python 复制代码
SYSTEM_PROMPT = f"""你是一个 AI Agent,拥有通过工具获取信息并执行代码的能力。

{get_tools_prompt()}    # 工具列表拼在这里

## 工作流程
1. 分析用户的任务
2. 如果需要信息或计算,调用对应工具
3. 查看工具返回结果,判断是否还需要进一步操作
4. 信息足够后,整合所有信息给出最终答案

## 回答规则
- 用中文回答
- 需要调用工具时,输出 JSON 格式并写出完整的 JSON 对象,
  示例:{{"tool": "search_kb", "args": {{"query": "关键词"}}}}
- 直接回答时,使用自然语言,不要输出 JSON
"""

然后调用 LLM 时,把 system prompt 放在对话第一句:

python 复制代码
messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": "查一下 KV Cache 的优化方法"},
]

大模型收到这个消息时,它"知道"的信息是:

  1. 我是一个 Agent------不是单纯的聊天机器人
  2. 我有这些工具可以用------名字和作用
  3. 需要工具时我应该输出 JSON------告诉代码"调什么、传什么参数"
  4. 不需要工具时我应该直接说话------告诉代码"这就是最终答案"

如果没有这一层 system prompt,你把同样的用户问题发给模型,它只会当成一个普通问答来回复。System Prompt 就是把 LLM 从"聊天机器人"变成"Agent"的那一层。 它不需要改模型、不需要微调------只需要告诉模型"你可以做什么、该怎么做"。

为什么不能写死 if-else?

有人可能会想:为什么不让代码直接判断"用户问的是不是 KV Cache 相关"来决定调不调工具?因为 LLM 能理解的问题范围太广了------你不可能为每一种问题类型写一个判断分支。把"判断权"交给模型本身,是 Agent 系统灵活性的来源。


三、工具描述的艺术:模型是"看"到你的工具的

有了 system prompt,模型知道了自己有工具可用。但它怎么知道每个工具是干什么的

答案就在 get_tools_prompt() 这个函数里。它把工具列表渲染成模型能理解的文本:

python 复制代码
def get_tools_prompt():
    lines = ["## 可用工具"]
    for t in TOOLS:
        lines.append(f"- {t.name}({', '.join(t.params)}): {t.description}")
    return "\n".join(lines)

渲染出来的效果是:

复制代码
## 可用工具
- search_kb(query): 在本地技术博客知识库中搜索相关内容
- run_python(code): 执行 Python 代码并返回结果,用于计算和数据处理
- read_file(path): 读取本地文件内容

模型看到的就是这段纯文本。 没有 schema、没有类型定义、没有 function calling 协议------就是自然语言描述。

这意味着工具描述的质量直接决定模型能不能正确调用它。两个对比:

复制代码
# 差的描述
- search_kb(q): 搜索知识库

# 好的描述
- search_kb(query): 在本地技术博客知识库中搜索与 query 相关的内容,
  返回最匹配的 3 个文本片段及来源。用于需要查阅本地资料的回答。

差别在哪里?

  • 差的描述 :模型可能不知道 q 是什么(query 还是 question?),也不知道搜索的是"我自己的博客"还是"百度"
  • 好的描述:模型知道传什么(搜索关键词)、得到什么(3 个片段)、什么时候用(需要本地资料时)

在我们的实验中,即使是这样简单的描述也足够让模型正确调用工具了。但在更复杂的场景下,工具描述可能需要精确到参数类型、取值范围、返回值格式------本质上,你是在用自然语言给模型写 API 文档


四、Agent 循环:模型的"思考-行动-观察"

有了 system prompt 和工具描述,Agent 循环就可以转起来了。核心代码只有十几行:

python 复制代码
messages = [
    {"role": "system", "content": SYSTEM_PROMPT},
    {"role": "user", "content": task},
]

for round_idx in range(max_rounds):
    # 1. 模型"思考"
    result = call_llm(messages)

    # 2. 看它输出的内容
    tool_name, tool_args = parse_tool_call(result["content"])

    if tool_name:
        # 3. 行动:执行工具
        tool_result = tools[tool_name](**tool_args)
        
        # 4. 观察:把结果追加回对话
        messages.append({"role": "assistant", "content": result["content"]})
        messages.append({"role": "user", "content": f"工具返回:{tool_result}"})
        # → 继续循环,模型再次"思考"
    else:
        # 没有工具调用 → 这是最终答案
        break

这个循环对应到人类的认知过程非常直观:

Agent 循环 人类对应 说明
模型输出 思考 分析当前信息,决定下一步
解析到 JSON → 调工具 行动 用工具获取信息或执行计算
工具结果追加到对话 观察 看行动的结果是什么
继续循环 再思考 基于新信息重新判断
输出自然语言 → 结束 回答 信息够了,给出结论

消息是怎么"长大"的?

一个 2 轮 Agent 任务的 messages 结构会让你直观地看到"长大"的过程:

复制代码
初始:
  system: [工具列表 + 规则]
  user:   "查一下 KV Cache 优化方法"

第 1 轮 LLM 调用后:
  system: [工具列表 + 规则]
  user:   "查一下 KV Cache 优化方法"
  assistant: {"tool": "search_kb", "args": {"query": "KV Cache 优化"}}    ← 新增
  user:   工具返回:KV Cache 的优化方法包括量化、动态上下文...            ← 新增

第 2 轮 LLM 调用后:
  system: [工具列表 + 规则]
  user:   "查一下 KV Cache 优化方法"
  assistant: {"tool": "search_kb", "args": {"query": "KV Cache 优化"}}
  user:   工具返回:KV Cache 的优化方法包括量化、动态上下文...
  assistant: KV Cache 的优化方法主要包括以下三种...                       ← 最终回答

每次工具调用的结果都追加到对话里,后续的推理轮次可以"看到"之前做了什么、搜到了什么。这就是为什么 Agent 循环的 prompt token 会不断增长------历史在累积。


五、模型怎么决定"要不要调工具"?

这是最让人好奇的问题。我们的实验中有三组任务,正好展示了模型在这个问题上的三种判断:

任务①:知识够用 → 不调

用户问:"什么是 KV Cache?用一句话回答。"

模型的心理活动(推断):

这个我知道,KV Cache 是 Transformer 推理时的优化技术...

不需要查资料,直接回答即可。

结果:输出纯文字 → 结束。零工具调用。

任务②:需要外部信息 → 调

用户问:"搜一下 KV Cache 的优化方法。"

模型的心理活动:

我知道 KV Cache 是什么,但具体的优化方法?我的训练数据里可能不全...

有 search_kb 工具,让我查查。

结果:输出 {"tool": "search_kb", "args": {"query": "KV Cache 优化"}} → 调工具。

任务③:需要精确计算 → 组合

用户问:"查 Qwen3-8B 的 KV Cache 公式,算一下 ctx=16384 时的大小。"

模型的心理活动:

先搜公式 → 搜到了 → 要用 run_python 计算 → 用搜到的参数代入 → 输出结果

结果:两次工具调用(search_kb → run_python),信息组合后输出。

判断依据是什么?

模型做出这个判断,基于它在训练中获得的对自身知识的元认知

模型觉得 行为 结果
我知道,而且确信 直接回答 ① 简单问答
我知道一点,但不确定 调用 search_kb 验证 ② 搜索+总结
我不知道,但可以算 调用 run_python ③ 搜索+计算
我不知道,也不知道怎么用工具 编造答案(幻觉) ❌ 失败案例

这个判断不是硬编码的,完全由模型自己决定。 你给的 system prompt 只是告诉模型"你可以选择调工具",但最终"要不要调"是模型基于当前问题 + 内部知识 + 工具描述综合判断的结果。

这也是为什么我们之前实验中发现了一个关键现象:

推理模型(DeepSeek-R1-7B)在任务③中失败了------它搜到了正确的公式参数,但推理过程中觉得"我记忆中的参数才是对的",用自己的知识覆盖了工具结果。

它"知道"的参数是错的,但它"不知道自己不知道"。 这就是 Agent 系统中最难解决的问题:模型对自己的知识边界判断不准。


六、工具调用失败了会怎样?

既然"判断是否调工具"交给模型,那它当然可能判断错。我们在实验中遇到了几种典型的失败模式:

模式一:搜到了,但覆盖了

复制代码
Round 1: search_kb → 正确返回 "36 layers, 8 kv_heads"
Round 2: 但模型内心:"我记得 Qwen3-8B 的 d_model=512" → 用自己的数据算 ❌

原因:推理模型被训练成"想清楚再回答"。它对自己产出的推理链有过度自信,当工具结果和"我觉得"不一致时,倾向于相信自己的判断。

模式二:重复搜索相同内容

复制代码
Round 1: search_kb("KV Cache Qwen3-8B")
Round 2: search_kb("Qwen3-8B KV Cache 公式")  ← 几乎一样

原因:小模型的上下文注意力在增长后开始衰减------它"忘了"自己第一轮搜了什么。这不是恶意,是模型能力的客观限制。

模式三:选错工具

这种模式我们没有在实验中遇到,但在实际使用中很常见------比如应该调 run_python 计算时,却调了 search_kb("如何计算")

原因 :工具描述不够精确。如果 search_kb 的描述是"搜索资料,也可以用来查计算公式",模型就会混淆。

三种模式的应对

失败模式 原因 改进方向
覆盖工具结果 推理模型的"自我推理"倾向 换指令模型,或在 system prompt 强调"完全相信工具结果"
重复搜索 小模型注意力衰减 限制历史轮次,或在工具描述中要求一次性搜全
选错工具 工具描述有歧义 优化工具名称和描述,让用途一目了然
死循环 模型无法判断"信息够了" 设置 max_rounds 硬限制,或在 system prompt 要求"必须给出最终答案或明确指出信息缺失"

模式四:死循环------模型停不下来

这是用户最常遇到也最头疼的问题:Agent 一直在调工具,永远不输出最终答案。

复制代码
Round 1: 调 search_kb → 返回结果
Round 2: "让我再搜一下" → 又调 search_kb → 返回结果
Round 3: "再确认一下" → 又调 search_kb → ...
Round 4: ...
Round N: 达到 max_rounds 被强制终止

为什么会出现死循环?

根本原因是模型无法做出"信息够了"的判断

  • 工具返回的信息模棱两可------搜到了相关内容但和问题不是完全匹配,模型觉得"还不够,再搜一次"
  • 模型本身的不自信------小模型对自己"有没有足够信息"的元认知更弱,倾向于重复搜索来"确认"
  • system prompt 被误解------如果提示词强调"要充分利用工具",模型可能理解为"要多调工具几次"
  • 工具链太长------复杂任务需要调 3-5 个不同工具,中间任何一环的信息不完整都会触发更多调用

怎么治?

我们代码里的 max_rounds=10 就是最简单粗暴的防线------无论模型怎么跑,10 轮之后强制结束。

更好的做法是在 system prompt 加一条规则:

"如果你发现连续两次调用同一工具得到了相似的结果,请直接基于已有信息给出答案,不要再继续搜索。"

但最终的治本之道还是模型本身的能力------强模型(V4、GPT-4)很少出现死循环,因为它们能更准确地判断"这些信息够了"。

幻觉 vs 死循环:一张表看懂

幻觉和死循环是最常被混为一谈的两个 Agent 失败模式,但本质完全不同:

对比维度 幻觉 死循环
表现 输出错误的回答 不输出回答,一直调工具
本质 "我知道"(其实不知道) "我不够确定"(其实可能已经够了)
元认知状态 不知道自己不知道 不能确信自己知道
对用户的影响 收到一个错的答案 收不到任何答案
治标方案 加 RAG / 工具验证 设 max_rounds 硬限制
治本方案 换更强的模型 换更强的模型

共同根源:模型的元认知能力不足。 强模型既能减少幻觉(知道什么不该说),也能减少死循环(知道什么时候该停)。弱模型两个问题都容易犯------只是在不同场景下表现出不同的"症状"。


七、AI 编程智能体------为什么有的好用,有的不好用?

读到这里,你已经理解了 Agent 循环的每一环。现在我们把视野聚焦到编程 Agent------OpenCode、Cursor、Claude Code、文心快码......为什么体验差距这么大?

主流 AI 编程 Agent 一览

产品 形态 底层模型 是否开源 适合场景
OpenCode 终端 Agent 中立(支持 75+ 模型) ✅ MIT 通用开发,离线/涉密环境
Codex CLI(OpenAI) 终端 Agent GPT-5.5(默认) ✅ Apache 2.0 通用开发,Terminal-Bench 最高分
Claude Code 终端 Agent Claude 专属 复杂任务,全栈开发
Cursor AI IDE 可切换(GPT/Claude) 日常编码,多文件编辑
GitHub Copilot IDE 插件 OpenAI 专属 代码补全,VS Code 生态
Aider 终端 Agent 中立 Git 工作流,脚本开发
文心快码 Comate IDE+Agent 多模型接入 企业级开发,全流程管控
TRAE(字节) AI IDE 可切换 个人效率,多端协同
Qoder(阿里) 桌面 Agent 通义+第三方 阿里云生态,Java 开发
CodeBuddy(腾讯) IDE 插件 混元+第三方 腾讯生态团队
ZCode 3.0(智谱) IDE 插件 GLM-5.2 专业开发,工程稳定性
MiMo Code(小米) 终端 Agent 中立(基于 OpenCode) 小米生态,语音编码

编程 Agent 的两种形态

市面上编程 Agent 虽多,但架构上只有两种:

① 终端 Agent(Terminal-first)

代表:OpenCode、Claude Code、Aider、MiMo Code

  • 跑在终端里,不依赖 IDE,启动快资源低
  • 直接操作文件系统,可以执行编译、测试等命令
  • 适合远程开发、服务器开发、自动化流水线
  • 对 C++/系统级开发更友好------不依赖 IDE 插件生态,直接操作文件即可

② IDE 内嵌 Agent(IDE-first)

代表:Cursor、文心快码、GitHub Copilot、TRAE

  • 嵌入 VS Code / JetBrains 等 IDE
  • 上下文来自你当前打开的文件和项目结构
  • 补全体验流畅,适合日常编码
  • 对前端/全栈开发更友好(实时预览、调试集成)

两者不是互斥的------很多开发者组合使用:终端 Agent 做大重构,IDE Agent 做日常补全。

编程 Agent 的独特之处:工具链集成决定天花板

通用 Agent 的工具是"搜索"、"计算"、"读文件"。编程 Agent 需要与开发工具链深度集成:

工具 说明
文件读写搜索 理解项目结构,跨文件读取和修改
Git 操作 自动 commit、创建 PR、解决冲突
终端命令 编译、运行、测试、lint
LSP 查询 查找引用、跳转定义、获取类型信息

一个编程 Agent 集成的工具链越深,自主性就越强------不需要你手动"拉代码、跑测试、看报错"。这也是 OpenCode 和 Claude Code 这类终端 Agent 在复杂编码任务上表现更好的原因:它们天然可以操作终端、解析编译器输出、跨文件搜索,不需要通过 IDE 插件做中转。

限制编程 Agent 的核心因素

四个因子和通用 Agent 类似,但具体表现不同:

① 底层模型对代码的理解能力(权重 50%)

这是最大的瓶颈。编程 Agent 需要的不是"能聊天",而是真正理解代码:

能力维度 强模型(GPT-4o / Claude 4+) 弱模型(7B 级别)
语法理解 准确理解语言特性,生成符合规范的代码 经常生成语法错误或不存在的 API
项目上下文 能跨文件理解结构和依赖关系 只关注当前文件,改 A 坏 B
工具链操作 git、编译、测试一条龙 命令格式出错,或执行危险操作
Debug 推理 能跟踪调用栈,定位根本原因 只能改表面症状
代码审查 发现逻辑漏洞、性能问题、安全风险 只能检查格式和命名

② 工具链集成的深度(权重 25%)

模型再强,Agent 的工具链不够深,能力也发挥不出来:

  • OpenCode / Claude Code 能做全项目搜索替换------它们有完整的文件索引
  • Aider 的 Git 集成最好------每次修改前自动 diff,可以逐行确认
  • Cursor 的 Composer 能同时改多个文件------它在 IDE 层面拿到了整个项目的 AST 信息

③ System Prompt 中代码规范的注入(权重 15%)

编程 Agent 的 prompt 比通用 Agent 更讲究:

复制代码
差的做法:
"你是一个编程助手,帮助用户写代码。"

好的做法(针对 C++ 开发):
"你是一个 C++ 编程助手。
- 遵循 Google C++ Style Guide
- 使用智能指针代替裸指针
- 优先考虑异常安全
- 所有公有方法添加 Doxygen 注释
- 修改前先 git diff 查看当前变更"

代码规范写进 system prompt,生成质量会显著提升------"企业级编程 Agent"和"个人玩具"的差距往往在 prompt 中注入的工程经验,而不是模型本身。

④ 安全与交互设计(权重 10%)

编程 Agent 比其他 Agent 有更大的破坏潜力------调错了不是返回一个结果,而是改你的源码、删你的文件、执行危险命令 。好的 Agent 会在每次写入前 diff 预览、默认只读需确认才写、阻止 rm -rf / 等危险操作。

一个公式

复制代码
编程 Agent 可用性 = 代码理解能力 × 工具链深度 × Prompt 质量 × 安全设计

仍然是乘法------任何一个短板都会拖垮整体体验。

选型建议

不同开发场景适合不同的编程 Agent:

场景 推荐 原因
日常编码和项目理解 Cursor AI IDE 上下文感知好,开箱即用,适合快速上手
复杂跨文件重构 OpenCode / Claude Code 终端 Agent 的全项目索引更擅长批量修改
服务器/远程开发 OpenCode / Aider 终端 Agent 不依赖 IDE,SSH 环境下也能用
涉密/离线环境 OpenCode 完全本地化,可接 DeepSeek/Ollama 等本地模型
企业级合规需求 文心快码 Comate 私有化部署、数据不出 VPC,已过万家企业验证
阿里云生态开发 Qoder 深度绑定阿里云产品线,Java 生态优化最好
腾讯生态团队 CodeBuddy 微信、企微、腾讯文档无缝衔接
快速脚本和原型 Aider / OpenCode Git 原生工作流,启动快,适合快速迭代
多端协同开发 TRAE(字节) Web/Desktop/Mobile 三端覆盖,任务可云端运行
系统级/C++ 开发 OpenCode / Claude Code 终端 Agent 对文件操作、编译命令、构建工具更自然

别忘了:工具只是放大器

编程 Agent 能大幅提升效率,但它不会替你理解业务和算法。好的 Agent 用户和差的用户之间,差距往往不在工具本身:

  • 知道怎么分解任务、明确边界比用哪个 Agent 更重要
  • 知道怎么审查生成的代码比让 Agent 写多少更重要
  • 知道什么该自己写比盲目信任更重要

这和我们前面讲的 Agent 局限性一脉相承------模型不知道自己在做什么,它只知道"下一个 token 最可能是什么"。


八、总结

拆完 Agent 循环的每一环,核心结论其实很简单:

  1. System Prompt 是 Agent 的起点。 没有它,模型只是个聊天机器人。

  2. 工具描述是模型理解工具的窗口。 模型看到的不是结构化的 API 定义,而是一段自然语言描述。描述质量直接决定调用成功率。

  3. Agent 循环就是"思考-行动-观察"的重复。 模型输出 → 解析 → 调用工具 → 结果追加 → 再推理,直到模型认为信息足够为止。

  4. 模型自己决定"要不要调工具"。 这不是 if-else 硬编码的,而是模型基于自身知识 + 工具描述综合判断的。

  5. 失败通常不是因为"没工具",而是模型"不知道自己不知道"。 死循环的根源也是如此------模型无法判断"信息够了"。

  6. 市面上 Agent 产品的差距不在"有没有 Agent",而在模型能力和工具设计的成熟度。 Agent 可用性 = 模型智能 × 工具质量 × Prompt 质量 × 安全设计,任何一个短板都会拖垮整体。

最后,用一个表总结 Agent 系统四个要素以及它们各自的重要性:

要素 权重 出问题了会怎样
底层模型智能 50% 工具选不对、参数传错、信息判断不准
工具体系设计 25% 模型面对太多工具选择困难,或描述模糊导致误用
System Prompt 15% 行为边界模糊,输出格式不规范
安全与交互设计 10% 暴露调用细节给用户,缺少循环兜底机制

Agent 可用性 = 模型智能 × 工具质量 × Prompt 质量 × 安全设计。 这是乘法------任何一个为零,整体为零。


系列全篇(CSDN):

  1. 从零到一:用 AI Agent 辅助在 6GB 显卡上本地部署大模型实战
  2. 只有 B 级能力的大模型,怎么干出 A 级的活?
  3. Agent 不是更聪明的模型,而是长了手脚的模型
  4. 从 Ollama 到 llama.cpp:一次"降一层"的本地推理探索
  5. KV Cache 优化实战:6GB 显存上的每一 MB 都算数
  6. 从零搭建本地 RAG 系统:200 行 Python 让你的模型"带着资料回答问题"
  7. RAG 配置怎么调最好?6GB 显存上的 4 组对比实验 --- RAG 实验
  8. Agent 不是工具调用器------理解 Agent 的工作机制 --- 本文

附录 A:常见的 Agent 状态词

用 Agent 时,你经常会看到各种状态提示一闪而过------"Thinking..."、"Transmuting..."、"Thought"。这些提示到底在说什么?

来源一:ReAct 模式(Thought → Action → Observation)

ReAct(Reasoning + Acting)是 Agent 系统最经典的论文框架,它的三个核心状态词是:

状态 含义 对应我们代码中的
Thought 模型在"思考"------分析当前状态,决定下一步做什么 每一轮 LLM 调用的输出
Action 模型决定"行动"------调用某个工具并传参 {"tool": "search_kb", "args": {"query": "..."}}
Observation 模型"观察"------看到工具返回的结果 工具 search_kb 返回: ... 追加到对话

你看到的 Agent 推理链路,本质上就是这三个词的循环:

复制代码
Thought: 用户想知道 KV Cache 优化方法,我需要查资料
Action: search_kb(query="KV Cache 优化")
Observation: 返回了量化和动态上下文等方法
Thought: 信息够了,我直接总结
→ 最终回答

来源二:推理模型的状态

推理模型(DeepSeek-R1、o1、o3 等)在输出最终答案前,会先展示一段内部推理过程:

状态 来源 含义
Thinking... DeepSeek-R1 / o1 模型正在进行内部思维链推理
Reasoning... 部分推理模型界面 同上,不同 UI 的叫法不同

对应我们代码中的 reasoning_content 字段。在实验数据中,R1-7B 的任务③就有大量推理内容------它在"想"怎么计算 KV Cache,但反而把它想错了。

来源三:工具执行状态

Agent 执行工具时,界面会展示当前在做什么:

状态 典型场景
Searching... 搜索知识库或网络(Perplexity 等)
Computing... 执行代码并等待结果
Executing... 正在执行工具调用
Reading... 读取文件内容
Planning... 分解复杂任务,制定执行计划
Processing... 通用状态------信息处理中
Transmuting... 花哨的叫法,本质和 Processing 一样------模型正在处理或转换信息
Verifying... 验证结果是否正确
Reflecting... 反思之前的推理是否合理

和我们实验的关系

你在我们实验日志中看到的其实是"没有 UI 包装"的 ReAct 状态:

复制代码
思考中... → 调用了 search_kb → 思考中... → 调用了 run_python → 思考中... → 输出最终答案

每个"思考中..."背后就是一个 Thought ,每次工具调用就是 Action ,工具返回就是 Observation。我们的 Agent 代码实现的就是最经典的 ReAct 模式------只是界面没有花哨的动画,只有文字日志。


附录 B:上下文窗口用完了会怎样

Agent 循环有一个隐蔽问题:对话在不断增长。每一轮 LLM 调用都会追加新的内容,几轮下来可能从几百 token 涨到几千甚至上万。

当上下文窗口接近极限时

表现 原因 我们的实验印证
模型开始"忘记"早期内容 Attention 在长上下文中衰减,越靠前的内容影响越小 R1-7B 在 Round 2 又搜了几乎一样的关键词------它"忘了"第一轮已经搜过
工具结果被忽略 工具返回在对话中位置靠前,模型更关注最近的对话 R1-7B 搜到了正确参数,但推理时用了自己"记得"的错误参数
System Prompt 指令被稀释 System Prompt 在对话最开头,随对话增长被"挤"出有效注意力范围 输出格式开始混乱,不再遵循 JSON 规范
推理质量整体下降 可用上下文变少,模型只能看到局部信息 回答变短、更笼统、信息量下降

当上下文窗口被超过时

场景 发生什么 后果
本地部署(llama.cpp) 物理内存不够会 OOM;--ctx-size 限制超出后被截断 进程退出 / 回答残缺
API 调用(V4 / GPT) 返回 400 错误:"Context length exceeded" Agent 循环中断,需手动处理
部分 API 自动截断 自动丢弃对话最前面的内容来腾空间 模型丢失 system prompt 和早期工具结果,后续推理不可靠

如何避免?

  1. 限制最大轮次max_rounds=10 是最简单的防线------避免无限累积
  2. 摘要中间步骤:不把完整工具结果追加到对话,而是用一个摘要:"search_kb 返回了 3 个方法"
  3. 滑动窗口:只保留最近 N 轮的工具结果和推理,抛弃最早的内容
  4. 选择更大的上下文窗口:云端模型(如 DeepSeek V4)支持 1M 上下文,而本地 7B 模型通常只有 8K-32K------这也是强模型做 Agent 的另一个优势。

在实际使用中,一个 2-3 轮的 Agent 任务通常消耗几千 token,但随着轮次增加,上下文压力会显著放大。如果 Agent 出错需要重试,消耗还会翻倍。关于具体数字,我们会在下一篇《Agent 推理的"显微镜"》中用实测数据详细分析。

相关推荐
带娃的IT创业者1 小时前
深度解析 GPT-5.6 Sol:当 AI 模型开始具备“物理世界“的感知力
人工智能·gpt·大模型·技术演进·gpt-5.6·物理世界感知·认知架构
青风971 小时前
16-ADAPTRACK:基于自适应阈值的多目标跟踪匹配算法
人工智能·算法·目标跟踪
AI前沿资讯1 小时前
AI3D角色生产如何减少返工?用 V2Fun 前移建模与动画流程
人工智能·3d
aqi002 小时前
15天学会AI应用开发(十一)从TXT文件构建RAG知识库
人工智能·python·大模型·ai编程·ai应用
AIJWAI2 小时前
朱雀 AI 检测的核心逻辑是什么?
人工智能
汤姆yu2 小时前
macOS系统下Aider完整安装、配置与实战使用教程
大数据·人工智能·算法·macos·github·copilot
阿部多瑞 ABU2 小时前
软权力:先行植入的意义置换 ——文化殖民的结构逻辑与资本剥削的后续包装
人工智能
Sam09272 小时前
【AI 算法精讲 14】TF-IDF:词频与逆文档频率
人工智能·python·算法·ai
m0_626535202 小时前
MRR(Mean Reciprocal Rank)和 NDCG(Normalized Discounted Cumulative Gain)
人工智能·机器学习