12|Codex、Claude Code、Copilot:从公开事实到可诊断运行链
本章目录
本章预备知识
源码阅读先跟对象流向,不先背类名。把一个输出项怎样变成工具调用、怎样携带调用ID、怎样进入执行注册表画清;闭源产品只描述已公开的边界。
本章各题列出所需基础。可先完成相应基础课,也可通过自检确认掌握程度。
进入深读前的小检查:用“已注册→可发现→已暴露→可授权→执行成功”逐级排查工具为何没用上,而不是看到配置文件就宣布生效。
术语带虚线时可点击速查。公式中的字母先看定义;不要用猜缩写替代推导。
三个产品不是同一套实现,也不是同一个界面的三个模型选项。本章对Codex使用固定源码快照,对Claude Code和Copilot使用公开产品文档;不声称知道闭源提示、全部调度策略或未公开服务端实现。
沿着对象与状态理解本章
比较编码产品时,先使用同一条公开的可诊断链:上下文组成、模型提议、宿主执行、结果回填、状态恢复。闭源产品未公开的内部机制保持未知;教学泳道不是对三款产品逐行源码的共同断言。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
把一个Agent任务拆成真正发生的几轮
同一句用户任务,为什么可能调用模型不止一次?
LLM生成下一步提议,宿主执行获准的动作,把结果再交给LLM。这是外层任务循环;每次模型调用内部还要经历逐Token生成。外层的一轮工具行动不等于内部生成一个Token,两种“循环”不能混用。
输入与适用条件:构造的只读教学任务:文件config.txt含timeout=30。泳道展示职责边界,不声称调用了真实商业API;数据和动作顺序固定。
1 / 8 · 1. 用户任务:读config.txt中的timeout
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
模型尚未看到磁盘文件。先区分用户给的任务与环境真实内容。
未读取默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
模型可以借助工具获得新证据,而不把一切都塞入参数。
怎样实现
按请求、校验、执行、结果回填、继续决策组织循环。
可能的代价与误解
忘记回填工具结果会让模型重复请求;把输出文本当执行证明会误报完成。
如何判断是否做对
对照每轮模型输入和工具日志,确认结果与call_id匹配。
Q045|Codex源码里,工具从模型可见到真正分派经过哪些对象?压缩又怎样替换历史?
核心回答
在本书检查的Codex提交ce0323f2f2d3b25e82806cd555237da3db4e1897中,ToolRouter区分注册表、模型可见schema与不同暴露方式;响应项先转成ToolCall,再包装ToolInvocation交给registry分派。压缩路径则构造新的历史、重建上下文并替换当前历史,不只是清空聊天文本。这是该快照的局部源码观察,不保证所有版本或托管产品相同。[CODEX-SRC]
先看真实字段,而不是凭名称猜架构
codex-rs/core/src/tools/router.rs包含registry、model_visible_specs、tool_mode、code_mode_tool_names和can_manage_children等字段。它们的分离直接说明“工具已注册”不等于“模型本轮能直接看见”。
exposes_tool检查标准化名称是否通过code mode映射、直接/命名空间schema暴露,或在存在tool search能力时能作为deferred工具找到。has_terminal_controls需要同时暴露启动与继续交互终端的两个工具;子Agent管理能力也根据相关工具集合计算。这里的“可达”仍不是对每次具体参数的最终授权。
调用转换路径可以按下列顺序阅读:
ResponseItem
→ build_tool_call
→ ToolCall {tool_name, call_id, payload, ...}
→ dispatch_tool_call_with_state
→ dispatch_tool_call_with_code_mode_result_inner
→ ToolInvocation {session, step_context, cancellation_token, tracker, ...}
→ registry.dispatch_any_with_state
build_tool_call对FunctionCall、客户端执行的ToolSearchCall、CustomToolCall分支处理,其余项可返回None。这说明普通文本与工具调用不是靠扫描自然语言里的函数名区分。参数解析错误也有返回模型的错误通道。真正handler、权限或沙箱检查需继续追踪registry和对应工具实现;只读router不能宣称已经审计全部执行安全。
压缩路径在compact.rs中克隆历史、准备模型输入、处理流完成或错误;成功后收集用户消息与摘要,构建压缩历史,重建world-state相关上下文,再调用replace_compacted_history并重算Token使用。源码还区分响应不完整、上下文超限、取消等错误,不能把所有失败都当普通重试。
从工具注册到真正dispatch,逐道看它能否到达
输入与条件。以本书记录的Codex固定提交ce0323f2f2d3b25e82806cd555237da3db4e1897为背景,设工具x已经进入注册表。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1注册 | registry中存在x | 这只证明执行实现可被找到,不证明本轮模型能看见。 |
| 2暴露 | 核对model_visible_specs、延后发现或code-mode路径 | 隐藏工具不能仅凭注册成功就判定可用。 |
| 3模型输出 | 响应项是可识别的FunctionCall或相应CustomToolCall | build_tool_call把它转换成内部ToolCall。 |
| 4分派 | 构建携带call_id、payload、session和step_context的ToolInvocation | 送到registry的dispatch路径。 |
| 5诊断 | 注册计数增加但本轮调用仍不出现 | 先检查暴露计划和实际请求,不急着归因于模型“不愿用”。 |
把过程连起来。注册、可发现、可见、可调用和执行成功是不同状态,应逐道观察。
边界与反例。源码讲解限已存档固定提交与路径,不声称当前所有Codex版本一致;权限等完整链路还需阅读相邻实现。
依据与适用范围:[CODEX]。示例输入和场景由本书明确设定。
追问:为什么固定commit比引用main路径重要?
主分支会变化,函数移动和行为改变会让行号失效。固定快照能让读者核对同一段代码。本书只用上述快照证明局部结构,不把它称为永远最新;更新产品时应重新定位调用链和测试。
追问:看到代码有retry,就能断言工具一定幂等吗?
不能。这里有模型流重试、压缩重试和工具执行等不同层。是否重复副作用取决于执行记录、handler和远端接口。源码阅读必须沿数据与副作用路径走,不能根据一个函数里的backoff推导全系统保证。
追问:为什么不逐字复制完整源码作为教程?
完整源码会淹没核心路径,且读者可能误把某版内部类型当稳定API。本书给出真实符号、路径、固定版本及中文控制流解释,原理与教学实现分开。需要编译该仓库时应使用其依赖与测试环境,本文没有声称完成源码编译或全量审计。
Q046|Claude Code怎样获得项目上下文?Subagent为什么不等于主Agent的完全复制?
核心回答
公开文档将Claude Code描述为围绕模型、工具和反馈运行的Agent,支持项目指令、技能、子Agent和上下文管理。子Agent可使用独立上下文与配置,主Agent通常接收其结果而非全部内部过程。具体内建提示和服务端细节未完整公开。[CLAUDE][SUBAGENTS][CLAUDEMEM]
用诊断视角展开一轮工作
当你打开仓库并提问,应分别检查工作目录是否正确、哪些项目指令被发现、技能是否相关、工具和权限是否可用、模型实际读取了哪些文件。这些检查是通用诊断方法,不是断言内部每个字段的精确顺序。
对于“分析某模块”子任务,主Agent可以传递目标与范围,子Agent在自己的工具/权限配置下读代码,返回根因、证据与建议。独立上下文减少主线程被长日志占满,但也形成信息瓶颈:子Agent若只说“没有问题”,主Agent无法知道检查了哪个版本、什么范围和什么测试。
项目指令适合稳定约束;自动记忆或持久笔记适合跨会话可用信息;当前任务进度则需要具体证据与状态。三者不能全部塞进一份越来越长的文件。长会话的工具输出清理或压缩可以减少上下文负担,但仍可能丢失细节,因此关键证据最好可重新定位。[CONTEXT]
子Agent只收到摘要,为什么它不知道主Agent的限制
输入与条件。主任务有约束“只读,不修改文件”。父Agent委派“调查配置问题”,子Agent拥有独立上下文。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1父上下文 | 用户原话明确禁止修改 | 限制在父会话可见。 |
| 2委派消息 | 只写“调查配置问题” | 不能假设子会话自动包含整段原历史。 |
| 3最小交接 | 追加只读范围、具体问题、仓库版本和证据路径 | 不必复制无关长对话。 |
| 4工具限制 | 宿主对子任务只提供只读能力 | 自然语言要求与工具权限共同约束。 |
| 5返回整合 | 子Agent提供发现、证据和未解决问题 | 父Agent仍负责用户整体目标。 |
把过程连起来。独立上下文节省父上下文成本,同时也需要有意识地传递必要约束。
边界与反例。这是独立子Agent模式的风险示例,不断言任何当前产品的默认继承字段;配置必须按公开文档与实际请求核查。
依据与适用范围:[CLAUDE-SUB] [CLAUDE]。示例输入和场景由本书明确设定。
追问:主Agent能用的MCP,子Agent一定能用吗?
不能作为跨产品、跨配置的普遍假设。要检查子Agent工具配置、权限模式、实际发现结果和运行日志。文件权限、MCP连接、模型可见schema是不同层;主线程已授权一次也未必意味着所有子Agent在所有上下文下继承同样能力。
追问:CLAUDE.md越长越能保证行为吗?
长指令可能互相冲突或淹没重点,也占用输入。应保留全局稳定规则,把局部专业流程放到按需内容,把事实放回可查询来源。自然语言规则是模型输入,不是确定性执行器;高风险限制仍需要真实权限和验证。
追问:无法看到内部Prompt,还能深入理解吗?
可以理解公开架构、接口语义、工具行为、上下文观察与可复现实验,但应停止在证据边界。通过控制变量测试某个配置是否改善任务是有效工程研究;从一次行为猜出“它内部一定用了某种隐藏路由”则不是事实。
Q047|Copilot的IDE、CLI、云端Agent为什么必须分开看?
核心回答
Copilot包含不同使用表面,工具、上下文来源、自定义Agent、权限和执行环境不应混为一谈。CLI的自定义Agent文档描述了基于配置的专用行为与工具集合;同名能力在IDE或云端可能有不同支持范围。[COPILOT][COPILOTSKILL]
从一次“Task Agent没加载Skill”逐层排查
先确定具体入口与版本:IDE聊天、CLI、后台编码任务还是其他表面。再看技能文件是否在该表面支持的发现位置,元数据是否有效,任务描述是否足以触发,是否实际读到了技能正文。最后检查技能要求的工具是否在这个Agent中暴露并允许使用。
故障可能出在发现:文件路径不被扫描;也可能出在选择:描述太泛或不匹配;也可能出在执行:技能读了但其依赖工具不可用。仅看到最终代码缺少某个模式,并不能证明Skill完全没加载。应看读取和执行证据,而不是让模型自报“我用了”。
仓库指令、路径特定指令、skill、custom agent和MCP各负责不同层。把同一大段规则复制到所有位置,可能暂时增加触发概率,也可能产生重复与冲突。更好的做法是先查目标表面的职责,再设计最小充分配置。
同一份仓库指令,在IDE、CLI与云任务里为什么可能不同
输入与条件。本地工作区有未提交的 rules.md;远程分支仍是旧版本。用户在IDE、本地CLI和云端任务分别运行相同问题。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1IDE | 可能接入当前工作区与选中内容 | 需要确认实际提供哪些缓冲区内容。 |
| 2CLI | 通常围绕当前目录和工具读到的文件 | 读取范围、配置路径不能只凭IDE里看见什么来推定。 |
| 3云任务 | 若从远程分支创建环境 | 本地未提交修改不会自然出现。 |
| 4对账 | 分别记录产品表面、仓库commit、工作树与指令来源 | 先统一环境,再比较模型能力。 |
| 5反例 | 云任务“没遵守新规则” | 原因可能是根本没取得该文件,而不是模型不会遵循。 |
把过程连起来。比较Agent产品前先比较它们拿到的版本、上下文入口与权限。
边界与反例。具体加载规则依产品表面和版本而定;本例展示一般分布式工作区差异,不伪造内部Prompt。
依据与适用范围:[COPILOT]。示例输入和场景由本书明确设定。
追问:为什么同一仓库同一句Prompt在IDE和CLI表现不同?
上下文来源可能不同,例如当前编辑器选择、未保存缓冲区、工作目录或可用工具;模型、默认指令、历史和权限也可能不同。比较时要控制这些变量,不能直接把差异归为某个产品“更懂业务”。
追问:自定义Agent声明了工具,模型就一定使用它吗?
声明通常是暴露/允许条件之一,而非强制调用程序。模型仍会根据任务判断。需要必然执行的验证可用确定性工作流或适当hook,而不是依赖描述里反复写“必须”。强制机制也应与风险相称。
追问:怎样做三产品公平对照?
同一任务、同一仓库快照、相同可见证据、相近预算、可比权限,分别记录结果与成本。无法控制的模型和托管环境差异明确列出。比较真实完成率而非只看一次演示或输出长度;也不要把不同表面的能力拼成一个不存在的“统一Copilot”。
Q048|怎样把“产品不好用”的抱怨转成可定位的问题?
核心回答
把失败分解为信息未发现、信息未加载、信息未利用、动作未执行、执行状态未知、验证不足和错误交付。逐层寻找首个偏离预期的证据,而不是在最后一层盲改Prompt。
一个可操作的诊断树
用户说“让它保留API兼容,它还是改了接口”。第一步确认原始约束进入了本轮有效上下文;若在旧会话且已压缩,检查压缩结果是否保留。第二步确认模型读取了调用方与兼容测试;若没读,应改善检索。第三步看计划/补丁是否明确识别兼容风险;若识别了但错误实现,需要实现和验证。第四步看测试是否覆盖旧调用;若只有新功能测试,绿灯不能排除回归。
同样,“没有用MCP”先看连接,再看发现,再看模型可见,再看调用意图,最后看实际执行拒绝。“Token太高”先看冗余上下文、工具返回量、重复失败和多Agent交接,不应一开始降低模型推理能力。
每个诊断结论都要带能反驳自己的检查。如果认为是检索问题,给定正确证据后任务仍失败,就要继续查模型/执行层;如果认为多Agent带来质量,单Agent在同预算下相同成功率,就需重新考虑编排价值。
找不到技能:用四个可观察事实逐步排除
输入与条件。症状:任务未使用某技能。可拿到配置、发现列表、本轮输入和执行日志。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1查安装 | 文件实际存在且格式可解析 | 不存在时修安装,不能分析模型路由。 |
| 2查发现 | 技能元数据是否出现在候选列表 | 目录扫描或作用域不对时修发现链路。 |
| 3查输入 | 本轮是否收到相关描述或显式指定 | 未进入输入时不能说模型“忽略”。 |
| 4查执行 | 技能已读但内部工具被禁用 | 问题是能力或授权,不是技能未选择。 |
| 5最后才评测语义 | 完整证据说明可见且可用但稳定误选 | 用边界任务对比描述,不一开始增加更多规则。 |
把过程连起来。沿实际运行链找第一个缺失环节,比同时改模型、描述和权限更容易得到可靠结论。
边界与反例。流程是诊断方法,不要求为所有简单任务保留大量敏感原始Prompt;日志可做必要脱敏。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:什么是可重复的最小失败例?
保留导致故障的最少任务说明、仓库片段、配置和操作步骤,同时不删除关键边界。把不相关大仓库缩成小fixture能加快回归,但要确认缩小后仍复现同一机制,而不是制造另一个玩具问题。
追问:为什么不能凭一条trace推算系统成功率?
Trace解释一次运行发生什么,不提供任务分布上的概率。机制诊断与统计评估互补:前者找根因,后者判断改动是否普遍有效。单次成功可作为案例,不是总体收益证明。
追问:哪些地方不值得继续优化?
没有具体失败、没有可测收益、增加长期维护负担的机制要谨慎。若简单直接执行已稳定满足目标,再加一层路由和审查可能只增加成本。深入系统的最终目的,是知道哪里该改、哪里不该改,而不是把每层都做复杂。