| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975 |
- from __future__ import annotations
- import re
- from collections import Counter, defaultdict
- from datetime import datetime, timezone
- from typing import Any
- from zoneinfo import ZoneInfo
- from .business_detail_text import branch_task_sections, branch_task_text, business_text
- from .decision_projection import AgentDecisionProjector
- from .main_decision_text import (
- parse_goal_text,
- parse_objective_text,
- plan_mode_label,
- plan_path_summary,
- preview as decision_preview,
- )
- from .runtime_event_projection import RuntimeEventProjector
- _DECISIONS = AgentDecisionProjector()
- TERMINAL_STATUSES = {
- "success",
- "partial",
- "failed",
- "stopped",
- "completed",
- "cancelled",
- "cancel",
- "cancle",
- }
- class ExecutionViewBuilder:
- """Deterministic V8 business narrative projector.
- The projector consumes plain dictionaries and has no database, HTTP or
- framework dependencies. Business rows win over runtime events; events may
- only fill clearly labelled evaluation/activity detail.
- """
- def __init__(self, event_projector: RuntimeEventProjector | None = None):
- self._event_projector = event_projector or RuntimeEventProjector()
- def from_bundle(self, bundle: dict[str, Any]) -> dict[str, Any]:
- record = bundle.get("record") or {}
- raw_rounds = sorted(
- bundle.get("rounds") or [],
- key=lambda item: (_integer(item.get("round_index")) or 0, _integer(item.get("id")) or 0),
- )
- raw_branches = sorted(
- bundle.get("branches") or [],
- key=lambda item: (
- _integer(item.get("round_index")) or 0,
- _integer(item.get("branch_id")) or 0,
- ),
- )
- data_decisions = [
- item
- for item in bundle.get("dataDecisions") or []
- if (_integer(item.get("branch_id")) or 0) > 0
- ]
- multipath_decisions = bundle.get("multipathDecisions") or []
- domain_info = bundle.get("domainInfo") or []
- events = bundle.get("events") or []
- valid_rounds = {
- value
- for value in (_integer(item.get("round_index")) for item in raw_rounds)
- if value is not None
- }
- valid_branches = {
- value
- for value in (_integer(item.get("branch_id")) for item in raw_branches)
- if value is not None
- }
- captured_at = datetime.now(timezone.utc)
- runtime = self._event_projector.project(
- events,
- valid_rounds=valid_rounds,
- valid_branches=valid_branches,
- captured_at=captured_at,
- run_status=str(record.get("status") or "unknown"),
- script_direction=record.get("script_direction"),
- rounds=raw_rounds,
- multipath_decisions=multipath_decisions,
- )
- implementer_tasks = _implementer_tasks_by_branch(events)
- decisions_by_branch: dict[int, list[dict[str, Any]]] = defaultdict(list)
- for item in data_decisions:
- branch_id = _integer(item.get("branch_id"))
- if branch_id is not None:
- decisions_by_branch[branch_id].append(item)
- domain_by_branch: dict[int, list[dict[str, Any]]] = defaultdict(list)
- domain_by_round: dict[int, list[dict[str, Any]]] = defaultdict(list)
- for item in domain_info:
- branch_id = _integer(item.get("branch_id"))
- round_index = _integer(item.get("round_index"))
- if branch_id is not None:
- domain_by_branch[branch_id].append(item)
- if round_index is not None:
- domain_by_round[round_index].append(item)
- multipath_by_round: dict[int, list[dict[str, Any]]] = defaultdict(list)
- unassigned_business: list[dict[str, Any]] = []
- for item in multipath_decisions:
- round_index = _integer(item.get("round_index"))
- if round_index in valid_rounds:
- multipath_by_round[round_index].append(item)
- else:
- unassigned_business.append(
- {
- "type": "multipath-decision",
- "id": item.get("id"),
- "reason": "没有可确认的所属轮次",
- }
- )
- projected_rounds: list[dict[str, Any]] = []
- for index, raw_round in enumerate(raw_rounds):
- round_index = _integer(raw_round.get("round_index")) or index + 1
- branch_rows = [
- item
- for item in raw_branches
- if _integer(item.get("round_index")) == round_index
- ]
- next_round = raw_rounds[index + 1] if index + 1 < len(raw_rounds) else None
- projected_rounds.append(
- self._round_story(
- record,
- raw_round,
- branch_rows,
- decisions_by_branch,
- domain_by_branch,
- domain_by_round.get(round_index, []),
- multipath_by_round.get(round_index, []),
- runtime,
- implementer_tasks,
- next_round,
- )
- )
- data_shape = _data_shape(
- raw_branches, multipath_decisions, domain_info, events
- )
- warnings = _warnings(
- data_shape=data_shape,
- ignored_legacy_count=_integer(
- bundle.get("ignoredLegacyDataDecisionCount")
- )
- or 0,
- rounds=projected_rounds,
- runtime_unassigned=runtime.get("unassigned") or [],
- business_unassigned=unassigned_business,
- )
- artifact = bundle.get("currentArtifact") or {}
- current_round = projected_rounds[-1]["roundIndex"] if projected_rounds else None
- view = {
- "schemaVersion": "8",
- "capturedAt": captured_at.isoformat(),
- "dataShape": data_shape,
- "header": _header(record, current_round),
- "planning": _planning_analysis(
- (runtime.get("mainAgentDecisions") or {}).get("planningAnalysis"),
- ),
- "objective": _objective(
- record,
- (runtime.get("mainAgentDecisions") or {}).get("objective"),
- ),
- "rounds": projected_rounds,
- "finalResult": _final_result(record, projected_rounds, artifact),
- "unassigned": {
- "runtimeEvents": runtime.get("unassigned") or [],
- "businessRecords": unassigned_business,
- },
- "warnings": warnings,
- }
- if view["planning"] is None:
- view.pop("planning")
- return view
- def _round_story(
- self,
- record: dict[str, Any],
- raw_round: dict[str, Any],
- branch_rows: list[dict[str, Any]],
- decisions_by_branch: dict[int, list[dict[str, Any]]],
- domain_by_branch: dict[int, list[dict[str, Any]]],
- round_domain_info: list[dict[str, Any]],
- multipath_rows: list[dict[str, Any]],
- runtime: dict[str, Any],
- implementer_tasks: dict[int, str],
- next_round: dict[str, Any] | None,
- ) -> dict[str, Any]:
- round_index = _integer(raw_round.get("round_index")) or 0
- main_decisions = runtime.get("mainAgentDecisions") or {}
- goal_context = (main_decisions.get("roundGoalsByRound") or {}).get(round_index)
- plan_context = (main_decisions.get("implementationPlansByRound") or {}).get(
- round_index
- )
- plan = _plan_projection(
- raw_round,
- plan_context,
- )
- branches = [
- _branch_story(
- round_index,
- row,
- decisions_by_branch.get(_integer(row.get("branch_id")) or 0, []),
- domain_by_branch.get(_integer(row.get("branch_id")) or 0, []),
- runtime.get("retrievalStagesByBranch", {}).get(
- _integer(row.get("branch_id")) or 0
- ),
- runtime.get("creativeByBranch", {}).get(
- _integer(row.get("branch_id")) or 0
- ),
- implementer_tasks.get(_integer(row.get("branch_id")) or 0),
- str(record.get("status") or "unknown"),
- )
- for row in branch_rows
- ]
- branch_ids = {branch["branchId"] for branch in branches}
- decision_batches = [
- _decision_batch(round_index, row, branch_ids, branches)
- for row in sorted(multipath_rows, key=_record_order)
- ]
- convergence_batches, unassigned_reviews = _convergence_batches(
- round_index,
- runtime.get("multipathReviewsByRound", {}).get(round_index, []),
- decision_batches,
- str(record.get("status") or "unknown"),
- )
- if not convergence_batches and branches:
- convergence_batches = [
- _convergence_batch(
- round_index,
- None,
- None,
- "records-missing",
- str(record.get("status") or "unknown"),
- branch_ids=sorted(branch_ids),
- )
- ]
- _annotate_unscoped_round_reviews(
- convergence_batches,
- runtime.get("unassigned") or [],
- round_index,
- )
- if unassigned_reviews:
- runtime.setdefault("unassigned", []).extend(unassigned_reviews)
- overall_evaluation = _round_evaluation(
- round_index,
- runtime.get("overallEvaluationsByRound", {}).get(round_index, []),
- str(record.get("status") or "unknown"),
- )
- state = _round_state(record, raw_round, overall_evaluation)
- result = _round_result(
- record,
- round_index,
- branches,
- round_domain_info,
- state,
- next_round,
- )
- goal_summary = _goal_summary(raw_round.get("goal"))
- round_summary = _round_summary(
- round_index=round_index,
- goal=goal_summary,
- plan=plan,
- branches=branches,
- batches=decision_batches,
- domain_info=round_domain_info,
- run_status=str(record.get("status") or "unknown"),
- )
- goal_parsed = parse_goal_text(raw_round.get("goal"))
- goal_lines: list[dict[str, Any]] = []
- focus_items = goal_parsed.get("focusItems") or []
- if focus_items:
- focus_summary = ";".join(str(item) for item in focus_items[:2])
- if len(focus_items) > 2:
- focus_summary += f";另有 {len(focus_items) - 2} 项,详情查看"
- goal_lines.append(_line("focus", "本轮聚焦", focus_summary))
- visible_inputs = _context_input_labels(goal_context)
- if visible_inputs:
- goal_lines.append(_line("visible-before", "形成前可见", " · ".join(visible_inputs)))
- goal_decision = _direction_decision(
- id=f"round:{round_index}:goal",
- subtype="round-goal",
- context=goal_context,
- decision_items=(goal_context or {}).get("decisionItems")
- or [goal_parsed["headline"]],
- primary_label="要解决",
- primary_value=goal_parsed["headline"],
- secondary=goal_lines,
- prompt_ref="prompt:main",
- )
- return {
- "id": f"round:{round_index}",
- "roundIndex": round_index,
- "state": state,
- "summary": round_summary,
- "goal": _story(
- id=f"round:{round_index}:goal",
- role="goal",
- kind="decision",
- title="本轮目标",
- label="要解决",
- value=goal_parsed["headline"],
- secondary=goal_lines,
- detail_ref=f"round:{round_index}:goal",
- source_notice=None if raw_round.get("goal") else "record-missing",
- decision=goal_decision,
- ),
- "plan": plan,
- "branches": branches,
- "convergenceBatches": convergence_batches,
- "overallEvaluation": overall_evaluation,
- "result": result,
- "detailRef": f"round:{round_index}",
- }
- def _header(record: dict[str, Any], current_round: int | None) -> dict[str, Any]:
- agent_config = record.get("agent_config")
- model = None
- if isinstance(agent_config, dict):
- model = agent_config.get("model_name") or agent_config.get("model")
- return {
- "id": str(record.get("id") or ""),
- "title": f"脚本构建 #{record.get('id')}",
- "status": str(record.get("status") or "unknown"),
- "summary": _plain(record.get("summary")),
- "errorMessage": _plain(record.get("error_message")),
- "model": model,
- "totalCost": record.get("cost_usd"),
- "currentRound": current_round,
- "createdAt": record.get("start_time"),
- "updatedAt": record.get("end_time"),
- "direction": {
- "currentValue": _plain(record.get("script_direction")),
- "note": "这是数据库当前保存的创作方向。",
- },
- }
- def _objective(
- record: dict[str, Any], decision_context: dict[str, Any] | None
- ) -> dict[str, Any]:
- raw_direction = record.get("script_direction")
- direction = _plain(raw_direction)
- # Section parsing needs the persisted Markdown line breaks. ``direction``
- # is only the compact truthy/display fallback used by the surrounding view.
- parsed = parse_objective_text(raw_direction)
- secondary: list[dict[str, Any]] = []
- input_labels = _context_input_labels(decision_context, relation="direct-read")
- if input_labels:
- secondary.append(
- _line("inputs", "决策前读取", " · ".join(input_labels))
- )
- structure: list[str] = []
- target_count = int(parsed.get("targetCount") or 0)
- dimension_count = int(parsed.get("domainDimensionCount") or 0)
- if target_count:
- structure.append(f"{target_count} 个创作目标")
- if dimension_count:
- structure.append(f"{dimension_count} 个领域评估维度")
- if structure:
- secondary.append(_line("structure", "目标结构", " · ".join(structure)))
- decision = _direction_decision(
- id="run:objective",
- subtype="objective",
- context=decision_context,
- decision_items=(decision_context or {}).get("decisionItems")
- or ([parsed.get("headline")] if parsed.get("headline") else []),
- primary_label="核心目标",
- primary_value=parsed.get("headline"),
- secondary=secondary,
- prompt_ref="prompt:main",
- )
- return _story(
- id="run:objective",
- role="objective",
- kind="decision" if direction else "missing",
- title="创作目标",
- label="核心目标",
- value=parsed.get("headline") or "创作方向未记录",
- secondary=secondary,
- detail_ref="run:objective",
- source_notice=None if direction else "record-missing",
- decision=decision,
- )
- def _planning_analysis(context: dict[str, Any] | None) -> dict[str, Any] | None:
- if not context or context.get("completeness") != "complete" or not context.get("eventRef"):
- return None
- secondary = [
- _line("plan", "执行计划", context.get("plan")),
- _line("next", "下一步", context.get("action")),
- ]
- return _story(
- id="run:planning-analysis",
- role="planning-analysis",
- kind="execution",
- title="创作规划解析",
- label="规划概要",
- value=context.get("summary") or "主 Agent 已完成首次创作规划",
- secondary=secondary,
- detail_ref=context.get("eventRef"),
- status="completed",
- )
- def _plan_projection(
- raw_round: dict[str, Any], decision_context: dict[str, Any] | None
- ) -> dict[str, Any]:
- round_index = _integer(raw_round.get("round_index")) or 0
- paths = raw_round.get("multipath_plan")
- if not isinstance(paths, list):
- paths = []
- raw_mode = _plain(raw_round.get("race_or_divide"))
- mode = plan_mode_label(raw_mode, len(paths))
- summary = (
- f"{mode or '方式未记录'} · {len(paths)} 个方案"
- if paths
- else "多路规划未记录"
- )
- decision = _direction_decision(
- id=f"round:{round_index}:plan",
- subtype="implementation-plan",
- context=decision_context,
- decision_items=(decision_context or {}).get("decisionItems")
- or [summary],
- primary_label="实现方式",
- primary_value=summary,
- secondary=_plan_card_lines(paths, raw_round.get("plan_note")),
- prompt_ref="prompt:main",
- )
- node = _story(
- id=f"round:{round_index}:plan",
- role="plan",
- kind="decision" if paths else "missing",
- title="实现规划",
- label="实现方式",
- value=summary,
- secondary=_plan_card_lines(paths, raw_round.get("plan_note")),
- detail_ref=f"round:{round_index}:plan",
- source_notice=None if paths else "record-missing",
- decision=decision,
- )
- return {
- "mode": mode,
- "rawMode": raw_mode,
- "paths": paths,
- "note": _plain(raw_round.get("plan_note")),
- "currentOnly": True,
- "runtimeRevisionCount": len((decision_context or {}).get("revisionRefs") or []),
- "runtimeRevisionRefs": list((decision_context or {}).get("revisionRefs") or []),
- "node": node,
- }
- def _branch_story(
- round_index: int,
- row: dict[str, Any],
- decisions: list[dict[str, Any]],
- domain_info: list[dict[str, Any]],
- retrieval_stage: dict[str, Any] | None,
- creative_decision: dict[str, Any] | None,
- implementer_task: str | None,
- run_status: str,
- ) -> dict[str, Any]:
- branch_id = _integer(row.get("branch_id")) or 0
- path_type = str(row.get("path_type") or "unknown")
- if path_type not in {"内容", "领域信息"}:
- path_type = "unknown"
- task = (
- branch_task_text(implementer_task)
- or branch_task_text(row.get("impl_task"))
- or business_text(row.get("target"))
- )
- retrieval_stage = retrieval_stage or _missing_retrieval_stage(
- round_index, branch_id, run_status
- )
- projected_data_decisions = [
- _DECISIONS.data_tradeoff(
- {
- **item,
- "decision": business_text(item.get("decision")),
- "reasoning": business_text(item.get("reasoning")),
- "id": f"data-decision:{item.get('id')}",
- "detailRef": f"data-decision:{item.get('id')}",
- "promptRef": _implementer_prompt_ref(retrieval_stage),
- }
- )
- for item in decisions
- ]
- for item, projection in zip(decisions, projected_data_decisions):
- full_conclusion = business_text(item.get("decision"))
- if full_conclusion:
- projection.setdefault("card", {}).setdefault("primary", {})[
- "value"
- ] = full_conclusion
- decision_nodes = [
- _story(
- id=f"data-decision:{item.get('id')}",
- role="data-decision",
- kind="decision",
- title="数据取舍",
- label="取舍结论",
- value=projection["card"]["primary"]["value"],
- secondary=projection["card"]["secondary"],
- detail_ref=f"data-decision:{item.get('id')}",
- decision=projection,
- )
- for item, projection in zip(decisions, projected_data_decisions)
- ]
- output = _branch_output(row, path_type, domain_info)
- if creative_decision and output.get("type") == "script-artifact":
- creative_decision = dict(creative_decision)
- explicit_basis = [
- {
- "label": business_text(item.get("decision")) or "已记录一项数据取舍",
- "value": business_text(item.get("reasoning")) or None,
- "detailRef": f"data-decision:{item.get('id')}",
- }
- for item in decisions
- if item.get("decision")
- ]
- creative_decision["artifactRef"] = output.get("snapshotRef")
- creative_decision["body"] = {
- **(creative_decision.get("body") or {}),
- "output": output.get("summary"),
- "explicitBasis": explicit_basis,
- "candidateSnapshot": {
- "snapshotRef": output.get("snapshotRef"),
- "paragraphCount": output.get("paragraphCount"),
- "elementCount": output.get("elementCount"),
- "linkCount": output.get("linkCount"),
- "exactness": _candidate_output_exactness(output),
- },
- }
- detail = creative_decision.get("detail")
- if isinstance(detail, dict):
- blocks = [
- block
- for block in detail.get("blocks") or []
- if not (
- isinstance(block, dict)
- and block.get("title") in {"实际产出", "产出的候选表"}
- )
- ]
- if explicit_basis:
- blocks.insert(
- 0,
- {
- "type": "items",
- "title": "明确采用的数据取舍",
- "items": explicit_basis,
- "visualRole": "evidence",
- "presentation": "list",
- "collapsible": True,
- },
- )
- blocks.append(
- {
- "type": "summary",
- "title": "产出的候选表",
- "value": output.get("summary"),
- "visualRole": "key-result",
- "presentation": "prose",
- }
- )
- creative_decision["detail"] = {**detail, "blocks": blocks}
- secondary = list((creative_decision.get("card") or {}).get("secondary") or [])
- if explicit_basis:
- secondary.append(_line("basis", "采用数据", f"{len(explicit_basis)} 项明确取舍"))
- secondary.append(_line("output", "产出的候选表", output.get("summary")))
- creative_decision["card"] = {
- **(creative_decision.get("card") or {}),
- "secondary": secondary[:3],
- }
- output["node"] = _story(
- id=creative_decision["id"],
- role="candidate-output",
- kind="decision",
- title="产生候选创作表",
- label="创作处理",
- value=creative_decision["card"]["primary"]["value"],
- secondary=creative_decision["card"]["secondary"],
- detail_ref=creative_decision["detailRef"],
- artifact_ref=output.get("snapshotRef"),
- has_changes=bool(output.get("snapshotRef")),
- decision=creative_decision,
- )
- status = str(row.get("status") or "open")
- disposition_context = _branch_disposition_context(status, run_status)
- title = _plain(row.get("target")) or f"方案 {branch_id}"
- return {
- "id": f"round:{round_index}:branch:{branch_id}",
- "branchId": branch_id,
- "pathType": path_type,
- "status": status,
- "title": title,
- "summary": {
- "task": task or "实现任务未记录",
- "output": output["summary"],
- },
- "task": _branch_task_story(
- id=f"round:{round_index}:branch:{branch_id}:task",
- task=task,
- detail_ref=f"round:{round_index}:branch:{branch_id}:task",
- ),
- "retrievalStage": retrieval_stage,
- "dataDecisions": [
- {
- "id": f"data-decision:{item.get('id')}",
- "recordId": item.get("id"),
- "decision": _plain(item.get("decision")),
- "reasoning": _plain(item.get("reasoning")),
- "sources": item.get("sources") or [],
- "createdAt": item.get("created_at"),
- "node": node,
- "decisionProjection": projection,
- }
- for item, node, projection in zip(
- decisions, decision_nodes, projected_data_decisions
- )
- ],
- "output": output,
- "outcome": {
- "status": status,
- "label": disposition_context["label"],
- "reasoning": _plain(row.get("decision_reasoning")),
- "sourceNotice": disposition_context["sourceNotice"],
- },
- "detailRef": f"round:{round_index}:branch:{branch_id}",
- }
- def _branch_output(
- row: dict[str, Any], path_type: str, domain_info: list[dict[str, Any]]
- ) -> dict[str, Any]:
- branch_id = _integer(row.get("branch_id")) or 0
- round_index = _integer(row.get("round_index")) or 0
- if path_type == "领域信息":
- facts = [
- item
- for item in domain_info
- if _integer(item.get("branch_id")) == branch_id
- and _integer(item.get("round_index")) in {None, round_index}
- ]
- summary = (
- f"新增 {len(facts)} 条已核实领域事实"
- if facts
- else "已查询完成,本方案没有新增领域事实"
- )
- node = _story(
- id=f"round:{round_index}:branch:{branch_id}:output",
- role="domain-output",
- kind="result",
- title="领域事实",
- label="产出",
- value=summary,
- detail_ref=f"round:{round_index}:branch:{branch_id}:output",
- has_changes=bool(facts),
- source_notice=None,
- )
- return {
- "type": "domain-info",
- "summary": summary,
- "factCount": len(facts),
- "factRefs": [f"domain-info:{item.get('id')}" for item in facts],
- "node": node,
- }
- candidate = row.get("candidate_snapshot") or {}
- snapshot = candidate.get("snapshot") if isinstance(candidate, dict) else None
- counts = _snapshot_counts(snapshot)
- if any(counts.values()):
- summary = (
- f"候选脚本包含 {counts['paragraphs']} 个段落、"
- f"{counts['elements']} 个元素"
- )
- elif row.get("self_assessment"):
- assessment = business_text(row.get("self_assessment"))
- first_item = next(
- (line.strip(" -*") for line in assessment.splitlines() if line.strip()),
- assessment,
- )
- summary = f"实现 Agent 自评:{first_item}"
- else:
- summary = "候选脚本内容未记录"
- has_output = bool(snapshot) or bool(row.get("self_assessment"))
- node = _story(
- id=f"round:{round_index}:branch:{branch_id}:output",
- role="candidate-output",
- kind="result" if has_output else "missing",
- title="候选产出",
- label="产出",
- value=summary,
- detail_ref=f"round:{round_index}:branch:{branch_id}:output",
- has_changes=bool(snapshot),
- source_notice=None if has_output else "record-missing",
- artifact_ref=f"artifact:branch:{branch_id}" if snapshot else None,
- )
- return {
- "type": "script-artifact" if has_output else "missing",
- "summary": summary,
- "snapshotRef": f"artifact:branch:{branch_id}" if snapshot else None,
- "paragraphCount": counts["paragraphs"],
- "elementCount": counts["elements"],
- "linkCount": counts["links"],
- "accuracy": {
- "historical": candidate.get("historicalAccuracy")
- if isinstance(candidate, dict)
- else "unknown",
- "currentProjection": candidate.get("currentProjectionAccuracy")
- if isinstance(candidate, dict)
- else "unknown",
- },
- "node": node,
- }
- def _branch_task_story(
- *, id: str, task: str | None, detail_ref: str
- ) -> dict[str, Any]:
- if not task:
- return _story(
- id=id,
- role="branch-task",
- kind="missing",
- title="实现任务",
- label="实现任务",
- value="实现任务未记录",
- detail_ref=detail_ref,
- source_notice="record-missing",
- )
- sections = branch_task_sections(task)
- preferred = next(
- (
- item
- for kind in ("scope", "method", "goal", "notes")
- for item in sections
- if item.get("kind") == kind and item.get("content")
- ),
- None,
- )
- primary = (preferred or {}).get("content") or task
- secondary = [
- _line(str(item.get("kind") or index), item.get("title") or "任务说明", item.get("content"))
- for index, item in enumerate(sections, 1)
- if item is not preferred and item.get("content")
- ][:3]
- node = _story(
- id=id,
- role="branch-task",
- kind="execution",
- title="实现任务",
- label=(preferred or {}).get("title") or "实现任务",
- value=primary,
- secondary=secondary,
- detail_ref=detail_ref,
- )
- node["taskStructure"] = {
- "sectionCount": len(sections),
- "sectionKinds": [item.get("kind") for item in sections],
- }
- return node
- def _candidate_output_exactness(output: dict[str, Any]) -> str:
- historical = (output.get("accuracy") or {}).get("historical")
- if historical == "exact":
- return "historical-snapshot"
- return "current-only" if output.get("snapshotRef") else "unknown"
- def _missing_retrieval_stage(
- round_index: int, branch_id: int, run_status: str
- ) -> dict[str, Any]:
- running = str(run_status or "").lower() not in TERMINAL_STATUSES
- return {
- "id": f"round:{round_index}:branch:{branch_id}:retrieval",
- "roundIndex": round_index,
- "branchId": branch_id,
- "implementerEventId": None,
- "observedMode": "unknown",
- "waves": [],
- "directToolGroups": [],
- "agentRuns": [],
- "status": "running" if running else "missing",
- "startedAt": None,
- "endedAt": None,
- "detailRef": f"round:{round_index}:branch:{branch_id}:retrieval",
- "sourceNotice": None if running else "record-missing",
- }
- def _decision_batch(
- round_index: int,
- row: dict[str, Any],
- valid_branch_ids: set[int],
- branches: list[dict[str, Any]],
- ) -> dict[str, Any]:
- branch_ids = _ids(row.get("branch_ids"))
- missing = [branch_id for branch_id in branch_ids if branch_id not in valid_branch_ids]
- record_id = row.get("id")
- decision = _plain(row.get("decision")) or "决策内容未记录"
- display_decision = _business_terms(decision)
- secondary = [
- _line(
- "branches",
- "比较方案",
- "、".join(f"方案 {branch_id}" for branch_id in branch_ids)
- or "涉及方案未记录",
- )
- ]
- if row.get("reasoning"):
- secondary.append(
- _line("reason", "理由", _preview(row.get("reasoning"), 120))
- )
- branch_by_id = {branch["branchId"]: branch for branch in branches}
- outcomes = [
- {
- "branchId": branch_id,
- "status": branch_by_id.get(branch_id, {}).get("outcome", {}).get("status", "unknown"),
- "label": branch_by_id.get(branch_id, {}).get("outcome", {}).get("label", "状态未记录"),
- "reasoning": branch_by_id.get(branch_id, {}).get("outcome", {}).get("reasoning"),
- }
- for branch_id in branch_ids
- ]
- if outcomes:
- secondary.insert(
- 1,
- _line(
- "outcomes",
- "各方案结果",
- ";".join(
- f"方案 {item['branchId']} · {item['label']}" for item in outcomes
- ),
- ),
- )
- projection = _DECISIONS.multipath_tradeoff(
- {
- "id": f"multipath-decision:{record_id}",
- "detailRef": f"multipath-decision:{record_id}",
- "promptRef": "prompt:main",
- "branchIds": branch_ids,
- "scopeItems": [f"方案 {branch_id}" for branch_id in branch_ids],
- "decision": display_decision,
- "reasoning": business_text(row.get("reasoning")),
- "selected": [
- f"方案 {item['branchId']}"
- for item in outcomes
- if item["status"] == "merged"
- ],
- "parked": [
- f"方案 {item['branchId']}"
- for item in outcomes
- if item["status"] == "parked"
- ],
- "rejected": [
- f"方案 {item['branchId']}"
- for item in outcomes
- if item["status"] == "discarded"
- ],
- "completeness": "partial" if missing or not branch_ids else "complete",
- }
- )
- projection.setdefault("card", {}).setdefault("primary", {})[
- "value"
- ] = display_decision
- return {
- "id": f"multipath-decision:{record_id}",
- "recordId": record_id,
- "roundIndex": round_index,
- "branchIds": branch_ids,
- "decision": decision,
- "reasoning": _plain(row.get("reasoning")),
- "createdAt": row.get("created_at"),
- "completeness": "partial" if missing or not branch_ids else "complete",
- "missingBranchIds": missing,
- "branchOutcomes": outcomes,
- "decisionProjection": projection,
- "node": _story(
- id=f"multipath-decision:{record_id}",
- role="multipath-decision",
- kind="decision",
- title="主 Agent 多路决策",
- label="决定",
- value=projection["card"]["primary"]["value"],
- secondary=projection["card"]["secondary"],
- detail_ref=f"multipath-decision:{record_id}",
- source_notice="record-missing" if missing else None,
- decision=projection,
- ),
- }
- def _convergence_batches(
- round_index: int,
- reviews: list[dict[str, Any]],
- decisions: list[dict[str, Any]],
- run_status: str,
- ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
- ordered_reviews = sorted(reviews, key=_runtime_order)
- ordered_decisions = sorted(decisions, key=_decision_order)
- unmatched_reviews = set(range(len(ordered_reviews)))
- batches: list[dict[str, Any]] = []
- for decision in ordered_decisions:
- decision_set = set(decision.get("branchIds") or [])
- decision_time = _date_value(decision.get("createdAt"))
- candidates = [
- index
- for index in unmatched_reviews
- if set(ordered_reviews[index].get("branchIds") or []) == decision_set
- and _not_after(ordered_reviews[index].get("endedAt"), decision_time)
- ]
- review_index = max(
- candidates,
- key=lambda index: _runtime_order(ordered_reviews[index]),
- default=None,
- )
- review = ordered_reviews[review_index] if review_index is not None else None
- if review_index is not None:
- unmatched_reviews.remove(review_index)
- batches.append(
- _convergence_batch(
- round_index,
- review,
- decision,
- "exact-branch-set-and-time" if review else "decision-only",
- run_status,
- )
- )
- for index in sorted(unmatched_reviews, key=lambda value: _runtime_order(ordered_reviews[value])):
- review = ordered_reviews[index]
- batches.append(
- _convergence_batch(
- round_index, review, None, "review-only", run_status
- )
- )
- batches.sort(key=_convergence_order)
- return batches, []
- def _annotate_unscoped_round_reviews(
- batches: list[dict[str, Any]],
- unassigned: list[dict[str, Any]],
- round_index: int,
- ) -> None:
- """Keep unscoped reviews visible without fabricating a candidate link."""
- reviews = [
- item
- for item in unassigned
- if str(item.get("name") or "") == "script_multipath_evaluator"
- and _integer(item.get("roundIndex")) == round_index
- and str(item.get("association") or "") == "missing-branch-scope"
- ]
- if not reviews:
- return
- refs = [item.get("detailRef") for item in reviews if item.get("detailRef")]
- for batch in batches:
- missing = batch.get("missingReview")
- if not isinstance(missing, dict):
- continue
- missing["card"] = {
- "primary": {
- "label": "评审关联",
- "value": "发现同轮评审,但无法安全关联到具体候选",
- },
- "secondary": [
- _line("records", "未关联记录", f"{len(refs)} 条评审记录")
- ],
- }
- missing["sourceNotice"] = "association-incomplete"
- missing["unscopedReviewRefs"] = refs
- def _convergence_batch(
- round_index: int,
- review: dict[str, Any] | None,
- decision: dict[str, Any] | None,
- association: str,
- run_status: str,
- branch_ids: list[int] | None = None,
- ) -> dict[str, Any]:
- branch_ids = branch_ids or list(
- dict.fromkeys(
- (review or {}).get("branchIds")
- or (decision or {}).get("branchIds")
- or []
- )
- )
- suffix = (
- f"review-{review.get('eventId')}"
- if review
- else f"decision-{decision.get('recordId')}"
- if decision
- else "records-missing"
- )
- missing_context = _missing_multipath_context(run_status)
- if review is not None and not review.get("decisionProjection"):
- review = dict(review)
- review["decisionProjection"] = _evaluation_decision(
- review,
- subtype="multipath-evaluation",
- detail_ref=review.get("detailRef"),
- prompt_ref=review.get("promptRef"),
- )
- missing_review = None
- if review is None:
- missing_review_id = f"round:{round_index}:{suffix}:review-missing"
- missing_review = _story(
- id=missing_review_id,
- role="multipath-review",
- kind=missing_context["kind"],
- title="多方案评审",
- label="评审记录",
- value="未找到可与本次决策安全关联的多方案评审记录",
- detail_ref=missing_review_id,
- source_notice=missing_context["sourceNotice"],
- status=missing_context.get("status"),
- )
- missing_decision = None
- if decision is None:
- missing_decision_id = f"round:{round_index}:{suffix}:decision-missing"
- missing_decision = _story(
- id=missing_decision_id,
- role="multipath-decision",
- kind=missing_context["kind"],
- title="主 Agent 多路决策",
- label=missing_context["label"],
- value=missing_context["value"],
- detail_ref=missing_decision_id,
- source_notice=missing_context["sourceNotice"],
- status=missing_context.get("status"),
- )
- if review is not None and decision is not None:
- _attach_review_comparison(review, decision, association)
- return {
- "id": f"round:{round_index}:convergence:{suffix}",
- "roundIndex": round_index,
- "branchIds": branch_ids,
- "review": review,
- "decision": decision,
- "missingReview": missing_review,
- "missingDecision": missing_decision,
- "association": association,
- }
- def _round_evaluation(
- round_index: int, evaluations: list[dict[str, Any]], run_status: str
- ) -> dict[str, Any]:
- if not evaluations:
- context = _missing_evaluation_context(run_status)
- status = context.get("status")
- if not status:
- status = (
- "not-evaluated"
- if context["sourceNotice"] == "record-missing"
- else "unknown"
- )
- return _story(
- id=f"round:{round_index}:evaluation",
- role="round-evaluation",
- kind=context["kind"],
- title="主脚本整体评审",
- label=context["label"],
- value=context["value"],
- detail_ref=f"round:{round_index}:evaluation",
- source_notice=context["sourceNotice"],
- status=status,
- )
- latest = evaluations[-1]
- conclusion = _plain(latest.get("conclusion"))
- projection = _evaluation_decision(
- latest,
- subtype="overall-evaluation",
- detail_ref=latest.get("detailRef"),
- prompt_ref=latest.get("promptRef"),
- )
- return _story(
- id=f"round:{round_index}:evaluation",
- role="round-evaluation",
- kind="decision" if conclusion else "missing",
- title="主脚本整体评审",
- label="评审结论",
- value=projection["card"]["primary"]["value"],
- secondary=projection["card"]["secondary"],
- detail_ref=latest.get("detailRef"),
- source_notice="runtime-associated",
- status=_evaluation_state(conclusion),
- decision=projection,
- )
- def _evaluation_decision(
- evaluation: dict[str, Any],
- *,
- subtype: str,
- detail_ref: Any,
- prompt_ref: Any,
- ) -> dict[str, Any]:
- projection = _DECISIONS.evaluation(
- {
- "id": evaluation.get("id") or detail_ref,
- "detailRef": detail_ref,
- "promptRef": prompt_ref,
- "subtype": subtype,
- "subjects": evaluation.get("subjects") or [
- f"方案 {value}" for value in evaluation.get("branchIds") or []
- ],
- "criteria": evaluation.get("criteria") or [],
- "itemConclusions": evaluation.get("itemConclusions")
- or evaluation.get("branchConclusions")
- or [],
- "branchEvaluations": evaluation.get("branchEvaluations") or [],
- "comparisonRows": evaluation.get("comparisonRows") or [],
- "achievements": evaluation.get("achievements") or [],
- "problems": evaluation.get("problems") or [],
- "conclusion": evaluation.get("conclusion"),
- "recommendation": evaluation.get("recommendation")
- or evaluation.get("comparison"),
- "nextGoal": evaluation.get("nextGoal"),
- "completeness": "partial"
- if evaluation.get("notices")
- else "complete",
- }
- )
- full_conclusion = business_text(
- evaluation.get("conclusion")
- or evaluation.get("recommendation")
- or evaluation.get("comparison")
- )
- if full_conclusion:
- projection.setdefault("card", {}).setdefault("primary", {})[
- "value"
- ] = full_conclusion
- return projection
- def _attach_review_comparison(
- review: dict[str, Any], decision: dict[str, Any], association: str
- ) -> None:
- projection = decision.get("decisionProjection")
- if not isinstance(projection, dict):
- return
- conclusions = {
- int(item.get("branchId") or 0): item
- for item in review.get("branchConclusions") or []
- if isinstance(item, dict)
- }
- outcomes = {
- int(item.get("branchId") or 0): item
- for item in decision.get("branchOutcomes") or []
- if isinstance(item, dict)
- }
- reasoning = business_text(decision.get("reasoning"))
- rows: list[dict[str, Any]] = []
- for branch_id in decision.get("branchIds") or []:
- row = {
- "candidate": f"方案 {branch_id}",
- "recommendation": (conclusions.get(branch_id) or {}).get("conclusion")
- or review.get("recommendation")
- or "评审未给出单独结论",
- "finalDecision": (outcomes.get(branch_id) or {}).get("label")
- or "最终状态未记录",
- }
- handling = _explicit_recommendation_handling(reasoning, branch_id)
- if handling:
- row["handling"] = handling
- rows.append(row)
- projection.setdefault("body", {})["reviewComparison"] = rows
- projection["body"]["reviewAssociation"] = association
- review_input = {
- "label": "同批候选方案的评审建议",
- "summary": business_text(
- review.get("recommendation") or review.get("comparison")
- ),
- "observedRelation": "returned-to-actor",
- "decisionUse": "not-recorded",
- "detailRef": review.get("detailRef"),
- }
- projection.setdefault("inputs", []).append(review_input)
- projection.setdefault("notices", []).append(
- {
- "code": "same-batch-association",
- "message": "评审与最终决定按同一批候选方案和发生顺序关联;系统没有保存直接的一对一关系。",
- }
- )
- if rows:
- blocks = projection.setdefault("detail", {}).setdefault("blocks", [])
- blocks.insert(
- 0,
- {
- "type": "items",
- "title": "参与判断",
- "items": [
- {
- "label": review_input["label"],
- "value": review_input["summary"],
- "note": "可确认评审在最终决定前返回;是否采用应以下方的逐方案对照为准。",
- "detailRef": review_input["detailRef"],
- }
- ],
- "visualRole": "evidence",
- "presentation": "list",
- "collapsible": True,
- },
- )
- blocks.insert(
- 3,
- {
- "type": "comparison",
- "title": "评审建议与最终决定",
- "rows": rows,
- "visualRole": "section",
- "presentation": "comparison",
- },
- )
- def _explicit_recommendation_handling(reasoning: str, branch_id: int) -> str | None:
- if not reasoning:
- return None
- for sentence in re.split(r"[。;;\n]+", reasoning):
- if not re.search(rf"方案\s*{branch_id}\b", sentence):
- continue
- if any(
- token in sentence
- for token in (
- "采纳",
- "吸收",
- "保留",
- "延后",
- "转入",
- "下一轮",
- "转译",
- "不直接合并",
- "未采用",
- "拒绝",
- "不采纳",
- )
- ):
- return sentence.strip()
- return None
- _DATABASE_TIMEZONE = ZoneInfo("Asia/Shanghai")
- def _date_value(value: Any) -> datetime | None:
- if isinstance(value, datetime):
- parsed = value
- elif isinstance(value, str) and value:
- try:
- parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
- except ValueError:
- return None
- else:
- return None
- if parsed.tzinfo is None:
- parsed = parsed.replace(tzinfo=_DATABASE_TIMEZONE)
- return parsed.astimezone(timezone.utc)
- def _not_after(value: Any, boundary: datetime | None) -> bool:
- current = _date_value(value)
- return boundary is None or current is None or current <= boundary
- def _record_order(row: dict[str, Any]) -> tuple[str, int]:
- return (str(row.get("created_at") or ""), _integer(row.get("id")) or 0)
- def _runtime_order(row: dict[str, Any]) -> tuple[str, int]:
- return (
- str(row.get("startedAt") or row.get("endedAt") or ""),
- _integer(row.get("eventId")) or 0,
- )
- def _decision_order(row: dict[str, Any]) -> tuple[str, int]:
- return (str(row.get("createdAt") or ""), _integer(row.get("recordId")) or 0)
- def _convergence_order(row: dict[str, Any]) -> tuple[str, int]:
- review = row.get("review") or {}
- decision = row.get("decision") or {}
- return (
- str(review.get("startedAt") or decision.get("createdAt") or ""),
- _integer(review.get("eventId") or decision.get("recordId")) or 0,
- )
- def _round_result(
- record: dict[str, Any],
- round_index: int,
- branches: list[dict[str, Any]],
- domain_info: list[dict[str, Any]],
- state: str,
- next_round: dict[str, Any] | None,
- ) -> dict[str, Any]:
- merged_content = [
- branch
- for branch in branches
- if branch["pathType"] != "领域信息" and branch["status"] == "merged"
- ]
- script_summary = (
- f"确认采用 {len(merged_content)} 个内容方案"
- if merged_content
- else "没有可确认的内容方案合入记录"
- )
- domain_summary = (
- f"新增 {len(domain_info)} 条领域事实"
- if domain_info
- else "本轮没有新增领域事实记录"
- )
- if next_round:
- next_step = f"数据库已记录第 {next_round.get('round_index')} 轮"
- elif str(record.get("status") or "").lower() in TERMINAL_STATUSES:
- next_step = "构建已经结束"
- else:
- next_step = "等待本轮收口"
- primary = f"{script_summary};{domain_summary}"
- return _story(
- id=f"round:{round_index}:result",
- role="round-result",
- kind="result",
- title="本轮产出",
- label="产出",
- value=primary,
- secondary=[_line("next", "下一步", next_step)],
- detail_ref=f"round:{round_index}:result",
- artifact_ref=f"artifact:round:{round_index}",
- has_changes=bool(merged_content or domain_info),
- status=state,
- )
- def _round_state(
- record: dict[str, Any],
- raw_round: dict[str, Any],
- evaluation: dict[str, Any] | None,
- ) -> str:
- if evaluation and evaluation.get("status") not in {None, "unknown"}:
- return str(evaluation["status"])
- run_status = str(record.get("status") or "").lower()
- if str(raw_round.get("status") or "").lower() == "open" and run_status not in TERMINAL_STATUSES:
- return "running"
- return "unknown"
- def _evaluation_state(conclusion: str | None) -> str:
- text = str(conclusion or "")
- if any(token in text for token in ("未通过", "不通过", "回退", "重试")):
- return "needs-retry"
- if "部分通过" in text:
- return "partially-passed"
- if "通过" in text:
- return "passed"
- return "unknown"
- def _final_result(
- record: dict[str, Any], rounds: list[dict[str, Any]], artifact: dict[str, Any]
- ) -> dict[str, Any]:
- branches = [branch for round_ in rounds for branch in round_["branches"]]
- statuses = Counter(branch["status"] for branch in branches)
- counts = _snapshot_counts(artifact)
- status = str(record.get("status") or "unknown")
- summary = _plain(record.get("summary"))
- error = _plain(record.get("error_message"))
- primary = summary or error or _final_status_text(status)
- return {
- "id": "run:final-result",
- "role": "final-result",
- "kind": "result",
- "title": "最终结果",
- "status": status,
- "terminal": status.lower() in TERMINAL_STATUSES,
- "roundCount": len(rounds),
- "branchCounts": {
- "total": len(branches),
- "merged": statuses.get("merged", 0),
- "parked": statuses.get("parked", 0),
- "discarded": statuses.get("discarded", 0),
- "open": statuses.get("open", 0),
- },
- "summary": _plain(record.get("summary")),
- "errorMessage": _plain(record.get("error_message")),
- "artifact": {
- "snapshotRef": "artifact:base:current",
- "paragraphCount": counts["paragraphs"],
- "elementCount": counts["elements"],
- "linkCount": counts["links"],
- "historicalVersion": "current-only",
- },
- "card": {
- "primary": {"label": "构建结论", "value": primary},
- "secondary": [
- _line("rounds", "轮次", f"{len(rounds)} 轮"),
- _line(
- "branches",
- "方案处置",
- f"采用 {statuses.get('merged', 0)} · 暂存 {statuses.get('parked', 0)} · 未采用 {statuses.get('discarded', 0)}",
- ),
- _line(
- "artifact",
- "主脚本规模",
- f"{counts['paragraphs']} 段落 · {counts['elements']} 元素 · {counts['links']} 关联",
- ),
- ],
- },
- "artifactRef": "artifact:base:current",
- "detailRef": "run:final-result",
- }
- def _final_status_text(status: str) -> str:
- return {
- "success": "构建已完成",
- "completed": "构建已完成",
- "partial": "构建部分完成",
- "failed": "构建失败",
- "stopped": "构建已停止",
- "cancelled": "构建已取消",
- "running": "构建进行中",
- }.get(str(status or "").lower(), "构建状态已记录")
- def _story(
- *,
- id: str,
- role: str,
- kind: str,
- title: str,
- label: str,
- value: Any,
- secondary: list[dict[str, Any]] | None = None,
- detail_ref: str | None = None,
- has_changes: bool = False,
- source_notice: str | None = None,
- status: str | None = None,
- artifact_ref: str | None = None,
- decision: dict[str, Any] | None = None,
- ) -> dict[str, Any]:
- node = {
- "id": id,
- "role": role,
- "kind": kind,
- "title": title,
- "card": (decision or {}).get("card")
- or {
- # Callers pass a deliberately selected card field. Keep that field
- # intact; line clamping is a view concern, not a data projection.
- "primary": {"label": label, "value": _plain(value)},
- "secondary": [item for item in (secondary or []) if item.get("value")],
- },
- "hasChanges": bool(has_changes),
- }
- if detail_ref:
- node["detailRef"] = detail_ref
- if source_notice:
- node["sourceNotice"] = source_notice
- if status:
- node["status"] = status
- if artifact_ref:
- node["artifactRef"] = artifact_ref
- if decision:
- node["decision"] = decision
- return node
- def _line(key: str, label: str, value: Any, tone: str = "normal") -> dict[str, Any]:
- return {"key": key, "label": label, "value": _plain(value), "tone": tone}
- def _plan_card_lines(paths: list[Any], note: Any) -> list[dict[str, Any]]:
- lines: list[dict[str, Any]] = []
- if paths:
- route_values = [plan_path_summary(path, index) for index, path in enumerate(paths[:2], 1)]
- route_summary = ";".join(route_values)
- if len(paths) > 2:
- route_summary += f";另有 {len(paths) - 2} 路,详情查看"
- lines.append(_line("routes", "各路任务", route_summary))
- reason = _plain(note)
- if reason:
- lines.append(_line("reason", "规划理由", reason))
- return lines[:2]
- def _context_input_labels(
- context: dict[str, Any] | None, *, relation: str | None = None
- ) -> list[str]:
- result: list[str] = []
- for item in (context or {}).get("inputs") or []:
- if not isinstance(item, dict):
- continue
- if relation and item.get("relation") != relation:
- continue
- label = _plain(item.get("label"))
- if label and label not in result:
- result.append(label)
- return result[:3]
- def _direction_decision(
- *,
- id: str,
- subtype: str,
- context: dict[str, Any] | None,
- decision_items: list[Any],
- primary_label: str,
- primary_value: Any,
- secondary: list[dict[str, Any]],
- prompt_ref: str,
- ) -> dict[str, Any]:
- context = context if isinstance(context, dict) else {}
- decision = _DECISIONS.direction(
- {
- "id": id,
- "detailRef": id,
- "promptRef": prompt_ref,
- "subtype": subtype,
- "decisionItems": [str(item) for item in decision_items if item],
- "explicitReasoning": context.get("explicitReasoning"),
- "constraints": context.get("constraints") or [],
- "inputs": context.get("inputs") or [],
- "sourceDocument": context.get("sourceDocument"),
- "implementationPlan": {
- "mode": context.get("displayMode"),
- "rawMode": context.get("rawMode"),
- "routes": context.get("routeItems") or [],
- "reasoning": context.get("explicitReasoning"),
- }
- if subtype == "implementation-plan"
- else None,
- "completeness": context.get("completeness"),
- }
- )
- decision["card"] = {
- "primary": {
- "label": primary_label,
- "value": _plain(primary_value) or "决策结论未记录",
- },
- "secondary": [item for item in secondary if item.get("value")][:3],
- }
- revisions = []
- for revision in context.get("revisions") or []:
- if not isinstance(revision, dict):
- continue
- paths = revision.get("paths") if isinstance(revision.get("paths"), list) else []
- parts = [str(revision.get("mode"))] if revision.get("mode") else []
- if paths:
- parts.append(f"{len(paths)} 路方案")
- revisions.append(
- {
- "current": bool(revision.get("current")),
- "detailRef": revision.get("detailRef"),
- "summary": " · ".join(parts) or "已保存一次版本",
- }
- )
- if revisions:
- decision["body"]["revisions"] = revisions
- decision["technicalRefs"] = list(context.get("technicalRefs") or [])
- return decision
- def _implementer_prompt_ref(retrieval_stage: dict[str, Any] | None) -> str | None:
- event_id = _integer((retrieval_stage or {}).get("implementerEventId"))
- return f"prompt:event:{event_id}" if event_id is not None else None
- def _snapshot_counts(value: Any) -> dict[str, int]:
- if isinstance(value, dict) and isinstance(value.get("snapshot"), dict):
- value = value["snapshot"]
- if not isinstance(value, dict):
- return {"paragraphs": 0, "elements": 0, "links": 0}
- stats = value.get("stats") if isinstance(value.get("stats"), dict) else {}
- if stats:
- return {
- "paragraphs": int(stats.get("paragraph_count") or 0),
- "elements": int(stats.get("element_count") or 0),
- "links": int(stats.get("link_count") or 0),
- }
- return {
- "paragraphs": len(value.get("paragraphs") or []),
- "elements": len(value.get("elements") or []),
- "links": len(value.get("paragraphElements") or value.get("paragraph_elements") or []),
- }
- def _ids(value: Any) -> list[int]:
- if not isinstance(value, list):
- return []
- result: list[int] = []
- for item in value:
- number = _integer(item)
- if number is not None and number not in result:
- result.append(number)
- return result
- def _implementer_tasks_by_branch(
- events: list[dict[str, Any]],
- ) -> dict[int, str]:
- """Return complete dispatch tasks when the lightweight event carries them.
- Historical bundles may only retain previews; callers deliberately keep the
- branch business record as a labelled fallback in that case.
- """
- result: dict[int, str] = {}
- for event in events:
- if (
- str(event.get("event_type") or "") != "agent_invoke"
- or str(event.get("event_name") or "") != "script_implementer"
- ):
- continue
- branch_id = _integer(event.get("branch_id"))
- if branch_id is None:
- continue
- data = event.get("inputData")
- task = data.get("task") if isinstance(data, dict) else None
- if not task:
- wrapped = event.get("input")
- content = wrapped.get("content") if isinstance(wrapped, dict) else None
- task = content.get("task") if isinstance(content, dict) else None
- text = branch_task_text(task)
- if text:
- result[branch_id] = text
- return result
- def _round_summary(
- *,
- round_index: int,
- goal: dict[str, Any],
- plan: dict[str, Any],
- branches: list[dict[str, Any]],
- batches: list[dict[str, Any]],
- domain_info: list[dict[str, Any]],
- run_status: str,
- ) -> dict[str, Any]:
- paths = plan.get("paths") or []
- branch_ids = sorted(
- {
- branch_id
- for batch in batches
- for branch_id in batch.get("branchIds") or []
- }
- )
- decision_context = _missing_multipath_context(run_status)
- decision_summary = (
- ";".join(_preview(_business_terms(batch.get("decision")), 64) for batch in batches[:2])
- if batches
- else decision_context["value"]
- )
- if len(batches) > 2:
- decision_summary += f";另有 {len(batches) - 2} 个决策批次"
- merged_content_count = sum(
- 1
- for branch in branches
- if branch.get("pathType") != "领域信息" and branch.get("status") == "merged"
- )
- script_summary = (
- f"{merged_content_count} 个内容方案进入主脚本"
- if merged_content_count
- else "没有可确认的内容方案合入记录"
- )
- domain_summary = (
- f"新增 {len(domain_info)} 条领域事实"
- if domain_info
- else "没有新增领域事实记录"
- )
- return {
- "title": f"第 {round_index} 轮",
- "goal": goal,
- "approach": {
- "mode": plan.get("mode"),
- "candidateCount": len(branches) or len(paths),
- "plannedPathCount": len(paths),
- "contentBranchCount": sum(
- 1 for branch in branches if branch.get("pathType") == "内容"
- ),
- "domainBranchCount": sum(
- 1 for branch in branches if branch.get("pathType") == "领域信息"
- ),
- "summary": _plan_summary(plan),
- },
- "decision": {
- "batchCount": len(batches),
- "branchIds": branch_ids,
- "summary": decision_summary,
- },
- "output": {
- "mergedContentCount": merged_content_count,
- "domainFactCount": len(domain_info),
- "scriptSummary": script_summary,
- "domainSummary": domain_summary,
- "summary": f"{script_summary};{domain_summary}",
- },
- }
- def _goal_summary(value: Any) -> dict[str, Any]:
- parsed = parse_goal_text(value)
- if not parsed.get("raw"):
- return {
- "headline": "本轮目标未记录",
- "itemCount": 0,
- "previewItems": [],
- "truncated": False,
- }
- items = list(parsed.get("focusItems") or [])
- headline_source = str(parsed.get("headline") or parsed.get("raw"))
- headline = _preview(headline_source, 96)
- preview_items = [_preview(item, 72) for item in items[:3]]
- return {
- "headline": headline,
- "itemCount": len(items),
- "previewItems": preview_items,
- "truncated": bool(
- items
- or len(headline_source) > len(headline)
- or len(items) > len(preview_items)
- ),
- }
- def _business_terms(value: Any) -> str:
- text = _plain(value) or "未记录"
- text = re.sub(r"(?i)\s*\bbranch\s*#?(\d+)\b\s*", r"方案 \1 ", text)
- text = re.sub(r"(?i)\s*\bbase\b", "主脚本", text)
- text = re.sub(r"\s+([,。;!?、:)】,.])", r"\1", text)
- return text
- def _missing_multipath_context(run_status: str) -> dict[str, Any]:
- status = str(run_status or "unknown").lower()
- if status == "running":
- return {
- "kind": "execution",
- "label": "当前进度",
- "value": "等待主 Agent 比较候选方案",
- "sourceNotice": None,
- "status": "running",
- }
- interrupted = {
- "stopped": "构建停止前尚未形成多路决策",
- "cancelled": "构建取消前尚未形成多路决策",
- "cancel": "构建取消前尚未形成多路决策",
- "cancle": "构建取消前尚未形成多路决策",
- "failed": "构建失败前尚未形成多路决策",
- }
- if status in interrupted:
- return {
- "kind": "missing",
- "label": "流程进度",
- "value": interrupted[status],
- "sourceNotice": "process-incomplete",
- }
- return {
- "kind": "missing",
- "label": "记录状态",
- "value": "构建已经结束,但未找到主 Agent 多路决策记录",
- "sourceNotice": "record-missing",
- }
- def _missing_evaluation_context(run_status: str) -> dict[str, Any]:
- status = str(run_status or "unknown").lower()
- if status == "running":
- return {
- "kind": "execution",
- "label": "当前进度",
- "value": "等待整体评审",
- "sourceNotice": None,
- "status": "running",
- }
- interrupted = {
- "stopped": "构建停止前尚未进行整体评审",
- "cancelled": "构建取消前尚未进行整体评审",
- "cancel": "构建取消前尚未进行整体评审",
- "cancle": "构建取消前尚未进行整体评审",
- "failed": "构建失败前尚未形成整体评审结论",
- }
- if status in interrupted:
- return {
- "kind": "missing",
- "label": "流程进度",
- "value": interrupted[status],
- "sourceNotice": "process-incomplete",
- }
- return {
- "kind": "missing",
- "label": "记录状态",
- "value": "构建已经结束,但未找到明确的整体评审结论",
- "sourceNotice": "record-missing",
- }
- def _branch_disposition_context(branch_status: str, run_status: str) -> dict[str, Any]:
- if branch_status != "open":
- return {
- "kind": "decision",
- "label": _status_label(branch_status),
- "sourceNotice": None,
- }
- status = str(run_status or "unknown").lower()
- if status == "running":
- return {
- "kind": "execution",
- "label": "等待主 Agent 处置",
- "sourceNotice": None,
- }
- interrupted = {
- "stopped": "构建停止时尚未处置",
- "cancelled": "构建取消时尚未处置",
- "cancel": "构建取消时尚未处置",
- "cancle": "构建取消时尚未处置",
- "failed": "构建失败时尚未处置",
- }
- if status in interrupted:
- return {
- "kind": "missing",
- "label": interrupted[status],
- "sourceNotice": "process-incomplete",
- }
- return {
- "kind": "missing",
- "label": "构建结束时仍未处置",
- "sourceNotice": "record-missing",
- }
- def _plan_summary(plan: dict[str, Any]) -> str:
- paths = plan.get("paths") or []
- if not paths:
- return "多路规划未记录"
- return f"{plan.get('mode') or '方式未记录'}:{len(paths)} 个候选方案"
- def _status_label(status: str) -> str:
- return {
- "merged": "已采用",
- "parked": "暂存",
- "discarded": "未采用",
- "open": "尚未处置",
- }.get(status, status or "状态未记录")
- def _data_shape(
- branches: list[dict[str, Any]],
- multipath_decisions: list[dict[str, Any]],
- domain_info: list[dict[str, Any]],
- events: list[dict[str, Any]],
- ) -> str:
- if (
- multipath_decisions
- or domain_info
- or events
- or any(branch.get("path_type") for branch in branches)
- ):
- return "current"
- return "legacy-incomplete"
- def _warnings(
- *,
- data_shape: str,
- ignored_legacy_count: int,
- rounds: list[dict[str, Any]],
- runtime_unassigned: list[dict[str, Any]],
- business_unassigned: list[dict[str, Any]],
- ) -> list[dict[str, str]]:
- warnings: list[dict[str, str]] = []
- if data_shape == "legacy-incomplete":
- warnings.append(
- {
- "id": "legacy-shape",
- "level": "warning",
- "message": "这是旧结构记录,只展示新数据契约能够直接确认的内容。",
- }
- )
- if ignored_legacy_count:
- warnings.append(
- {
- "id": "ignored-legacy-data-decisions",
- "level": "info",
- "message": f"已忽略 {ignored_legacy_count} 条旧语义主决策记录。",
- }
- )
- missing_rounds = [
- round_["roundIndex"]
- for round_ in rounds
- if round_["branches"]
- and not any(
- batch.get("decision")
- for batch in round_.get("convergenceBatches") or []
- )
- and any(
- (batch.get("missingDecision") or {}).get("sourceNotice")
- == "record-missing"
- for batch in round_.get("convergenceBatches") or []
- )
- ]
- if missing_rounds:
- warnings.append(
- {
- "id": "missing-multipath-decisions",
- "level": "warning",
- "message": "第 " + "、".join(map(str, missing_rounds)) + " 轮未记录主 Agent 多路决策。",
- }
- )
- if runtime_unassigned:
- warnings.append(
- {
- "id": "unassigned-runtime-events",
- "level": "info",
- "message": f"有 {len(runtime_unassigned)} 条运行记录无法安全归属,已留在技术详情中。",
- }
- )
- if business_unassigned:
- warnings.append(
- {
- "id": "unassigned-business-records",
- "level": "warning",
- "message": f"有 {len(business_unassigned)} 条多路决策缺少可确认的轮次归属。",
- }
- )
- return warnings
- def _plain(value: Any) -> str | None:
- if value is None:
- return None
- text = str(value).replace("\r\n", "\n").strip()
- if not text:
- return None
- text = re.sub(r"```[a-zA-Z0-9_-]*", "", text).replace("```", "")
- text = re.sub(r"^\s{0,3}#{1,6}\s*", "", text, flags=re.M)
- text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
- text = re.sub(r"(?m)^\s*[-*]\s+", "• ", text)
- return text.strip()
- def _preview(value: Any, limit: int = 140) -> str:
- text = _plain(value) or "未记录"
- text = re.sub(r"\s+", " ", text)
- if len(text) <= limit:
- return text
- window = text[:limit]
- cut = max(window.rfind(mark) for mark in "。!?;")
- if cut >= max(30, limit // 2):
- return window[: cut + 1]
- return window.rstrip(",、;:,. ") + "…"
- def _integer(value: Any) -> int | None:
- try:
- return int(value) if value is not None else None
- except (TypeError, ValueError):
- return None
|