对照引用来源核查论断
一篇关于某城市共享单车的文章草稿提出了七条论断,每条都引用了一份报告、市议会会议记录或一项调查。发布前,每条论断都要对照它引用的段落核查。来源明确支持的论断通过;被反驳和无依据的论断退回给作者;其余的交给编辑。
工作原理
每条论断与其来源组成一个输入,配两道判断题:
| 问题 | 类型 |
|---|---|
supported:来源陈述了论断所说的内容 | predicate |
contradicted:来源陈述的内容与论断相冲突 | predicate |
“没有依据”和“被反驳”会导向不同的动作,所以分成两道题来问。真值表:
contradicted | supported | 状态 |
|---|---|---|
| 0.7 及以上 | 任意 | contradicted |
| 低于 0.7 | 0.7 及以上 | supported |
| 低于 0.3 | 低于 0.3 | unsupported |
| 其他 | 其他 | escalate |
运行
先下载一次示例包。caches/citation_check.json 中录制的回答让脚本无需 API key 即可运行;加 --replay off 则改为调用免费托管 API。
Bash
curl -fLO https://valen-team.github.io/cookbook/valen-cookbook.zip
python -m zipfile -e valen-cookbook.zip .
cd valen-cookbook && python -m pip install -r requirements.txt
python citation_check.py
python citation_check.py --replay off --base-url https://api.valen-team.workers.dev/v1
代码
citation_check.py
"""Check each claim in a draft against the source it cites; escalate what the source does not settle. Apache-2.0."""
import json
from common import HERE, client, emit, parser, request
QUESTIONS = [
{"name": "supported", "type": "predicate", "instructions": "The source states what the claim says."},
{"name": "contradicted", "type": "predicate", "instructions": "The source states something that conflicts with the claim."},
]
def run(predict, claim, sources):
answers = request(predict, f"Claim: {claim['text']}\n\nSource [{claim['cites']}]: {sources[claim['cites']]}",
QUESTIONS)
supported, contradicted = answers["supported"]["probability"], answers["contradicted"]["probability"]
if contradicted >= 0.7:
status = "contradicted"
elif supported >= 0.7:
status = "supported"
elif supported < 0.3 and contradicted < 0.3:
status = "unsupported"
else:
status = "escalate"
return {"id": claim["id"], "cites": claim["cites"], "status": status,
"supported": supported, "contradicted": contradicted}
def main():
args = parser(__doc__).parse_args()
data = json.loads((HERE / "citation_check_data.json").read_text())
with client(args) as predict:
checks = [run(predict, claim, data["sources"]) for claim in data["claims"]]
emit({"publishable": all(c["status"] == "supported" for c in checks), "claims": checks}, args)
if __name__ == "__main__":
main()
citation_check_data.json
citation_check_data.json
{
"sources": {
"1": "City Transport Annual Report 2025, p. 12: Bike-share trips rose to 4.1 million in 2025, up from 3.4 million in 2024. E-bikes made up 38% of trips. Average trip length was 14 minutes.",
"2": "Council minutes, 3 February 2026: The council approved 120 new docking stations, to be installed in the eastern districts by the end of 2026. Funding comes from the regional mobility fund.",
"3": "Rider survey, November 2025 (n = 2,300): 61% of riders said they use the service to commute. The most requested improvement was more stations near train stations (44%)."
},
"claims": [
{"id": "c1", "cites": "1", "text": "Bike-share trips grew to 4.1 million in 2025."},
{"id": "c2", "cites": "1", "text": "The average trip lasted 40 minutes."},
{"id": "c3", "cites": "2", "text": "The council approved 120 new docking stations for the eastern districts."},
{"id": "c4", "cites": "2", "text": "The new stations will be paid for by a fare increase."},
{"id": "c5", "cites": "3", "text": "Most riders use bike share to get to work."},
{"id": "c6", "cites": "3", "text": "Riders are happier with the service than they were a year ago."},
{"id": "c7", "cites": "1", "text": "Bike-share trips fell in 2025 compared with 2024."}
]
}
输出
"The average trip lasted 40 minutes" 被来源中的 "14 minutes" 反驳(0.89)。"Riders are happier than a year ago" 在调查中找不到任何依据,因此判为无依据。"Trips fell in 2025" 的 contradicted 得分为 0.62,交给编辑处理。
录制的输出
{
"publishable": false,
"claims": [
{
"id": "c1",
"cites": "1",
"status": "supported",
"supported": 0.992473812984103,
"contradicted": 0.060028307587119245
},
{
"id": "c2",
"cites": "1",
"status": "contradicted",
"supported": 0.11813096878127707,
"contradicted": 0.8891227576554849
},
{
"id": "c3",
"cites": "2",
"status": "supported",
"supported": 0.9837899291028864,
"contradicted": 0.09080726585680793
},
{
"id": "c4",
"cites": "2",
"status": "contradicted",
"supported": 0.012904512852205586,
"contradicted": 0.7954747317872861
},
{
"id": "c5",
"cites": "3",
"status": "supported",
"supported": 0.9694863250698544,
"contradicted": 0.06629886029506596
},
{
"id": "c6",
"cites": "3",
"status": "unsupported",
"supported": 0.19670646185735557,
"contradicted": 0.10636692573403511
},
{
"id": "c7",
"cites": "1",
"status": "escalate",
"supported": 0.041889894149444565,
"contradicted": 0.6167122309420178
}
]
}
改成你自己的
- 把数量表述("most"、"a third"、"doubled")与来源中的数字作比较属于算术:在代码里提取数字并比较,Valen 只负责判断文本说了什么。
- 把
escalate的论断交给编辑或推理模型;其余的都在一次低成本的处理中定下来。