From a46bad05925e7a8ea2ae77847785f894c7e1a30a Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Sun, 4 Oct 2026 11:09:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A9=E6=89=8B=E6=90=AD=E5=A4=9A=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E6=B1=87=E6=80=BB=E7=9A=84=E5=88=86=E6=9E=90=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E6=97=B6=E9=BB=98=E8=AE=A4=E5=BC=80=E7=BB=93=E8=AE=BA?= =?UTF-8?q?=E5=8F=A5=E8=A3=81=E5=88=A4=EF=BC=9A=E8=8A=82=E7=82=B9=E6=89=8B?= =?UTF-8?q?=E5=86=8C=E7=9A=84=20report=20=E4=B8=80=E6=9D=A1=E5=86=99?= =?UTF-8?q?=E6=98=8E=E5=87=A0=E4=B8=AA=20Agent=20=E5=88=86=E5=A4=B4?= =?UTF-8?q?=E6=9F=A5=E5=BA=93=E3=80=81=E6=88=96=E8=80=85=E5=87=A0=E6=AC=A1?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=90=88=E5=9C=A8=E4=B8=80=E8=B5=B7=E5=86=99?= =?UTF-8?q?=E6=88=90=E7=9A=84=E5=88=86=E6=9E=90=E6=8A=A5=E5=91=8A=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=86=99=20claims:=20judge=EF=BC=8C=E4=B8=8D=E5=86=99?= =?UTF-8?q?=20judge=20=E7=9A=84=E9=A2=84=E7=AE=97=EF=BC=88=E5=8F=96?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=87=8C=E7=9A=84=E9=BB=98=E8=AE=A4=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E6=AD=A3=E5=BC=8F=E8=BF=90=E8=A1=8C=E5=9C=A8=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E9=87=8C=E8=87=AA=E5=8A=A8=E5=88=A4=E3=80=81=E6=8E=A2?= =?UTF-8?q?=E7=B4=A2=E8=BF=90=E8=A1=8C=E7=94=B1=E8=AF=BB=E7=9A=84=E4=BA=BA?= =?UTF-8?q?=E6=8C=89=E9=9C=80=E5=88=A4=EF=BC=8C=E7=94=A8=E6=88=B7=E6=98=8E?= =?UTF-8?q?=E7=A1=AE=E4=B8=8D=E8=A6=81=E7=9A=84=E4=B8=8D=E5=8A=A0=E3=80=82?= =?UTF-8?q?=E5=92=8C=E5=8F=97=E7=AE=A1=E7=BA=A7=E5=88=AB=E3=80=8Cjudge=20?= =?UTF-8?q?=E5=BF=85=E9=A1=BB=E5=86=99=E9=A2=84=E7=AE=97=E3=80=81=E6=8B=BF?= =?UTF-8?q?=E4=B8=8D=E5=87=86=E5=B0=B1=E9=97=AE=E3=80=8D=E7=9A=84=E5=8F=96?= =?UTF-8?q?=E8=88=8D=EF=BC=9A=E9=82=A3=E6=9D=A1=E5=8F=AA=E7=AE=A1=E8=A6=81?= =?UTF-8?q?=E6=8C=89=E5=8F=97=E7=AE=A1=E7=BA=A7=E5=88=AB=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E7=9A=84=E6=A8=A1=E6=9D=BF=EF=BC=8C=E6=99=AE=E9=80=9A=E8=8D=89?= =?UTF-8?q?=E7=A8=BF=E9=87=8C=E9=BB=98=E8=AE=A4=E5=BC=80=E7=9A=84=20judge?= =?UTF-8?q?=20=E4=B8=8D=E4=B8=BA=E9=A2=84=E7=AE=97=E9=97=AE=E4=BA=BA?= =?UTF-8?q?=EF=BC=9B=E5=8F=91=E5=B8=83=E5=89=8D=E4=BF=AE=E5=A4=8D=E7=BC=BA?= =?UTF-8?q?=E9=A2=84=E7=AE=97=E8=A6=81=E9=97=AE=E7=9A=84=E8=A7=84=E7=9F=A9?= =?UTF-8?q?=E4=B8=8D=E5=8F=98=E3=80=82=E9=97=AE=E6=95=B0=E6=8D=AE=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=8E=9F=E6=9C=AC=E5=AE=9A=E4=BA=86=E4=B8=8D=E6=9B=BF?= =?UTF-8?q?=E4=BA=BA=E6=89=93=E5=BC=80=E7=BB=93=E8=AE=BA=E5=8F=A5=E7=AD=96?= =?UTF-8?q?=E7=95=A5=EF=BC=8C=E4=BF=9D=E7=95=99=EF=BC=8C=E5=B9=B6=E5=86=99?= =?UTF-8?q?=E6=98=8E=E8=BF=99=E6=9D=A1=E9=BB=98=E8=AE=A4=E5=9C=A8=E9=97=AE?= =?UTF-8?q?=E6=95=B0=E9=87=8C=E4=B8=8D=E9=80=82=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/copilot.py | 9 +++- backend/tests/test_copilot_claims_default.py | 53 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 backend/tests/test_copilot_claims_default.py diff --git a/backend/app/api/copilot.py b/backend/app/api/copilot.py index be9168a5..2c49e468 100644 --- a/backend/app/api/copilot.py +++ b/backend/app/api/copilot.py @@ -236,6 +236,10 @@ NODE_REFERENCE = """\ judge.provider 指定裁判模型(建议和写作模型不同,同一个模型等于自己审自己;不写就用设置里的「证据裁判模型」); judge.max_cost_usd、judge.max_claims、judge.timeout_s 是每份报告的裁判金额、句数、时长上限,写 null 表示不限, 不写取设置里的默认;judge.rewrite_once 默认 false,打开后把证据相矛盾、证据不足的句子交回写作者只改一次。 + **多个来源汇总成的分析报告**(几个 agent 分头查库,或者几次查询的结果合在一起写成的分析、结论)默认写 claims: "judge", + 不写 judge 的预算(judge 整个不写,金额、句数、时长上限都取设置里的默认):正式运行在节点里自动判,探索运行由读的人 + 点开哪句判哪句,不在节点里花钱。用户明确说不要核对结论句的不加;只回答一个问题、一次查询的不必加。要按受管级别 + 发布的模板不适用这条默认,按下面受管级别的规则写预算 entities 默认 link (核对表名、字段名),报告里根本不提表名时才写 off - output 的出具契约:config.contract = {report_from: 报告节点id, metrics_from:[口径卡id], @@ -245,7 +249,7 @@ NODE_REFERENCE = """\ (问数据的典型图),契约不写 metrics_from,也不写 required / expected 要按受管级别发布的模板(发布前检查不过就发不出去):报告节点写明 numbers: "strict"、on_violation: "fail"、 claims: "require_citation"(或者更严的 claims: "judge",这时 judge 里必须写 judge.max_cost_usd——写一个金额,或者 - 写 null 表示不限,预算要用户定,拿不准就问);契约用 report_from(受管级别不收 narrative),成果字段只取报告节点的正文;给口径卡 + 写 null 表示不限,预算要用户定,拿不准就问;只有受管级别要这样,普通草稿里默认开的 judge 不写预算、不为预算问人);契约用 report_from(受管级别不收 narrative),成果字段只取报告节点的正文;给口径卡 供数的 agent 配 output_schema 和 cite_fields: true,code 节点喂口径卡的必须是真取数(evidence_role: "source"), 否则把计算写进口径卡;llm、agent、协作团队写的文字不能绕过报告节点直接进出口,llm 也不能喂口径卡 @@ -355,7 +359,8 @@ def _user_message(payload: GenerateIn, *, patch: bool) -> str: "- report 提到表名、字段名时写 [[t:表名]] / [[c:表名.字段名]](英文名也可以放进反引号,中文名不行:" "反引号里的中文名不核对),只写 agent 真查过的名字," "编造的会被标出来;要引用知识库原话就在 report 前接 retrieve,用 [[q:K1|原话]] 逐字引用。问数是探索运行," - "report 的 claims 不用写:结论句支不支持,由读答案的人点开时按需请模型判断\n" + "report 的 claims 不用写:结论句支不支持,由读答案的人点开时按需请模型判断。节点说明里「多个来源汇总成的" + "分析报告默认开结论句裁判」那条在问数里不适用,几个 agent 分头查的也一样\n" "- 用户要的是周报、要三档出具时,按搭图规则配口径卡和 report_from 契约\n" "- 不要用 transform 解析 agent / llm 的文字:要把 agent 查到的数交给下游,就给它配 output_schema + " "cite_fields\n" diff --git a/backend/tests/test_copilot_claims_default.py b/backend/tests/test_copilot_claims_default.py new file mode 100644 index 00000000..979d4c2b --- /dev/null +++ b/backend/tests/test_copilot_claims_default.py @@ -0,0 +1,53 @@ +"""助手搭多来源汇总的分析报告时,默认给报告撰写节点开结论句裁判(claims: judge)。 + +真实运行里:四个 Agent 分头查库、汇总成一份分析报告,报告节点没写 claims。有一句结论和证据相矛盾,审批人批准时 +完全不知道,事后在证据面板点开那句才判出来。分析报告的结论句恰恰最需要核对:几个来源拼在一起,最容易写出 +证据撑不住的归因和判断。 + +取舍:默认写 claims: "judge"、不写 judge 的预算(取设置里的默认)——正式运行在节点里自动判,探索运行不在节点里 +花钱、由读的人按需判。和「受管级别的 judge 必须写预算、拿不准就问」不冲突:那条只管要按受管级别发布的模板, +普通草稿不问预算;发布前修复里缺预算要问的规矩不变。用户明确说不要核对结论句的不加。问数据那条路径原本定了 +不替人打开结论句策略(一律探索运行,读的人按需判),那条保留,并写明节点说明里的默认在问数里不适用。 + +这组守的是节点手册和问数据那条路径里的说法,以及它们和受管级别规则、发布前修复规则不打架。 +""" +from __future__ import annotations + +from app.api.copilot import NODE_REFERENCE, GenerateIn, _publish_fix_request, _user_message + + +def _report_rule() -> str: + start = NODE_REFERENCE.index("- report:报告撰写") + return NODE_REFERENCE[start:NODE_REFERENCE.index("\n- output 的出具契约", start)] + + +def test_multi_source_analysis_reports_default_to_the_judge(): + rule = _report_rule() + assert '默认写 claims: "judge"' in rule + # 什么算多来源:几个 Agent 分头查、或者几次查询的结果合在一起写 + assert "多个来源汇总成的分析报告" in rule and "几个 agent" in rule and "几次查询" in rule + # 不写预算,交给设置里的默认值 + assert "不写 judge 的预算" in rule and "设置里的默认" in rule + # 正式运行在节点里自动判,探索运行由读的人按需判 + assert "正式运行在节点里自动判" in rule and "探索运行由读的人" in rule + # 用户明确不要的不加 + assert "用户明确说不要" in rule + + +def test_the_managed_level_budget_rule_still_applies_and_says_so(): + # 受管级别仍然要写预算、拿不准就问;默认开裁判的那一条说明它让位给受管级别的规则 + assert "judge 里必须写 judge.max_cost_usd" in NODE_REFERENCE + assert "拿不准就问" in NODE_REFERENCE + rule = _report_rule() + assert "受管级别" in rule + # 发布前修复(受管级别)缺预算时照旧问人,不替人定 + request = _publish_fix_request({"nodes": [], "edges": []}, [], "governed") + assert "judge 缺预算(judge.max_cost_usd)时输出 question 问写多少" in request + + +def test_answer_path_keeps_its_own_rule_and_says_which_one_wins(): + """问数据那条路径原本就定了不替人打开结论句策略(test_copilot_entity_guide 守着):问数一律是探索运行,读答案的人 + 点开哪句判哪句。节点说明里新加的默认和它说法相反,所以这里写明那条默认在问数里不适用,免得模型两头为难。""" + text = _user_message(GenerateIn(instruction="各渠道上月表现怎么样", intent="answer"), patch=False) + assert "report 的 claims 不用写" in text + assert "分析报告默认开结论句裁判」那条在问数里不适用" in text