games-development-ai/contracts/trace/tier2-trace-event.schema.json
zizi d6e977a5ac feat(tier2): 图说对账补全核心引擎待补——n≥30 runbook基建+观测成本接线+L3软检(全加性/observe-only)
按 tier2 图说目标做缺口分析(8族逐元素比对),补齐 0号 spike 为过门收窄掉、
但图说明确要求的「核心引擎待补」项。全部加性/observe-only:金标冒烟仍 ACCEPT
(九门9/9+富游戏三门3/3,门一道没放松),真依赖下全链 import+自测+一款真 M3 跑验证通过。

G族(n≥30 runbook 执行基建):
- worker/config.py: build_model_openai 便宜档 client(deepseek 经 new-api OpenAI 兼容路,与 M3 Anthropic 路并存)
- worker/run_record.py: G4 采集字段表 → 可序列化 RunRecord(含退路树分流键 fail_system)
- worker/fallback_tree.py: 退路树五出口判定器(Q1–Q4 数字触发线,★阈值常量区待校准)
- batch_run.py / aggregate.py: model×variant×n 批跑(断点续跑/失败隔离)+ 矩阵聚合三图喂判定器

H族(观测/成本接线,把孤儿件缝进 run 主链):
- observability/newapi_pricing.py: 活读 new-api /api/pricing 倍率(取不到回落显式参数+告警)
- middleware.py: Tier2TraceMiddleware 挂 writer agent 最外层洋葱,ReAct 全事件旁路 ingest
- agent_loop/studio.py 收口: records→cost_for_run 折¥;真跑实测 cost_rmb=1.29(newapi-live)、trace 647事件 dropped=0
- contracts/trace/: additive trace 事件契约位(忠实 trace.py 落 sink 形状)

D族(L3 视觉软检接线,observe-only):
- agent_loop/studio.py: 收口调一次 M3 多模态(真截图+真玩取证→fun映射0-100),只写 verdict.L3,绝不参与 decision
- 真跑实测 L3 score=25 准确指出空心表现层;decision=fix 仍由 L1硬门/熔断裁、与 L3 无关(防 Goodhart 成立)

留后(不投机抢建):工作室 Agent Team/第二装载落库/控制面/Agent Service 等按 plan 决策②⑤ gate 到 B门后;
n≥30 等统计相是「跑」非「写」(批跑底座已就位);A-model 4插件复用待合并对账;4处图说 spec-drift 待 doc 线回写。
详见 tier2/HANDOFF.md「图说对账补全」节。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 02:11:59 +00:00

144 lines
12 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://wanxiang.ai/contracts/trace/tier2-trace-event.schema.json",
"title": "Tier2TraceEvent",
"description": "tier2 富游戏自治生成线的统一 trace 事件契约(H 族 · A2)。【这是什么】一条 trace 记录 = 一次生成轨迹里的一步,由 tier2/gen-worker/observability/trace.py 的 TraceAdapter 订阅 AgentScope 2.0.2 官方 typed Event System(reply_stream() 吐的 AgentEvent 流)映射而成。tier2 这条线只「订阅 + 映射」,不「埋点 + 采集」——事件流是框架现成件(源码 agentscope/src/agentscope/event/_event.py 已逐条核过)。【为什么独立成约】统一 trace 要让两条异构生成线(tier2 ReAct 多轮 / SAA 16 节点)写进同一张表,靠的是「接口对称、内容不对称」:一个对称的公共核心五字段子集 + 各轨一个不对称的 ext 扩展段——消灭 split-brain 是『每条派发路诚实镜像它真有的字段、没有的绝不编造』,不是强求字段对齐(H1)。本 schema 定义的是 tier2 这一轨写进统一表的记录形状;SAA 那一轨的扩展段由 Java 线另一个 adapter 产、字段各写各的。【字段口径来源】所有字段忠实映射 trace.py 已实现的 TraceStep / to_trace_step,凡代码未产的字段一律不造;代码注释提及为可选、当前未实填的字段(如 cost.tokens.cached、cost.cost_rmb)在各自 description 标注其填充时机。【与图说的对应】公共核心五字段 = H1 对称子集;ext 三段(reasoning/action/observation)= H1/H2 tier2 ReAct 三段扩展段,逐段对到 H2「七类强类型事件」(TextBlock*/ThinkingBlock* → reasoning、ToolCall* → action、ToolResult* → observation;ModelCall* 不单独成段而落 cost.tokens + ext.raw,ReplyStart/End 落 ext.raw 作一步回复边界)。【additive 立位】contracts/trace/ 是契约组新一类 additive 立位,本约不改、不破坏任何现有契约(events / game-package / tier2-verdict 等);随 0号 spike 或控制面 phase-1 才真接代码。状态=建(tier2 待 spike)。详见 docs/architecture/架构/生成引擎/tier2细节图说-H-观测与成本.md 图 H1/H2,以及 tier2/gen-worker/observability/trace.py。",
"type": "object",
"required": ["traceId", "step", "timestamp", "ext"],
"additionalProperties": false,
"properties": {
"traceId": {
"description": "公共核心子集字段(两条生成线同名同义必填)。一次生成的轨迹主键,贯穿整条生成任务链路,是对账 / 成本关联键,对接 tier2-verdict.schema.json 的 evidence.traceId。由 TraceAdapter 在构造时钉定,本次轨迹所有 step 共用同一个 traceId(trace.py TraceAdapter.trace_id)。",
"type": "string",
"minLength": 1
},
"step": {
"description": "公共核心子集字段。第几步(tier2 = TraceAdapter 每 ingest 一个 AgentEvent 递增一次的步序;在 SAA 那一轨语义为第几节点)。从 0 起,由 adapter 内部计数器维护(trace.py TraceAdapter._step)。",
"type": "integer",
"minimum": 0
},
"cost": {
"description": "公共核心子集字段。这一步折成人民币的成本载体;仅在 ModelCallEndEvent 那一步非空(End 事件带 token 用量),其余事件步为 null。本 adapter 只记 token 量,折人民币(cost_rmb)由 cost.py 按 new-api quota 权威口径事后 best-effort 回填(成本权威源是 new-api 计费行,trace 不携金额估算)。",
"type": ["object", "null"],
"$ref": "#/$defs/cost"
},
"verdict": {
"description": "公共核心子集字段。这一步 / 这道门的裁决(可空)。tier2 这一轨当前仅在 ToolResultEndEvent 处由工具执行最终状态(ToolResultState)填入(成功 / 失败 / 拒绝 / 中断 / 运行中),其余事件步为 null;门级裁决(九门 / 富游戏三门)由裁决引擎 judge 另行回填,不在 adapter 映射阶段产生(trace.py to_trace_step:verdict = ToolResultEnd 的 state)。",
"type": ["string", "null"]
},
"timestamp": {
"description": "公共核心子集字段。本步对应事件的发生时刻,取 AgentEvent.created_at(ISO8601 字符串,源码 EventBase.created_at = datetime.now().isoformat())。事件缺 created_at 时为兜底空串。",
"type": "string"
},
"ext": {
"description": "tier2 扩展段(不对称段,各轨各写各的,SAA 轨的扩展段字段与此不同名也不要求对齐)。一个 JSON 对象,必含 raw(原始事件类型 + 关键字段,反查用),按当前事件类型可含 reasoning / action / observation 三段之一(ReAct 三段:想一步 reason → 做一个动作 act → 看一次结果 observe)。一个事件步至多落三段中的一段;ModelCall* 与 ReplyStart/End 不落三段、只把关键字段写进 raw(trace.py to_trace_step:ext = {raw, [reasoning|action|observation]?})。",
"$ref": "#/$defs/ext"
}
},
"$defs": {
"cost": {
"type": ["object", "null"],
"description": "成本载体(ModelCallEndEvent 步)。tokens 由 adapter 实填(从 End 事件抓 input_tokens / output_tokens);cost_rmb 与 tokens.cached 是代码注释明确为可选、当前未由 adapter 实填的字段,标注其归属:cost_rmb 由 cost.py 折算回填,cached 预留缓存命中 token(代码 TraceStep 注释 {tokens?:{in,out,cached}, cost_rmb?})。",
"additionalProperties": false,
"required": ["tokens"],
"properties": {
"tokens": {
"description": "本次模型调用的 token 用量。in / out 由 adapter 从 ModelCallEndEvent.input_tokens / output_tokens 抓取并实填;cached 预留(缓存命中 token,当前 adapter 未填,见 trace.py 注释)。",
"type": "object",
"additionalProperties": false,
"required": ["in", "out"],
"properties": {
"in": { "description": "输入 token 数(ModelCallEndEvent.input_tokens)。", "type": "integer", "minimum": 0 },
"out": { "description": "输出 token 数(ModelCallEndEvent.output_tokens)。", "type": "integer", "minimum": 0 },
"cached": { "description": "缓存命中 token 数(可选,当前 adapter 未实填,预留;代码注释 {in,out,cached})。", "type": "integer", "minimum": 0 }
}
},
"cost_rmb": {
"description": "本步折算人民币(可选)。adapter 不填,由 cost.py 读 new-api logs.quota 按 traceId 关联后 best-effort 回填(权威口径;trace 阶段不带金额估算)。",
"type": "number",
"minimum": 0
}
}
},
"ext": {
"type": "object",
"description": "tier2 扩展段对象。必含 raw;reasoning / action / observation 三段按事件类型至多落一段(纯函数 to_trace_step 对单事件的映射结果)。additionalProperties:false 锁死段名,防止 adapter 之外注入未约定段。",
"additionalProperties": false,
"required": ["raw"],
"properties": {
"raw": { "$ref": "#/$defs/raw" },
"reasoning": { "$ref": "#/$defs/reasoningSeg" },
"action": { "$ref": "#/$defs/actionSeg" },
"observation": { "$ref": "#/$defs/observationSeg" }
}
},
"raw": {
"type": "object",
"description": "原始事件类型 + 关键字段(调试 / 反查用,非判定来源)。所有事件步必填 event(原始 AgentEvent 类型名);其余键按事件类型有条件出现:ModelCallStart 带 model_name、ModelCallEnd 带 model_call_end=true(token 已落 cost),ReplyStart/End 带 reply_boundary 与 reply_id(ReplyStart 另带 agent_name)。映射源:to_trace_step 对 ModelCall* / ReplyStart-End / 其余事件(熔断 / HITL / Custom 等)只落 raw 不落三段。",
"additionalProperties": true,
"required": ["event"],
"properties": {
"event": { "description": "原始 AgentEvent 类型名(如 ReplyStartEvent / ModelCallEndEvent / ToolCallStartEvent;映射逻辑取 type(event).__name__,序列化 dict 形态取 type 字段)。", "type": "string", "minLength": 1 },
"model_name": { "description": "模型名(仅 ModelCallStartEvent 步;ModelCallStartEvent.model_name)。", "type": ["string", "null"] },
"model_call_end": { "description": "标记本步是 ModelCallEndEvent(token 用量已落 cost.tokens),恒为 true。", "type": "boolean" },
"reply_boundary": { "description": "一轮回复边界事件类型名(ReplyStartEvent / ReplyEndEvent;框住一步完整 reason→act→observe)。", "type": "string" },
"reply_id": { "description": "本轮回复 id(ReplyStart/End 步;AgentEvent.reply_id)。", "type": ["string", "null"] },
"agent_name": { "description": "agent 名(仅 ReplyStartEvent 步;ReplyStartEvent.name)。", "type": ["string", "null"] }
}
},
"reasoningSeg": {
"type": "object",
"description": "reason 段(想一步)。映射七类事件中的思考块与文本块:ThinkingBlockStart/Delta/End、TextBlockStart/Delta/End(源码事件类 ThinkingBlock* / TextBlock*)。phase 恒为 reasoning;kind 记具体原始事件类名;delta 仅在 *DeltaEvent 带增量文本 / 思考片段(落 sink 时可截断,此处保真)。",
"additionalProperties": false,
"required": ["phase", "kind"],
"properties": {
"phase": { "description": "段标记,恒为 reasoning。", "type": "string", "const": "reasoning" },
"kind": {
"description": "具体原始事件类名(thinking / text 块的六类之一)。",
"type": "string",
"enum": ["ThinkingBlockStartEvent", "ThinkingBlockDeltaEvent", "ThinkingBlockEndEvent", "TextBlockStartEvent", "TextBlockDeltaEvent", "TextBlockEndEvent"]
},
"delta": { "description": "增量文本 / 思考片段(仅 Delta 事件带;ThinkingBlockDeltaEvent.delta / TextBlockDeltaEvent.delta)。", "type": "string" }
}
},
"actionSeg": {
"type": "object",
"description": "act 段(做一个动作)。映射工具调用三类事件:ToolCallStart/Delta/End(源码 ToolCall* 事件类)。phase 恒为 action;kind 记具体原始事件类名;tool / toolCallId 在事件带对应字段时填(ToolCallStart 带工具名 tool_call_name 与 tool_call_id);argsDelta 仅 ToolCallDeltaEvent 带工具参数 JSON 增量片段。",
"additionalProperties": false,
"required": ["phase", "kind"],
"properties": {
"phase": { "description": "段标记,恒为 action。", "type": "string", "const": "action" },
"kind": {
"description": "具体原始事件类名(工具调用三类之一)。",
"type": "string",
"enum": ["ToolCallStartEvent", "ToolCallDeltaEvent", "ToolCallEndEvent"]
},
"tool": { "description": "工具名(事件带 tool_call_name 时填;ToolCallStartEvent.tool_call_name)。", "type": "string" },
"toolCallId": { "description": "工具调用 id(事件带 tool_call_id 时填;贯穿同一次调用的 Call 与 Result)。", "type": "string" },
"argsDelta": { "description": "工具参数 JSON 增量片段(仅 ToolCallDeltaEvent 带;ToolCallDeltaEvent.delta)。", "type": "string" }
}
},
"observationSeg": {
"type": "object",
"description": "observe 段(看一次结果)。映射工具结果四类事件:ToolResultStart / ToolResultTextDelta / ToolResultDataDelta / ToolResultEnd(源码 ToolResult* 事件类)。phase 恒为 observation;kind 记具体原始事件类名;toolCallId / tool 在事件带对应字段时填;state 仅 ToolResultEndEvent 带(工具执行最终状态 ToolResultState,use_enum_values 故为小写字符串值),同时该 state 被填进顶层 verdict。",
"additionalProperties": false,
"required": ["phase", "kind"],
"properties": {
"phase": { "description": "段标记,恒为 observation。", "type": "string", "const": "observation" },
"kind": {
"description": "具体原始事件类名(工具结果四类之一)。",
"type": "string",
"enum": ["ToolResultStartEvent", "ToolResultTextDeltaEvent", "ToolResultDataDeltaEvent", "ToolResultEndEvent"]
},
"toolCallId": { "description": "对应工具调用 id(事件带 tool_call_id 时填)。", "type": "string" },
"tool": { "description": "工具名(事件带 tool_call_name 时填;ToolResultStartEvent.tool_call_name)。", "type": "string" },
"state": {
"description": "工具执行最终状态(仅 ToolResultEndEvent 带;源码 ToolResultState 枚举,use_enum_values 落为小写字符串值)。该值同时被填进顶层 verdict 作这一步裁决。",
"type": "string",
"enum": ["success", "error", "interrupted", "denied", "running"]
}
}
}
}
}