Erpilot 系列第 6 篇 · 2026-10-20 发布 · 实现与验收截至 2026-10-19
上一篇:从工具名到业务状态的评测。本文分开讨论片段检索与模型答复;最新结构化答复两模型均为 11/13,严格质量门槛尚未达成。
本文引用的源码与报告保存在随文资料清单中,包含当时尚未推送到项目仓库的工作区内容,文件版本由 SHA-256 校验值标识。代码文本与复现脚本为局部摘录,运行仍需 Erpilot 工作区依赖。
用户问退货地址和联系电话,检索器返回了退换货政策。片段与“退货”主题相关,却没有地址或电话。此时提高回答的流畅度,无法补上缺失的信息。
Erpilot 的政策问答围绕这个边界做了两组工作:先冻结问题与相关片段,比较检索策略;再在受控检索条件下检查回答、引用和拒答。它们回答不同问题,数据也分别记录。
一、先定义哪些内容有资格成为证据
政策不是只有一段正文。当前语料还有来源、小节、政策标识、版本与生效区间。正式检索先通过 select_policy_scope 核对这些信息,再把候选片段交给排名算法。
这样,旧版本或冲突版本不能仅因关键词相似重新进入正常证据集合。运行时还会核对当前语料与已建索引是否一致;正文变化后,不能继续无提示地使用旧向量。
工具 search_policy 返回查询时间、政策状态和编号引用。每条引用包含实际正文、来源与版本、生效信息、citation、可复制的 quoted_citation 和 text_sha256。
这里的哈希用来核对正文一致性,不判断正文真假或结论适用性。检索分数同样只是排名依据,不能在证据卡里直接解释成答案正确概率。
特别是有命中时,返回的 answerability 仍是 not_assessed:找到了可能相关的材料,是否足以回答用户每个问题,还没有完成判断。
二、把检索实验从回答实验里拆出来
冻结检索集包含 36 条问题,其中 24 条有依据、12 条语料外。dev 为 12 条,test 为 24 条;语料有 3 份政策、18 个片段。问题与标签基于现有政策工程标注,未经第二名标注者独立复核,规模较小。
负例包含词面相关但无法回答的问题,例如退货联系方式、退款银行手续费或会员券叠加。它们用于区分“相关主题”与“存在答案”,不能只靠随机不相关句子检验拒答。
实验比较三种路径:向量余弦排序、项目实现的 BM25,以及按两路名次融合的 RRF。各方法使用相同有效政策候选集合,分数则属于不同数值空间,需要各自校准阈值。
阈值只在 dev 上选择,再冻结后计算 test 指标。真实向量对照还保留 vector_current,表示正式检索预先固定的 0.57 阈值;它与本次 dev 重新校准的向量方案分别报告。
数据、分词、融合和校准规则见检索实验契约,算法代码在 lexical.py。本项目的 RRF 使用名次倒数相加;融合分数不冒充证据卡原有的余弦相似度。
三、混合检索需要用结果决定是否采用
以下数字来自同一份真实查询向量缓存报告的 test 部分。排名在本地完成,报告本身没有再发模型或嵌入请求;缓存所需的真实查询向量采集另有账本。
| 方法 | 阈值后 Recall@3 | 证据精度 | 语料外空返回 |
|---|---|---|---|
| 当前向量策略,阈值 0.57 | 96.9% | 58.1% | 87.5% |
| dev 校准向量,阈值约 0.670668 | 59.4% | 100.0% | 100.0% |
| BM25,dev 校准阈值 | 87.5% | 54.8% | 62.5% |
| RRF,dev 校准阈值 | 78.1% | 87.5% | 75.0% |
Recall@3 是有依据问题逐题的相关小节召回宏平均;证据精度统计所有查询返回的片段,包含负例错误返回。语料外空返回是检索结果为空的比例,不是最终模型拒答率。
提高向量阈值减少了无关证据,但也丢失了更多有依据问题;重新校准方案还出现正例空返回。RRF 的精度有所变化,召回与语料外空返回没有同时超过当前策略。本批保留正式默认值,没有为了采用混合检索而更换默认链路。
这个取舍只适用于本次小语料与冻结用例。继续根据 test 错例反复调阈值,会让它失去独立检验的作用;新的改动应使用新批次和独立用例验证。
报告同时保存本地排名延迟,但它不包括嵌入网络请求、政策范围筛选或建索引,不能写成端到端回答耗时。local-tfidf 代理模式也只用于离线排名烟测,不代表真实语义嵌入效果。
四、命中之后,还要逐问判断支持关系
当前向量策略仍会给退货联系方式问题返回相关条款,而条款没有提供联系方式。这个失败说明,单靠提高召回率无法完成回答可靠性工作。
一个问题还可能同时包含有据和缺口。例如询问退货期限以及退货地址,需要回答有依据的部分,同时说明地址缺失;不能因为一部分可回答,就让另一部分也获得政策口吻。
条件与例外同样需要保留。质量问题的运费承担规则,不能移用于无理由退货;定制商品的例外,不能因为满足一般期限就被忽略。合法原文引用也可能被模型用于错误问题,来源一致性检查无法单独发现这种语义错误。
早期实现通过提示词要求逐问核对条件、例外、起算点和费用承担者,并提供完整引用以减少拼接错误。它改善了表达约束,但仍允许模型在引用前后自由生成结论。项目因此进一步收窄了最终回答通道。
五、让模型选证,让程序输出原文
结构化工具 compose_policy_answer 不接收自由回答正文。每个分项只允许提交:
| 字段 | 约束 |
|---|---|
question_quote | 当前用户问题的连续原文子串 |
status | evidence 或 gap |
citations | 本轮实际检索返回的引用标签;缺口分项必须为空 |
可信运行时读取实际工具历史,核对来源、版本、生效信息、正文哈希与原文;模型不能把自带的正文或历史标签塞进来,宣称已经得到验证。
结束工具还必须在独立工具步骤里调用。若模型把检索和结束一起并行提出,结束提案尚未看到这次检索结果,不能据此绑定证据。
随后由程序渲染用户原话、政策原文和缺口。同一证据只展示一次,其他分项使用程序编号回指。没有合格提案时,程序输出明确未确认的降级答复;检索不可用、冲突、失效或无命中不会触发政策常识补写。
search_policy 返回本轮材料
↓
模型提交逐问 evidence / gap 与引用标签
↓
可信回调校验来源和原文绑定
↓
程序生成原文与缺口 → 写入可见答复与历史 → 结束本轮图运行时会缓冲模型正文,触发政策契约后不向用户展示自由草稿;草稿保存在审计状态里。程序渲染的最终文本进入 SSE、CLI、历史与评测,避免界面、持久化和评分各看一份不同回答。
这仍然没有实现通用语义蕴含验证。模型负责把问题和证据对应起来,程序主要保证来源与原文。若模型完全不触发检索,或者把背景条件误当成独立问题,现有约束仍有边界。实现见政策答复适配及运行契约。
六、检索与生成,各自保留分母
结构化答复专项冻结 13 个场景,使用真实政策原文,但检索片段和故障条件由夹具控制,不再调用嵌入端点。它检查真实模型在已知检索条件下的选证与结束行为,不能作为前面 36 条问题的逐题端到端成绩。
最终同源码结果为:
| 模型 | 完整执行 | 严格通过 | 质量门槛 |
|---|---|---|---|
| GLM | 13/13 | 11/13 | 未通过 |
| DeepSeek | 13/13 | 11/13 | 未通过 |
GLM 在政策不可用和失效场景没有提交结构化结束工具;程序已经安全拒答,但流程义务没有完成。DeepSeek 在不可用场景也缺少结束提交;另一例把无理由退货问题的背景条件单列为证据分项,运费本身虽被拒答,仍不符合该例的分项金标。
这些失败需要分别解释。安全降级不应被改判成结构化流程通过;背景条件拆分失败也不等于模型已经编造了运费。两模型 strict 对照通过,只表明实验条件可比,质量 gate 仍然失败。
最终结果、原文复核和原币种计量见验收记录。其中可见正文由开发代理逐例复核,属于开发过程审查,没有独立外部盲评。
七、分段续跑,也不能重试到通过
真实评测还会面对上游中断和未知用量。当前 campaign 在开始时固定源码、范围、语料、模型配置、费率与超时,请求前写日志,每例保存结果;显式续跑只补上游中断或未执行项,已经完成的失败不重新运行。
分段报告按用例身份和指纹汇总,不能挑选每次运行中最好的答案。存在未知 usage 时保留未知费用,并阻止最终计量验收;补齐用例不自动核销旧未知请求。
最终批次本身没有出现模型上游中断,中断续跑路径由离线故障模拟验证;政策“不可用”场景则是受控检索夹具。不能把这两种故障来源合成一次真实供应商事故。
当前待处理项也很具体:阻断检索状态的结束可以进一步交给可信程序,问题拆分需要区分询问和背景。修复应单列新批次验证,保留本次失败。
政策问答最终需要回答“哪些事实可以支持哪些问题”。下一篇多轮状态与上下文实验转向另一类证据丢失:原始历史还在,但模型本轮实际看到了什么。