跳到主要内容

会话、追踪与人在环路

本章讲三个横切整个 run 的机制:会话记忆(自动管理多轮历史)、追踪(自动可观测)、人在环路(让工具调用暂停等人类批准再继续)。

4.1 会话(Session):自动记住多轮对话

它要解决的小问题

多轮对话里,你不想每次 Runner.run 都手动把上一轮的全部历史拼进 input。你希望「把这轮的新输入丢进去,SDK 自己接着上次的历史跑,并把这轮产物存回去」。

思路:一个极简的存储协议

Session 是个 Protocol(memory/session.py:14),只要求四个方法:

方法干什么
get_items(limit)取历史 item
add_items(items)追加新 item
pop_item()弹出最后一个(用于回滚/重试)
clear_session()清空

就这么薄。任何实现了这四个方法的对象都能当会话用。SDK 内置 SQLiteSession(memory/sqlite_session.py),pyproject.toml 里还有 Redis / SQLAlchemy / MongoDB 等可选实现(pyproject.toml:46-50 的 optional 依赖)。

run 循环怎么用它

传了 session 后,AgentRunner.run 在开头用 prepare_input_with_session(run.py:527run.py:541)把「会话历史 + 本轮新输入」合并成喂给模型的 input;run 过程中用 save_result_to_session(run.py:751 等多处)把新产物持久化。

注意一个互斥: 会话持久化只在「没用 OpenAI 服务端对话追踪」时启用(run.py:571session_persistence_enabled = session is not None and server_conversation_tracker is None)。如果你用了 conversation_id / previous_response_id(让 OpenAI 服务端管历史),本地 Session 就不再重复存。

4.2 追踪(Tracing):每一步都留痕

它要解决的小问题

agent 跑起来是个黑盒:调了几次模型?每次花多少 token?跑了哪些工具?哪一步交接的?出问题要能回放。

思路:trace + 嵌套 span

SDK 借用分布式追踪的模型:一次 run 是一个 trace,里面嵌套一棵 span 树。你在 01 章的循环里其实已经见过它们:task_spanagent_spanturn_spanrun.py 里被层层 start(run.py:645run.py:1045run.py:1166)。

常见 span 类型(tracing/__init__.py):

span包住什么
agent_span某个 agent 的整段执行
turn_span一个回合
generation_span / response_span一次模型调用
function_span一次工具调用
handoff_span / guardrail_span交接 / 护栏

上报机制:批量异步导出

默认的处理器是 BatchTraceProcessor(tracing/processors.py:522),默认导出器 BackendSpanExporter(tracing/processors.py:33)会把 span 批量 POST 到 OpenAI 的追踪后端(tracing/processors.py:34_OPENAI_TRACING_INGEST_ENDPOINT = "https://api.openai.com/v1/traces/ingest"),所以你在 OpenAI 控制台能看到可视化的 run。

你可以换处理器(add_trace_processor / set_trace_processors,tracing/__init__.py:4658)接到自己的可观测系统,或用 set_tracing_disabled / run_config.tracing_disabled 关掉。

4.3 人在环路(HITL):工具调用暂停等审批

这是 01 章那个 NextStepInterruption 的完整故事,也是 SDK 最体现「可恢复」设计的地方。

它要解决的小问题

有些工具很危险(删库、转账、发邮件)。你想在模型决定调用、但尚未执行时暂停,弹给人类「批准 / 拒绝」,批准了才真跑。

思路:中断 → 序列化状态 → 审批 → 续跑

整个流程是一个「run 可以中途存档再读档」的故事:

1. 工具标了 needs_approval=True (tool.py:426)


2. 执行该工具前,产生一个 ToolApprovalItem(中断),不执行工具
│ execute_tools_and_side_effects 收集到 interruptions

3. 返回 NextStepInterruption → run 提前结束
│ RunResult.interruptions 里是待审批的工具调用 (result.py:367)

4. 调用方: result.to_state() 拿到可序列化的 RunState (result.py:393)
│ 对每个中断: state.approve(item) 或 state.reject(item)

5. 把 RunState 当 input 再传给 Runner.run(state) → 从断点续跑
│ run.py:469 检测 isinstance(input, RunState) → is_resumed_state

6. resolve_interrupted_turn(run.py:849) 用已审批的决定继续执行那些工具

审批的 API

中断信息是 ToolApprovalItem(items.py:508)。审批决定挂在两个层面:

  • RunState.approve(item) / RunState.reject(item)(run_state.py:332 / run_state.py:338)——给「读档续跑」用。
  • RunContextWrapper.approve_tool / reject_tool / get_approval_status(run_context.py:355 / 363 / 377)——运行时上下文里的审批记录。

always_approve=True 可让某工具后续调用都自动通过,不再每次问。

续跑如何不重跑已完成的工作

RunState 保存了 _current_step_last_processed_response、已生成的 items、模型响应等(result.py:71_populate_state_from_result)。续跑时 run.py:840 检测到 _current_stepNextStepInterruption,直接进 resolve_interrupted_turn 接着那个回合往下走,而不是从头来过——这就是为什么「下一步即数据 + 可序列化状态」(01 章 §8)是基础设施级的设计。

这套设计也支撑嵌套审批

当被 Agent.as_tool 包装的子 agent 内部触发审批时,父 run 的审批会被镜像进子 run(agent.py:694_apply_nested_approvals),保证嵌套场景下审批语义一致。

4.4 巧妙之处

  • Session 协议极简(4 个方法),把「历史存哪」完全交给实现,所以能从内存 SQLite 一路扩到 Redis/Mongo/SQLAlchemy 而内核不变。
  • Tracing 复用分布式追踪心智模型(trace + span 树 + 批量导出),既能在官方控制台可视化,又能接自家系统。
  • HITL 不是特例而是 RunState 可序列化的自然结果:中断只是「一种提前返回的 next_step」,审批只是「往状态里写决定」,续跑只是「拿状态当输入再跑」——一套机制干三件事。

4.5 代码地图

主题文件路径符号名
会话协议src/agents/memory/session.pySessionSessionABC
SQLite 实现src/agents/memory/sqlite_session.pySQLiteSession
会话合并/持久化src/agents/run_internal/session_persistence.pyprepare_input_with_sessionsave_result_to_session
追踪入口src/agents/tracing/__init__.pytraceagent_spangeneration_span
批量导出src/agents/tracing/processors.pyBatchTraceProcessorBackendSpanExporter
中断/下一步src/agents/run_internal/run_steps.pyNextStepInterruption
审批 itemsrc/agents/items.pyToolApprovalItem
运行状态src/agents/run_state.pyRunStateRunState.approveRunState.reject
续跑src/agents/run.pyAgentRunner.run(is_resumed_state 分支)、resolve_interrupted_turn
上下文审批src/agents/run_context.pyRunContextWrapper.approve_toolget_approval_status