架构分层与 run_internal 内部设计
DEV-01: 架构分层与 run_internal 内部设计
学习目标
- 理解
run.py与run_internal/的分层边界 - 掌握
run_internal/各模块的职责划分 - 理解添加新工具/输出项类型时的 7 文件联动
- 掌握 RunState 序列化版本管理机制
项目实践
run.py:精简编排入口
run.py(93KB)仅保留公共流程控制:
Runner类:三个入口方法(run/run_sync/run_streamed)AgentRunner类:主循环编排(初始化 → 循环 → 结果构建)RunConfig:运行时配置(max_turns、guardrails、sandbox 等)- 循环内部的 guardrails 调度与
NextStep分发
所有循环 internals 外包到 run_internal/。
run_internal/ 模块职责
| 模块 | 核心函数 | 职责 |
|---|---|---|
run_loop.py | run_single_turn(), get_new_response() | 模型调用、工具执行编排、单步结果构建 |
turn_resolution.py | process_model_response(), check_for_final_output_from_tools() | 模型响应解析、handoff/tool/final-output 判定 |
tool_execution.py | _execute_tool_plan(), _FunctionToolBatchExecutor | 工具并行执行、故障仲裁、guardrails 执行 |
tool_planning.py | _build_plan_for_fresh_turn(), execute_mcp_approval_requests() | 工具规划、MCP 审批收集 |
items.py | 归一化、去重 | RunItem 类型转换、审批过滤 |
session_persistence.py | save_result_to_session(), rewind_session_items() | Session 保存/回退 |
oai_conversation.py | OpenAIServerConversationTracker | 服务端对话 delta 计算 |
error_handlers.py | _handle_run_error() | 错误处理分发 |
7 文件联动:添加新工具类型
添加新的工具/输出项类型时,需要同步更新:
src/agents/items.py— RunItem 类型定义与转换src/agents/run_internal/run_steps.py— ProcessedResponse 与工具运行结构体src/agents/run_internal/turn_resolution.py— 模型输出处理与 RunItem 提取src/agents/run_internal/tool_execution.py— 工具执行src/agents/run_internal/tool_planning.py— 工具规划src/agents/run_internal/items.py— 归一化、去重、审批过滤src/agents/stream_events.py— 流事件名称src/agents/run_state.py— RunState 序列化/反序列化
为什么需要这么多文件:每个文件负责一个横切面(类型系统、执行、规划、流事件、持久化),新类型需要在每个横切面注册。
RunState 序列化版本管理
RunState 是可序列化的运行快照,支持暂停/恢复(HITL 场景)。
版本管理机制:
CURRENT_SCHEMA_VERSION = "1.10"SUPPORTED_SCHEMA_VERSIONS:所有历史版本"1.0"~"1.10"的集合- 每个版本有一行摘要在
SCHEMA_VERSION_SUMMARIES - 反序列化对不支持的版本直接失败(fail-fast),清晰列出支持的版本
- 未发布的 schema 版本可在发布前重编号/压缩(中间快照有意不支持)
序列化内容:
current_turn,current_agent(含重复名称的 BFS 身份解析)original_input,model_responses(含 usage、output、response_id)context(usage、approvals、tool_input、类型往返警告)generated_items,session_items(完整 RunItem 列表)last_processed_response(工具动作、中断、嵌套 agent-as-tool 状态)current_step(待处理工具审批)- Guardrail 结果(重新创建返回序列化输出的 stub guardrail 函数)
问题与规避
| 问题 | 规避方案 |
|---|---|
| 修改 RunState 格式后旧版本无法加载 | 更新 CURRENT_SCHEMA_VERSION 并在 SCHEMA_VERSION_SUMMARIES 添加变更说明 |
| Agent 重复名称导致反序列化身份混淆 | BFS 遍历解析 agent 图,使用稳定身份键 |
| Context 类型在序列化/反序列化中丢失 | 使用 context_override / context_deserializer 优先级恢复 |
设计取舍
为什么选择 run_internal 分包而非单个巨型文件
将 80KB+ 的循环逻辑拆分为 8 个模块:(1) 每个模块可独立测试;(2) 修改循环 internals 不影响 run.py 的公共 API;(3) 代码导航更清晰。
代价:新工具类型需要跨 7-8 个文件同步修改。SDK 通过清晰的模块边界和命名约定来降低认知负担。