mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 19:22:00 +00:00
9727c21d8d
一、结论句裁判
- 报告节点新增 claims: judge:写完、核对完之后,把结论句和它引用的证据摘录交给裁判模型,
判为支持 / 部分支持 / 不支持 / 不是结论,附一句理由;判定只标注,不改写正文
- 送裁判的句子按固定规则预筛:带引用的、含方向词或因果词的送去判,短句、过渡句不送
- 证据摘录:指标写名称、值和原式;查询给 SQL 前 300 字和被引用的行,被引用的列一定给到;遮罩列一律不给;
规则里写明「数字已经由系统核对过,不要复算」
- 一份报告合并成一次调用,超出句数就分批;结构化输出失败时退回 JSON 解析,再失败记为未裁判
- 裁判模型的优先级:节点上写的 > 设置里的「证据裁判模型」> Copilot 的模型;思考关闭;
和写作模型相同时给警告(等于自己审自己)
- rewrite_once(默认关):把不支持的句子和理由交回写作者只改这几句,再裁一次;
改写稿冒出原稿没有的问题就不采用,保留原稿和原来的判定
二、成本上限
- 每份报告的句数、金额、时长,探索运行每次点击的金额,全局每日金额,每一项都可以自定义,也都可以设成不限;
默认 40 句、$0.05、30 秒,每次点击 $0.01,每天 $2
- 调用前先估算,超出上限按优先级截断:先判带引用的,再判含方向词、因果词的,其余记为未裁判并写明「已到上限」;
不报错,也不让报告失败
- 每日花费按本地日期计数,持久化在设置表里;重跑同一次运行不重复计费;价格目录里没有的模型只计次数,并写明估不出金额
三、正式运行与探索运行
- 正式运行在报告节点内裁判,判定写进报告文档、随文档封存;不支持的按 on_unsupported(degrade / withhold)降档或不予出具,
裁判没跑成记缺口,不能判完整出具
- 探索运行按需裁判:POST /api/runs/{id}/evidence/judge,点开哪句判哪句;判定作为 evidence.judged 事件追加在封存之后,
标「封存后追加」,封存核对仍然一致;只读封存范围内的工件;同一句不重复判;运行在裁判期间被接着跑时不记录
- 正式运行调这个接口返回 409,写明「正式运行的裁判在节点内完成」
四、治理与自动修复
- G3 放开 judge:受管级别写 judge 必须写预算(judge.max_cost_usd,或显式写 null 表示不限)
- 发布前自动修复:缺预算时给选项(默认金额 / 不限),不替人选;
从 require_citation 改成 judge 不算降低要求,judge 改回 off、withhold 放宽成 degrade 算降低要求
- Copilot 手册:受管模板可以写 judge 并带预算;问数据按需裁判,不替人打开结论句策略
五、界面
- 结论句句末放徽标:支持 ◆、部分支持 ◇、不支持 ! 整句浅底、未裁判 ?;不用表示「已核对」的绿色;n / N 也跳到不支持的句子
- 证据面板里「模型的解释」:带「模型判断 · 模型名 · 非确定」,封存后追加的另外标出;
「请模型判断这句」只在探索运行、还没判过时出现,触顶时写明已到上限和怎么调
- 出具横幅多一段结论句计数;设置页新增「证据裁判」一组(裁判模型、各项上限、今日花费),设成不限时写明费用受什么约束;
报告节点可以配 judge;运行轨迹里裁判相关的事件都有中文标题
六、门禁修正
- 引用模式下,图里没有口径卡(只引查询单元格)的,出具契约不再要求 metrics_from,受管门禁也不再要求 required:
strict 加 cells 已经保证每个数都点得开、对不上就不予出具;有口径卡的照旧要求。
以前问数据的图怎么都发不成受管,Copilot 补上契约也会被判「冒出新的问题」作废
77 lines
4.2 KiB
Python
77 lines
4.2 KiB
Python
"""Copilot 的节点手册要教三期、四期的写法:表名字段名、知识库原话、结论句策略(含模型裁判 judge),以及受管模板的
|
||
报告节点怎么配。
|
||
|
||
手册里没写的配置,Copilot 就不会用;写错的写法,报告节点会一直被打回。所以这里不只查关键词,
|
||
还把手册里举的标记例子交给真正的解析器,确认写法和系统认的是同一套。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
|
||
from app.api.copilot import _ASSIST_RULES, NODE_REFERENCE, GenerateIn, _user_message
|
||
from app.engine.evidence import QUOTE_MIN, SUPPORTED_KINDS, parse_markers
|
||
from app.engine.schema import CLAIMS_VALUES, JUDGE_KEYS, JUDGE_ON_UNSUPPORTED
|
||
|
||
|
||
def _report_section() -> str:
|
||
start = NODE_REFERENCE.index("- report:")
|
||
return NODE_REFERENCE[start:NODE_REFERENCE.index("\n- output", start)]
|
||
|
||
|
||
def test_the_report_entry_teaches_entities_quotes_and_claims():
|
||
report = _report_section()
|
||
for words in (
|
||
["[[t:", "[[c:", "反引号", "可能是编造的名字"], # 表名、字段名
|
||
["[[q:K1|", "逐字", f"至少 {QUOTE_MIN} 个字", "[[see:K1]]"], # 知识库原话
|
||
["claims", "require_citation", "off", "judge"], # 结论句策略
|
||
["judge.max_cost_usd", "null", "不限", "rewrite_once", "on_unsupported", "按需"], # 模型裁判怎么配
|
||
["entities", "link"], # 实体层开关
|
||
):
|
||
assert all(w in report for w in words), [w for w in words if w not in report]
|
||
# 结论句策略的取值和校验认的是同一份;judge 已经能用,不能再说「后续版本」「跑不起来」
|
||
assert all(value in report for value in CLAIMS_VALUES)
|
||
assert all(value in report for value in JUDGE_ON_UNSUPPORTED)
|
||
assert "后续版本" not in report and "跑不起来" not in report
|
||
# 裁判模型建议和写作模型不同:否则等于自己审自己
|
||
assert "不同" in report and "自己审自己" in report
|
||
|
||
|
||
def test_the_judge_keys_in_the_guide_are_the_ones_the_validator_knows():
|
||
named = set(re.findall(r"judge\.(\w+)", NODE_REFERENCE))
|
||
assert named and named <= set(JUDGE_KEYS), named - set(JUDGE_KEYS)
|
||
|
||
|
||
def test_the_markers_in_the_guide_parse_as_the_system_reads_them():
|
||
kinds = {m["kind"] for m in parse_markers(_report_section())}
|
||
assert {"t", "c", "q", "m", "v", "table", "i", "see"} <= kinds
|
||
assert kinds <= SUPPORTED_KINDS
|
||
[quote] = [m for m in parse_markers(_report_section()) if m["kind"] == "q"]
|
||
assert quote["ref"] == "K1" and quote["quote"]
|
||
|
||
|
||
def test_governed_templates_spell_out_the_report_settings():
|
||
ref = NODE_REFERENCE
|
||
governed = ref[ref.index("要按受管级别发布的模板"):]
|
||
for words in (['numbers: "strict"', 'on_violation: "fail"', 'claims: "require_citation"'],
|
||
['claims: "judge"', "judge.max_cost_usd", "null"],
|
||
["report_from", "narrative"], ["cite_fields: true"], ["evidence_role", "source"]):
|
||
assert all(w in governed for w in words), [w for w in words if w not in governed]
|
||
|
||
|
||
def test_the_publish_assist_knows_judge_is_the_stricter_one():
|
||
"""发布前的 Copilot 兜底只许往严里改:它要知道 judge 比 require_citation 严,不能为了过门禁把 judge 改回去。"""
|
||
assert "judge" in _ASSIST_RULES and "max_cost_usd" in _ASSIST_RULES
|
||
# update_node 把 judge 整个换掉:补预算只写 {max_cost_usd} 会把 withhold 连同裁判模型一起丢了
|
||
rules = " ".join(_ASSIST_RULES.split())
|
||
assert re.search(r"on_unsupported.{0,20}withhold.{0,20}不改回\s*degrade", rules), "withhold 不许改回 degrade"
|
||
assert re.search(r"补预算.{0,40}整份", rules), "补预算时 judge 要整份带上原来的键"
|
||
|
||
|
||
def test_the_answer_branch_mentions_names_and_quotes():
|
||
text = _user_message(GenerateIn(instruction="上周哪张表的退款最多", intent="answer"), patch=False)
|
||
assert "[[t:" in text and "反引号" in text and "编造" in text
|
||
assert "[[q:K1|" in text and "逐字" in text
|
||
# 问数是探索运行:不要替人打开结论句策略(结论句由读的人点开时按需请模型判断)
|
||
assert not re.search(r"claims\s*[:=]\s*['\"]?(require_citation|judge)", text)
|
||
assert "按需" in text
|