
千笔-AIWritePaper · https://www.aiwritepaper.com
把 Agents SDK 的 tracing 当成「开着就好」很容易踩坑:默认已启用 ,会把 LLM 生成、工具调用、handoff、guardrail 等写入 Traces 仪表盘;但默认也会在 span 里带上可能敏感的输入/输出 。官方 Tracing 与 Running agents 把控制面收在 RunConfig:tracing_disabled、trace_include_sensitive_data、workflow_name / trace_id / group_id、trace_metadata,以及 tracing={"api_key": ...} / include_task_and_turn_spans。全局还有 set_tracing_disabled、OPENAI_AGENTS_DISABLE_TRACING、set_tracing_export_api_key、长驻 worker 的 flush_traces()。ZDR(Zero Data Retention)组织上 tracing 不可用 。本文按工程笔记钉死开关语义、可跑 smoke 与生产禁区。示例模型名写作 gpt-4o,以你账号可用快照与官方文档为准。

图:上方 RunConfig 追踪字段;中部敏感数据与导出键;下方生产禁区与 flush。
目标说明
读完你应能独立完成五件事:
- 用一句话说清:tracing 默认开;单次 run 用
RunConfig.tracing_disabled=True关,或用环境变量 /set_tracing_disabled全局关。 - 写出可跑片段:
RunConfig(workflow_name=..., group_id=..., trace_include_sensitive_data=False)smoke。 - 分清三层关闭:全局 env / 代码全局 / 单次
RunConfig;以及「关 tracing ≠ 立刻丢掉已缓冲导出」。 - 钉死敏感数据:
trace_include_sensitive_data=False(或OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA=0)省略 generation/function 的原始 I/O。 - 列出生产禁区:默认开敏感数据上生产、ZDR 环境仍指望仪表盘、Celery/FastAPI 后台任务不
flush_traces、把模型流量密钥与 tracing 导出混用却不隔离。
规格钉死(对照官方 Tracing / RunConfig):
- 默认 :tracing 开启;默认 workflow 名是字面量
Agent workflow。 - 关闭三路径 :
OPENAI_AGENTS_DISABLE_TRACING=1;set_tracing_disabled(True);RunConfig(tracing_disabled=True)。 - 敏感数据 :
RunConfig.trace_include_sensitive_data默认 True;可按 run 关,或用环境变量改默认。 - 元数据 :
workflow_name/trace_id(须trace_<32 字母数字>)/group_id/trace_metadata。 - 导出键 :可用
set_tracing_export_api_key;单次 run 可用RunConfig(tracing={"api_key": "..."});模型客户端可use_for_tracing=False再单独配 tracing 键。 - 长驻进程 :
BatchTraceProcessor后台批导出;需要立刻可见时在trace()退出后调用flush_traces()。
适用边界
适合开 tracing
- 开发与预发:要看 agent / generation / tool / guardrail / handoff span。
- 多轮会话:用同一
group_id(如 thread id)串联多条 trace。 - 非 OpenAI 模型:仍可用 OpenAI tracing 导出键把链路送到 Traces 仪表盘(见官方 third-party 说明)。
- 需要按工作流命名:
workflow_name="客服退款"比默认Agent workflow可检索。
更适合先关或脱敏
- 生产处理 PII / 密钥 / 病历 :先
trace_include_sensitive_data=False,再决定是否整 run 关闭。 - ZDR 组织:官方写明 tracing 不可用;不要把「仪表盘空白」当 SDK bug。
- 合规要求不得出境调试载荷 :关 tracing 或自建
set_trace_processors,不要默认打到 OpenAI 后端还当「本地日志」。
不该指望它单独搞定
- 关 tracing = 已缓冲数据消失 :禁用后仍可能
flush_traces()导出已缓冲内容。 include_task_and_turn_spans=False= 无追踪:只是更紧凑;agent/generation/function 等仍记。- 有
response_id相关元数据 = 已脱敏完毕:官方端点在脱敏后仍可能保留关联 id;自定义端点行为不同,勿一刀切假设。 - 日志开关 = tracing 开关 :
OPENAI_AGENTS_DONT_LOG_MODEL_DATA管的是 Python 日志/诊断,不是 Traces 仪表盘。
风险提示
把生产流量的默认敏感 span 直接导出到共享项目,等于把用户输入写进可检索调试面。长驻 worker 不 flush,排障时会误判「没跑到 tracing」。模型走兼容端点却继续用同一把业务键导出 tracing,权限面纠缠难审计。
步骤与机制
1. RunConfig 追踪字段对照
| 字段 / 开关 | 做什么 | 不做什么 |
|---|---|---|
tracing_disabled |
本 run 不记录 | 不清理已缓冲导出 |
trace_include_sensitive_data |
是否纳入 LLM/工具 I/O | 不等于关 tracing |
workflow_name |
逻辑工作流名 | 不是计费标签 |
group_id |
多 trace 串联 | 不是自动会话存储 |
trace_id |
指定 id | 格式不对会出问题 |
tracing.api_key |
本 run 导出键 | 不改模型请求键 |
include_task_and_turn_spans |
是否含 task/turn | 关了仍有其他 span |
flush_traces() |
阻塞刷出缓冲 | 不要在 trace() 未结束时乱刷半截 |
2. 可跑 smoke:workflow + 脱敏 + 可选关闭
先 pip install openai-agents,并导出 OPENAI_API_KEY(无 key 时至少应能 import 与构造 RunConfig)。
python
import asyncio
from agents import Agent, Runner, RunConfig
MODEL = "gpt-4o" # 占位:以账号可用快照为准
async def main() -> None:
agent = Agent(
name="TraceSmoke",
instructions="用一两句回答;不要编造未给出的数字。",
model=MODEL,
)
cfg = RunConfig(
workflow_name="runconfig_tracing_smoke",
group_id="thread_smoke_001",
trace_include_sensitive_data=False, # 生产默认建议关敏感载荷
# tracing_disabled=True, # 需要本 run 全关时再打开
tracing={"include_task_and_turn_spans": False},
)
result = await Runner.run(
agent,
"金门大桥在哪座城市?只答城市名。",
run_config=cfg,
)
print("ok", bool(result.final_output), "workflow", cfg.workflow_name)
if __name__ == "__main__":
asyncio.run(main())
验收:进程无异常;有 key 时能拿到输出;构造时显式写了 workflow_name 与 trace_include_sensitive_data=False。无 key 时至少确认 RunConfig(...) 可实例化,且没有把脱敏开关留成「以后再说」。
3. 全局关闭与导出键隔离(可选)
python
from openai import AsyncOpenAI
from agents import (
set_default_openai_client,
set_tracing_export_api_key,
set_tracing_disabled,
)
# 模型走兼容端点时,常需要把 tracing 导出键拆开
client = AsyncOpenAI(base_url="https://example.invalid/v1", api_key="provider-key")
set_default_openai_client(client, use_for_tracing=False)
set_tracing_export_api_key("sk-tracing-only")
# 合规窗口临时全局关闭
# set_tracing_disabled(True)
单次 run 覆盖导出键:
python
cfg = RunConfig(tracing={"api_key": "sk-tracing-123"})
4. 长驻 worker:trace 包一层 + flush
python
from agents import Runner, flush_traces, trace
def job(prompt: str) -> str:
try:
with trace("celery_task", group_id="worker_pool_a"):
result = Runner.run_sync(agent, prompt)
return result.final_output
finally:
flush_traces() # 在 trace 上下文退出后再刷
生产禁区
- 默认敏感数据开着上生产:用户输入、工具参数进仪表盘。
- ZDR 环境仍依赖 Traces 排障:官方不可用;另建可审计日志。
- Celery / FastAPI BackgroundTasks 不 flush:任务结束仪表盘空白,误判「没追踪」。
- 业务 key 与 tracing key 混用且不隔离 :
use_for_tracing=False未设,权限难拆。 - 把
tracing_disabled当「已删除历史 span」:只影响新记录;缓冲仍可能被 flush。 trace_id手写不合规格式:应用启动期才暴露,难查。- 日志脱敏 env 当 tracing 脱敏 :
DONT_LOG_*≠TRACE_INCLUDE_SENSITIVE_DATA。 - 多租户共用同一 workflow 名且无 group_id:仪表盘无法按会话切开。
可验证清单
| # | 检查项 | 通过标准 |
|---|---|---|
| 1 | 本 run 是否显式 workflow_name |
非默认 Agent workflow 或已文档化接受默认 |
| 2 | 生产是否关敏感载荷 | trace_include_sensitive_data=False 或等价 env |
| 3 | 关闭路径是否选对 | 全局 / 单次 / ZDR 三选一写进配置说明 |
| 4 | 长驻任务是否 flush | trace() 退出后有 flush_traces() |
| 5 | 导出键是否可轮换 | tracing key 与模型 key 可分开轮换 |
| 6 | smoke 是否可跑 | 上文片段 import + 构造通过 |
踩坑
- 只在本地关敏感数据,预发配置漂移回默认 True。
- 用
set_trace_processors([])清空后以为「完全无导出」,却忘了自己还add_trace_processor了一个会打外网的处理器。 - 在
with trace()内部过早flush_traces(),刷出半截 span。 - 把
group_id当成 session 持久化:它只关联 traces,不存对话历史。
开关决策树(可进配置说明)
| 场景 | 推荐 | 记录什么 |
|---|---|---|
| 本地单测 | 可开敏感数据,短 workflow 名 | 便于对照失败输入 |
| 预发联调 | 开 tracing,关敏感数据 | group_id=预发环境名 |
| 生产 PII | 关敏感或整 run 关 | 配置项 + 变更单 |
| ZDR 组织 | 不要指望仪表盘 | 自建审计日志方案名 |
| 兼容端点 | use_for_tracing=False + 导出键 |
两把键的轮换人 |
| 长驻队列 | with trace + flush_traces |
任务结束可见性 SLA |
把上表抄进仓库 observability.md 比在聊天里约定「我们注意点」更可审计。
与 sessions / server 续写的边界
tracing 的 group_id 不是 会话存储。对话历史仍应选 sessions、to_input_list() 或 server 续写三选项之一;追踪只负责「这次工作流发生了什么」。常见误配:
- 以为设置了
group_id就等于SQLiteSession已持久化。 - 在 session 与
previous_response_id同 run 互斥报错时,去关 tracing「碰运气」。 - 用 trace 仪表盘里的输入回放代替业务审计日志。
正确拆分:状态 用会话机制;观测 用 tracing;授权用业务日志。三者不要互相冒充。
环境变量速查(启动前)
| 变量 | 作用 | 生产注意 |
|---|---|---|
OPENAI_AGENTS_DISABLE_TRACING |
1 全局关 |
排障窗口要有打开流程 |
OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA |
0/1 默认敏感载荷 |
与 RunConfig 覆盖关系写清 |
OPENAI_AGENTS_DONT_LOG_MODEL_DATA |
日志是否含模型 I/O | 不等于 tracing 脱敏 |
OPENAI_AGENTS_DONT_LOG_TOOL_DATA |
日志是否含工具 I/O | 同上 |
OPENAI_ORG_ID / OPENAI_PROJECT_ID |
默认导出归属 | 多项目勿混 |
建议在应用入口打印「tracing_disabled / include_sensitive / workflow_default」三元组到启动日志(不要打印密钥),便于值班对照。
最小回归:两轮对照
无仪表盘时,至少在代码层做两轮构造回归:
- 轮 A :
trace_include_sensitive_data=True(仅本地),确认字段可设。 - 轮 B :改为
False,确认生产配置模板采用 B。 - 额外一轮:
tracing_disabled=True,确认与 B 可切换且有文档说明何时用。
把三轮结果记进 _w/tracing-smoke-checklist.md。没有记录的「我设过了」不算过线。
自定义处理器时的禁区加码
若使用 add_trace_processor / set_trace_processors:
- 替换默认处理器后,不会自动再发到 OpenAI,除非你自己加回去。
- 自建处理器若同步写磁盘,注意别把敏感 span 明文落无加密盘。
- 处理器抛错不应默默吞掉业务成功;至少打告警计数。
当天可交付
- 一份
RunConfig生产模板(脱敏开、workflow 名规范、group_id 规则)。 - 一份 worker
flush_traces范例(或明确「接受分钟级延迟」的书面决定)。 - 一份
_w/tracing-smoke-checklist.md勾选记录。 - 一份禁区清单贴进 PR 模板:敏感默认、ZDR、混用密钥、不 flush。
FAQ(工程值班常问)
Q:关了 trace_include_sensitive_data 之后,仪表盘里还能看见什么?
A:仍可看见工作流结构、agent/tool/handoff 等 span 骨架;官方端点场景下可能保留关联用的 response_id 一类元数据。不要假设「完全空白」,也不要假设「仍等于全量 I/O」。
Q:tracing_disabled=True 和环境变量全局关有何优先?
A:单次 RunConfig 适合「这一类请求脱敏/关闭」;环境变量适合整进程合规窗口。不要两套开关互相不知道,配置说明里写清覆盖关系。
Q:没有 OpenAI 模型键,还能看 Traces 吗?
A:官方允许为 tracing 单独设置导出键;非 OpenAI 模型也可在具备导出键时把链路送到仪表盘。具体以官方 Models / Tracing 章节为准,不要把「免费额度」数字写进对外承诺。
Q:流式 run 要不要特殊对待 tracing?
A:仍走同一套 RunConfig。需要立即可见时,同样在 trace 上下文结束后 flush_traces()。不要把「流式事件没刷完」误诊成 tracing 坏了。
Q:能不能用 tracing 替代审计日志?
A:不能。审计要回答「谁在何时对哪条业务记录做了授权决策」;tracing 回答「这次 agent 工作流的 span 树」。混用会在合规审查时两边都不合格。
配置模板示例(可进仓库)
python
# prod_run_config.py --- 示意,键名勿提交
from agents import RunConfig
def build_prod_run_config(*, thread_id: str, workflow: str) -> RunConfig:
return RunConfig(
workflow_name=workflow,
group_id=thread_id,
trace_include_sensitive_data=False,
tracing={"include_task_and_turn_spans": False},
)
def build_local_debug_run_config(*, thread_id: str) -> RunConfig:
return RunConfig(
workflow_name="local_debug",
group_id=thread_id,
trace_include_sensitive_data=True, # 仅本地
)
把 build_prod_run_config 设为唯一生产入口,比在各调用点零散传字典更不容易漏脱敏。
失败含义(写进值班手册)
| 现象 | 可能含义 | 下一步 |
|---|---|---|
| 仪表盘完全无新 trace | 全局关 / ZDR / 导出键错 / 未 flush | 查三开关与 flush |
| 有 trace 但无输入输出 | 脱敏已生效 | 预期行为;勿当故障 |
| 有输入输出在生产 | 脱敏未生效 | 立刻改默认并轮换项目权限 |
| 偶发可见、偶发不可见 | worker 未统一 flush | 在 finally 固定调用 |
| trace_id 报错 | 格式不合规 | 用自动生成或严格校验 |
总结
RunConfig 是 tracing 的单次控制面:关不关、敏不敏、叫什么名字、跟哪次会话一组、用哪把导出键,都应在跑之前写死。可跑 smoke 证明开关可构造;生产禁区证明「默认开着」不等于「可以原样上线」。对照官方 Tracing / Running agents / Configuration;模型名与键名以你环境为准。
参考
- Tracing:https://openai.github.io/openai-agents-python/tracing/
- Running agents(RunConfig):https://openai.github.io/openai-agents-python/running_agents/
- Configuration:https://openai.github.io/openai-agents-python/config/