构建库
用项目来源填充Zotero集合。两层防御机制防止虚构来源:(1) 在添加前,对每个候选来源进行真实来源验证;(2) 添加后,运行捆绑的确定性检查器——没有未经验证的项目被提交。所有全局权威规则(绝不编造引用;标记不确定性)始终适用。
0. 第零步——在执行任何其他操作前,先阅读整个库
在范围界定、搜索或提出任何候选来源之前,先导出目标库并仔细阅读全部内容:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/build-library/library_inventory.py --group <ID> # 或 --user
python3 ${CLAUDE_PLUGIN_ROOT}/skills/build-library/library_inventory.py --group <ID> --grep <terms>
每行一个顶级项目:年份、是否有PDF、作者、标题、出版物、项目键。一个100项目的库字符数远低于3k。没有预算理由跳过这一步,也没有替代方案。对于大型库(数千个项目),将完整导出保存到文件,并读取相关的--grep切片和整体结构——但协调者仍然负责检查,未经grep的切片不能证明项目不存在。
这是第零步,而不是去重筛选的一部分,因为了解库的内容会改变你去寻找什么。跳过这一步的运行会提出用户已经拥有的内容,将其描述为“新识别”,并对集合中的空白提出虚假声明。
四条硬性规则,每条都来自实际失败案例(2026-08-01,针对一个105项目的群组库):
- 绝不要将库存盘点或去重检查委托给子代理。 一个研究子代理报告该群组“只有列出的13个项目加上少量无关作品”。实际有105个。协调者相信了它,并创建了三个重复项。子代理适合用来查找候选来源;协调者负责了解库。
- 将子代理的否定性发现视为未经验证。 “我检查过,它不在那里”正是子代理容易出错的说法,因为在狭窄搜索内部,不存在性是无法证伪的。根据你自己的库存重新检查任何“不在库中”的声明。
- 每次添加都必须经过检查,包括后期添加。 三个重复项中的两个是在会话快结束时添加的,基于刚读完的一篇文章的脚注——在主流程之外,因此跳过了筛选。没有哪个候选来源太小或太晚而无需检查;对你已有的库存进行grep。
- 向下传递库存。 任何被要求查找来源的子代理都会获得实际列表(或过滤后的切片)粘贴到其提示中——绝不要只指示子代理自己去检查。
库也是证据。 当任务是“缺少什么”时,用户已经拥有的内容告诉你他们关心什么,并且常常会重新定义答案。在触发此规则的那次运行中,用户的库恰好包含了大量关于备忘录刚描述为未解决的问题的刑法学术著作——这将发现从“没有人写过这个”转变为“这两个文献互不引用”。阅读库存,了解它对项目的意义,而不仅仅是为了匹配重复项。
1. Zotero基础设施
zotero / zotero-group MCP 工具是只读的(适用于重复检查和搜索)。所有写入操作通过Zotero Web API v3进行,使用来自~/.claude.json中zotero条目的密钥(env.ZOTERO_API_KEY;已验证对个人库、<USER_ID>以及你使用的任何群组库具有写入权限)。
- 集合:
GET /users/<USER_ID>/collections?limit=100→ 找到目标键;如果用户想要一个新集合(先确认名称),则使用POST /collections创建。 - 重复检查——库存优先(§0),筛选候选列表(§2a),添加前最后把关:先读取整个库(§0);在任何验证或PDF工作之前对候选列表进行去重(§2a);然后在
POST /items之前保持一个轻量级的最终检查作为最后关口——这会捕获同一运行中之前添加的项目以及更早筛选遗漏的精确版本冲突。对于运行中的一次性查找,library_inventory.py --grep <terms>优于mcp__zotero__zotero_search_items,因为它搜索你已经持有的列表,而不是发出新的狭窄查询。 - 创建项目:
POST /users/<USER_ID>/items使用JSON数组(每次最多50个)。检查响应的success/failed映射。如果不确定字段,从GET https://api.zotero.org/items/new?itemType=book获取空白模板。 - 约定:插画师/“with”署名 →
contributor创建者加上extra注释(Zotero书籍没有插画师角色);编辑 →editor;填写版本的真正出版商/印记(例如Sentinel,而非“Penguin”);已验证时填写完整出版日期,否则填写年份;书籍总是填写ISBN,文章总是填写DOI。
1a. 法律评论文章元数据——修复Zotero连接器缺陷(作者,2026-07-16)
Claude通过Web API创建项目,因此可以正确获取这些信息,而用户的Zotero连接器经常出错。适用于添加的每篇法律评论文章;Hein(§4a)显示正确的数据,因此从那里获取字段。
- 开头的“The”/“A”/“An”: 将冠词保持在标题自然的前置位置——标题为
The Common Law,绝不要使用连接器排序倒置的Common Law, The。 - 年份 = 期号的年份,而不是卷册的跨度。 连接器经常抓取整个卷册的日期范围;使用具体期号的年份,Hein的引用会给出。
- 结尾的问号: 保留它——
Are Judges Political?,而不是Are Judges Political。 - 标题中的斜体: Zotero标题字段接受内联HTML——将案件名称(以及Bluebook中其他斜体术语)包裹在
<i>…</i>中,例如The Real Meaning of <i>Erie</i>或Rethinking <i>Chevron</i> Deference。Zotero会将这些渲染并导出为斜体(这就是用户原本需要手动添加的<i>case name</i>)。 - 从标题中删除附加的文章类型/研讨会标签——但在
extra中保留真正的Bluebook标识。 连接器将标识附加到标题上——Essay、Comment、Note、Foreword、Book Review、Symposium: <name>等。只保留文章的真正标题,因为它出现在文章开头/Bluebook引用中。如果不确定结尾文本是真正的副标题还是附加标签,请检查首页。- 学生笔记和评论(以及Bluebook在作者和标题之间打印的其他标识——
Recent Development、Essay、Foreword、Book Review等):不要只丢弃标签。将其记录在项目的extra字段中,作为一个genre:行——学生笔记用genre: Note,评论用genre: Comment(首字母大写;样式会自动将其首字母大写,但存储时保持干净)。然后Bluebook样式会将其插入正确的位置(Jane Doe, Note, <i>Title</i>, 120 Harv. L. Rev. 1627 (2007))。按照上述方法从标题中删除它并添加genre:行——对于这些文章,两者必须同时进行。 - 匿名学生笔记(无作者):将创建者字段留空,并仍然设置
genre: Note。如果extra字段已经携带了其他行(例如Verified-URL:),在新的一行上添加genre: Note——不要覆盖它们。
- 学生笔记和评论(以及Bluebook在作者和标题之间打印的其他标识——
- 后缀(“Jr.”、“III”、“Sr.”): Zotero没有后缀字段——将后缀编码到名字字段中,附加在给定名称后,用逗号分隔且不加空格(姓氏只保留姓氏;绝不要将后缀附加到姓氏或添加第二个作者)。后缀前的
!控制是否在后缀前渲染逗号(citeproc-js标记,根据作者):- “John Smith Jr.”(无逗号)→
firstName: "John,Jr.",lastName: "Smith" - “John Smith, Jr.”(有逗号)→
firstName: "John,!Jr.",lastName: "Smith"给定的名称(包括中间名/首字母)放在逗号之前(例如John B.,Jr.)。数字/其他后缀同理——John,IIIvsJohn,!III,John,Sr.等。匹配作者/来源使用的逗号形式。
- “John Smith Jr.”(无逗号)→
- 期刊缩写(Bluebook,混合大小写): 始终用期刊的Bluebook缩写(表T13)填充Zotero的
journalAbbreviation字段(UI中的“Journal Abbr”),使用正常的混合大小写——Colum. L. Rev.、Harv. L. Rev.、Nw. U. L. Rev.、J. Sup. Ct. Hist.、Sup. Ct. Rev.——而非全大写。引用样式会自动应用小体大写字母,因此存储Colum. L. Rev.,永远不要用COLUM. L. REV.。全名保留在publicationTitle中;缩写放在journalAbbreviation中。Hein的引用显示将其渲染为大写/小体大写字母(“62 COLUM. L. REV. 633”)——使用其措辞但转换为混合大小写;对于不熟悉的期刊,验证其T13形式,而不是猜测。对于在线配套/增刊,按照Bluebook缩写增刊名称——Forum→F.(例如Harv. L. Rev. F.),而Colloquy没有简短形式(Nw. U. L. Rev. Colloquy)。 已知名称特定的例外(不要“纠正”这些):California Law Review→Calif. L. Rev.(不是Cal.,即使加利福尼亚在报告人和其他地方是Cal.)。Calif.的特例特定于该期刊——对比**Southern California Law Review→S. Cal. L. Rev.**,其中California使用通常的Cal.。
2. 查找候选来源
如果请求是开放式的,与用户一起界定范围(哪些大法官/学者/时代;只限书籍还是也包括文章;商业书籍与案例书/专著——在报告中标记判断性决策,而不是默默决定)。在§0之后执行此操作,绝不要在之前执行——库存告诉你用户已经拥有什么,这会改变值得寻找的内容。
并行搜索:WebSearch;Open Library(search.json?title=&author=优于简单的q=);Wikipedia信息框(/w/index.php?title=<page>&action=raw,grep信息框)获取知名书籍的出版商/日期/ISBN;出版商页面。对于法律学术资源,HeinOnline(§4)。Google Books API是一个共享的匿名配额,经常耗尽——将其视为额外资源,而非主干。
2a. 根据库筛选候选来源
手握库存,在进入昂贵的验证→下载→添加流程(§3+)之前对候选列表进行去重。验证元数据和拉取PDF(Hein curl链、attach_pdf.py)是运行中成本最高的部分;为库中已有的来源做这些完全是浪费。
- 根据库存将每个候选来源与作者+年份优先,标题其次进行匹配——标题变化多端(副标题、“Part I”、排序倒置的冠词、HTML实体),但作者加年份的冲突是进一步查看的可靠触发条件。
- 丢弃明确匹配项(相同作品已存在)——无需验证或下载它们。
- 标记,而非静默丢弃,模糊的近似匹配(相同作者,相似但不完全相同的标题;不同的版本/印刷;文章与其在线配套)。你有时只在验证期间才能确定精确版本,因此将真正的可能项向前推进并在报告中注明,而不是猜测将其排除。
只有幸存者进入§3。§1中的添加前最后关口然后会捕获运行内重复项和精确版本冲突。
如果你确实创建了重复项,请在删除前合并。 预先存在的项目可能包含用户的PDF、注释、标签和集合成员关系;你的新项目可能包含更好的元数据。将好的字段PATCH到现有项目上,然后DELETE你的项目。绝不要删除更老的记录。(在2026-08-01的清理中,这恢复了一个真实缺陷:用户的Kahan项目的issue字段中存有字符串“10.2307”,且没有DOI。)
工作流规则(作者,2026-07-16):通过HeinOnline解析每一条法律评论来源。 Hein是干净的自主动路径(§4a:IP认证,无机器人墙,无门禁)。Hein不收录且没有开放存储库提供的来源将按此报告(§5),而不是在无验证记录的情况下添加。
3. 验证→添加→确定性检查(不可协商)
添加前:你写入的每个字段必须来自获取的来源——Open Library版本记录(/isbn/<isbn>.json)、出版商页面、Wikipedia信息框、书商列表。绝不要凭记忆编写看似合理的ISBN/日期/出版商。小心损坏的OL版本记录(一个错误的书籍绑定了一个ISBN的情况会发生);根据第二个来源交叉检查任何意外情况。
添加后,在Zotero中正好落地的项目上运行捆绑的检查器:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/build-library/check_source.py --collection <KEY> # 整个集合
python3 ${CLAUDE_PLUGIN_ROOT}/skills/build-library/check_source.py --keys K1,K2,K3 # 仅新项目
它会通过五个独立目录(Open Library版本+搜索索引、Google Books、Harvard Library、Library of Congress)解析每个项目的ISBN,或通过Crossref解析其DOI,并进行标题/作者/年份的规范化字符串比较。判定结果:PASS、FAIL(已解析但不匹配)、UNRESOLVED(在任何检查的目录中未找到标识符——提示需要重新验证,而非虚构证明:目录不完整且有时宕机)、BAD_ISBN(校验和)、NO_IDENTIFIER。--stdin接受Zotero格式项目的JSON数组,用于在添加前预检查候选项目。
你添加的每个项目必须最终得到PASS。 针对非PASS的操作手册:
FAIL仅来自一个来源→通常是目录损坏;检查器已经尝试了其他来源,因此持续的FAIL通常意味着你的数据错误——根据打开的权威页面重新验证并PATCH项目(发送If-Unmodified-Since-Version: <item version>)。如果打开的页面确认了你的数据,将其记录为Verified-URL:并在报告中说明,而不是“修复”正确的元数据以匹配目录。UNRESOLVED/对目录来说太新(出版约<1年的书籍)→添加一个可获取的权威页面作为extra标注(Verified-URL: <url>),然后在报告中说明。UNRESOLVED不是PASS;它只是表明要将其视为未经验证,直到你将其提升。BAD_ISBN→ISBN错误。检查你使用的来源;可能你将其从另一个字段复制了,字符串中有隐藏空格或OCR错误。修复并PATCH。NO_IDENTIFIER→项目没有ISBN或DOI。验证添加操作是否成功(HeinOnline条目在Zotero连接器的自动链接后应该有一个DOI)。如果没有,添加一个。
对于一次执行多个添加的运行,使用check_source.py --log-pass将PASS项静默记录到stderr而不显示输出文本,或使用--summary仅显示非PASS结果。这些在捆绑的介绍性消息中有记录。
4. HeinOnline
越过Zotero连接器——直接通过cURL(或用于Zotero元数据的Pythonrequests)使用HeinOnline的HTML接口。对于法律评论文章,这是权威路径,也是唯一允许不通过Zotero连接器自动处理的路径。
4a. 使用HeinOnline查找和验证法律评论文章
HeinOnline托管美国法律评论、法律评论、法律期刊以及相关学术资源。其接口是HTML/表单驱动的,没有公共API密钥,但IP认证通过连接的大学代理工作。对于通过Zotero添加的文章,这是获取干净、已验证的元数据的最可靠方式。
URL模式(2026-07-16验证):
- 基于引用的搜索:
/HOL/Page?handle=hein.journals/<journal-code><volume>&div=<first-page> - 页面级链接:Hein通过页面范围提供文章——从项目第一页的div(通常是文章标题出现的页面)开始。
- DOI解析:许多Hein文章现在也有DOI——尝试
/HOL/Page?handle=hein.journals/<journal-code><volume>&div=<first-page>&id=<first-page>或直接搜索。
查找文章的工作流程:
- 期刊代码:使用Hein的
/HOL/Journals索引找到标准缩写。常见代码:colyr(Columbia Law Review)、hlr(Harvard Law Review)、ylr(Yale Law Journal)、vclr(University of Virginia Law Review)、uclr(University of Chicago Law Review)、michlr(Michigan Law Review)、stanlr(Stanford Law Review)、nylr(New York University Law Review)、calr(California Law Review)、geolj(Georgetown Law Journal)。 - 卷册和首页:从被引用的文章或搜索结果中获取。URL:
hein.journals/<代码><卷册>&div=<首页> - 获取元数据:去除PDF周围的主体HTML。Hein的文章页包含标题、作者、卷册、期号、年份、ISBN和DOI。Zotero的连接器从格式良好的HTML自动提取标签和字段;由于我们通过API工作,我们直接提取字段(见§1a的修复)。
- 通过搜索URL跨多个卷册查找文章:
/HOL/Search?q=<terms>&collection=journals&type=0&searchtype=advanced。结果以易于解析的HTML呈现。 - 对于已知引用,直接导航到div级别URL,然后从页面提取元数据(标题、作者、卷册、期号、年份、DOI、首页)。记住标记时间的
!表示法(§1a)用于后缀。 - 验证:一旦你有了元数据,以JSON格式使用Open Library或Crossref解析DOI/ISBN以进行交叉检查。
添加: 对于Hein文章,设置url字段为https://heinonline.org/HOL/Page?handle=hein.journals/<卷册>&div=<首页>。从Hein页面填写元数据到你的Zotero项目结构中。遵循§1a的格式规则。
5. 报告
在添加所有来源后,而不是之前,写报告。报告要全面但结构化:
- 已添加:列出你添加的每个项目,包括其Zotero键和检查状态(始终为PASS)。标注任何错过的机会或未采用的来源。
- 被拒绝:列出被拒绝的来源(重复项、未验证、UNRESOLVED且未添加)。显示来源和理由。
- 未找到:对某些搜索词未找到的来源。
- 声明:对现有收藏的主张(该收藏现在有X个项目关于Y主题,覆盖Z年)以及对你未添加的内容的诚实评估。
- 不确定性:标注任何判断性决定或关于准确性的不确定性。
报告不应重复元数据;它应提供有关搜索过程、你做出的决定以及添加内容的完整性说明的叙述。从项目日志(check_source.py --log-pass)附上统计数据。
6. 成本与预算
此技能会进行大量API调用和降价/搜索。预算:
- 在法律评论搜索期间,HeinOnline可能会连续调用15-30次(主要是元数据提取,非PDF)。
- 验证步骤向多个来源发出请求(Open Library、Google Books、Harvard、LC)。
- 编号:期望一个中等搜索/添加会话使用8-15个API配额,每个配额与查询长度和网络延迟成正比。
在Hein上大范围搜索(特别是对所有期刊的未索引术语搜索)成本加权,因为Hein的分页搜索结果需要多次获取。
7. 安全与错误处理
- 始终使用
If-Unmodified-Since-Version头进行Zotero PATCH操作,以确保以原子方式更新。 - 处理网络超时:重试3次,指数退避。如果仍失败,记录错误并继续。
- 保留操作日志:保持运行中会话的记录以供审计(每个添加了什么、为什么、具有什么键)。
- 在将标签/集合关系附加到项目之前,验证Zotero列表中存在集合键。
- 如果Zotero的版本头(
last-modified-version)发生变化,始终获取最新版本以协调冲突。 - 验证Zotero键盘映射:在尝试写入之前,从
/items/new确认项目字段名称。 - 所有写入操作(创建、更新、删除)在发送前使用正则表达式验证字段值(检查ISBN校验和、DOI格式、日期合理性)。
- 错误处理必须关闭响应体以及
success/failed映射,以正确处理只有部分成功的请求。 - 项目实施:每个
POST /items调用最多50个;对于包含数百个条目的书目,分块进行。