【从零构建AI Code终端系统】03 -- Agent 循环:一个 while 就是全部

03 -- Agent 循环:一个 while 就是全部

7 行代码

上一章结尾留了一个问题:bash 只能执行一次,然后呢?

答案是把它放进循环:

复制代码
while true:
    response = model(messages, tools)
    if no tool_calls in response:
        break
    for call in response.tool_calls:
        result = execute(call)
        messages.append(result)

这就是 agent 的全部。学术界叫它 ReAct(Reasoning + Acting),但名字不重要。重要的是------循环内部没有一行 if/else 决定"下一步做什么"。

用什么工具、以什么顺序、什么时候停------全部由模型决定。代码只做三件事:调用模型、执行工具、把结果喂回去。
Agent 循环
输入与上下文
最终响应
推理 LLM
工具选择
工具执行


核心命题:零分支

数一下这 7 行里有几处代码在做任务级判断。零处。

循环越短,agent 越聪明。 每往里加一行 if/else,就从模型手里夺走一个决策权。"如果文件不存在就搜索"------听上去合理,但你在用硬编码规则替代推理。你能预见 50 种情况,模型面对的是无限种。第 51 种到来时,你的规则沉默了,模型不会。

退出条件同理。循环什么时候停?当模型的响应不再包含 tool_calls 时。不是计时器到了,不是执行次数到了上限------是模型自己判断任务完成了,选择返回文本而不是继续调用工具。

你写的每一行分支逻辑,都是在用有限规则替代无限推理。保持循环极简,把决策空间完整地留给模型。


自愈:循环的免费副产品

用户说"帮我跑测试":

复制代码
轮次 1: "好,先跑测试看看"
        --> bash("npm run test")
        --> 报错: "Cannot find module './utils'"

轮次 2: "找不到 utils?看看这个文件"
        --> read_file("src/utils.ts")
        --> 报错: "File not found"

轮次 3: "不存在?可能被移动了,搜一下"
        --> grep("utils", "src/")
        --> 结果: src/lib/utils.ts

轮次 4: "原来搬到 lib 下了,改引用"
        --> edit_file("src/index.ts", "./utils" -> "./lib/utils")

轮次 5: "改完了,再验证"
        --> bash("npm run test")
        --> "All tests passed"

没有一行重试代码,没有一个 catch 块,没有"如果 test 失败就搜索文件"的预设规则。模型看到错误输出,理解含义,调整策略,再试一次。整个行为 100% 来自推理,0% 来自预编程。

这就是上一章坚持"不抛异常,结构化返回"的原因。exit code + stdout + stderr 被追加到消息历史,成为下一轮推理的输入------错误信息从终点变成路标。


中间件:洋葱模型

循环建立了。但模型每次被调用时,请求先穿过一层层中间件:

复制代码
请求进入 -->
  [ 中间件 A ]
    [ 中间件 B ]
      [ 中间件 C ]
        === 模型调用 ===
      [ 中间件 C ]
    [ 中间件 B ]
  [ 中间件 A ]
<-- 响应返回

每个中间件有两次介入机会------进去的路上和出来的路上。

前与后

进去的路上(调用前):修改即将送达模型的消息。注入技能列表、裁剪过长上下文、检查是否需要压缩------模型还没看到这些消息,你有完全的编辑权。

出来的路上(调用后):处理模型返回的响应。记录 token 消耗、检测暂时性错误决定是否重试、提取结构化数据------响应已经产生,你有完全的审计权。

后续每引入一个新能力,几乎都落在这两个时机上:

  • 技能注入?调用前,把可用技能列表塞进消息。
  • 上下文压缩?调用前,把过长的历史替换为摘要。
  • Token 追踪?调用后,从响应里提取用量数据。
  • 重试?调用后,判断是否限流或超时,决定是否退避重发。

一个典型的中间件:

复制代码
function retryMiddleware(next):
    for attempt in range(maxRetries):
        response = next(request)
        if response is not transient_error:
            return response
        wait(exponential_backoff(attempt))
    return last_response

它不知道 next 里面是另一个中间件还是真正的模型调用。不关心请求内容,不关心上一层做了什么。只管一件事:失败就重试。

组装

把工具、中间件和循环拼在一起:

复制代码
function createAgent(modelRoute, tools, middlewares):
    model = getModel(modelRoute)         // 第 01 章的路由
    chain = compose(middlewares, model)   // 洋葱
    return Agent(chain, tools)           // 带循环的实例

三行。模型从路由来(第 01 章),工具从注册来(第 02 章),中间件堆叠成洋葱。后续每引入一个新能力,都是往这个函数里安装一个新零件------要么是一个新工具,要么是一层新中间件。


小结

循环给模型行动力,中间件给系统扩展力。但行动力没有方向感,就是原地打转------循环跑多了,早期意图被调试日志和错误信息淹没,注意力漂移不可避免。


相关推荐
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章26-图像拼接
图像处理·人工智能·opencv·算法·计算机视觉
2501_926978332 小时前
思想波与引力共振理论:统一物理主义意识框架的革命性探索--AGI理论系统基础12
人工智能·经验分享·架构·langchain·agi
朴实赋能2 小时前
当AI成为“家人”:心伴机器人如何重塑老年居家康养新模式
人工智能·陪伴机器人·情感计算·认知衰退干预·亲人音色复刻·虚拟家人·多智能体协同15分钟养老圈
模型时代3 小时前
Arista暗示正在开发AI网络管理遥测工具
开发语言·人工智能·php
紧固视界3 小时前
2026 紧固件质检三大难题揭秘|上海紧固件专业展
大数据·人工智能·紧固件·上海紧固件展·紧固件展
十铭忘3 小时前
动作识别12——yolo26s-pose+PoseC3D第1篇之标注工具升级2.0
人工智能·python·深度学习
becatjd3 小时前
VScode的claude code插件报错command ‘claude-vscode.editor.openLast‘ not found
ide·vscode·编辑器·claude·anthropic·claude code
好家伙VCC3 小时前
# 发散创新:基于Python的TTS语音合成实战与优化策略 在人工智能加速落地的今天,**文本转
java·开发语言·人工智能·python
沃达德软件3 小时前
模糊图像复原技术解析
图像处理·人工智能·深度学习·目标检测·机器学习·计算机视觉·目标跟踪