AI Hero Club

AI 智能体工程 · 第 6 章

已自行标记完成0 / 8

仅保存在当前浏览器;完成标记不代表通过项目验收。

本章提纲 10 个小节
全课目录 8 章
完整课程计划 ↗
AI 智能体工程 / 课程计划 ↗

CHAPTER 06 / 8建议学习 120 分钟

从工具观察到可自动核查的结果

回答附了一个真实文档编号,怎样继续核对每项结论和预留结果确有依据?

本章要解决的 3 个问题
  • 从真实文档自动抽取带版本与行号的证据
  • 区分文本支持、事实真实性和事务确认
  • 构造引用突变与假完成负对照
课程项目 ZIP ↓

结果必须由环境事实支撑

前五章控制动作如何发生,现在控制系统如何描述结果。一个可靠执行器不能只接受 policy 返回的流畅答案,尤其不能因它说“已预留”就写成功。项目结束动作只给出有限 status,正文由执行器根据已有观察与实际资料生成;reserved 必须有 reserve 回执,info 必须是查询任务且已读唯一文档,insufficient 必须有库存不足观察。策略可以 abstain,但不能制造工具没有报告的效果。此处结果生成是确定性摘录,不是自由语言创作。

资料实际存储在 SQLite documents 表,含 id、title、body 和 sha。导入器读取 data/docs 内的原创器材卡,按正文UTF-8字节计算摘要并冻结。evidence(item) 枚举真实正文行,从以短横加空格开头的项目符号行自动产生 claim:text 为去掉前缀的内容,另附 item_id、sha256、一基行号和原始quote。没有为每个任务人工写一份claims答案;同一资料导入、读取、摘录、引用与自动检查接在同一执行入口。

支持性检查的精确定义

给一条 claim,先按 item_id 找到资料版本,再要求摘要相同;把一基行号减一得到列表索引,检查索引合法且该行恰等于 quote,最后检查 quote 恰为“- ”加 text。四个条件同时成立才视为摘录支持。这样会发现错文档、错版本、错位置以及换了文本却没换引用的错误。它比“附了一个链接”强,但范围仍很窄:只接受精确摘录,不判断同义改写或跨段推理是否被支持。

Support(c,d)=ID(c,d)∧Hash(c,d)∧Line(c,d)∧ExactQuote(c,d)\mathrm{Support}(c,d)=\mathrm{ID}(c,d)\land\mathrm{Hash}(c,d)\land\mathrm{Line}(c,d)\land\mathrm{ExactQuote}(c,d)
c为一条claim,d为指定文档版本;该谓词是精确摘录的可执行定义,不是通用语义蕴含判断器。

一基行号是给读者定位的约定,Python列表索引从零开始,两者差一不可省略。换行处理也要冻结:项目按读取后的 splitlines 枚举,摘要对原正文字符串的UTF-8字节计算。若编辑器改变内容或换行导致摘要变化,即使句子看似相同,也不能沿用旧版本引用。当前导入器直接拒绝同id内容变更,保住恢复证据;动态版本管理和撤销应采用明确版本协议,不能在原记录上就地修改后还称原引用有效。

完整算例一:四条相机事实自动产生

相机文档第一行为标题,第二到第五行分别是器材名称、用途、借用前提和归还要求。查询相机时,search唯一命中camera,document返回冻结正文;结束生成四条claims,第一条text为“器材名称:入门相机。”,line为二,quote保留“- ”前缀。supported 重新从数据库取实际第二行,逐项比较后为真。四条都通过才完成正常结果。这里精确的行与摘要来自文件,不由策略猜测,读者可打开正文逐条复核。

如果policy用本地JSON或HTTP提出同一结束动作,执行器仍走同一evidence函数,不接受外部返回的自造引用。这使支持性验证独立于决策源。不过它也限制输出能力:系统只提供当前资料的摘录,不会综合判断“最适合哪个学生”,也没有比较不同器材的开放式推荐。课程把这个受限生成器作为可验收基线;未来自由生成必须增加声明拆分、引用匹配及失败时拒答,而不能把当前摘录通过率当作语义问答质量。

在课程项目目录运行项目目录运行:从实际导入文档生成证据,再突变文本检查失败。
import tempfile,pathlib
from agent import Store
from run import ROOT
with tempfile.TemporaryDirectory() as td:
    s=Store(pathlib.Path(td)/'e.sqlite',ROOT/'data');s.import_documents()
    claims=s.evidence('camera')
    assert len(claims)==4 and claims[0]['line']==2
    assert all(s.supported(c) for c in claims)
    wrong={**claims[0],'text':'库存有一百台'}
    assert not s.supported(wrong)
    s.db.close()
print('extraction and mutation PASS')

完整算例二:有引用的假完成仍应失败

某任务只有search和document观察,policy提出finish reserved,并附一段真实相机资料引用。引用即使精确,也只支持器材说明,不支持事务已发生;finalize 因缺reserve观察拒绝。反过来,有真实预留回执但引用了错误SHA,也不能当作完整资料结果交付。资料证据与事务证据是两条不同链:前者绑定文本,后者绑定业务效果。任何一条完整都不能自动补上另一条缺口。

另一个失败例是把正确claim的line由二改成三而text不变。第三行是用途,与quote不一致,supported为假;若同时把quote改成第三行却text仍为名称,最后一项精确前缀比较仍失败。若将text和quote都换为第三行,支持性可通过,但它现在是另一条事实,不能声称仍回答原来指定的问题。项目未实现问句与事实相关性的语义判断,检索与确定性任务模板只提供有限的相关性控制,需在报告中单独指出。

事务回执如何支持行为结果

reserve结果包括item_id、quantity、remaining、revision和receipt。receipt是幂等键,工具端ledger保存同一结果;库存与ledger在同一事务更新。完成reserved时读取这个已观察结果,报告预留数量和剩余,不从资料正文估库存。库存是动态状态,器材说明是冻结文档,不能用文档里“有三台”的旧句子覆盖实时stock。第四章提交后失联任务还提醒我们:没有客户端回执观察时,不应简单说业务未发生,应标未确认并按原键恢复。

对正常只读info任务,ledger必须为零;对预留成功任务,ledger一条且库存相应减少;对缺货或无权限任务,预期不发生写入。自动评价不仅看status,也查效果数量和剩余库存,再检查claims支持与事件重放。这样的多路径核对比单看答案字符串更强,因为回答、状态和数据库可以互相暴露不一致。它仍不能发现现实仓库里器材丢失等外部事实,本地库存表是当前模型的唯一环境状态。

支持率、覆盖率与诚实拒答

定义支持率为通过支持检查的输出claims数除以输出claims总数。若一条也不输出,分母为零,不能自动说百分之百质量;可以单列“无claims”并评价任务是否应有证据。覆盖率则需要任务要求的事实集合,当前模板要求读取资料时输出其四条项目符号事实,数量可从文档结构自动得到。支持率高但漏掉全部关键事实仍可能不好,覆盖完整但引用不实也不合格。两个维度需分别定义,不用一个总分掩盖空回答。

Ps=nsupportednemitted,Cs=ncoverednrequiredP_s=\frac{n_{supported}}{n_{emitted}},\qquad C_s=\frac{n_{covered}}{n_{required}}
分母为零时单独标不适用,不把空集合通过冒充任务完成。

项目 all(supported(c) for c in claims) 对空列表返回真,只表示未发现不受支持的条目。修订后的评价器还独立建立必答事实集合、检查完整覆盖和重复引用。正常 info、reserved、insufficient 缺少任一要求事实均失败;not_found、denied 或 abstain 按任务规范可没有资料事实。支持检查不是任务完成判断。

两类验证与迁移边界

第一类检查是逐字段突变:改变text、SHA、line或item_id,要求支持性失败。第二类是交叉效果检查:policy假称reserved无回执应失败;真实预留的库存、ledger和客户端结果应一致。两类检查分别约束文本来源与业务事实。真实语言模型可能把多个片段概括成新句子,精确摘录检查不再足够;可以保留抽取模式作为正向控制,另设计语义支持数据与人工复核,但未完成前不得称已实现自动事实真实性验证。

资料可能包含错误、矛盾或危险建议,正确引用只帮助追责,不负责判真。要拓展到真实学校管理,需要受控来源、版本更新、有效期与授权治理,且用户应能看到引用来自哪一版。本课的原创资料不是外部机构规则,讲义不借它们制造权威背书。这里完成的是从环境观察到可定位输出的证据路径,使以后替换生成器时有清楚的基准和失败边界,而非凭一段漂亮回答判断可靠性。

本项目没有做Unicode归一化或模糊纠错。全角标点、不同空格或同形字符都会影响精确比较,这对冻结摘录是可接受的严格约定。若允许规范化,必须同时定义正文存储、摘要、行定位和比较所用表示,避免显示文本与被哈希字节各用一套规则。

独立运行标准库独立检查,不依赖项目目录。
lines=['# title','- name: camera','- purpose: light']
claim={'line':2,'text':'name: camera','quote':'- name: camera'}
def supported(c):
    i=c['line']-1
    return 0<=i<len(lines) and lines[i]==c['quote']=='- '+c['text']
assert supported(claim)
assert not supported({**claim,'line':3})
assert not supported({**claim,'text':'invented'})
print('exact-source support PASS')

练习与项目增量

TRY IT YOURSELF

基础:支持性四条件

一条claim的text正确,line也正确,但SHA属于旧正文。能通过吗?为什么?

提示 1
引用身份由哪些字段共同绑定,句子碰巧相同能否补上版本不符?
提示 2
分别比对当前正文SHA和指定行;正文更新时必须显式版本化,不把文本一致当旧hash有效。
对照参考解答
不能。引用必须绑定当前指定版本,SHA不符说明来源身份不同。即使句子碰巧未变,也应显式使用正确版本,而非绕过版本检查。当前项目保守拒绝同id正文更新,防止恢复时混合来源。

TRY IT YOURSELF

进阶:空回答陷阱

某系统不输出任何claim,all检查为True,于是称支持率100%。怎样修正?

提示 1
空集上的all为真是逻辑约定,先问任务实际要求多少必需事实。
提示 2
将支持性分母为零与字段覆盖分开;用required集合减去去重输出身份检查缺失。
对照参考解答
空全称布尔值不等于有意义的支持率。应把分母为零标不适用,并独立判断任务是否要求资料事实;若要求四条而输出零条,覆盖率为零且任务失败。不要用没有断言规避错误。

TRY IT YOURSELF

挑战:摘录与语义推断

资料说“借用前提:由老师示范调焦”,模型输出“任何学生都可独自调焦”。可以靠同一个引用通过吗?

提示 1
先逐字比较claim与引用,再比较两句的允许行为是否一致。
提示 2
当前精确摘录检查能验证什么?若改为自由改写,需要新增语义支持判断而非沿用旧门槛。
对照参考解答
不能,文本不一致且语义还相反。本项目精确摘录检查会拒绝新句子,但不具备通用语义推理。若允许自由生成,需另外定义支持性标注和判断器,保留反例与人工抽检;当前不应自动把附引用的新句子当真。

本章增量是evidence、supported、finalize的资料与事务双链验证。后继评估将用同一资料、同一策略入口和固定任务集,实际统计应完成、应拒绝、预算失败和恢复成功,而不是人工挑选几条好看的答案。

支持检查之外,还要独立检查漏答

修订后的 fixture 先由 required_claims(row) 从 catalog 与原始 Markdown 逐行建立必答身份集合,不调用候选 Store.evidence。对 info、reserved、insufficient,输出须覆盖唯一器材全部项目符号事实。以 (item_id,line,quote) 去重后计算 covered_claims / required_claims,并另查重复引用;最终 passed 同时要求状态、支持、完整性、业务效果与库存正确。 相机原件有四条事实:正常结果 coverage=1;把 evidence 替换为空列表时 support_pass=True,但 coverage=0、completeness_pass=False、passed=False;删除一条得到 3/4;重复已有引用也不能补漏,且独立重复门失败。无需资料的拒答状态分母为零,coverage 标为不适用,而不是 100%。新回归保留这三种有意错误,并在同一修订代码上重新跑十七个正常任务;原十七题结果仍为 17/17。

BUILD THE MECHANISM

实现支持与必要事实联合验收

实现LearnerRunner.validate_claims;不把空集合all为真或重复claims当作完成。 下载本课完整项目,在项目根目录运行;只编辑 learner/starter.py,已有构造框架由项目提供。

输入与输出 逐条来源支持且正常结果覆盖真实文档所有bullet事实;空、缺项和重复拒绝。
拒答可以空claims;义务从真实source读取,不调用evidence抽取器生成期望。

在课程项目目录运行待完成的学习任务
# LearnerRunner.validate_claims(替换已提供类的此方法)
def validate_claims(self,item,claims,status):
        raise NotImplementedError('evidence')
提示 1

先写支持率与内容完整性两个布尔量,不能合成一个分母。

提示 2

从原始文档行号和事实文本构造独立身份集合。

提示 3

要求列表长度等于唯一身份数,再比较必需集合。

独立检查 python3 -m learner.check --stage evidence --output runs/learner-evidence.json

迁移检验 删除一条、重复一条、改SHA分别预测哪个门拒绝;然后对新器材文档复核,不能只记camera数量4。

参考实现与解释
在课程项目目录运行参考实现
# LearnerRunner.validate_claims(替换已提供类的此方法)
def validate_claims(self,item,claims,status):
        if not all(self.store.supported(c) for c in claims):return False
        if status not in ['info','reserved','insufficient']:return not claims
        # Independent obligations are read from actual source facts, never Store.evidence.
        row=self.store.db.execute('SELECT * FROM documents WHERE id=?',(item,)).fetchone()
        if not row:return False
        expected={(i,line[2:])for i,line in enumerate(row['body'].splitlines(),1)if line.startswith('- ')}
        actual=[(c['line'],c['text'])for c in claims]
        return len(actual)==len(set(actual)) and set(actual)==expected and bool(expected)

CHECK YOUR UNDERSTANDING

检验理解

先作答,再决定是否展开解析。选择题用于发现理解缺口;独立实现与迁移任务仍需另行检验。

1. 精确支持性检查能证明?

查看解析它是来源一致性,不是事实真实性。
2. 预留成功主要依赖什么?

查看解析说明文档不能证明事务发生。
3. 空claims的all为True意味着?

查看解析空全称不等于有效支持率。
4. 本项目claims怎样产生?

查看解析来源、行号和正文来自同一实际资料链。

自行标记用于记录学习进度,不代表通过项目验收。