拆开 AI Agent 之后,我发现核心就是一个 while 循环
拆开 AI Agent 之后,我发现核心就是一个 while 循环
先想这样一个问题:“你这个 Agent,和直接调一次大模型 API,本质区别在哪?”
为了回答这个问题,我把 LangChain 的 Agent 执行器源码完整翻了一遍。翻完的第一感受是幻灭:剥掉层层抽象之后,所谓 Agent 的核心就是一个 while 循环——模型说要调工具,就调,把结果塞回上下文,再问模型,直到模型不再要求调工具为止。
但这个幻灭感是个分水岭。背概念的人觉得 Agent 神秘,看懂循环的人觉得 Agent 简单,而真正动手复现过、烧过钱、修过死循环的人会发现:循环本身不值钱,值钱的是循环里每一个工程决策。

一、先回答“和调一次 API 有什么区别”
Agent 是一个能感知环境、做出决策、执行动作的软件系统:LLM 负责理解和决策,工具负责执行,记忆负责保存状态和历史经验。
它和聊天机器人的差别不在“会不会说话”,而在生命周期:聊天机器人一轮交互就结束,Agent 会在动态环境里持续观察、判断、执行,直到任务完成才停。调一次 API 是“问一句答一句”,Agent 是“领了任务自己去干,干完回来汇报”——我烧掉的那四十块钱,就是“自己干”的过程费。
业内流传一个公式:
Agent=LLM+Planning+Memory+ToolsAgent = LLM + Planning + Memory + ToolsAgent=LLM+Planning+Memory+Tools
这个公式没错,但它悄悄省略了最关键的一项:Observation——工具执行结果回填上下文那个动作。没有 Observation,另外三样东西根本拼不到一起。真正让公式成立的是这个反馈闭环:
四个组件的分工:
Planning(推理与规划):用 LLM 分析当前任务状态、拆解目标、决定下一步。Chain-of-Thought 这类提示技术的作用就在这里——强迫模型逐步推理,降低“拍脑袋”的概率。
Memory(记忆):分两层。短期记忆就是上下文历史,解决“刚才说过什么”;长期记忆是外部存储——向量数据库、知识图谱,解决“过去积累了什么”。这个分层结构我第一眼就觉得眼熟:几乎就是操作系统课上内存和磁盘分层的翻版,快而贵的在里面,慢而持久的在外面。
Tools(工具):查数据、调 API、读文件、执行代码。没有工具,Agent 大部分时候只能停留在“建议你怎么做”的层面——我的文献调研 demo 之所以能干活,就是因为它真的会去调检索接口。
Observation:工具执行后返回结果,Agent 把结果放回上下文,再进入下一轮推理。这个闭环是 Agent Loop 能转起来的关键,也是教科书公式里最容易被忽略的一环。
二、Agent Loop:把三件事转起来
Agent Loop 每一轮做三件事:让 LLM 推理、调用工具、把工具结果写回上下文。循环往复,直到任务完成或触发停止条件。
读源码不如自己写一遍。抛开框架,一个最小可用的实现大概五十行:
def agent_loop(user_request, tools, system_prompt,
max_turns=20, max_tokens=150_000):
context = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_request},
]
spent = 0
for turn in range(max_turns): # 兜底 1:轮次上限
response = llm(context, tools=tools)
spent += response.usage.total_tokens
if spent > max_tokens: # 兜底 2:Token 预算
raise BudgetExceeded()
if not response.tool_calls: # 模型不再调工具 → 任务完成
return response.text # 唯一的正常出口
for call in response.tool_calls: # 一轮可能并行多个工具
result = execute_with_guard(call) # 校验 + 执行 + 异常捕获
context.append(tool_result(call.id, result))
raise MaxTurnsExceeded() # 兜底触发,留好现场
完整流程画出来:
对照流程图,有四个我在烧钱那晚才真正理解的点:
循环的唯一正常出口,是模型自己决定“不再调工具”。这是个概率性判断——模型说完成了不代表真完成,模型不说完成也不代表没完成。我的死循环就出在这里:检索工具返回了报错,模型换个表述重试,再报错,再重试……每轮上下文还涨两千 Token。max_turns 和 Token 预算这两个兜底,就是给这种场景准备的保险丝。
execute_with_guard 是不可省的安全层。循环会放大一切错误:单次工具调错损失一次,循环里调错会持续损失。类型校验、参数校验、异常捕获一个都不能少——而且异常必须转成模型可读的文本回填上下文,让模型明确知道“这条路走不通”。否则它收不到负反馈,就会无限重试。
tool_use_id 必须原样带回。一轮并行调用多个工具时,如果结果和调用 id 对不上,观察就会张冠李戴——工具 A 的返回值被安到工具 B 头上,后面所有推理全部跑偏。
修复死循环的方案朴素得可笑:在工具执行层加幂等检测——同一个工具、同一组参数、连续失败三次就强制中断循环。模型没换,Prompt 没改,问题消失了。Agent 的“智能”是模型白送的,工程的价值在于让这份智能在预算内可控地释放。
三、真正的难点:上下文是 Agent 唯一的状态载体
while 循环谁都会写,LangChain、LlamaIndex、Spring AI 的封装也大同小异。Agent 的工程难点九成在上下文管理上,原因很根本:
Agent 运行时的全部状态只有两个去处——上下文里(模型看得见),或外部存储里(模型看不见,但可以查)。循环每转一轮,上下文就膨胀一次。
具体有三个污染源:
工具返回结果是噪音大户。我的 demo 里,一次检索接口返回几十条文献的完整元数据,直接塞进上下文就是几千 Token 的噪音,而模型下一轮真正需要的可能只有标题和年份。工程处理手段包括:结果投影(只保留相关字段)、截断(保留 schema 和几行样例加总数)、失败结果转摘要。
循环轮数稀释关键信息。第 3 轮定下的检索策略,到第 15 轮时被几万 Token 的执行日志包围,模型对它的注意力被摊薄,行为开始漂移。长任务后期 Agent 跑偏,不是模型变笨了,是信噪比塌了——这是我在实验记录里对比前后轮输出质量时确认的。
历史消息滚雪球。多轮对话叠加工具结果,上下文体积近似线性甚至超线性增长,而 API 账单严格跟着涨。
这些问题催生了 Context Engineering 这个方向。它和 Prompt Engineering 的分工可以一句话说清:Prompt Engineering 决定“怎么问”,Context Engineering 决定“每一轮模型该看见什么”。工具结果怎么裁、何时触发压缩、哪些信息转存外部记忆、检索片段怎么注入,全是这一层的事。框架不会替你做好这些——最多给你留了 hook。
四、循环之外的第二条路:Plan-and-Execute
读论文的时候会发现,Agent Loop 其实就是 ReAct 范式的工程化——2022 年那篇 ReAct 论文的核心思想就是 Reason 和 Act 交错进行,走一步看一步。而文献里还有另一条主流路线:Plan-and-Execute——先让 LLM 产出完整计划,再逐步执行,偏离预期时触发重规划。
| 维度 | Agent Loop(ReAct) | Plan-and-Execute |
|---|---|---|
| 决策粒度 | 每轮只决定下一步 | 先出全量计划,再逐步执行 |
| 灵活性 | 高,随时根据观察调整 | 低,偏离预期需重规划 |
| Token 成本 | 每轮全量上下文问一次大模型 | 规划一次,执行步可用小模型 |
| 典型失败模式 | 绕路、反复试探、死循环 | 计划错了,执行越高效错得越远 |
| 适用场景 | 环境不确定、需要探索 | 目标明确、步骤可预先枚举 |
| 两者不是二选一。实践中常见的混合形态是:Plan 定骨架,执行步内部允许局部 ReAct 处理意外。比如“调研这个方向的近两年论文并写综述提纲”——整体计划可以预排,但每一步内部的检索调试天然是探索式的。 | ||
| 选型判断就一条:任务的不确定性主要在哪。不确定性在“下一步做什么”用 Loop;不确定性在“整体怎么做”用 Plan——后者错了代价高,值得先花一轮 Token 把计划聊清楚。 |
五、记忆:把状态搬出上下文的系统性方案
短期/长期记忆的分法前面提过,这里补上工程闭环。记忆系统的本质是给上下文做“换入换出”机制——又是一个操作系统页面调度的类比,研究生阶段学的 OS 知识在这里全部复用:
- 写入策略:什么内容值得长期保存。不是所有对话都值得存——通常是任务结论、用户偏好、踩过的坑,而不是中间执行日志
- 检索策略:新任务开始时,用语义检索(向量库 top-k + 重排)把相关记忆注入上下文
- 衰减与强化:长期不被检索命中的记忆降权,频繁命中的强化——否则记忆库会变成越攒越大的垃圾场
- 冲突更新:新旧记忆矛盾时的覆盖规则——不然 Agent 会同时“记得”两个相反的结论
记忆机制和上下文管理是同一个问题的两面:上下文管理决定“现在看见什么”,记忆系统决定“过去留下什么”。一个管窗口内,一个管窗口外。
六、框架祛魅:先手写,再用框架
翻完 LangChain 之后我顺手对比了 LlamaIndex 和 Spring AI,结论是三者对 Agent Loop 的封装思路高度一致:while 循环 + 工具分发 + 停止判断,差异主要在 API 形态和生态。LangChain 新一代的 LangGraph 把循环显式建模成了状态图,支持分支、并行和人工介入节点——但拆到底还是那个循环,只是把“循环里发生什么”从代码约定升级成了显式的图结构。
所以我给同门的学习建议是反直觉的:先自己手写五十行 loop 跑通一个 demo,再去用框架。手写过的人知道 max_turns 是保险丝、知道工具异常要转文本回填、知道上下文为什么会膨胀——用框架时才知道每个参数在保护什么。直接上框架的人只会调参,出问题时连日志往哪看都不知道。
收尾
回到开始的问题:调一次 API 是“完成一次推理”,Agent 是“维护一个循环”——它在循环里持续消耗 Token 换取环境反馈,直到自己判断任务完成。多出来的不是智能,是“持续干活的授权”和“干多久该停的约束”。
更多推荐

所有评论(0)