mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 05:21:57 +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 补上契约也会被判「冒出新的问题」作废
92 lines
3.6 KiB
Python
92 lines
3.6 KiB
Python
from __future__ import annotations
|
||
|
||
import time
|
||
import uuid
|
||
from enum import StrEnum
|
||
from typing import Any
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class EventType(StrEnum):
|
||
"""前端与后端之间的实时协议。画布高亮、时间线、token 流全部基于这套事件。"""
|
||
|
||
# run 级
|
||
RUN_STARTED = "run.started"
|
||
RUN_FINISHED = "run.finished"
|
||
RUN_FAILED = "run.failed"
|
||
RUN_CANCELLED = "run.cancelled"
|
||
RUN_INTERRUPTED = "run.interrupted" # 等待人工介入
|
||
RUN_RESUMED = "run.resumed"
|
||
|
||
# 节点级
|
||
NODE_STARTED = "node.started"
|
||
NODE_FINISHED = "node.finished"
|
||
NODE_FAILED = "node.failed"
|
||
NODE_SKIPPED = "node.skipped"
|
||
EDGE_TAKEN = "edge.taken" # 条件分支实际走了哪条边
|
||
|
||
# 模型级
|
||
LLM_START = "llm.start"
|
||
LLM_TOKEN = "llm.token"
|
||
# 思考的流式增量:只推给画布做实时显示,不落库
|
||
LLM_THINKING_DELTA = "llm.thinking.delta"
|
||
# 一次完整思考:调用结束时落一条,这才是进轨迹的事件
|
||
LLM_THINKING = "llm.thinking"
|
||
LLM_END = "llm.end"
|
||
|
||
# 工具 / 沙箱
|
||
TOOL_START = "tool.start"
|
||
TOOL_END = "tool.end"
|
||
TOOL_ERROR = "tool.error"
|
||
# 信任档是「始终允许 · 门控把关」的工具,门控模型对这一次调用的判定(tools/trust.py)。
|
||
# allow 之后是正常的 tool.start;escalate 之后是人工审批,协作团队里则是不执行
|
||
TOOL_GATED = "tool.gated"
|
||
# 知识检索。以前只发一条 info 日志,于是"这句结论依据的是哪份文档的哪一段"
|
||
# 在轨迹里查不到——而 SQL 取数那条路早就有工件快照可以下钻
|
||
RETRIEVE_END = "retrieve.end"
|
||
# 记忆读写。以前发的是 info 日志,而解码器丢弃所有 info——于是往长期记忆里
|
||
# 写东西这件事在界面上是隐形的。让 Copilot 主动记之后,这条不可接受
|
||
MEMORY_END = "memory.end"
|
||
SANDBOX_START = "sandbox.start"
|
||
SANDBOX_END = "sandbox.end"
|
||
|
||
# 多 agent 协作中的单个 agent 步骤
|
||
AGENT_STEP_START = "agent.step.start"
|
||
AGENT_STEP_END = "agent.step.end"
|
||
# 调度者的一次决策。它是一次结构化模型调用,常常占掉团队节点大半的时间;
|
||
# 以前前后都没有事件,界面上那几十秒是黑盒
|
||
AGENT_ROUTE_START = "agent.route.start"
|
||
AGENT_ROUTE_END = "agent.route.end"
|
||
|
||
# 人工介入
|
||
HUMAN_REQUESTED = "human.requested"
|
||
HUMAN_RESOLVED = "human.resolved"
|
||
|
||
# 出具与口径治理
|
||
ISSUANCE = "issuance" # 出具契约的判定结果(档位 / 缺数据 / 未回指数字)
|
||
CALIBER_UPGRADE = "caliber.upgrade" # 钉住的方法卡有新版本,按声明的策略处置
|
||
# 报告撰写节点的自查结果:报告文档工件 id、统计、违规清单。落在封存范围内,
|
||
# 证据接口靠它找到文档——不去翻不受封存保护的 artifacts 表
|
||
REPORT_CHECKED = "report.checked"
|
||
# 探索运行里按需裁判的结论句判定:点开哪句才判哪句,追加在封存之后(post_seal),是批注不是证据。
|
||
# 封存核对只覆盖 manifest_seq 之前的事件,追加它不影响 verify;封存的报告文档一个字都不改
|
||
EVIDENCE_JUDGED = "evidence.judged"
|
||
|
||
# 其他
|
||
LOG = "log"
|
||
USAGE = "usage" # token / 成本累计
|
||
|
||
|
||
class RunEventModel(BaseModel):
|
||
id: str = Field(default_factory=lambda: uuid.uuid4().hex)
|
||
run_id: str
|
||
seq: int = 0
|
||
type: EventType
|
||
node_id: str | None = None
|
||
ts: float = Field(default_factory=time.time)
|
||
data: dict[str, Any] = Field(default_factory=dict)
|
||
|
||
def to_wire(self) -> dict[str, Any]:
|
||
return self.model_dump(mode="json")
|