Agent 说「我已经改好文件了」,这句话到底能不能信?

用 AI 编程工具的人多半见过这个场面:它一路说得很笃定------「已定位到问题」「已修改 shipping.ts」「测试已通过」------你一看文件,一个字没动。

第一反应通常是「这模型在胡说」。

但读完六套编程智能体的源码(Codex、Claude Agent SDK、Gemini CLI、DeepSeek Harness、pi、OpenCode,笔记整理成了一套开源中文教材,文末有链接)之后我发现,这个判断不太对。模型不是在撒谎,它根本不知道文件有没有被改

因为改文件这件事,压根不是它干的。

1. 三个角色,别混成一个

读 Agent 源码最容易犯的错,就是把所有行为都算到模型头上。实际上一次任务至少牵扯三方:

复制代码
Model        产生候选决定 ------ 只是「建议这么做」
  ↓
Harness      把候选接进任务循环 ------ 检查、放行、调用执行器
  ↓
Environment  副作用真正发生的地方 ------ 文件、进程、网络

模型输出的从来不是动作,是动作的请求。简化成教学示例大概是这样:

json 复制代码
{
  "type": "tool_call",
  "name": "read_file",
  "arguments": {"path": "src/shipping.ts"}
}

这一刻文件还没打开。模型不知道宿主进程有没有读权限,也不知道产品策略会不会放行这个路径。它只是提了个请求。

所以当它说「我读了文件」,准确的说法是「我请求读文件,并且假设那一步成功了」。

2. Harness 在中间做了六件事

夹在中间的这一层通常要负责:

  1. 把系统提示、用户消息、历史、工具定义、动态状态拼成模型输入
  2. 发起模型请求,消费流式或非流式响应
  3. 区分文本完成、工具请求、错误、取消、超时
  4. 检查工具名、参数、权限、用户确认
  5. 调执行器,把结果写回消息历史
  6. 保存 Session、发事件,决定继续还是结束

六套实现切法完全不同。Codex 把核心循环、协议和多种界面分到不同 crate;OpenCode 让多个客户端连到服务化的 Session;Claude 的 Agent SDK 只公开应用进程能看到的部分,产品内部怎么做我们看不到。职责看着相似,模块名字对不上。

回到那个运费 bug:Harness 拿到 read_file 请求后,先交给工具注册表,检查路径在不在工作区内,然后才真的去读。读到的内容回写进下一轮:

json 复制代码
{
  "type": "tool_result",
  "tool_call_id": "call-1",
  "content": "export function shippingFee(total) { return total > 100 ? 0 : 10 }"
}

模型看到这个结果,才知道边界条件确实写错了,才有依据提出把 > 改成 >=

在这之前它说的任何「我看到了」,都只是它以为。

3. Environment 才是说了算的那层

策略放行了 src/shipping.ts,文件仍然可能打不开:文件不存在、宿主进程没权限、Sandbox 没挂载那个目录、远程工作区断线。

这些都是执行失败。把它们记成「策略拒绝」,账就算错了------你会以为是权限配置问题,实际是环境问题,排查方向全反。

反过来也一样:宿主进程也许有能力删掉整个仓库,但 Harness 不该因此就批准删除。系统能做什么,和产品授权它做什么,是两笔账。

4. 一张排查表

出问题时先判断落在哪一层,能省很多时间:

现象 更可能是哪一层 去查什么
模型压根没请求任何工具 Model 或输入构造 模型请求、工具定义、上下文
工具请求被判为未知 Harness 工具注册和名称解析
请求被规则拒绝 Harness 权限策略、确认结果
请求获准但文件打不开 Environment 路径、挂载、系统权限
测试退出码 0 Environment 事实 再查测试有没有覆盖用户目标

最后一行值得单说。

5. 「测试通过了」也不等于「做完了」

退出码 0 要成为有效证据,得同时满足三件事:

  • 它覆盖了用户目标
  • 跑的是修改后的代码
  • 输出确实来自这次任务

三条缺一条,这个 0 就说明不了问题。举个具体的:如果测试只跑了 shippingFee(101),退出码是 0,但金额 100 的那个 bug 一点没修------而那正是用户要解决的问题。

进程状态和任务结果是两回事。前者是 Environment 的事实,后者需要按明确口径去判断。

6. 问题考察

下面四句话,分别是谁作出的判断?

  1. 「下一步应当把 > 改成 >=。」
  2. 「本次写入需要用户确认。」
  3. shipping.test.ts 返回退出码 0。」
  4. 「边界值测试通过,且测试文件未被修改,因此任务结果合格。」

依次是:Model 提出的候选做法、Harness 按策略作出的决定、Environment 里真实发生的执行结果、以及对整个结果的独立判定。

第三句推不出第四句------你还得确认那个退出码对应的是正确的命令、正确的工作区、以及这一次的补丁。

7. 所以回到开头那个问题

Agent 说「我已经改好文件了」,能不能信?

不能,但也不该怪它。 它说的是「我提了这个请求」,中间隔着两层它看不见的东西。想确认到底改没改,去看 Environment:文件内容、Diff、退出码。

这也是为什么读 Agent 源码时,第一件该做的事不是找「智能」在哪,而是先把这三层的边界画出来。边界画清楚了,你才知道一个 bug 该去哪一层排查。

8. 全量笔记

这篇讲的是三层边界,是理解 Agent Harness 的地基。完整教材还包括:配置和 Prompt 怎么拼成模型看到的输入、工具循环怎么转、权限在哪几层拦、Turn 和 Step 的边界在哪、会话状态怎么存和恢复、多个子 Agent 怎么协作------六套实现每套一条课程,外加横向对比。

免费开源,觉得有用点个 star,写错的地方欢迎提 Issue。

相关推荐
luckystar513~1 小时前
Hermes 工程化实战专栏:安装部署实战——从装好到跑通第一个任务
人工智能·agent·智能体·hermes·实战专栏·hermes安装配置
新知图书2 小时前
14.5 AI试驾预约系统的完整实现
人工智能·agent·ai agent·智能体
水管在开花.4 小时前
Agent范式与LangGraph-②零基础保姆级教程
人工智能·面试·langchain·agent
ShallWeL4 小时前
RAG 文档变更后的检索回归清单
人工智能·agent·知识库·rag·检索
Patrick在香港4 小时前
Claude Agent 进阶编排:循环控制 + 写权限审批闸门 + 幂等重试
python·agent·claude·编排·anthropic api
李燚5 小时前
数据库迁移不翻车:golang-migrate 实战,143 个 DDL 有序执行(第97篇-E83)
golang·agent·multiagent·migrate·eino·subagent·deepflux
彼日花5 小时前
我做了一个开源项目,让 AI 记住我们解决过的问题:Usora
人工智能·agent·ai编程
wangfpp5 小时前
生产级 RAG 知识库全流程实践
人工智能·agent·全栈
Haooog5 小时前
从 Tool Calling 到 State 并发控制:一个 Agent 如何安全地连续执行任务
java·agent·后端开发