前面的系列已经覆盖了模型调用、上下文、提示词、结构化输出、错误处理、评估和安全边界。本篇在学习路线完成后做一个新的综合小项目:给模型一份“已确认事实”和一段待发布周报,让它标出哪些句子能被事实支持、哪些句子需要人工核对。程序只生成检查报告,不自动发布、不修改原文,重点是练习把模型放在“候选分析器”位置,而不是让它替人做最终决定。
先定义任务和边界
核对工具接收两类输入:事实清单和周报草稿。事实清单中的每条记录都有编号,例如 F1,内容可以是已经从项目系统确认过的日期、数量或状态;周报是普通文本。模型需要返回每个段落的结论:supported 表示能在事实中找到直接依据,needs_review 表示信息不足或表述超出了事实范围。
这个项目不会判断事实本身是否真实,也不会访问数据库、发送通知或直接改写周报。模型返回的编号、结论和理由都只是建议,程序先检查格式,再由人决定是否修改原文。尤其要注意:语言模型可能把常识或上下文猜测当成事实,所以“没有找到依据”比“模型觉得合理”更适合作为人工复核信号。
准备环境和输入文件
在独立虚拟环境中安装官方 Python SDK:
1 2 3
| python -m venv .venv source .venv/bin/activate python -m pip install openai
|
密钥和模型名只通过环境变量提供,不要写进代码或提交到 Git:
1 2
| export OPENAI_API_KEY="替换为你的真实密钥" export MODEL_NAME="替换为你可用的模型名称"
|
新建 fact_check.json:
1 2 3 4 5 6 7
| { "facts": [ {"id": "F1", "text": "版本 2.4 已于 10 月 8 日发布。"}, {"id": "F2", "text": "本周收到 18 条用户反馈,其中 12 条已经关闭。"} ], "draft": "版本 2.4 已于 10 月 8 日发布。本周收到 18 条反馈,全部已经关闭。" }
|
输入文件允许被人编辑,因此代码仍要检查顶层字段、事实编号和文本类型,不能把 JSON 解析成功误认为内容可信。
编写最小核对程序
创建 fact_checker.py。下面的程序使用官方 Python SDK 的 Responses API;response.output_text 是 SDK 提供的文本结果便捷属性。为了让示例容易运行,输出协议先采用“要求 JSON + 本地解析校验”的方式。真实项目可以进一步使用服务端支持的结构化输出配置,但仍不能省略本地校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| import json import os import sys from pathlib import Path
from openai import OpenAI
ALLOWED_VERDICTS = {"supported", "needs_review"}
def load_case(path: Path) -> dict: case = json.loads(path.read_text(encoding="utf-8")) facts = case.get("facts") draft = case.get("draft") if not isinstance(facts, list) or not facts: raise ValueError("facts 必须是非空列表") if not isinstance(draft, str) or not draft.strip(): raise ValueError("draft 必须是非空字符串") for fact in facts: if not isinstance(fact, dict) or not isinstance(fact.get("id"), str): raise ValueError("每条事实都必须有字符串 id") if not isinstance(fact.get("text"), str): raise ValueError("每条事实都必须有字符串 text") return case
def check_result(result: object, fact_ids: set[str]) -> list[dict]: if not isinstance(result, list) or not result: raise ValueError("模型结果必须是非空数组") checked = [] for item in result: if not isinstance(item, dict): raise ValueError("每项核对结果必须是对象") if item.get("verdict") not in ALLOWED_VERDICTS: raise ValueError("verdict 不在允许范围内") refs = item.get("evidence_ids") if not isinstance(refs, list) or not all(x in fact_ids for x in refs): raise ValueError("evidence_ids 含有未知事实编号") reason = item.get("reason") if not isinstance(reason, str) or not reason.strip(): raise ValueError("reason 必须是非空字符串") checked.append(item) return checked
def main() -> int: if len(sys.argv) != 2: print("用法:python fact_checker.py fact_check.json", file=sys.stderr) return 2 case = load_case(Path(sys.argv[1])) client = OpenAI() facts = json.dumps(case["facts"], ensure_ascii=False) instructions = ( "你是事实核对助手。只能依据 FACTS 判断 DRAFT,禁止补充外部知识。" "逐个核对草稿中的独立陈述,返回 JSON 数组,不要 Markdown。" "每项必须包含 verdict、evidence_ids、reason;verdict 只能是 " "supported 或 needs_review。证据不足时必须 needs_review。" ) prompt = f"FACTS:\n{facts}\n\nDRAFT:\n{case['draft']}" response = client.responses.create( model=os.environ["MODEL_NAME"], instructions=instructions, input=prompt, ) checked = check_result( json.loads(response.output_text), {fact["id"] for fact in case["facts"]}, ) print(json.dumps(checked, ensure_ascii=False, indent=2)) return 0
if __name__ == "__main__": raise SystemExit(main())
|
load_case 负责输入边界,check_result 负责输出边界。后者不仅检查 JSON 是否可解析,还检查结论枚举、证据编号和理由是否存在。这样即使模型返回了格式正确但引用了不存在的 F99,程序也会失败,而不会生成看似可信的报告。运行命令是:
1
| python fact_checker.py fact_check.json
|
这个示例不会伪造输出:实际结果取决于密钥、模型版本和当前请求。正常情况下,第二句因为“全部关闭”与 F2 的“12 条已经关闭”不一致,应进入 needs_review;但仍应以程序实际打印的报告为准。
常见问题
为什么不让模型直接重写周报? 重写会把核对、编辑和发布混在一起,出错时难以追责。先输出带证据编号的报告,人确认后再编辑,边界更清楚。
为什么要把事实编号传给模型? 自由复制长句不利于程序校验。稳定编号能让报告与原始记录建立可追踪关系,后续也能保存为审计日志。
模型说 supported 就一定正确吗? 不一定。模型只能比较文本,不能证明事实来源可靠;高风险数据仍应由业务系统或人工复核。生产版本还应限制输入长度、记录请求耗时和错误,并对重复请求设置超时与重试策略。
一段话里有多个结论怎么办? 可以要求模型逐条拆分,但不要只看返回条数。实际项目应给草稿段落或句子编号,让每条判断都能回到原文位置,并增加一组人工标注样例做回归测试。
小结
这个小项目的核心不是让模型“审定”周报,而是建立一条可检查的流水线:事实和草稿作为上下文输入,模型提出带证据的核对建议,Python 校验协议,人工完成最终决策。把模型输出视为不可信输入,并保留事实编号、原文和检查结果,才能让 AI 功能从一次性演示逐步走向可复核、可维护的应用。