lili f4cea4677a feat(tier2): 统一 trace 落库 sink + SAA schema + 生成控制面只读后端(切片一 C1/C3a)
JsonlFileSink 把五字段 trace 落 workdir/trace.jsonl(非阻塞·写失败不阻断生成);studio.py sink 接线;SAA 扩展段 schema(接口对称五核心+内容不对称 ext);管理面 3 只读端点 roles/traces/cost(惰性 import 保住「仅 import service.app 不牵 fastapi」红线)。测试 test_jsonl_sink 3 / test_admin_routes 6 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 01:39:06 -07:00
..

contracts/trace/ —— tier2 统一 trace 事件契约(additive 立位)

这一目录登记 tier2 富游戏自治生成线的统一 trace 事件契约。它是契约组的新一类 additive 立位:只新增、不改动也不破坏任何现有契约(events / game-package / tier2-verdict 等),随 0号 spike 或控制面 phase-1 才真接代码。当前状态是「建」——设计已出、schema 已立,tier2 这条线本身整体待 spike 验证。

这是什么

tier2-trace-event.schema.json 定义一条 trace 记录的形状。一条记录 = 一次生成轨迹里的一步,由公共核心五字段(traceId / step / cost / verdict / timestamp)加一个 tier2 私有的 ext 扩展段构成。核心五字段是两条生成线(tier2 ReAct 多轮、SAA 16 节点廉价线)都必须老老实实填上的对称子集;ext 是 tier2 这一轨各写各的不对称段,塞它独有的 ReAct 三段——推理、动作、观察。

这份契约约束的是数据口径,不是采集机制。两条线各按各的框架采集轨迹,但写进同一张表时字段同名同义。要让两条异构线共用一张表,靠的不是强求字段对齐,而是「接口对称、内容不对称」:核心子集对称,扩展段各写各的、没有的字段绝不编造。这才是消灭 split-brain 的真口径。

谁产

产出方是 tier2/gen-worker/observability/trace.py 里的 TraceAdapter。它订阅 AgentScope 2.0.2 官方的 typed Event System(ReAct agent 经 reply_stream() 吐的 AgentEvent 流),把每个事件经纯函数 to_trace_step 映射成这份契约规定的形状。tier2 这条线只「订阅 + 映射」,不「埋点 + 采集」——事件流是框架现成件,源码已逐条核过,不必自造。schema 里每个字段的口径都忠实映射 adapter 已实现的产出:adapter 没产的字段一概不写进契约;代码注释里标为可选、当前还没实填的字段(如 cost.tokens.cachedcost.cost_rmb),在 schema 的 description 里注明了它们各自的填充时机。

SAA 那一轨的扩展段由 Java 线另一个 adapter 产,字段与本契约的 ext 不同名也不要求对齐,各写各的——本契约只管 tier2 这一轨。

谁消费(后续)

消费链路按 H 族图说 H2 是「Event System → TracingMiddleware → OpenTelemetry span → AgentScope Studio」。trace 记录最终落进已部署的 MySQL 加对象存储,不上重型可观测中间件;观测要早建,是为了 spike 调试和成本对账当下就用得上,而不是要先铺一套独立基建。traceId 贯穿整条生成任务链路,既是反查键,也是成本关联键——它对接 tier2-verdict.schema.jsonevidence.traceId,让一次验收终判能反查到它对应的全过程轨迹。成本侧由 cost.py 读 new-api 的 logs.quota 权威口径,按 traceId 关联后把人民币金额 best-effort 回填进 cost.cost_rmb(trace 阶段只记 token,不携金额估算)。

两份 schema 的关系

本目录维护两份 trace schema,对应两条异构生成线:

schema 文件 对应生成线 特有扩展段
tier2-trace-event.schema.json tier2 富游戏自治线(Python / AgentScope ReAct) ext:reasoning / action / observation 三段(ReAct 推理→动作→观察)
saa-trace-event.schema.json SAA 16 节点廉价生成线(Java / Spring AI Alibaba) ext:nodeIndex / nodeName / nodeStatus(节点位置与执行状态)

核心五字段(traceId / step / cost / verdict / timestamp)在两份 schema 里完全同名同义——这是 H1「接口对称」子集,让两条线写进同一张 trace 表时无需 join 也能按 traceId 对账。ext 扩展段各写各的、字段不要求对齐(H1「内容不对称」):tier2 的推理/动作/观察三段 SAA 轨不存在,SAA 的节点编号/名称/状态 tier2 轨也不存在,没有的字段绝不编造

谁消费哪份:目前 tier2 侧 adapter(tier2/gen-worker/observability/trace.pyTraceAdapter)已产出并落盘(JsonlFileSink 写进 _tier2-gen/<game_id>/trace.jsonl);SAA 侧 adapter 在 Java 后端待产线接线(控制面 phase-1)。

与 H 族图说的对应

设计权威是 docs/architecture/架构/生成引擎/tier2细节图说-H-观测与成本.md

  • 公共核心五字段 = 图 H1 的对称子集。
  • ext 的三段(reasoning / action / observation)= 图 H1/H2 讲的 tier2 ReAct 三段扩展段,逐段对到图 H2 摊开的七类强类型事件:TextBlock*ThinkingBlock* 映射进 reasoning,ToolCall* 映射进 action,ToolResult* 映射进 observation
  • 图 H2 的另外两类事件——ModelCallStart / ModelCallEndReplyStart / ReplyEnd——在代码里不单独成段:ModelCallEnd 带的 token 用量落进 cost.tokens(供 H3 成本台账抓取),其余关键字段(模型名、回复边界、reply_id、agent 名)落进 ext.raw。这一点是代码实现与图说「七类事件」措辞的精确对应,schema 的顶层 description 与各 $defs 段里都注明了。

写失败时的策略遵循 H1/H2 的 best-effort 铁律:轨迹写失败默认不阻塞主生成流程,但落一条告警——不让一次落库抖动把整局已经跑出来的生成废掉,也不让它无声丢失。这条策略在 adapter 代码里实现(ingest / _emit_to_sink 全包 try、只告警不抛),不在 schema 的约束范围内,但读契约时该一并知道。