| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- from __future__ import annotations
- from app.main_agent_decision_projection import (
- MainAgentDecisionProjector,
- parse_goal_items,
- parse_script_direction,
- )
- from app.runtime_event_index import RuntimeEventIndex
- from app.runtime_tool_catalog import business_tool_label
- DIRECTION = """## 选题价值主张
- 用真实事件解释行业变化。
- ## 账号价值主张
- 保持专业深度与信息密度。
- ## 创作总目标
- ### 目标 1
- - 目标语句:拆解机制并让读者看懂因果链。
- - 通过标准:结构清楚。
- ### 目标 2
- - 目标语句:用可核验数据支撑关键结论。
- ## 领域评估维度
- 1. 数据可信度
- 2. 适用边界
- """
- def _event(
- event_id: int,
- event_seq: int,
- event_type: str,
- event_name: str,
- **extra,
- ):
- return {
- "id": event_id,
- "event_seq": event_seq,
- "event_type": event_type,
- "event_name": event_name,
- "status": "ok",
- "ended_at": f"2026-07-14T10:00:{event_seq:02d}",
- **extra,
- }
- def _main_scope(event_id: int = 1):
- return _event(
- event_id,
- 1,
- "agent_scope",
- "main",
- agent_role="main",
- agent_depth=0,
- scope_event_id=event_id,
- parent_event_id=None,
- )
- def _main_tool(event_id: int, event_seq: int, name: str, **extra):
- return _event(
- event_id,
- event_seq,
- "tool_call",
- name,
- agent_role="main",
- agent_depth=0,
- scope_event_id=1,
- parent_event_id=1,
- **extra,
- )
- def test_runtime_index_requires_real_main_scope_for_direct_reads():
- events = [
- _main_scope(),
- _main_tool(2, 2, "get_topic_detail"),
- _event(
- 3,
- 3,
- "tool_call",
- "get_topic_detail",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=30,
- parent_event_id=30,
- ),
- _event(
- 4,
- 4,
- "tool_call",
- "get_topic_detail",
- agent_role="main",
- agent_depth=0,
- scope_event_id=999,
- parent_event_id=999,
- ),
- _main_tool(5, 5, "get_domain_info", status="error"),
- ]
- index = RuntimeEventIndex(events)
- assert index.main_scope_ids == {1}
- assert [event["id"] for event in index.main_direct_reads()] == [2]
- assert [event["id"] for event in index.main_direct_reads(successful=False)] == [5]
- assert [event["id"] for event in index.events_for_actor("main")] == [1, 2, 4, 5]
- assert [event["id"] for event in index.events_for_actor("overall-evaluator")] == [3]
- assert business_tool_label("get_topic_detail") == "选题"
- def test_planning_analysis_uses_last_real_main_thought_before_direction_save():
- events = [
- _main_scope(),
- _main_tool(2, 2, "think_and_plan", inputData={
- "thought_number": 1,
- "thought_summary": "先形成旧规划",
- "thought": "旧思考",
- "plan": "旧计划",
- "action": "旧下一步",
- }),
- _main_tool(3, 3, "think_and_plan", inputData={
- "thought_number": 2,
- "thought_summary": "锁定创作目标与首轮骨架",
- "thought": "完整分析账号结构与领域下限。",
- "plan": "保存目标,然后开启首轮。",
- "action": "写入创作总目标",
- }),
- _main_tool(4, 4, "save_script_direction", inputData={"script_direction": DIRECTION}),
- _main_tool(5, 5, "think_and_plan", inputData={
- "thought_summary": "保存后的其他规划",
- "thought": "不应成为开场规划",
- "plan": "后续计划",
- "action": "继续",
- }),
- ]
- planning = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction=DIRECTION,
- rounds=[],
- )["planningAnalysis"]
- assert planning == {
- "stage": "planning-analysis",
- "summary": "锁定创作目标与首轮骨架",
- "thought": "完整分析账号结构与领域下限。",
- "plan": "保存目标,然后开启首轮。",
- "action": "写入创作总目标",
- "eventRef": "event:3",
- "technicalRefs": ["event:3"],
- "completeness": "complete",
- }
- def test_begin_round_prefers_success_output_then_falls_back_to_round_marker():
- events = [
- _main_scope(),
- _main_tool(
- 2,
- 2,
- "begin_round",
- round_index=0,
- output_preview='{"success": true, "round_index": 1}',
- ),
- _main_tool(
- 3,
- 3,
- "begin_round",
- round_index=1,
- output_preview='{"success": true}',
- ),
- _event(
- 4,
- 4,
- "round_begin",
- "2",
- agent_role="main",
- agent_depth=0,
- scope_event_id=1,
- parent_event_id=1,
- payload={"round_index": 2, "goal": "第二轮"},
- ),
- ]
- index = RuntimeEventIndex(events)
- assert index.resolved_begin_round(events[1]) == 1
- assert index.resolved_begin_round(events[2]) == 2
- assert {key: [event["id"] for event in value] for key, value in index.successful_begin_rounds().items()} == {1: [2], 2: [3]}
- def test_evaluator_association_uses_hierarchy_and_structured_round_not_report_text():
- events = [
- _main_scope(),
- _event(
- 10,
- 2,
- "agent_invoke",
- "script_evaluator",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=10,
- parent_event_id=1,
- round_index=1,
- agentOutputData={"summary": "## 评估报告 round=99\n### 整体结论:部分通过"},
- ),
- _event(
- 11,
- 3,
- "agent_invoke",
- "script_evaluator",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=11,
- parent_event_id=None,
- round_index=1,
- agentOutputData={"summary": "### 整体结论:通过"},
- ),
- ]
- associations = RuntimeEventIndex(events).agent_returns(
- "script_evaluator", round_index=1
- )
- assert [(event["id"], relation) for event, relation in associations] == [
- (10, "returned-report"),
- (11, "runtime-associated"),
- ]
- assert RuntimeEventIndex(events).agent_returns("script_evaluator", round_index=99) == []
- def test_direction_parser_selects_real_goals_instead_of_value_proposition():
- parsed = parse_script_direction(DIRECTION)
- assert parsed["goals"] == [
- "拆解机制并让读者看懂因果链。",
- "用可核验数据支撑关键结论。",
- ]
- assert parsed["constraints"] == ["数据可信度", "适用边界", "结构清楚。"]
- assert parsed["valueJudgements"][0]["label"] == "选题价值主张"
- assert parsed["goals"][0] != parsed["valueJudgements"][0]["value"]
- assert parsed["raw"] == DIRECTION.strip()
- def test_full_projection_preserves_db_truth_and_three_honest_decision_chains():
- first_paths = [
- {
- "path_index": 1,
- "path_type": "内容",
- "action": "新增",
- "target": "机制拆解段",
- "method": "案例对照",
- "emphasis": "结构可读性",
- },
- {
- "path_index": 2,
- "path_type": "领域信息",
- "action": "核验",
- "target": "行业数据",
- "method": "交叉验证",
- },
- ]
- second_paths = [
- {"path_index": 1, "path_type": "内容", "action": "补齐", "target": "结尾"}
- ]
- events = [
- _main_scope(),
- _main_tool(2, 2, "get_topic_detail", output_preview='{"topic":{"result":"解释真实机制"},"points":[{"point_result":"测试选题"}]}'),
- _main_tool(3, 3, "get_account_script_section_patterns", output_preview='{"\u5206\u6bb5\u89c4\u5f8b\u6458\u8981":"成品展示到机制拆解的四段结构"}'),
- _main_tool(4, 4, "get_account_script_persona_points", output_preview='{"returned_count":82,"records":[{"\u5217":"形式"},{"\u5217":"作用"}]}'),
- _event(
- 40,
- 5,
- "tool_call",
- "get_topic_detail",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=39,
- parent_event_id=39,
- output_preview='{"count":1}',
- ),
- _main_tool(
- 5,
- 6,
- "save_script_direction",
- inputData={"script_direction": DIRECTION},
- output_preview='{"success": true}',
- ),
- _main_tool(
- 6,
- 7,
- "begin_round",
- round_index=0,
- inputData={"goal": "搭建机制拆解主结构"},
- output_preview='{"success": true, "round_index": 1}',
- ),
- _event(
- 7,
- 8,
- "round_begin",
- "1",
- agent_role="main",
- agent_depth=0,
- scope_event_id=1,
- parent_event_id=1,
- payload={"round_index": 1},
- ),
- _main_tool(8, 9, "get_script_snapshot", round_index=1, output_preview='{"count":0}'),
- _main_tool(
- 9,
- 10,
- "record_multipath_plan",
- round_index=1,
- inputData={"paths": first_paths, "mode": "竞争", "note": "一路搭结构,一路核验数据。"},
- output_preview='{"success": true, "round_index": 1, "path_count": 2}',
- ),
- _main_tool(
- 90,
- 11,
- "record_multipath_plan",
- round_index=1,
- inputData={"paths": [], "mode": "竞争"},
- status="error",
- output_preview='{"success": false, "error": "paths required"}',
- ),
- _event(
- 10,
- 12,
- "agent_invoke",
- "script_evaluator",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=10,
- parent_event_id=1,
- round_index=1,
- agentOutputData={
- "summary": "## 评估报告 round=7\n### 整体结论:部分通过\n引领目标:补齐结尾\n遗留问题:缺少行动建议"
- },
- ),
- _main_tool(
- 11,
- 13,
- "begin_round",
- round_index=1,
- inputData={"goal": "补齐结尾。\n具体任务:增加行动建议"},
- output_preview='{"success": true, "round_index": 2}',
- ),
- _event(
- 12,
- 14,
- "round_begin",
- "2",
- agent_role="main",
- agent_depth=0,
- scope_event_id=1,
- parent_event_id=1,
- payload={"round_index": 2},
- ),
- _main_tool(
- 13,
- 15,
- "record_multipath_plan",
- round_index=2,
- inputData={"paths": second_paths, "mode": "分工", "note": "任务已收敛,不需要重复探索。"},
- output_preview='{"success": true, "round_index": 2, "path_count": 1}',
- ),
- ]
- rounds = [
- {
- "round_index": 1,
- "goal": "搭建机制拆解主结构",
- "multipath_plan": first_paths,
- "race_or_divide": "竞争",
- "plan_note": "一路搭结构,一路核验数据。",
- },
- {
- "round_index": 2,
- "goal": "补齐结尾。\n具体任务:增加行动建议",
- "multipath_plan": second_paths,
- "race_or_divide": "分工",
- "plan_note": "任务已收敛,不需要重复探索。",
- },
- ]
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction=DIRECTION,
- rounds=rounds,
- multipath_decisions=[
- {
- "id": 501,
- "round_index": 1,
- "decision": "采用结构方案,保留数据核验结果。",
- "reasoning": "两路结果可组合。",
- "created_at": "2026-07-14T10:00:11",
- }
- ],
- )
- objective = projection["objective"]
- assert objective["decisionItems"][0] == "拆解机制并让读者看懂因果链。"
- assert [item["label"] for item in objective["inputs"]] == [
- "选题",
- "账号段落模式",
- "账号人设",
- ]
- assert [item["summary"] for item in objective["inputs"]] == [
- "测试选题:解释真实机制",
- "成品展示到机制拆解的四段结构",
- "返回 82 个账号表达特征,主要涉及形式、作用",
- ]
- assert 40 not in [int(item["detailRef"].split(":")[1]) for item in objective["inputs"]]
- round_two = projection["roundGoalsByRound"][2]
- assert [item["relation"] for item in round_two["inputs"][:2]] == [
- "returned-report",
- "persisted-record",
- ]
- assert round_two["inputs"][0]["summary"] == "部分通过"
- assert "下一步:补齐结尾" in round_two["constraints"]
- assert round_two["revisionRefs"] == ["event:11"]
- first_plan = projection["implementationPlansByRound"][1]
- assert first_plan["decisionItems"] == [
- "方案 1 · 新增:机制拆解段",
- "方案 2 · 核验:行业数据",
- ]
- assert first_plan["inputs"][1]["label"] == "当前主脚本"
- assert first_plan["currentRevisionRef"] == "event:9"
- assert first_plan["technicalRefs"] == ["event:90"]
- assert first_plan["routeItems"][0] == {
- "pathIndex": 1,
- "pathType": "内容",
- "action": "新增",
- "target": "机制拆解段",
- "method": "案例对照",
- "emphasis": "结构可读性",
- }
- second_plan = projection["implementationPlansByRound"][2]
- assert second_plan["displayMode"] == "单路"
- assert second_plan["rawMode"] == "分工"
- assert projection["objective"]["sourceDocument"] == DIRECTION.strip()
- first_goal = projection["roundGoalsByRound"][1]
- assert first_goal["decisionItems"] == ["搭建机制拆解主结构"]
- assert first_goal["sourceDocument"] == "搭建机制拆解主结构"
- assert "goalSections" not in first_goal
- def test_round_goal_inputs_keep_complete_business_text():
- long_goal = "完整创作目标需要保留全部约束和判断依据。" * 24
- long_report = "上一轮整体评审的完整结论需要原样传给下一轮。" * 20
- long_decision = "上一轮主 Agent 的完整多路决策需要原样保留。" * 20
- direction = f"## 创作总目标\n- 目标语句:{long_goal}"
- events = [
- _main_scope(),
- _main_tool(
- 2,
- 2,
- "save_script_direction",
- inputData={"script_direction": direction},
- output_preview='{"success": true}',
- ),
- _main_tool(
- 3,
- 3,
- "begin_round",
- round_index=0,
- output_preview='{"success": true, "round_index": 1}',
- ),
- _event(
- 4,
- 4,
- "agent_invoke",
- "script_evaluator",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=4,
- parent_event_id=1,
- round_index=1,
- agentOutputData={"summary": f"### 整体结论:{long_report}"},
- ),
- _main_tool(
- 5,
- 5,
- "begin_round",
- round_index=1,
- output_preview='{"success": true, "round_index": 2}',
- ),
- ]
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction=direction,
- rounds=[
- {"round_index": 1, "goal": "第一轮", "multipath_plan": []},
- {"round_index": 2, "goal": "第二轮", "multipath_plan": []},
- ],
- multipath_decisions=[
- {
- "id": 88,
- "round_index": 1,
- "decision": long_decision,
- "created_at": "2026-07-14T10:00:04",
- }
- ],
- )
- first_round_input = projection["roundGoalsByRound"][1]["inputs"][0]["summary"]
- second_round_inputs = projection["roundGoalsByRound"][2]["inputs"]
- assert first_round_input == "\n\n".join(projection["objective"]["decisionItems"])
- assert len(first_round_input) > 220
- assert second_round_inputs[0]["summary"] == long_report
- assert second_round_inputs[1]["summary"] == long_decision
- assert all(not str(item["summary"]).endswith("…") for item in second_round_inputs[:2])
- def test_db_only_projection_degrades_without_inventing_runtime_evidence():
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex([]),
- script_direction="做一个清晰的行业解释。",
- rounds=[
- {
- "round_index": 1,
- "goal": "搭建结构",
- "multipath_plan": [{"path_index": 1, "target": "开场"}],
- "race_or_divide": "分工",
- "plan_note": None,
- }
- ],
- )
- assert projection["objective"]["completeness"] == "partial"
- assert projection["objective"]["inputs"] == []
- assert projection["roundGoalsByRound"][1]["completeness"] == "partial"
- assert projection["implementationPlansByRound"][1]["completeness"] == "partial"
- assert projection["implementationPlansByRound"][1]["explicitReasoning"] is None
- def test_multiple_saves_and_plan_versions_preserve_history_and_separate_failures():
- old_direction = "## 创作总目标\n- 目标语句:旧目标"
- current_direction = "## 创作总目标\n- 目标语句:新目标"
- old_paths = [{"path_index": 1, "action": "新增", "target": "开场"}]
- current_paths = [{"path_index": 1, "action": "改写", "target": "开场"}]
- events = [
- _main_scope(),
- _main_tool(2, 2, "save_script_direction", inputData={"script_direction": old_direction}, output_preview='{"success":true}'),
- _main_tool(3, 3, "save_script_direction", inputData={"script_direction": current_direction}, output_preview='{"success":true}'),
- _main_tool(4, 4, "save_script_direction", status="error", inputData={"script_direction": "失败版"}, output_preview='{"success":false,"error":"write failed"}'),
- _main_tool(5, 5, "begin_round", round_index=0, output_preview='{"success":true,"round_index":1}'),
- _main_tool(6, 6, "record_multipath_plan", round_index=1, inputData={"paths": old_paths, "mode": "竞争", "note": "初版"}, output_preview='{"success":true,"round_index":1}'),
- _main_tool(7, 7, "record_multipath_plan", round_index=1, inputData={"paths": current_paths, "mode": "分工", "note": "修订版"}, output_preview='{"success":true,"round_index":1}'),
- ]
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction=current_direction,
- rounds=[{
- "round_index": 1,
- "goal": "修正开场",
- "multipath_plan": current_paths,
- "race_or_divide": "分工",
- "plan_note": "修订版",
- }],
- )
- assert projection["objective"]["revisionRefs"] == ["event:2", "event:3"]
- assert projection["objective"]["currentRevisionRef"] == "event:3"
- assert projection["objective"]["technicalRefs"] == ["event:4"]
- plan = projection["implementationPlansByRound"][1]
- assert plan["revisionRefs"] == ["event:6", "event:7"]
- assert [item["current"] for item in plan["revisions"]] == [False, True]
- assert plan["currentRevisionRef"] == "event:7"
- def test_goal_parser_keeps_the_saved_goal_as_one_item():
- goal = "本轮要补齐结构\n① 增加开场\n② 补齐数据"
- assert parse_goal_items(goal) == [goal]
- def test_plan_revision_matches_business_alias_without_changing_raw_db_mode():
- paths = [{"path_index": 1, "action": "改写", "target": "开场"}]
- events = [
- _main_scope(),
- _main_tool(2, 2, "begin_round", round_index=0, output_preview='{"success":true,"round_index":1}'),
- _main_tool(
- 3,
- 3,
- "record_multipath_plan",
- round_index=1,
- inputData={"paths": paths, "mode": "竞争", "note": "比较两路表达"},
- output_preview='{"success":true,"round_index":1}',
- ),
- ]
- plan = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction=None,
- rounds=[{
- "round_index": 1,
- "goal": "修正开场",
- "multipath_plan": paths,
- "race_or_divide": "赛马",
- "plan_note": "比较两路表达",
- }],
- )["implementationPlansByRound"][1]
- assert plan["rawMode"] == "赛马"
- assert plan["currentRevisionRef"] == "event:3"
- def test_failed_begin_round_does_not_reuse_its_old_round_context():
- events = [
- _main_scope(),
- _main_tool(
- 2,
- 2,
- "begin_round",
- round_index=1,
- status="error",
- output_preview='{"success":false,"error":"write failed"}',
- ),
- ]
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction="创作目标",
- rounds=[
- {"round_index": 1, "goal": "第一轮", "multipath_plan": []},
- {"round_index": 2, "goal": "第二轮", "multipath_plan": []},
- ],
- )
- assert projection["roundGoalsByRound"][1]["technicalRefs"] == []
- assert projection["roundGoalsByRound"][2]["technicalRefs"] == []
- assert projection["unassigned"][0]["eventId"] == 2
- assert projection["unassigned"][0]["association"] == "failed-main-decision-call-unassigned"
- def test_round_goal_keeps_report_problems_and_rejects_late_db_decisions():
- events = [
- _main_scope(),
- _event(
- 10,
- 2,
- "agent_invoke",
- "script_evaluator",
- agent_role="script_evaluator",
- agent_depth=1,
- scope_event_id=10,
- parent_event_id=1,
- round_index=1,
- agentOutputData={
- "summary": """### 整体结论:部分通过
- **问题详情**:
- - **paragraph_id=9**:收尾段落缺失。
- **下一步引领目标**:
- **补齐收尾并形成闭环。**
- """
- },
- ),
- _main_tool(
- 11,
- 5,
- "begin_round",
- round_index=1,
- started_at="2026-07-14T10:00:05",
- output_preview='{"success":true,"round_index":2}',
- ),
- ]
- projection = MainAgentDecisionProjector().project(
- RuntimeEventIndex(events),
- script_direction="创作目标",
- rounds=[
- {"round_index": 1, "goal": "第一轮", "multipath_plan": []},
- {"round_index": 2, "goal": "补齐收尾", "multipath_plan": []},
- ],
- multipath_decisions=[
- {
- "id": 71,
- "round_index": 1,
- "decision": "采用方案 1",
- "created_at": "2026-07-14T10:00:06",
- }
- ],
- )
- goal = projection["roundGoalsByRound"][2]
- assert "问题:收尾段落缺失。" in goal["constraints"]
- assert "下一步:补齐收尾并形成闭环。" in goal["constraints"]
- assert not any(value.endswith("---") for value in goal["constraints"])
- assert not any(item["label"] == "上一轮主 Agent 多路决策" for item in goal["inputs"])
- assert "multipath-decision:71" in goal["technicalRefs"]
|