引用检查 (Cite Check)
对已完成或进行中的文章草稿进行验证。检查每个引用的来源是否存在,每个引用内容是否与真实来源逐字一致,每个 URL 是否可解析,以及在来源页码可恢复的情况下检查 pin cite(精确页码引用)的合理性。确定性脚本负责提取和交叉引用解析,几乎不消耗 token;模型代理则负责对每个唯一来源进行引用/页码验证,并对每个高风险发现进行强制性二次检查,确保最终结果可靠。
它复用了 legal-research 技能的机制(check_cite.py、fetch_page.py、CourtListener token、预算降级阶梯)及其内部规则(仅开放网络验证,从不依赖记忆,无法获取的来源报告为“未验证”——绝不假设)。
检查内容: 每个引用是否指向所声称的来源,标题/作者/名称是否拼写正确,判例汇编/卷号/起始页/法院/年份是否正确,引用内容是否逐字或诚实标记,supra/id. 交叉引用是否完整,以及 URL 是否有效。
覆盖记录(添加于 2026-09-06)——以及哪些仍未检查。 引文验证单元基于引用内容构建;它们自身会遗漏每个未引用的二手来源。现在 scripts/citation_ledger.py 会盘点每个脚注中的每个引用实例(包括判例或二手来源、是否被引用、是否带有 Id./supra/短引链条),对未引用的文章/书籍/法规/URL 进行二手来源存在性验证,报告的 H 节会为每个实例赋予一个状态——已验证、发现、仅存在性验证、无法验证、未检查、交叉引用、未解决的交叉引用——并列出每个脚注中未检查的实例。有两个限制必须在报告中说明:(1) 论点支持度——未引用来源是否实际支持其所在的句子——从不评估(这就是“仅存在性验证”的含义);(2) 只有引用内容所在的 pin cite 才会被检查。将“仅存在性验证”视为“真实来源,未阅读”,绝不要视为“验证通过”。
加上明显的格式问题(用户于 2026-08-10 解除了范围锁定——“解除锁定,允许检查明显的问题”)。 Form 阶段报告仅凭草稿即可决定的事项,无需查阅来源:
- 短引形式——在 Bluebook R. 10.9 的五个脚注窗口内重复完整引注,本该使用短引形式;判例短引超出该窗口或之前没有任何完整引注;或二手来源在 R. 4.2 要求使用
Author, supra note N时却被完整引用两次。 - 字体格式(仅
.docx格式)——信号词 /Id./supra/quoting未使用罗马体,斜体supra后的note N被错误地斜体化,斜体溢出到引注前的逗号,小型大写字母在单词中断开,以及根据 R. 2.1(a) 对判例名称的斜体处理(在引注从句中的完整引注应使用罗马体,在句子文本或短引中应使用斜体——这是唯一需要代理判断的判断依据,因为模式无法区分)。
这些检查使用 ${CLAUDE_PLUGIN_ROOT}/skills/legal-research/citeform.py,与 /proofread 使用同一模块,因此两个技能不会给出相互矛盾的建议。所应用的规则记录在 ${CLAUDE_PLUGIN_ROOT}/skills/legal-research/bluebook-typeface-and-short-forms.md。格式发现会在报告末尾单独成节,永远不会进入“需注意”部分——它们属于整理范畴,而非实质性问题。
它仍然不是: 完整的 Bluebook 格式检查器(信号词顺序、引注顺序以及引注是否正确组成仍超出范围)、实质性编辑工具,或期刊正式引用检查的替代品。与 /proofread 的分工保持不变:proofread 检查散文,cite-check 验证引注——它们恰好在引注格式层共享了代码。
如何运行
1. 解析请求
- draftPath — 草稿路径(必填)。
。docx(带真实 Word 脚注)是标准格式。。md/。txt(带 pandoc 风格[^n]脚注)可作为备选。 - footnotes — 可选的部分检查范围,例如“脚注 45–80”或一个列表。默认为整个草稿。
- deep — 可选;更深入的验证(中努力程度验证代理,更高的软上限)。在接近定稿的草稿或用户要求“深度/彻底”检查时开启。
- 如果草稿包含修订标记,请告知用户并询问是否要在检查前接受所有更改(解析器会读取当前/已接受的文本并跳过已删除的修订,但标记较多的草稿值得提醒)。
1b. 询问检查深度(由作者决定,2026-08-09 — 与 /proofread 步骤 1b 保持一致)
除非请求已经确定了深度,请提前询问——一次 AskUserQuestion,标题 Depth,在预检之前。将修订标记提醒(步骤 1)在适用时整合到同一调用中,以便用户一次完成所有回答,而不是被中断两次。
当请求的措辞已经确定了级别(“深度”、“彻底”、“接近定稿”、“提交前”、“快速”、“标准”),对已经在本会话中选定级别的部分范围重新运行,或用户仅要求检查狭窄的脚注范围时,跳过此询问。
两个选项——引用检查没有 fable 升级,因此阶梯比 proofread 短:
| 选项 | 参数 | 说明 |
|---|---|---|
| 标准 | deep:false | 验证代理低努力程度,软上限 650k。适合工作稿。 |
| 深度 | deep:true | 验证代理中努力程度,软上限 900k。对每个引用内容与其来源进行更仔细的比对——适合接近定稿的草稿。 |
不要重复使用 /proofread 的解释——两个 deep 的机制不同。 Proofread 的深度添加了第二次偏移提取(一种召回策略,基于约 75-80% 相同模型重叠发现)。Cite-check 的深度不添加新的提取或重新获取任何内容:它仅将验证代理的努力程度从 低 提高到 中(工作流第 557 行)并提升软上限。它提高了对每个引用的关注度,而不是增加更多引用。引用的覆盖率在两种模式下完全相同——每个引用在两种级别下都会被检查——因此不要暗示标准模式会跳过任何内容。
时间因素在这里不如 proofread 重要(深度不改变提取,因此后续重新运行可以通过 resumeFromRunId 从缓存重放分段/存在性验证),但验证是运行的主要部分——在 National Muffler 草稿的 33 个代理中占了 26 个——因此更改级别仍然意味着重新运行昂贵的阶段。请先询问。
2. 预检(在启动之前执行——运行中途的权限提示可能导致代理失败)
- 读取
~/.claude/settings.json;确保permissions.allow包含"WebFetch"和"WebSearch"(合并缺失项——绝不要替换整个数组)。例行设置;不要提示。 - 确认 CourtListener token 正常工作。测试它,不要去寻找文件——运行
python3 ${CLAUDE_PLUGIN_ROOT}/skills/legal-research/check_cite.py "347 U.S. 483";退出码为 0 且输出"found": true表示正常,无需报告。check_cite.py会按顺序从COURTLISTENER_API_TOKEN、COURTLISTENER_TOKEN和文件~/.config/courtlistener/token中解析 token——严格按此顺序,环境变量方式(通常在~/.claude/settings.json的env块中设置)是标准安装方式,因此缺少~/.config/courtlistener/目录不证明任何问题。如果探测确实失败,运行仍然可以继续(判例存在性验证将回退到网络),但在交付时提一次即可。Token 页面:https://www.courtlistener.com/profile/api-token/ - 解析项目的 Zotero 库(不同项目不同——有些是合著群组库,有些是个人库):
a. 在项目文件夹的
CLAUDE.md(或AGENTS.md)中查找Zotero library:行。如果存在,使用其命名的 MCP 服务器并跳到 (c)。 b. 如果不存在,询问用户该项目使用哪个 Zotero 库(个人库或已命名的群组库),然后将答案作为Zotero library: <name> → MCP server <server>行记录到项目文件夹的CLAUDE.md中,以便后续运行不再重复询问。 c. 确认该 MCP 服务器实际存在(个人库 =zotero;群组库有各自的服务器,例如zotero-group对应“5A general law”,群组 ID 在~/.claude.json中)。如果项目的库尚未有服务器,此时主动提供添加方式:复制~/.claude.json中的zotero条目,命名为zotero-<slug>,并设置ZOTERO_LIBRARY_TYPE=group和群组的ZOTERO_LIBRARY_ID(通过 zotero.org 群组 URL 或草稿的域代码查找);注意添加后需要重启 Claude Code,因此本次运行代理将回退到网络。 d. 将服务器名称作为zoteroServer(如果本次运行没有可用服务器,则传递zotero:false)。
2b. 生成草稿快照(始终执行)
实时 OneDrive/Word 协作文件可能在提取和交付之间发生变化,导致报告描述的是从未被检查的字节。将草稿复制一次,将副本设为只读,计算哈希值,并基于该快照运行下游所有操作。原始路径仅在此处使用,不会在任何其他地方使用——不在 Workflow 参数或代理提示中使用。
cp "<draftPath>" "<scratch>/snapshot.docx" && chmod a-w "<scratch>/snapshot.docx"
shasum -a 256 "<scratch>/snapshot.docx" # 记录此哈希值;它将出现在报告页脚中
3. 提取和准备(确定性脚本——自行运行,几乎不消耗 token)
将中间 JSON 放在工作流代理可读的位置(会话暂存区即可)。使用已安装的脚本路径,以便从任何文件夹都能运行(draftName 保持原始文件名,而非“snapshot”):
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cite-check/scripts/extract_cites.py "<scratch>/snapshot.docx" "<scratch>/extract.json"
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cite-check/scripts/prepare_units.py "<scratch>/extract.json" "<scratch>/units.json"
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cite-check/scripts/citation_ledger.py "<scratch>/extract.json" "<scratch>/ledger.json"
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cite-check/scripts/citation_ledger.py "<scratch>/extract.json" --compact > "<scratch>/ledger_args.json"
citation_ledger.py 会打印 instance_count / by_kind / unique_secondary_cites / unresolved_xrefs。简要查看 by_kind:如果 other 或 text 计数较大,意味着脚注使用了分割器无法识别的引用形式——这些实例仍会出现在账本中(标记为 unchecked / not-a-citation),因此报告保持诚实,但请在交付时说明。--compact 文件包含工作流需要的两个数组(instances,secondary)。
extract_cites.py 会打印摘要(footnote_count、body/footnote quote counts、unique_case_cite_count、supra_problem_candidates,以及来自形式层的 form_findings / form_short_form / form_typeface / formatting_available / case_name_candidates)。prepare_units.py 打印 unit_count / case_cites / supra_problems。读取 extract.json 以提取工作流需要的数组:unique_case_cites、supra_problem_candidates 以及整个 form_checks 块。
formatting_available: false 表示 md/纯文本草稿——短引形式仍会运行,但字体格式检查不会执行;请在交付时说明。如果出现 formatting_unaligned_footnotes,表示该数量的脚注未进行字体分析(其格式化文本与已验证解析器的文本不匹配,因此该层拒绝处理以避免偏移错误)——仅当计数非零时才提及。对于部分范围检查,将脚注范围解析为单位 ID:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/cite-check/scripts/prepare_units.py "<scratch>/extract.json" --footnotes 45-80
(打印 unit_ids——将它们作为 unitIds 传递)。
4. 启动工作流
Workflow({
scriptPath: "${CLAUDE_PLUGIN_ROOT}/skills/cite-check/cite-check.workflow.js",
args: {
skillsDir: "${CLAUDE_PLUGIN_ROOT}/skills", // 捆绑辅助脚本所在位置
extractPath: "<scratch>/extract.json"、 // 代理从此处读取单元和完整上下文
draftName: "<草稿文件名(不含扩展名)>",
footnoteCount: <extract.footnote_count>,
unitCount: <prepare.unit_count>,
caseCites: <extract.unique_case_cites>, // 数组
supraProblems: <extract.supra_problem_candidates>, // 数组
formChecks: <extract.form_checks>, // 字体格式 + 短引形式(整个块)
ledger: <ledger_args.instances>, // 每个引用实例(紧凑形式)
secondaryCites:<ledger_args.secondary>, // 唯一的未引用二手来源
unitIds: <null | [来自 --footnotes 的 ID]>、 // 仅部分范围检查时使用
deep: false,
zotero: true、 // 本次运行无可用 Zotero 服务器时设为 false
zoteroServer: "<来自预检 2.3>" // 例如“zotero-group”(5A)或“zotero”(个人)
}
})
流水线(除报告继承会话模型外,所有模型阶段均为 Sonnet):
- 分段/解析——分块处理;每个引用内容与它所引用的特定来源配对,遍历
Id.链找到其真实前驱(可能是两个脚注前的某个判例),并跟随supra note N重定向,使用预处理脚本组装的前驱/supra 上下文。U.S. Reports 的首页 pin cite 会被标记为可能的大纲,并路由到 LOC 合订本,由验证代理确认该段落实际位于哪个部分。 - 存在性验证——通过
check_cite.py验证所有唯一判例引用(CourtListener 每分钟限制 60 次);“未找到”的结果会通过网络升级验证(通常是 CL 对近期 U.S. Reports 卷的分页差距,而非错误引用),并提出卷号拼写建议。在同一轮次中,进行二手来源存在性验证:账本中的每个唯一未引用文章/书籍/法规/URL 会被定位(先是 Zotero,然后网络),并将其书目元数据与草稿的引用进行比较——每个代理处理 12 个,低努力程度,不阅读原文。在预算压力下……
[内容已截断以便翻译]