fake_data.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785
  1. """Deterministic, internally consistent fake run for the current pipeline."""
  2. from __future__ import annotations
  3. import hashlib
  4. import json
  5. from dataclasses import dataclass
  6. from decimal import Decimal
  7. from typing import Any
  8. from .contracts import ENUM_CATALOG, SCHEMA_CATALOG
  9. from .models import (
  10. FlowEdge,
  11. FlowNode,
  12. Metric,
  13. NodeDetail,
  14. PhaseFrame,
  15. RecordEnvelope,
  16. RunGraph,
  17. RunSummary,
  18. )
  19. RUN_ID = 10001
  20. ROOT_TRACE_ID = "trace_fake_phase3_10001"
  21. STAMP = "2026-07-20T10:24:36.482000+08:00"
  22. START = "2026-07-20T10:18:04.103000+08:00"
  23. def digest(label: str) -> str:
  24. return hashlib.sha256(label.encode()).hexdigest()
  25. def wire_digest(label: str) -> str:
  26. return f"sha256:{digest(label)}"
  27. def normalize_json(value: Any) -> Any:
  28. if value is None or isinstance(value, (str, bool, int)):
  29. return value
  30. if isinstance(value, float):
  31. rendered = format(Decimal(str(value)), "f")
  32. if "." in rendered:
  33. rendered = rendered.rstrip("0").rstrip(".")
  34. return "0" if rendered in {"-0", ""} else rendered
  35. if isinstance(value, dict):
  36. return {key: normalize_json(value[key]) for key in sorted(value)}
  37. if isinstance(value, (list, tuple)):
  38. return [normalize_json(item) for item in value]
  39. raise TypeError(f"unsupported fake canonical JSON value: {type(value).__name__}")
  40. def canonical_digest(payload: Any) -> str:
  41. encoded = json.dumps(
  42. normalize_json(payload),
  43. ensure_ascii=False,
  44. allow_nan=False,
  45. sort_keys=True,
  46. separators=(",", ":"),
  47. ).encode("utf-8")
  48. return hashlib.sha256(encoded).hexdigest()
  49. def canonical_wire_digest(payload: Any) -> str:
  50. return f"sha256:{canonical_digest(payload)}"
  51. def input_canonical_payload() -> dict[str, Any]:
  52. return {
  53. "schema_version": "script-build-input/v1",
  54. "script_build_id": RUN_ID,
  55. "execution_id": 88001,
  56. "topic_build_id": 67001,
  57. "topic_id": 7701,
  58. "topic": {"id": 7701, "title": "为什么 AI Agent 项目总在最后一步失控"},
  59. "account": {"id": 230, "name": "产品深水区"},
  60. "persona_points": [
  61. {
  62. "point_id": 7,
  63. "point_type": "voice",
  64. "point_result": "克制、具体、敢于下判断",
  65. }
  66. ],
  67. "section_patterns": [{"pattern_id": 14, "name": "事故切入—框架拆解—行动清单"}],
  68. "strategies": [{"strategy_id": 3, "version": 5, "name": "反常识开场"}],
  69. "prompt_manifest": [
  70. {
  71. "role": "planner",
  72. "name": "script_planner",
  73. "sha256": digest("planner-prompt"),
  74. }
  75. ],
  76. "datasource_manifest": {"adapter": "deterministic-fake", "version": "1"},
  77. "model_manifest": {
  78. "planner": "deterministic-fake-model",
  79. "worker": "deterministic-fake-model",
  80. },
  81. }
  82. ARTIFACT_DIGESTS: dict[int, str] = {}
  83. ARTIFACT_KINDS: dict[int, str] = {}
  84. def artifact_uri(version: int) -> str:
  85. return f"script-build://artifact-versions/{version}"
  86. def artifact_ref(version: int, kind: str | None = None) -> dict[str, Any]:
  87. return {
  88. "uri": artifact_uri(version),
  89. "kind": kind or ARTIFACT_KINDS[version],
  90. "version": str(version),
  91. "digest": ARTIFACT_DIGESTS.get(version, digest(f"artifact-{version}")),
  92. "summary": None,
  93. "metadata": {},
  94. }
  95. def criterion(identifier: str, description: str) -> dict[str, Any]:
  96. return {"criterion_id": identifier, "description": description, "hard": True}
  97. def decision_ref(decision_id: str, version: int, task_kind: str) -> dict[str, Any]:
  98. return {
  99. "decision_id": decision_id,
  100. "artifact_ref": artifact_ref(version),
  101. "scope_ref": f"script-build://decisions/{decision_id}",
  102. "expected_task_kind": task_kind,
  103. }
  104. def task_spec(objective: str, kind: str) -> dict[str, Any]:
  105. return {
  106. "version": 1,
  107. "objective": objective,
  108. "acceptance_criteria": [criterion("content_complete", "产物完整且引用闭合")],
  109. "context_refs": [f"script-build://task-kinds/{kind}"],
  110. "created_at": START,
  111. }
  112. def task_payload(
  113. task_id: str, objective: str, kind: str, parent: str | None = "task-root"
  114. ) -> dict[str, Any]:
  115. is_root = task_id == "task-root"
  116. return {
  117. "task_id": task_id,
  118. "goal_id": "goal-script-delivery",
  119. "parent_task_id": parent,
  120. "display_path": "Root" if is_root else f"Root / {objective[:24]}",
  121. "specs": [task_spec(objective, kind)],
  122. "current_spec_version": 1,
  123. "status": "completed",
  124. "child_task_ids": ["direction", "portfolio"] if is_root else [],
  125. "attempt_ids": [f"attempt-{task_id}"],
  126. "validation_ids": [f"validation-{task_id}"],
  127. "decision_ids": [f"decision-{task_id}"],
  128. "repair_count_by_version": {"1": 0},
  129. "blocked_reason": None,
  130. "superseded_by": None,
  131. "created_at": START,
  132. "updated_at": STAMP,
  133. }
  134. def contract_payload(
  135. task_id: str, objective: str, kind: str, output_schema: str
  136. ) -> dict[str, Any]:
  137. intent = {
  138. "compare": "compare",
  139. "compose": "compose",
  140. "candidate-portfolio": "portfolio",
  141. "root-delivery": "deliver",
  142. "paragraph": "expand",
  143. "element-set": "expand",
  144. "structure": "expand",
  145. }.get(kind, "explore")
  146. payload: dict[str, Any] = {
  147. "schema_version": "script-task-contract/v1",
  148. "task_kind": kind,
  149. "scope_ref": f"script-build://tasks/{task_id}",
  150. "intent_class": intent,
  151. "objective": objective,
  152. "input_decision_refs": [],
  153. "base_artifact_ref": None,
  154. "write_scope": [f"script-build://tasks/{task_id}/workspace"],
  155. "gap_ref": None,
  156. "output_schema": output_schema,
  157. "criteria": [criterion("content_complete", "产物完整且引用闭合")],
  158. "budget": {
  159. "max_attempts": 4,
  160. "max_tokens": 32000,
  161. "max_seconds": 900,
  162. "max_external_queries": 40,
  163. "max_no_improvement": 3,
  164. },
  165. "supersedes_decision_ids": [],
  166. "candidate_closure_decision_refs": [],
  167. "adopted_decision_ids": [],
  168. "held_or_rejected_decision_ids": [],
  169. "compose_order": [],
  170. "comparison_decision_refs": [],
  171. }
  172. if kind == "compare":
  173. payload["comparison_decision_refs"] = [
  174. decision_ref("decision-paragraph:a", 121, "paragraph"),
  175. decision_ref("decision-elements:a", 122, "element-set"),
  176. ]
  177. elif kind == "compose":
  178. closure = [
  179. decision_ref("decision-paragraph:a", 121, "paragraph"),
  180. decision_ref("decision-elements:a", 122, "element-set"),
  181. ]
  182. payload["candidate_closure_decision_refs"] = closure
  183. payload["adopted_decision_ids"] = [item["decision_id"] for item in closure]
  184. payload["compose_order"] = [item["decision_id"] for item in closure]
  185. payload["comparison_decision_refs"] = [
  186. decision_ref("decision-compare:ab", 130, "compare")
  187. ]
  188. elif kind == "candidate-portfolio":
  189. closure = [
  190. decision_ref("decision-compose:final", 140, "compose"),
  191. decision_ref("decision-compose:alternate", 141, "compose"),
  192. ]
  193. payload["candidate_closure_decision_refs"] = closure
  194. payload["adopted_decision_ids"] = ["decision-compose:final"]
  195. payload["held_or_rejected_decision_ids"] = ["decision-compose:alternate"]
  196. payload["compose_order"] = ["decision-compose:final"]
  197. elif kind == "root-delivery":
  198. payload["input_decision_refs"] = [
  199. decision_ref("decision-direction", 110, "direction"),
  200. decision_ref("decision-portfolio", 150, "candidate-portfolio"),
  201. ]
  202. payload["scope_ref"] = "script-build://scope/root"
  203. payload["write_scope"] = []
  204. return payload
  205. def operation_payload(task_id: str, index: int) -> dict[str, Any]:
  206. return {
  207. "operation_id": f"operation-{index:02d}",
  208. "root_trace_id": ROOT_TRACE_ID,
  209. "kind": "dispatch",
  210. "status": "completed",
  211. "task_ids": [task_id],
  212. "attempt_ids": [f"attempt-{task_id}"],
  213. "validation_ids": [f"validation-{task_id}"],
  214. "result_ref": {"task_id": task_id, "status": "completed"},
  215. "execution_epoch": 1,
  216. "deadline_at": "2026-07-20T10:34:00+08:00",
  217. "error": None,
  218. "started_at": START,
  219. "completed_at": STAMP,
  220. "created_at": START,
  221. "updated_at": STAMP,
  222. }
  223. def attempt_payload(
  224. task_id: str, preset: str, artifact_version: int, index: int
  225. ) -> dict[str, Any]:
  226. return {
  227. "attempt_id": f"attempt-{task_id}",
  228. "task_id": task_id,
  229. "spec_version": 1,
  230. "worker_trace_id": f"worker-trace-{index:02d}",
  231. "worker_preset": preset,
  232. "execution_mode": "agent",
  233. "accepted_child_decision_ids": (
  234. ["decision-direction", "decision-portfolio"]
  235. if task_id == "root-delivery"
  236. else []
  237. ),
  238. "status": "submitted",
  239. "operation_id": f"operation-{index:02d}",
  240. "execution_epoch": 1,
  241. "continue_from_trace_id": None,
  242. "snapshot_id": f"artifact-snapshot-{index:02d}",
  243. "submission": {
  244. "summary": "已保存唯一冻结产物并提交验证。",
  245. "artifact_refs": [artifact_ref(artifact_version)],
  246. "evidence_refs": [],
  247. },
  248. "execution_stats": {
  249. "primary_model": "deterministic-fake-model",
  250. "total_tokens": 1840 + index * 31,
  251. "total_cost": 0.012 + index * 0.001,
  252. "failure_code": None,
  253. },
  254. "error": None,
  255. "started_at": START,
  256. "completed_at": STAMP,
  257. "duration_ms": 2180 + index * 73,
  258. "created_at": START,
  259. "updated_at": STAMP,
  260. }
  261. def validation_payload(task_id: str, preset: str, index: int) -> dict[str, Any]:
  262. return {
  263. "validation_id": f"validation-{task_id}",
  264. "task_id": task_id,
  265. "attempt_id": f"attempt-{task_id}",
  266. "spec_version": 1,
  267. "snapshot_id": f"artifact-snapshot-{index:02d}",
  268. "validator_trace_id": f"validator-trace-{index:02d}",
  269. "validator_preset": preset,
  270. "validation_plan": {
  271. "mode": "agent",
  272. "validator_preset": preset,
  273. "rule_ids": [],
  274. "max_evidence_queries": 10,
  275. "max_evidence_items_per_query": 50,
  276. "evidence_timeout_seconds": 10.0,
  277. },
  278. "evidence_queries_used": 0,
  279. "status": "completed",
  280. "operation_id": f"operation-{index:02d}",
  281. "execution_epoch": 1,
  282. "verdict": "passed",
  283. "criterion_results": [
  284. {
  285. "criterion_id": "content_complete",
  286. "verdict": "passed",
  287. "reason": "字段、引用、digest 与冻结快照一致。",
  288. "evidence_refs": [],
  289. }
  290. ],
  291. "summary": "所有硬性标准均通过。",
  292. "evidence_refs": [],
  293. "unverified_claims": [],
  294. "risks": [],
  295. "recommendation": "accept",
  296. "execution_stats": {
  297. "primary_model": "deterministic-fake-model",
  298. "total_tokens": 620 + index * 11,
  299. "total_cost": 0.004,
  300. "failure_code": None,
  301. },
  302. "error": None,
  303. "started_at": START,
  304. "completed_at": STAMP,
  305. "duration_ms": 840 + index * 29,
  306. "created_at": START,
  307. "updated_at": STAMP,
  308. }
  309. def decision_payload(
  310. task_id: str, index: int, action: str = "accept"
  311. ) -> dict[str, Any]:
  312. return {
  313. "decision_id": f"decision-{task_id}",
  314. "task_id": task_id,
  315. "action": action,
  316. "reason": "Validator 已通过,冻结产物满足当前合同。",
  317. "from_status": "awaiting_decision",
  318. "to_status": "completed" if action == "accept" else "running",
  319. "attempt_id": f"attempt-{task_id}",
  320. "validation_id": f"validation-{task_id}",
  321. "payload": {"artifact_ref": artifact_uri(100 + index), "fenced_epoch": 1},
  322. "created_at": STAMP,
  323. }
  324. def lineage(scope: str) -> dict[str, Any]:
  325. return {
  326. "scope_ref": scope,
  327. "input_snapshot_ref": "script-build://input-snapshots/snapshot-10001-v1",
  328. "input_closure_digest": wire_digest(f"closure-{scope}"),
  329. "base_artifact_ref": None,
  330. "base_artifact_digest": None,
  331. "base_revision": None,
  332. "write_scope": [f"{scope}/workspace"],
  333. "supersedes_decision_ids": [],
  334. "source_lineage": [
  335. {"source": "accepted-decision", "decision_id": "decision-direction"}
  336. ],
  337. }
  338. PARAGRAPHS = [
  339. {
  340. "paragraph_id": 1,
  341. "paragraph_index": 1,
  342. "level": 1,
  343. "parent_id": None,
  344. "name": "反常识开场",
  345. "content_range": {"min_chars": 80, "max_chars": 130},
  346. "theme": "AI 不是答案机",
  347. "form": "场景反转",
  348. "function": "建立冲突",
  349. "feeling": "惊讶",
  350. "theme_elements": [{"name": "认知落差"}],
  351. "form_elements": [{"name": "短问句"}],
  352. "function_elements": [{"name": "钩子"}],
  353. "feeling_elements": [{"name": "紧迫"}],
  354. "description": "用失败场景破题",
  355. "full_description": "从团队盲目堆 Agent 的失败现场切入,指出流程边界比模型数量更重要。",
  356. "is_active": True,
  357. },
  358. {
  359. "paragraph_id": 2,
  360. "paragraph_index": 2,
  361. "level": 2,
  362. "parent_id": 1,
  363. "name": "三个边界",
  364. "content_range": {"min_chars": 180, "max_chars": 260},
  365. "theme": "合同、验证、发布",
  366. "form": "递进清单",
  367. "function": "交付方法",
  368. "feeling": "笃定",
  369. "theme_elements": [{"name": "可验证"}],
  370. "form_elements": [{"name": "三段式"}],
  371. "function_elements": [{"name": "解释"}],
  372. "feeling_elements": [{"name": "掌控"}],
  373. "description": "拆解三层工程边界",
  374. "full_description": "说明冻结合同、独立验证与原子发布如何形成可恢复的内容生产线。",
  375. "is_active": True,
  376. },
  377. ]
  378. ELEMENTS = [
  379. {
  380. "element_id": 1,
  381. "name": "失败现场",
  382. "dimension_primary": "实质",
  383. "dimension_secondary": "案例",
  384. "commonality_analysis": {"pattern": "先展示代价"},
  385. "topic_support": {"score": 0.94, "reason": "直接支撑流程治理主题"},
  386. "weight_score": {"value": 9.2, "scale": 10},
  387. "support_elements": [{"type": "quote", "ref": "evidence-pattern-1"}],
  388. "is_active": True,
  389. },
  390. {
  391. "element_id": 2,
  392. "name": "三层护栏",
  393. "dimension_primary": "形式",
  394. "dimension_secondary": "结构",
  395. "commonality_analysis": {"pattern": "三段递进"},
  396. "topic_support": {"score": 0.91, "reason": "形成清晰记忆点"},
  397. "weight_score": {"value": 8.8, "scale": 10},
  398. "support_elements": [{"type": "strategy", "ref": "strategy-3"}],
  399. "is_active": True,
  400. },
  401. ]
  402. LINKS = [{"paragraph_id": 1, "element_id": 1}, {"paragraph_id": 2, "element_id": 2}]
  403. def evidence_payload(kind: str, index: int) -> dict[str, Any]:
  404. return {
  405. "schema_version": "evidence-record/v1",
  406. "evidence_id": f"evidence-{kind}-{index}",
  407. "source_type": kind,
  408. "tool_name": f"retrieve_{kind.replace('-', '_')}",
  409. "query": {"topic_id": 7701, "limit": 12, "intent": "AI Agent 内容生产流程"},
  410. "source_refs": [f"{kind}://fake/source/{index}"],
  411. "raw_artifact_ref": f"script-build://raw-artifacts/sha256/{digest(kind)}",
  412. "summary": f"{kind} 返回了可用于方向与结构决策的高置信证据。",
  413. "supports": ["goal-retention", "criterion-evidence"],
  414. "confidence": "high",
  415. "limitations": ["fake 数据不代表真实检索覆盖率"],
  416. "created_at": STAMP,
  417. }
  418. def direction_payload() -> dict[str, Any]:
  419. return {
  420. "schema_version": "script-direction/v1",
  421. "goals": [
  422. {
  423. "goal_id": "goal-retention",
  424. "statement": "用工程事故切入,讲清智能创作系统的三层边界",
  425. "rationale": "目标用户需要可落地的流程而非泛泛 AI 概念。",
  426. }
  427. ],
  428. "criteria": [
  429. {"criterion_id": "criterion-evidence", "description": "关键主张均可追溯"}
  430. ],
  431. "domain_criteria": [
  432. {
  433. "criterion_id": "criterion-fencing",
  434. "description": "正确描述 fencing 与原子发布",
  435. }
  436. ],
  437. "topic_refs": ["script-build://inputs/topics/7701"],
  438. "persona_refs": ["script-build://inputs/personas/230"],
  439. "strategy_refs": ["script-build://inputs/strategies/3"],
  440. "evidence_refs": [artifact_uri(101), artifact_uri(102)],
  441. "legacy_markdown": "# 创作方向\n用一个失控的多 Agent 项目开场,落到合同、验证、发布三层护栏。",
  442. }
  443. def structured_payload() -> dict[str, Any]:
  444. return {
  445. "schema_version": "structured-script/v1",
  446. "direction_ref": artifact_uri(110),
  447. "input_closure_digest": wire_digest("structured-input-closure"),
  448. "paragraphs": PARAGRAPHS,
  449. "elements": ELEMENTS,
  450. "paragraph_element_links": LINKS,
  451. "source_artifact_refs": [
  452. artifact_uri(120),
  453. artifact_uri(121),
  454. artifact_uri(122),
  455. ],
  456. "evidence_refs": [
  457. artifact_uri(101),
  458. artifact_uri(102),
  459. artifact_uri(103),
  460. artifact_uri(104),
  461. ],
  462. "acceptance_notes": ["采用候选 A 的冲突开场", "保留候选 B 的三层递进结构"],
  463. }
  464. def legacy_canonical_payload() -> dict[str, Any]:
  465. paragraph_keys = {
  466. item["paragraph_id"]: item["paragraph_index"] for item in PARAGRAPHS
  467. }
  468. paragraphs = []
  469. for item in PARAGRAPHS:
  470. paragraphs.append(
  471. {
  472. "logical_key": item["paragraph_index"],
  473. "paragraph_index": item["paragraph_index"],
  474. "level": item["level"],
  475. "parent_logical_key": (
  476. paragraph_keys[item["parent_id"]]
  477. if item["parent_id"] is not None
  478. else None
  479. ),
  480. "name": item["name"],
  481. "content_range": item["content_range"],
  482. "theme": item["theme"],
  483. "form": item["form"],
  484. "function": item["function"],
  485. "feeling": item["feeling"],
  486. "theme_elements": item["theme_elements"],
  487. "form_elements": item["form_elements"],
  488. "function_elements": item["function_elements"],
  489. "feeling_elements": item["feeling_elements"],
  490. "description": item["description"],
  491. "full_description": item["full_description"],
  492. }
  493. )
  494. element_keys: dict[int, str] = {}
  495. elements = []
  496. for item in ELEMENTS:
  497. key = canonical_digest(
  498. [item["dimension_primary"], item["dimension_secondary"], item["name"]]
  499. )
  500. element_keys[item["element_id"]] = key
  501. elements.append(
  502. {
  503. "logical_key": key,
  504. "name": item["name"],
  505. "dimension_primary": item["dimension_primary"],
  506. "dimension_secondary": item["dimension_secondary"],
  507. "commonality_analysis": item["commonality_analysis"],
  508. "topic_support": item["topic_support"],
  509. "weight_score": item["weight_score"],
  510. "support_elements": item["support_elements"],
  511. }
  512. )
  513. links = [
  514. {
  515. "paragraph_logical_key": paragraph_keys[item["paragraph_id"]],
  516. "element_logical_key": element_keys[item["element_id"]],
  517. }
  518. for item in LINKS
  519. ]
  520. paragraphs.sort(key=lambda item: int(item["logical_key"]))
  521. elements.sort(key=lambda item: str(item["logical_key"]))
  522. links.sort(
  523. key=lambda item: (
  524. int(item["paragraph_logical_key"]),
  525. str(item["element_logical_key"]),
  526. )
  527. )
  528. return {
  529. "schema_version": "legacy-projection-canonical/v1",
  530. "direction": "用失控案例讲清合同、验证与原子发布。",
  531. "summary": "以事故切入,完整解释智能创作系统的合同、验证和原子发布。",
  532. "paragraph_count": len(paragraphs),
  533. "element_count": len(elements),
  534. "link_count": len(links),
  535. "paragraphs": [normalize_json(item) for item in paragraphs],
  536. "elements": [normalize_json(item) for item in elements],
  537. "paragraph_element_links": [normalize_json(item) for item in links],
  538. }
  539. def root_input_closure_digest() -> str:
  540. return canonical_wire_digest(
  541. {
  542. "schema_version": "root-delivery-input-closure/v1",
  543. "direction": {
  544. "ref": artifact_uri(110),
  545. "digest": ARTIFACT_DIGESTS[110],
  546. },
  547. "candidate_portfolio": {
  548. "ref": artifact_uri(150),
  549. "digest": ARTIFACT_DIGESTS[150],
  550. },
  551. "structured_script": {
  552. "ref": artifact_uri(140),
  553. "digest": ARTIFACT_DIGESTS[140],
  554. },
  555. }
  556. )
  557. def record(model_name: str, payload: dict[str, Any]) -> RecordEnvelope:
  558. expected = set(SCHEMA_CATALOG[model_name])
  559. actual = set(payload)
  560. if actual != expected:
  561. missing = sorted(expected - actual)
  562. extra = sorted(actual - expected)
  563. raise AssertionError(
  564. f"{model_name} fake field mismatch: missing={missing}, extra={extra}"
  565. )
  566. schema_version = payload.get("schema_version")
  567. return RecordEnvelope(
  568. model_name=model_name,
  569. schema_version=str(schema_version) if schema_version is not None else None,
  570. payload=payload,
  571. )
  572. @dataclass
  573. class Builder:
  574. nodes: list[FlowNode]
  575. edges: list[FlowEdge]
  576. sequence: int = 0
  577. def add(
  578. self,
  579. node_id: str,
  580. node_type: str,
  581. phase: str,
  582. lane: str,
  583. title: str,
  584. subtitle: str,
  585. status: str,
  586. x: int,
  587. y: int,
  588. model_name: str,
  589. payload: dict[str, Any],
  590. *,
  591. agent_role: str | None = None,
  592. task_kind: str | None = None,
  593. tags: list[str] | None = None,
  594. metrics: list[tuple[str, str]] | None = None,
  595. ) -> str:
  596. self.sequence += 1
  597. self.nodes.append(
  598. FlowNode(
  599. id=node_id,
  600. node_type=node_type, # type: ignore[arg-type]
  601. phase=phase, # type: ignore[arg-type]
  602. lane=lane,
  603. title=title,
  604. subtitle=subtitle,
  605. status=status,
  606. agent_role=agent_role,
  607. task_kind=task_kind,
  608. sequence=self.sequence,
  609. x=x,
  610. y=y,
  611. tags=tags or [],
  612. metrics=[
  613. Metric(label=label, value=value) for label, value in (metrics or [])
  614. ],
  615. record=record(model_name, payload),
  616. )
  617. )
  618. return node_id
  619. def link(
  620. self,
  621. source: str,
  622. target: str,
  623. label: str | None = None,
  624. kind: str = "control",
  625. animated: bool = False,
  626. ) -> None:
  627. self.edges.append(
  628. FlowEdge(
  629. id=f"edge:{source}:{target}",
  630. source=source,
  631. target=target,
  632. label=label,
  633. kind=kind,
  634. animated=animated, # type: ignore[arg-type]
  635. )
  636. )
  637. def _pipeline_unit(
  638. b: Builder,
  639. *,
  640. prefix: str,
  641. phase: str,
  642. lane: str,
  643. kind: str,
  644. output_schema: str,
  645. objective: str,
  646. artifact_model: str,
  647. artifact_payload: dict[str, Any],
  648. x: int,
  649. y: int,
  650. index: int,
  651. artifact_version: int,
  652. preset: str,
  653. validator_preset: str = "script_candidate_validator",
  654. ) -> tuple[str, str]:
  655. artifact_kind = {
  656. "EvidenceRecordV1": "evidence",
  657. "ScriptDirectionArtifactV1": "direction",
  658. "StructureArtifactV1": "structure",
  659. "ParagraphArtifactV1": "paragraph",
  660. "ElementSetArtifactV1": "element_set",
  661. "ComparisonArtifactV1": "comparison",
  662. "StructuredScriptArtifactV1": "structured_script",
  663. "CandidatePortfolioArtifactV1": "candidate_portfolio",
  664. "RootDeliveryManifestV1": "root_delivery_manifest",
  665. }[artifact_model]
  666. ARTIFACT_KINDS[artifact_version] = artifact_kind
  667. ARTIFACT_DIGESTS[artifact_version] = canonical_wire_digest(artifact_payload)
  668. contract_id = b.add(
  669. f"{prefix}:contract",
  670. "contract",
  671. phase,
  672. lane,
  673. "冻结 TaskContract",
  674. objective,
  675. "frozen",
  676. x,
  677. y,
  678. "ScriptTaskContractV1",
  679. contract_payload(prefix, objective, kind, output_schema),
  680. task_kind=kind,
  681. tags=["immutable", output_schema],
  682. metrics=[("spec", "v1"), ("预算", "32k tokens")],
  683. )
  684. op_id = b.add(
  685. f"{prefix}:operation",
  686. "operation",
  687. phase,
  688. lane,
  689. "Durable Operation",
  690. "dispatch · execution_epoch=1",
  691. "completed",
  692. x + 300,
  693. y,
  694. "OperationView",
  695. operation_payload(prefix, index),
  696. task_kind=kind,
  697. tags=["durable", "fenced"],
  698. metrics=[("耗时", f"{2 + index % 4}.2s")],
  699. )
  700. attempt_id = b.add(
  701. f"{prefix}:attempt",
  702. "attempt",
  703. phase,
  704. lane,
  705. f"{preset} Attempt",
  706. "受控工具白名单内执行",
  707. "submitted",
  708. x + 600,
  709. y,
  710. "AttemptView",
  711. attempt_payload(prefix, preset, artifact_version, index),
  712. agent_role="worker",
  713. task_kind=kind,
  714. tags=["worker", "snapshot"],
  715. metrics=[("tokens", str(1840 + index * 31)), ("attempt", "1 / 4")],
  716. )
  717. artifact_id = b.add(
  718. f"{prefix}:artifact",
  719. "artifact",
  720. phase,
  721. lane,
  722. artifact_model.removesuffix("V1"),
  723. f"{artifact_uri(artifact_version)} · canonical",
  724. "frozen",
  725. x + 900,
  726. y,
  727. artifact_model,
  728. artifact_payload,
  729. task_kind=kind,
  730. tags=["frozen", "sha256"],
  731. metrics=[("version", str(artifact_version)), ("state", "frozen")],
  732. )
  733. validation_id = b.add(
  734. f"{prefix}:validation",
  735. "validation",
  736. phase,
  737. lane,
  738. f"{validator_preset} Validation",
  739. "独立快照验证 · passed",
  740. "passed",
  741. x + 1200,
  742. y,
  743. "ValidationView",
  744. validation_payload(prefix, validator_preset, index),
  745. agent_role="validator",
  746. task_kind=kind,
  747. tags=["independent", "passed"],
  748. metrics=[("verdict", "passed"), ("缺陷", "0")],
  749. )
  750. decision_id = b.add(
  751. f"{prefix}:decision",
  752. "decision",
  753. phase,
  754. lane,
  755. "Planner ACCEPT",
  756. "显式接受,不由 Validator 自动发布",
  757. "accepted",
  758. x + 1500,
  759. y,
  760. "PlannerDecisionView",
  761. decision_payload(prefix, index),
  762. agent_role="planner",
  763. task_kind=kind,
  764. tags=["authority", "accept"],
  765. metrics=[("action", "ACCEPT")],
  766. )
  767. b.link(contract_id, op_id, "dispatch")
  768. b.link(op_id, attempt_id, "worker", animated=True)
  769. b.link(attempt_id, artifact_id, "freeze", "artifact")
  770. b.link(artifact_id, validation_id, "snapshot", "validation")
  771. b.link(validation_id, decision_id, "passed", "validation")
  772. return contract_id, decision_id
  773. def build_graph() -> RunGraph:
  774. b = Builder([], [])
  775. build = b.add(
  776. "run:record",
  777. "input",
  778. "phase-1",
  779. "mission",
  780. "Script Build #10001",
  781. "当前 Build 业务记录",
  782. "success",
  783. 80,
  784. 150,
  785. "ScriptBuildRecord",
  786. {
  787. "id": RUN_ID,
  788. "execution_id": 88001,
  789. "topic_build_id": 67001,
  790. "topic_id": 7701,
  791. "agent_type": "AigcAgent",
  792. "agent_config": {"model": "deterministic-fake-model", "temperature": 0},
  793. "data_source_url": None,
  794. "status": "success",
  795. "reson_trace_id": ROOT_TRACE_ID,
  796. "is_deleted": False,
  797. "is_favorited": True,
  798. "summary": "完成一篇讲解智能创作系统工程边界的结构化脚本。",
  799. "error_message": None,
  800. "script_direction": "用失控案例讲清合同、验证与原子发布。",
  801. "build_workflow": {
  802. "checkpoint": "PHASE_THREE_PUBLISHED",
  803. "schema_version": 3,
  804. },
  805. "paragraph_count": 2,
  806. "element_count": 2,
  807. "input_tokens": 28420,
  808. "output_tokens": 9360,
  809. "cost_usd": 0.1842,
  810. "strategies_config": {"always_on": [3], "on_demand": [8]},
  811. "start_time": START,
  812. "end_time": STAMP,
  813. },
  814. tags=["fake", "success"],
  815. metrics=[("段落", "2"), ("元素", "2"), ("成本", "$0.1842")],
  816. )
  817. input_payload = input_canonical_payload()
  818. input_sha256 = canonical_digest(input_payload)
  819. snapshot = b.add(
  820. "mission:input-snapshot",
  821. "input",
  822. "phase-1",
  823. "mission",
  824. "冻结 InputSnapshot",
  825. "选题 · 账号 · 人设 · 策略 · Prompt 清单",
  826. "frozen",
  827. 380,
  828. 150,
  829. "ScriptBuildInputSnapshotV1",
  830. {
  831. "snapshot_id": "301",
  832. "script_build_id": input_payload["script_build_id"],
  833. "execution_id": input_payload["execution_id"],
  834. "topic_build_id": input_payload["topic_build_id"],
  835. "topic_id": input_payload["topic_id"],
  836. "topic": input_payload["topic"],
  837. "account": input_payload["account"],
  838. "persona_points": input_payload["persona_points"],
  839. "section_patterns": input_payload["section_patterns"],
  840. "strategies": input_payload["strategies"],
  841. "prompt_manifest": input_payload["prompt_manifest"],
  842. "datasource_manifest": input_payload["datasource_manifest"],
  843. "model_manifest": input_payload["model_manifest"],
  844. "canonical_sha256": f"sha256:{input_sha256}",
  845. "created_at": START,
  846. "parent_snapshot_id": None,
  847. "parent_snapshot_sha256": None,
  848. "business_input_sha256": None,
  849. },
  850. tags=["immutable", "v1"],
  851. metrics=[("persona", "1"), ("strategy", "1"), ("prompt", "1")],
  852. )
  853. snapshot_row = b.add(
  854. "storage:input-snapshot-row",
  855. "artifact",
  856. "phase-1",
  857. "storage",
  858. "InputSnapshot 持久化行",
  859. "script_build_input_snapshot · version=1",
  860. "frozen",
  861. 80,
  862. 500,
  863. "InputSnapshotRow",
  864. {
  865. "id": 301,
  866. "script_build_id": RUN_ID,
  867. "version": 1,
  868. "canonical_json": input_payload,
  869. "canonical_sha256": input_sha256,
  870. "prompt_manifest_json": input_payload["prompt_manifest"],
  871. "schema_version": "script-build-input/v1",
  872. "created_at": START,
  873. },
  874. tags=["physical-row", "immutable"],
  875. metrics=[("table", "input_snapshot"), ("version", "1")],
  876. )
  877. binding = b.add(
  878. "mission:binding",
  879. "checkpoint",
  880. "phase-1",
  881. "mission",
  882. "Mission Binding",
  883. "Build ↔ Root Trace ↔ Snapshot 唯一绑定",
  884. "bound",
  885. 680,
  886. 150,
  887. "MissionBinding",
  888. {
  889. "binding_id": 501,
  890. "script_build_id": RUN_ID,
  891. "root_trace_id": ROOT_TRACE_ID,
  892. "input_snapshot_id": 301,
  893. "active_direction_artifact_version_id": 110,
  894. "accepted_root_artifact_version_id": 160,
  895. "engine_version": "host-phase3/1",
  896. "schema_version": "3",
  897. "created_at": START,
  898. "updated_at": STAMP,
  899. "owner_epoch": 1,
  900. "stop_epoch": 0,
  901. "owner_instance_id": "host-fake-01",
  902. "owner_acquired_at": START,
  903. },
  904. tags=["unique", "binding"],
  905. metrics=[("owner epoch", "1"), ("stop epoch", "0")],
  906. )
  907. owner = b.add(
  908. "mission:owner",
  909. "checkpoint",
  910. "phase-1",
  911. "mission",
  912. "Owner Lease + Fencing",
  913. "fd lock + owner_epoch CAS",
  914. "active",
  915. 980,
  916. 150,
  917. "MissionOwnerToken",
  918. {
  919. "script_build_id": RUN_ID,
  920. "root_trace_id": ROOT_TRACE_ID,
  921. "owner_epoch": 1,
  922. "stop_epoch": 0,
  923. "owner_instance_id": "host-fake-01",
  924. },
  925. tags=["lease", "fenced"],
  926. metrics=[("epoch", "1"), ("stop", "0")],
  927. )
  928. b.link(build, snapshot, "freeze")
  929. b.link(snapshot, snapshot_row, "persist", "artifact")
  930. b.link(snapshot, binding, "bind")
  931. b.link(binding, owner, "acquire")
  932. direction_contract, direction_decision = _pipeline_unit(
  933. b,
  934. prefix="direction",
  935. phase="phase-1",
  936. lane="direction",
  937. kind="direction",
  938. output_schema="script-direction/v1",
  939. objective="确定脚本目标、标准与证据闭包",
  940. artifact_model="ScriptDirectionArtifactV1",
  941. artifact_payload=direction_payload(),
  942. x=380,
  943. y=500,
  944. index=1,
  945. artifact_version=110,
  946. preset="script_direction_worker",
  947. )
  948. b.link(owner, direction_contract, "plan")
  949. direction_publication = b.add(
  950. "direction:publication",
  951. "publication",
  952. "phase-1",
  953. "direction",
  954. "Direction Publication",
  955. "独立 direction publication identity",
  956. "published",
  957. 2180,
  958. 500,
  959. "Publication",
  960. {
  961. "publication_id": 701,
  962. "script_build_id": RUN_ID,
  963. "publication_type": "direction",
  964. "accept_decision_id": "decision-direction",
  965. "artifact_version_id": 110,
  966. "expected_sha256": ARTIFACT_DIGESTS[110],
  967. "state": "published",
  968. "attempt_count": 1,
  969. "publication_revision": 1,
  970. "last_error_code": None,
  971. "last_error_summary": None,
  972. "created_at": START,
  973. "updated_at": STAMP,
  974. "published_at": STAMP,
  975. },
  976. tags=["published", "idempotent"],
  977. metrics=[("revision", "1")],
  978. )
  979. b.link(direction_decision, direction_publication, "publish", "publication")
  980. retrieval_specs = [
  981. ("pattern", "pattern-retrieval", "Pattern 结构模式", 180, 2, 101),
  982. ("decode", "decode-retrieval", "Case 解构证据", 500, 3, 102),
  983. ("knowledge", "knowledge-retrieval", "知识库事实", 820, 4, 103),
  984. ("external", "external-retrieval", "外部检索证据", 1140, 5, 104),
  985. ]
  986. retrieval_decisions: list[str] = []
  987. for name, kind, label, y, index, version in retrieval_specs:
  988. contract_id, decision_id = _pipeline_unit(
  989. b,
  990. prefix=f"retrieval:{name}",
  991. phase="phase-2",
  992. lane=f"retrieval-{name}",
  993. kind=kind,
  994. output_schema="evidence-record/v1",
  995. objective=f"检索并冻结{label}",
  996. artifact_model="EvidenceRecordV1",
  997. artifact_payload=evidence_payload(kind, index),
  998. x=2540,
  999. y=y,
  1000. index=index,
  1001. artifact_version=version,
  1002. preset="script_retrieval_worker",
  1003. validator_preset="script_retrieval_validator",
  1004. )
  1005. b.link(direction_decision, contract_id, "accepted direction")
  1006. retrieval_decisions.append(decision_id)
  1007. structure_artifact = {
  1008. "schema_version": "structure-artifact/v1",
  1009. **lineage("script-build://scopes/structure-a"),
  1010. "paragraphs": PARAGRAPHS,
  1011. }
  1012. structure_contract, structure_decision = _pipeline_unit(
  1013. b,
  1014. prefix="structure:a",
  1015. phase="phase-2",
  1016. lane="creative-a",
  1017. kind="structure",
  1018. output_schema="structure-artifact/v1",
  1019. objective="生成冲突递进式脚本结构候选 A",
  1020. artifact_model="StructureArtifactV1",
  1021. artifact_payload=structure_artifact,
  1022. x=4540,
  1023. y=1500,
  1024. index=6,
  1025. artifact_version=120,
  1026. preset="script_structure_worker",
  1027. )
  1028. for item in retrieval_decisions:
  1029. b.link(item, structure_contract, "evidence", "artifact")
  1030. paragraph_artifact = {
  1031. "schema_version": "paragraph-artifact/v1",
  1032. **lineage("script-build://scopes/paragraph-a"),
  1033. "paragraphs": PARAGRAPHS,
  1034. "elements": [],
  1035. "paragraph_element_links": [],
  1036. "change_manifest": {"created": [1, 2], "updated": [], "deleted": []},
  1037. }
  1038. paragraph_contract, paragraph_decision = _pipeline_unit(
  1039. b,
  1040. prefix="paragraph:a",
  1041. phase="phase-2",
  1042. lane="creative-a",
  1043. kind="paragraph",
  1044. output_schema="paragraph-artifact/v1",
  1045. objective="细化候选 A 的段落内容与父子关系",
  1046. artifact_model="ParagraphArtifactV1",
  1047. artifact_payload=paragraph_artifact,
  1048. x=4840,
  1049. y=1810,
  1050. index=7,
  1051. artifact_version=121,
  1052. preset="script_paragraph_worker",
  1053. )
  1054. b.link(structure_decision, paragraph_contract, "base structure", "artifact")
  1055. element_artifact = {
  1056. "schema_version": "element-set-artifact/v1",
  1057. **lineage("script-build://scopes/elements-a"),
  1058. "paragraphs": PARAGRAPHS,
  1059. "elements": ELEMENTS,
  1060. "paragraph_element_links": LINKS,
  1061. "change_manifest": {"created": [1, 2], "linked": 2},
  1062. }
  1063. element_contract, element_decision = _pipeline_unit(
  1064. b,
  1065. prefix="elements:a",
  1066. phase="phase-2",
  1067. lane="creative-a",
  1068. kind="element-set",
  1069. output_schema="element-set-artifact/v1",
  1070. objective="生成元素集合并闭合 Paragraph–Element Link",
  1071. artifact_model="ElementSetArtifactV1",
  1072. artifact_payload=element_artifact,
  1073. x=5140,
  1074. y=1500,
  1075. index=8,
  1076. artifact_version=122,
  1077. preset="script_element_set_worker",
  1078. )
  1079. b.link(paragraph_decision, element_contract, "paragraph closure", "artifact")
  1080. comparison_artifact = {
  1081. "schema_version": "comparison-artifact/v1",
  1082. **lineage("script-build://scopes/comparison"),
  1083. "candidate_artifact_refs": [artifact_uri(121), artifact_uri(122)],
  1084. "criterion_results": [
  1085. {
  1086. "criterion_id": "content_complete",
  1087. "candidate_results": [
  1088. {
  1089. "artifact_ref": artifact_uri(121),
  1090. "verdict": "passed",
  1091. "reason": "段落闭合",
  1092. },
  1093. {
  1094. "artifact_ref": artifact_uri(122),
  1095. "verdict": "passed",
  1096. "reason": "元素关联闭合",
  1097. },
  1098. ],
  1099. }
  1100. ],
  1101. "conflicts": ["候选 A 更强冲突,候选 B 信息密度更高"],
  1102. "recommendation": artifact_uri(122),
  1103. }
  1104. compare_contract, compare_decision = _pipeline_unit(
  1105. b,
  1106. prefix="compare:ab",
  1107. phase="phase-2",
  1108. lane="convergence",
  1109. kind="compare",
  1110. output_schema="comparison-artifact/v1",
  1111. objective="按同一标准比较候选并给出建议",
  1112. artifact_model="ComparisonArtifactV1",
  1113. artifact_payload=comparison_artifact,
  1114. x=5440,
  1115. y=1810,
  1116. index=9,
  1117. artifact_version=130,
  1118. preset="script_comparison_worker",
  1119. )
  1120. b.link(paragraph_decision, compare_contract, "candidate A", "artifact")
  1121. b.link(element_decision, compare_contract, "candidate B", "artifact")
  1122. compose_contract, compose_decision = _pipeline_unit(
  1123. b,
  1124. prefix="compose:final",
  1125. phase="phase-2",
  1126. lane="convergence",
  1127. kind="compose",
  1128. output_schema="structured-script/v1",
  1129. objective="按 accepted closure 合成 StructuredScript",
  1130. artifact_model="StructuredScriptArtifactV1",
  1131. artifact_payload=structured_payload(),
  1132. x=5740,
  1133. y=1500,
  1134. index=10,
  1135. artifact_version=140,
  1136. preset="script_compose_worker",
  1137. )
  1138. b.link(compare_decision, compose_contract, "comparison accepted")
  1139. alternate_script = structured_payload()
  1140. alternate_script["acceptance_notes"] = [
  1141. "保留候选 B 的信息密度",
  1142. "作为 Portfolio 中未采用但完整闭合的备选脚本",
  1143. ]
  1144. alternate_contract, alternate_decision = _pipeline_unit(
  1145. b,
  1146. prefix="compose:alternate",
  1147. phase="phase-2",
  1148. lane="convergence-alternate",
  1149. kind="compose",
  1150. output_schema="structured-script/v1",
  1151. objective="生成完整备选 StructuredScript 供 Portfolio 显式处置",
  1152. artifact_model="StructuredScriptArtifactV1",
  1153. artifact_payload=alternate_script,
  1154. x=5740,
  1155. y=2110,
  1156. index=13,
  1157. artifact_version=141,
  1158. preset="script_compose_worker",
  1159. )
  1160. b.link(compare_decision, alternate_contract, "alternate candidate")
  1161. portfolio_payload = {
  1162. "schema_version": "candidate-portfolio/v1",
  1163. "adopted_structured_script_ref": artifact_uri(140),
  1164. "candidate_structured_script_refs": [artifact_uri(140), artifact_uri(141)],
  1165. "accepted_decision_ids": ["decision-compose:final"],
  1166. "superseded_decision_ids": [],
  1167. "rejected_or_held_decision_ids": ["decision-compose:alternate"],
  1168. "input_closure_digest": wire_digest("portfolio-input-closure"),
  1169. "unresolved_defects": [],
  1170. "compose_order": ["decision-compose:final"],
  1171. }
  1172. portfolio_contract, portfolio_decision = _pipeline_unit(
  1173. b,
  1174. prefix="portfolio",
  1175. phase="phase-2",
  1176. lane="convergence",
  1177. kind="candidate-portfolio",
  1178. output_schema="candidate-portfolio/v1",
  1179. objective="恰好采用一个 StructuredScript 并处置其余候选",
  1180. artifact_model="CandidatePortfolioArtifactV1",
  1181. artifact_payload=portfolio_payload,
  1182. x=6040,
  1183. y=1810,
  1184. index=11,
  1185. artifact_version=150,
  1186. preset="script_portfolio_worker",
  1187. )
  1188. b.link(compose_decision, portfolio_contract, "candidate closure")
  1189. b.link(alternate_decision, portfolio_contract, "candidate closure")
  1190. checkpoint = b.add(
  1191. "phase2:checkpoint",
  1192. "checkpoint",
  1193. "phase-2",
  1194. "boundary",
  1195. "Phase 2 Checkpoint",
  1196. "PHASE_TWO_CANDIDATE_PORTFOLIO_READY",
  1197. "partial",
  1198. 7840,
  1199. 1810,
  1200. "TaskView",
  1201. task_payload("task-root", "交付最终脚本", "root-delivery", parent=None),
  1202. agent_role="planner",
  1203. tags=["partial", "closure-ready"],
  1204. metrics=[
  1205. ("accepted direction", "1"),
  1206. ("accepted portfolio", "1"),
  1207. ("active op", "0"),
  1208. ],
  1209. )
  1210. b.link(portfolio_decision, checkpoint, "closure ready")
  1211. migration = b.add(
  1212. "phase3:migration",
  1213. "checkpoint",
  1214. "phase-3",
  1215. "continuation",
  1216. "Policy Continuation",
  1217. "script-build-phase-three/v1 + 唯一 migration event",
  1218. "migrated",
  1219. 8200,
  1220. 180,
  1221. "TaskSpecView",
  1222. task_spec("执行 Root Delivery 并发布", "root-delivery"),
  1223. agent_role="planner",
  1224. task_kind="root-delivery",
  1225. tags=["reentrant", "policy-v1"],
  1226. metrics=[("message parent", "closed"), ("migration", "unique")],
  1227. )
  1228. b.link(checkpoint, migration, "advance")
  1229. canonical_payload = legacy_canonical_payload()
  1230. canonical_sha256 = canonical_wire_digest(canonical_payload)
  1231. root_manifest_payload = {
  1232. "schema_version": "root-delivery-manifest/v1",
  1233. "direction_ref": artifact_uri(110),
  1234. "candidate_portfolio_ref": artifact_uri(150),
  1235. "structured_script_ref": artifact_uri(140),
  1236. "input_closure_digest": root_input_closure_digest(),
  1237. "legacy_projection_digest": canonical_sha256,
  1238. "build_summary": "以事故切入,完整解释智能创作系统的合同、验证和原子发布。",
  1239. "blocking_defects": [],
  1240. }
  1241. root_contract, root_decision = _pipeline_unit(
  1242. b,
  1243. prefix="root-delivery",
  1244. phase="phase-3",
  1245. lane="root-delivery",
  1246. kind="root-delivery",
  1247. output_schema="root-delivery-manifest/v1",
  1248. objective="锁定 Direction、Portfolio、StructuredScript 并生成唯一交付 Manifest",
  1249. artifact_model="RootDeliveryManifestV1",
  1250. artifact_payload=root_manifest_payload,
  1251. x=8200,
  1252. y=500,
  1253. index=12,
  1254. artifact_version=160,
  1255. preset="script_root_worker",
  1256. validator_preset="script_root_validator",
  1257. )
  1258. b.link(migration, root_contract, "host-frozen contract")
  1259. artifact_version_row = b.add(
  1260. "storage:artifact-version-160",
  1261. "artifact",
  1262. "phase-3",
  1263. "storage",
  1264. "ArtifactVersion #160",
  1265. "root_delivery_manifest · frozen canonical_json",
  1266. "frozen",
  1267. 9100,
  1268. 1020,
  1269. "ArtifactVersionRow",
  1270. {
  1271. "id": 160,
  1272. "script_build_id": RUN_ID,
  1273. "task_id": "root-delivery",
  1274. "attempt_id": "attempt-root-delivery",
  1275. "spec_version": 1,
  1276. "artifact_type": "root_delivery_manifest",
  1277. "legacy_branch_id": None,
  1278. "base_revision": None,
  1279. "parent_artifact_version_id": None,
  1280. "canonical_json": root_manifest_payload,
  1281. "canonical_sha256": ARTIFACT_DIGESTS[160].removeprefix("sha256:"),
  1282. "state": "frozen",
  1283. "created_at": STAMP,
  1284. "frozen_at": STAMP,
  1285. "published_at": None,
  1286. },
  1287. task_kind="root-delivery",
  1288. tags=["physical-row", "artifact-version"],
  1289. metrics=[("artifact_type", "manifest"), ("branch", "null")],
  1290. )
  1291. b.link("root-delivery:artifact", artifact_version_row, "persist", "artifact")
  1292. canonical = b.add(
  1293. "publication:canonical-dry-run",
  1294. "readback",
  1295. "phase-3",
  1296. "root-delivery",
  1297. "Legacy Projection Dry-run",
  1298. "物理 ID 无关 canonical graph",
  1299. "matched",
  1300. 9100,
  1301. 820,
  1302. "LegacyProjectionCanonicalV1",
  1303. canonical_payload,
  1304. tags=["canonical", "sha256"],
  1305. metrics=[("digest", canonical_sha256[:12]), ("links", "2")],
  1306. )
  1307. b.link("root-delivery:attempt", canonical, "dry run", "artifact")
  1308. b.link(canonical, "root-delivery:artifact", "digest", "artifact")
  1309. journal = b.add(
  1310. "http:finalize-command",
  1311. "checkpoint",
  1312. "delivery",
  1313. "http",
  1314. "HTTP Idempotency Journal",
  1315. "POST /finalize · bounded response replay",
  1316. "completed",
  1317. 10100,
  1318. 180,
  1319. "HttpCommandRecord",
  1320. {
  1321. "id": 801,
  1322. "principal_scope_sha256": digest("tenant:fake"),
  1323. "route_family": "script-build-finalize",
  1324. "idempotency_key": "fake-finalize-10001",
  1325. "request_fingerprint": digest("finalize-request"),
  1326. "preallocated_root_trace_id": ROOT_TRACE_ID,
  1327. "resource_id": RUN_ID,
  1328. "state": "completed",
  1329. "response_status": 200,
  1330. "response_json": {"script_build_id": RUN_ID, "state": "published"},
  1331. "created_at": START,
  1332. "updated_at": STAMP,
  1333. },
  1334. tags=["idempotent", "replay"],
  1335. metrics=[("HTTP", "200"), ("state", "completed")],
  1336. )
  1337. b.link(root_decision, journal, "finalize")
  1338. publication = b.add(
  1339. "publication:identity",
  1340. "publication",
  1341. "delivery",
  1342. "publisher",
  1343. "Final Publication Identity",
  1344. "(script_build_id, publication_type) 唯一",
  1345. "published",
  1346. 10400,
  1347. 500,
  1348. "Publication",
  1349. {
  1350. "publication_id": 702,
  1351. "script_build_id": RUN_ID,
  1352. "publication_type": "final",
  1353. "accept_decision_id": "decision-root-delivery",
  1354. "artifact_version_id": 160,
  1355. "expected_sha256": ARTIFACT_DIGESTS[160],
  1356. "state": "published",
  1357. "attempt_count": 1,
  1358. "publication_revision": 1,
  1359. "last_error_code": None,
  1360. "last_error_summary": None,
  1361. "created_at": START,
  1362. "updated_at": STAMP,
  1363. "published_at": STAMP,
  1364. },
  1365. tags=["unique", "final"],
  1366. metrics=[("attempt", "1"), ("revision", "1")],
  1367. )
  1368. b.link(journal, publication, "reserve", "publication")
  1369. tx_steps = [
  1370. ("locks", "固定锁序", "binding → build → publication → content", 10700, 260),
  1371. ("clear", "清理 branch 0", "Link → Paragraph → Element", 11000, 260),
  1372. ("paragraphs", "插入 Paragraph", "父段 → 子段 · logical map", 11300, 260),
  1373. ("elements", "插入 Element", "natural key → physical ID", 11600, 260),
  1374. ("links", "插入 Link", "paragraph/element logical pair", 11900, 260),
  1375. (
  1376. "readback",
  1377. "同 Session canonical 回读",
  1378. "digest 必须与 Manifest 一致",
  1379. 12200,
  1380. 260,
  1381. ),
  1382. (
  1383. "pointers",
  1384. "发布指针与状态",
  1385. "accepted pointer + artifacts + build",
  1386. 12500,
  1387. 260,
  1388. ),
  1389. ("commit", "单次 Commit", "全部成功或全部 rollback", 12800, 260),
  1390. ]
  1391. physical_rows: dict[str, tuple[str, dict[str, Any]]] = {
  1392. "paragraphs": (
  1393. "ParagraphRow",
  1394. {
  1395. "id": 9001,
  1396. "script_build_id": RUN_ID,
  1397. "branch_id": 0,
  1398. "base_ref_id": None,
  1399. "paragraph_index": 1,
  1400. "level": 1,
  1401. "parent_id": None,
  1402. "name": PARAGRAPHS[0]["name"],
  1403. "content_range": PARAGRAPHS[0]["content_range"],
  1404. "theme": PARAGRAPHS[0]["theme"],
  1405. "form": PARAGRAPHS[0]["form"],
  1406. "function": PARAGRAPHS[0]["function"],
  1407. "feeling": PARAGRAPHS[0]["feeling"],
  1408. "theme_elements": PARAGRAPHS[0]["theme_elements"],
  1409. "form_elements": PARAGRAPHS[0]["form_elements"],
  1410. "function_elements": PARAGRAPHS[0]["function_elements"],
  1411. "feeling_elements": PARAGRAPHS[0]["feeling_elements"],
  1412. "description": PARAGRAPHS[0]["description"],
  1413. "full_description": PARAGRAPHS[0]["full_description"],
  1414. "is_active": True,
  1415. "created_at": STAMP,
  1416. "updated_at": STAMP,
  1417. },
  1418. ),
  1419. "elements": (
  1420. "ElementRow",
  1421. {
  1422. "id": 9101,
  1423. "script_build_id": RUN_ID,
  1424. "branch_id": 0,
  1425. "base_ref_id": None,
  1426. "name": ELEMENTS[0]["name"],
  1427. "dimension_primary": ELEMENTS[0]["dimension_primary"],
  1428. "dimension_secondary": ELEMENTS[0]["dimension_secondary"],
  1429. "commonality_analysis": ELEMENTS[0]["commonality_analysis"],
  1430. "topic_support": ELEMENTS[0]["topic_support"],
  1431. "weight_score": ELEMENTS[0]["weight_score"],
  1432. "support_elements": ELEMENTS[0]["support_elements"],
  1433. "is_active": True,
  1434. "created_at": STAMP,
  1435. "updated_at": STAMP,
  1436. },
  1437. ),
  1438. "links": (
  1439. "ParagraphElementLinkRow",
  1440. {
  1441. "id": 9201,
  1442. "script_build_id": RUN_ID,
  1443. "branch_id": 0,
  1444. "paragraph_id": 9001,
  1445. "element_id": 9101,
  1446. },
  1447. ),
  1448. }
  1449. previous = publication
  1450. for offset, (key, title, subtitle, x, y) in enumerate(tx_steps, 20):
  1451. default_record = (
  1452. "MissionOwnerToken",
  1453. {
  1454. "script_build_id": RUN_ID,
  1455. "root_trace_id": ROOT_TRACE_ID,
  1456. "owner_epoch": 1,
  1457. "stop_epoch": 0,
  1458. "owner_instance_id": "host-fake-01",
  1459. },
  1460. )
  1461. model_name, row_payload = physical_rows.get(key, default_record)
  1462. node = b.add(
  1463. f"uow:{key}",
  1464. "transaction",
  1465. "delivery",
  1466. "final-uow",
  1467. title,
  1468. subtitle,
  1469. "committed",
  1470. x,
  1471. y,
  1472. model_name,
  1473. row_payload,
  1474. tags=[
  1475. "one-session",
  1476. "physical-row" if key in physical_rows else "fenced",
  1477. ],
  1478. metrics=[("step", str(offset - 19)), ("tx", "same")],
  1479. )
  1480. b.link(previous, node, None, "publication", animated=True)
  1481. previous = node
  1482. paragraph_two = dict(physical_rows["paragraphs"][1])
  1483. paragraph_two.update(
  1484. {
  1485. "id": 9002,
  1486. "paragraph_index": 2,
  1487. "level": 2,
  1488. "parent_id": 9001,
  1489. "name": PARAGRAPHS[1]["name"],
  1490. "content_range": PARAGRAPHS[1]["content_range"],
  1491. "theme": PARAGRAPHS[1]["theme"],
  1492. "form": PARAGRAPHS[1]["form"],
  1493. "function": PARAGRAPHS[1]["function"],
  1494. "feeling": PARAGRAPHS[1]["feeling"],
  1495. "theme_elements": PARAGRAPHS[1]["theme_elements"],
  1496. "form_elements": PARAGRAPHS[1]["form_elements"],
  1497. "function_elements": PARAGRAPHS[1]["function_elements"],
  1498. "feeling_elements": PARAGRAPHS[1]["feeling_elements"],
  1499. "description": PARAGRAPHS[1]["description"],
  1500. "full_description": PARAGRAPHS[1]["full_description"],
  1501. }
  1502. )
  1503. element_two = dict(physical_rows["elements"][1])
  1504. element_two.update(
  1505. {
  1506. "id": 9102,
  1507. "name": ELEMENTS[1]["name"],
  1508. "dimension_primary": ELEMENTS[1]["dimension_primary"],
  1509. "dimension_secondary": ELEMENTS[1]["dimension_secondary"],
  1510. "commonality_analysis": ELEMENTS[1]["commonality_analysis"],
  1511. "topic_support": ELEMENTS[1]["topic_support"],
  1512. "weight_score": ELEMENTS[1]["weight_score"],
  1513. "support_elements": ELEMENTS[1]["support_elements"],
  1514. }
  1515. )
  1516. extra_rows = [
  1517. ("paragraphs", "ParagraphRow #2", "ParagraphRow", paragraph_two, 11300),
  1518. ("elements", "ElementRow #2", "ElementRow", element_two, 11600),
  1519. (
  1520. "links",
  1521. "ParagraphElementLinkRow #2",
  1522. "ParagraphElementLinkRow",
  1523. {
  1524. "id": 9202,
  1525. "script_build_id": RUN_ID,
  1526. "branch_id": 0,
  1527. "paragraph_id": 9002,
  1528. "element_id": 9102,
  1529. },
  1530. 11900,
  1531. ),
  1532. ]
  1533. for key, title, model_name, row_payload, x in extra_rows:
  1534. extra = b.add(
  1535. f"uow:{key}-row-2",
  1536. "transaction",
  1537. "delivery",
  1538. "final-uow-rows",
  1539. title,
  1540. "完整发布集合 · row 2 / 2",
  1541. "committed",
  1542. x,
  1543. 620,
  1544. model_name,
  1545. row_payload,
  1546. tags=["one-session", "physical-row"],
  1547. metrics=[("row", "2 / 2"), ("branch", "0")],
  1548. )
  1549. b.link(f"uow:{key}", extra, "row 2", "artifact")
  1550. result = b.add(
  1551. "publication:result",
  1552. "publication",
  1553. "delivery",
  1554. "publisher",
  1555. "Publication Result",
  1556. "Manifest 与 StructuredScript 已 published",
  1557. "published",
  1558. 13100,
  1559. 500,
  1560. "PublicationResult",
  1561. {
  1562. "script_build_id": RUN_ID,
  1563. "publication_id": 702,
  1564. "publication_type": "final",
  1565. "state": "published",
  1566. "legacy_projection_digest": canonical_sha256,
  1567. "committed": True,
  1568. "post_commit_readback_ok": True,
  1569. },
  1570. tags=["committed", "readback-ok"],
  1571. metrics=[("build", "success"), ("readback", "OK")],
  1572. )
  1573. b.link(previous, result, "commit ack", "publication")
  1574. legacy = b.add(
  1575. "legacy:http-readback",
  1576. "readback",
  1577. "delivery",
  1578. "compatibility",
  1579. "Legacy HTTP Detail",
  1580. "GET /api/pattern/script_builds/10001",
  1581. "200",
  1582. 13400,
  1583. 500,
  1584. "ScriptBuildRecord",
  1585. {
  1586. "id": RUN_ID,
  1587. "execution_id": 88001,
  1588. "topic_build_id": 67001,
  1589. "topic_id": 7701,
  1590. "agent_type": "AigcAgent",
  1591. "agent_config": {"model": "deterministic-fake-model"},
  1592. "data_source_url": None,
  1593. "status": "success",
  1594. "reson_trace_id": ROOT_TRACE_ID,
  1595. "is_deleted": False,
  1596. "is_favorited": True,
  1597. "summary": "完成一篇讲解智能创作系统工程边界的结构化脚本。",
  1598. "error_message": None,
  1599. "script_direction": "用失控案例讲清合同、验证与原子发布。",
  1600. "build_workflow": {"checkpoint": "PHASE_THREE_PUBLISHED"},
  1601. "paragraph_count": 2,
  1602. "element_count": 2,
  1603. "input_tokens": 28420,
  1604. "output_tokens": 9360,
  1605. "cost_usd": 0.1842,
  1606. "strategies_config": {"always_on": [3], "on_demand": [8]},
  1607. "start_time": START,
  1608. "end_time": STAMP,
  1609. },
  1610. tags=["compat", "200"],
  1611. metrics=[("paragraphs", "2"), ("elements", "2")],
  1612. )
  1613. b.link(result, legacy, "health readback", "publication")
  1614. frames = [
  1615. PhaseFrame(
  1616. id="frame-phase-1",
  1617. title="Phase 1 · Mission 与方向",
  1618. subtitle="冻结输入、绑定 Root、方向 Worker / Validator / ACCEPT",
  1619. x=0,
  1620. y=0,
  1621. width=2460,
  1622. height=1040,
  1623. tone="coral",
  1624. ),
  1625. PhaseFrame(
  1626. id="frame-phase-2",
  1627. title="Phase 2 · Evidence 与候选闭包",
  1628. subtitle="四路取数、结构、段落、元素、比较、合成与 Portfolio",
  1629. x=2480,
  1630. y=0,
  1631. width=5600,
  1632. height=2400,
  1633. tone="blue",
  1634. ),
  1635. PhaseFrame(
  1636. id="frame-phase-3",
  1637. title="Phase 3 · Root Delivery",
  1638. subtitle="Policy continuation、唯一 Manifest、Root Validation 与 ACCEPT",
  1639. x=8120,
  1640. y=0,
  1641. width=1920,
  1642. height=1320,
  1643. tone="purple",
  1644. ),
  1645. PhaseFrame(
  1646. id="frame-delivery",
  1647. title="Final Delivery · 原子发布",
  1648. subtitle="HTTP journal、唯一 publication、单 Session / 单 Commit 与旧 API 回读",
  1649. x=10060,
  1650. y=0,
  1651. width=3740,
  1652. height=1120,
  1653. tone="green",
  1654. ),
  1655. ]
  1656. summary = RunSummary(
  1657. script_build_id=RUN_ID,
  1658. title="AI Agent 项目为什么总在最后一步失控",
  1659. status="success",
  1660. checkpoint="PHASE_THREE_PUBLISHED",
  1661. root_trace_id=ROOT_TRACE_ID,
  1662. publication_state="published",
  1663. node_count=len(b.nodes),
  1664. created_at=START,
  1665. updated_at=STAMP,
  1666. )
  1667. return RunGraph(
  1668. schema_version="script-build-visualization/v1",
  1669. data_mode="fake",
  1670. generated_at=STAMP,
  1671. run=summary,
  1672. frames=frames,
  1673. nodes=b.nodes,
  1674. edges=b.edges,
  1675. schema_catalog={key: list(value) for key, value in SCHEMA_CATALOG.items()},
  1676. enum_catalog={key: list(value) for key, value in ENUM_CATALOG.items()},
  1677. declared_gaps=[
  1678. "当前 Host 没有面向可视化的聚合 execution-view API;此后端只返回 fake graph。",
  1679. "TraceStore 的消息父链、policy migration 与 fencing 审计尚无统一只读投影。",
  1680. "真实运行中模型 prompt 全量快照、工具调用参数与 Operation 时间线需要专用脱敏接口。",
  1681. "候选 Artifact 的 logical lineage 可以由现有字段还原,但尚无稳定分页/批量读取 API。",
  1682. ],
  1683. )
  1684. GRAPH = build_graph()
  1685. def node_detail(node_id: str) -> NodeDetail | None:
  1686. node = next((item for item in GRAPH.nodes if item.id == node_id), None)
  1687. if node is None:
  1688. return None
  1689. return NodeDetail(
  1690. data_mode="fake",
  1691. run_id=RUN_ID,
  1692. node=node,
  1693. incoming=[edge for edge in GRAPH.edges if edge.target == node_id],
  1694. outgoing=[edge for edge in GRAPH.edges if edge.source == node_id],
  1695. )