Appearance
代理循环 — 练习
练习 1:追踪一次完整的代理循环
从一个简单的用户指令(如"创建一个 hello world 程序")出发,追踪代理循环的完整执行过程。
参考答案
以 codex "创建一个 hello world 程序" 为例,完整执行过程如下:
- CLI 入口:
cli/src/main.rs的cli_main()将指令分发到 exec 模式 - Session 创建:
Codex::spawn(CodexSpawnArgs)创建tx_sub/rx_event队列对,初始化Session - 提交用户输入:
codex_thread.submit(Op::UserInput("创建一个 hello world 程序")) - 上下文构建:
session.build_initial_context()拼装:- System prompt(角色定义 + 安全规则)
- 工具声明(
shell、apply_patch等 function definitions) - 用户消息
- 首次模型调用:
ModelClientSession::stream()通过 WebSocket/SSE 发送到 LLM 后端 - 模型响应:LLM 返回工具调用
shell("echo 'print(\"hello world\")' > hello.py") - 工具执行:
Session通过SandboxManager::transform()包装命令,在沙箱中执行 - 结果回传:将执行结果(stdout/stderr)追加到上下文
- 二次模型调用:LLM 判断任务完成,返回纯文本响应
- Turn 完成:发送
Event::TurnCompleted,包含TurnItem列表
整个过程中,每一步状态变化都通过 rx_event 通道通知消费者,消费者(TUI/Exec/SDK)据此更新界面或输出。
练习 2:分析上下文构建过程
分析代理循环如何将对话历史、文件内容、工具调用结果构建为 LLM 的输入上下文。
参考答案
上下文构建的核心在 Session::build_initial_context() 方法中,组装以下内容:
System Prompt:由
build_prompt_input()构建,包含角色定义("你是 Codex,一个编程助手")、安全规则、workspace 根目录信息、可用工具描述。这是上下文的固定前缀,每次 turn 都包含。对话历史:
ConversationHistory是TurnItem的列表。每个TurnItem包含:- 用户输入(
UserInput) - 模型输出(文本 + 工具调用)
- 工具执行结果(stdout/stderr/exit code)
- 审批记录(如果涉及人工审批)
- 用户输入(
工具声明:内置工具(
shell、apply_patch)+ MCP 注册的外部工具。tool_is_model_visible()根据工具的 UI 可见性元数据过滤,确保模型只看到应使用的工具。压缩策略:当对话历史超过上下文窗口时,
compact_conversation_history()使用 LLM 对早期历史生成摘要,调用replace_compacted_history()替换原始历史。这确保了长时间对话不会因上下文溢出而失败。
上下文的 token 用量通过 update_token_usage_info() 持续跟踪,recompute_token_usage() 在历史变更后重新计算总量。
练习 3:理解 Rollout 策略
分析 Rollout 如何控制代理的迭代次数和终止条件。
参考答案
Rollout 策略从三个维度控制代理行为:
迭代次数控制:每个 turn 内的模型调用 + 工具执行循环受
max_turns参数限制。当达到上限时,代理停止发起工具调用,将当前结果作为 turn 的最终输出。这防止了代理陷入无限循环(如反复尝试修复一个无法修复的 bug)。执行策略过滤:
ExecPolicy的Policy::check()方法在工具执行前评估每个命令。Decision枚举有三个值:Allow:自动执行(如ls、cat等低风险命令)Prompt:需要用户审批(如rm、npm install等中风险命令)Forbidden:直接拒绝(如明确的危险操作)
规则通过
add_prefix_rule()和add_network_rule()添加,支持按命令前缀和网络域名匹配。执行轨迹记录:
RolloutRecorder记录每个 turn 的完整执行过程,包括模型输入/输出、工具调用和结果。RolloutRecorderParams包含Cursor(会话位置)和SessionMeta(会话元数据),用于后续回放和分析。
此外,steer_input() 提供了运行时中断机制:用户可以在 turn 执行过程中插入新指令或要求中断,SteerInputError 枚举处理了无活动 turn、turn 类型不匹配等边界情况。
拓展挑战
- 追踪
compact_conversation_history()的调用时机和压缩算法 - 分析
SteerInputError各变体在实际使用中的触发条件 - 阅读
RolloutRecorder的持久化格式,理解轨迹数据如何存储 - 分析
Event枚举的所有变体,理解事件分类体系