diff --git a/backend/app/api/copilot.py b/backend/app/api/copilot.py index 6b849d96..e78db38d 100644 --- a/backend/app/api/copilot.py +++ b/backend/app/api/copilot.py @@ -258,6 +258,16 @@ NODE_REFERENCE = """\ 写 null 表示不限,预算要用户定,拿不准就问;只有受管级别要这样,普通草稿里默认开的 judge 不写预算、不为预算问人);契约用 report_from(受管级别不收 narrative),成果字段只取报告节点的正文;给口径卡 供数的 agent 配 output_schema 和 cite_fields: true,code 节点喂口径卡的必须是真取数(evidence_role: "source"), 否则把计算写进口径卡;llm、agent、协作团队写的文字不能绕过报告节点直接进出口,llm 也不能喂口径卡 +- challenge:反方审稿。报告写完后挑出几句关键结论(挂了依据的、带比较或因果的优先),每句派一个只读 agent 站在反方 + 自己写查询找反例,逐句判经受住、被削弱、被推翻或无法判断,理由引用它自己查到的数据;不改报告。 + config: {report, max_claims, max_queries, timeout_s, provider, model, on_refuted, enabled} + **多个来源汇总成的分析报告、要正式出具的报告**加这个节点,接在 report 之后:report → challenge → human 或 output。 + report 写要审的报告节点 id(上游只有一个 report 时可以不写);on_refuted 是结论被推翻时怎么办:degrade(默认,计入 + 出具缺口、出具降档)/ flag(只记录)/ fail(判节点失败)。max_claims(每份报告最多审几句)、max_queries(每句最多 + 查询次数)、timeout_s(每句时长上限,秒)、enabled(不写就跟随设置里「核查」一节的开关)一般都不写,取设置里的默认; + provider、model 指定反方模型,不写用设置里的核查模型(建议和写作模型不同,同一个模型等于自己审自己)。后面接 human + 时,human 的 message 里引用 {{ nodes.报告节点id.text }},审批卡会附上这份报告的审稿结果。只回答一个问题、一次查询的 + 不必加 搭图规则(有数字结论时必须遵守): 1. 取数(tool 节点查库;或者 agent 查库并配 output_schema + cite_fields)→ 口径卡 → report → output diff --git a/backend/tests/test_copilot_challenge_guide.py b/backend/tests/test_copilot_challenge_guide.py new file mode 100644 index 00000000..563c0513 --- /dev/null +++ b/backend/tests/test_copilot_challenge_guide.py @@ -0,0 +1,31 @@ +"""助手的节点手册要教反方审稿节点:什么时候加(多个来源汇总的分析报告、要正式出具的报告),接在哪里,各配置项怎么写。 + +手册里没写的节点,助手就不会用;写错的键名、取值,画布校验会一直把图打回来。所以这里不只查关键词,还拿校验认的那一份 +配置项和取值来对。 +""" +from __future__ import annotations + +from app.api.copilot import NODE_REFERENCE +from app.engine.schema import CHALLENGE_LABEL, CHALLENGE_ON_REFUTED + + +def _challenge_entry() -> str: + start = NODE_REFERENCE.index("- challenge:反方审稿") + return NODE_REFERENCE[start:NODE_REFERENCE.index("\n\n", start)] + + +def test_the_guide_names_every_setting_the_validator_knows(): + entry = _challenge_entry() + assert all(key in entry for key in CHALLENGE_LABEL), [k for k in CHALLENGE_LABEL if k not in entry] + assert all(value in entry for value in CHALLENGE_ON_REFUTED) + + +def test_the_guide_says_when_to_add_it_and_where(): + entry = _challenge_entry() + # 什么时候加:多个来源汇总的分析报告、要正式出具的报告;接在报告之后,审批节点在后面时引用报告正文 + assert "多个来源" in entry and "正式出具" in entry and "report → challenge" in entry + assert "{{ nodes.报告节点id.text }}" in entry + # 反方模型建议和写作模型不同:同一个模型等于自己审自己 + assert "自己审自己" in entry + # 不写上限就取设置里的默认:助手不要替人把核查的花费写死 + assert "取设置里的默认" in entry