内容
# 利特姆兹
**面向生命科学研究代理的声明级验证层。**
粘贴 AI 代理的研究备忘录。利特姆兹将其分解为原子声明,检查每个声明的引文是否与主要文献相符,检索证据,借助 Claude 进行蕴涵判断,应用安全门控,并返回一个可审计的、按声明划分的交通灯判决:
- 🟢 **绿色 — 有据可查**:有证据支持,且引文清晰可解。
- 🟡 **黄色 — 需要审查**:无法验证、缺乏支持或安全关键。绝不直接通过。
- 🔴 **红色 — 被标记**:证据与声明相矛盾,或引文被伪造。
利特姆兹建立在一个原则之上 — **诚实的否定**。它绝不会将一个缺乏支持的声明伪装成通过,而且安全关键声明(剂量、适应症、分子靶点)绝不会自动通过,无论模型有多自信。
---
## 问题
AI 研究代理在生命科学领域表达流畅、快速,但可能自信地给出错误信息,这可能会带来昂贵的后果。它们可能引用不存在的论文,或引用不支持声明的真实论文,并以与有充分证据的事实相同的确定性陈述剂量或适应症。读者无法仅凭文本就能区分有据可查的声明和捏造的声明。在一个未经支持的剂量声明可能造成实际伤害的领域,仅凭“听起来正确”是不够的。
利特姆兹作为验证层,位于代理和读者之间:它逐一声明地重新推导代理所写内容是否真正得到其所指向的文献的支持,并在不支持时明确说明。
## 工作原理
流程(`packages/litmuz_core`)分为六个阶段。有一个可导入的核心,其名称与其他部分相同,因此 MCP 工具调用和网页提交会产生相同的判决。
```
备忘录
│
▼
1. 分解 ————————————— 将正文与参考文献分离(确定性);Claude 提取原子事实声明作为逐字逐句的子字符串;文本中的引文标记被解析为具体的标识符(PMID / DOI / PMCID)。备忘录中的空白被报告为“未声明的跨度”。
│
▼
2. 引文检查 ———————— 确定性,无 LLM。每个标识符都与权威元数据(PubMed / Crossref / PMC)进行解析。结果为以下之一:正常 · 元数据不匹配 · 伪造 · 未解决 · 未知,以及来源的撤回状态(活跃 · 已撤回 · 关注表达)。标题 / 作者 / 年份与记录进行匹配。
│
▼
3. 检索 ————————————— 提取证据段落:开放访问全文(PMC BioC)、被引摘要(PubMed E-utilities)或对未被引声明的关键词搜索。
│
▼
4. 蕴涵判断 ———————— Claude,每个声明 × 一个段落。可能仅回答以下内容:支持 / 矛盾 / 缺乏支持,并必须引用一段原始句子作为证据。有限重试;如果段落无法解析,则变为 judge_error,绝不抛出异常。按段落结果折叠最坏情况(矛盾 > 支持 > 缺乏支持)。
│
▼
5. 安全门控 + 严重性 ———————— 确定性,无 LLM,纯函数。判决 + 引文状态映射到诊断(D1–D5)、交通灯和路由标志。两个独立的门控保护绿色灯光(见下文)。
│
▼
6. 报告 ————————————— 可审计的逐声明记录:交通灯、诊断、判决 + 置信度、原始证据句子及其来源、每次引文检查、类别以及是否需要人类审查。持久且仅追加。
```
### 验证流程
<img width="1512" height="982" alt="Screenshot 2026-07-14 at 6 27 34 AM" src="https://github.com/user-attachments/assets/4060ff1f-8190-4701-b759-56ea2c07fb5c" />
### 人工审查
<img width="1512" height="982" alt="Screenshot 2026-07-14 at 6 27 39 AM" src="https://github.com/user-attachments/assets/6ee2f567-a772-4426-994a-2e6a04e276a6" />
### 两项保证
**诚实的否定**。声明仅在证据真正支持且引文清晰可解时才为绿色。黄色和红色声明绝不会悄悄升级,UI 也受到同样的规则约束:非绿色声明绝不能以“有据可查”的颜色或勾选图标渲染(有一个端到端的测试确保这一点)。伪造的引文会强制标记为红色,而无需调用判断模型。
**安全门控**。安全关键声明绝不会自动通过。关键的是,安全关键性在严重性阶段由独立的确定性词汇预言机(`safety.py`)重新推导 — 剂量、给药方案、适应症或分子靶点 — 因此上限绝不会依赖模型的自身类别标签。高召回率是故意的:假阳性只会将声明路由到人类。撤回的来源或关注表达同样绝不能为绿色。
**人工审查是唯一的例外**。审查员可以解决标记的声明;人类最终标签(支持 / 矛盾 / 缺乏支持)重新推导声明的灯光,并记录谁做出了决定。这是允许的唯一权限,可以提升声明,而流水线本身绝不会自动通过。
### 确定性和模型无关性
每个阈值、标签和模型 ID 都存在于一个 `Config`(`config.py`)中;状态字符串或数字在其他地方绝不硬编码。确定性阶段(引文检查、严重性/安全性)是纯函数 — 相同的输入,每次输出相同 — 而判断模型可通过单个配置值进行交换(默认为 `claude-opus-4-8`)。调用模型的阶段(分解、判断、分类)仅依赖于 `LlmClient` 协议,因此测试会注入确定性伪造品,而绝不会触及网络或模型。
### 提示注入防御
备忘录和每个检索到的段落都是不受信任的文本。在它们到达模型之前,它们被用围栏块包装,并用每个调用的新鲜、不可猜测的令牌(`prompt_safety.py`)标记,系统指示模型将围栏内的一切视为数据 — 因此声明或获取的摘要不能偷运指令。
## MCP 服务器
`apps/mcp` 提供了一个 [MCP](https://modelcontextprotocol.io) 服务器,因此 **任何 Claude 驱动的代理都可以调用验证作为工具** — 通过 stdio 进行本地开发,通过 Streamable HTTP 进行托管。它公开了四个工具:
| 工具 | 功能 |
| --- | --- |
| `verify_output` | 提交代理备忘录进行验证;返回用于轮询的 `job_id`。 |
| `verify_claim` | 同步验证单个声明;返回判决和 `requires_human_review`。 |
| `get_job_status` | 验证作业的状态和每阶段进度。 |
| `get_provenance` | 按 ID 获取存储的来源报告(仅限所有者)。 |
这些工具不包含任何逻辑 — 它们调用与 Web 应用程序相同的 `litmuz_core` 流水线,因此通过 MCP 进行自我检查的代理将获得与人类在 UI 中看到的确切判决。调用者提供的来源被视为附加证据:它们绝不会抑制确定性的引文检查或安全上限。
## 单体仓库架构
一个 `uv` 工作区,包含无框架的核心包,以及围绕它们的薄适配器。
```
packages/
litmuz_core 验证流水线 — 分解 · 引文检查 · 检索 · 判断 · 分类 · 严重性/安全性 · 报告 — 以及模式、配置和提示安全。无 Web、队列或数据库依赖。
litmuz_store PostgreSQL 持久性:编号 SQL 迁移、最小权限角色和仅追加的来源,通过 GRANT 强制执行。
litmuz_service 应用程序服务:提交和运行作业、每周配额和队列抽象(InMemoryQueue 用于开发/测试,SqsQueue 用于托管)。
apps/
api FastAPI REST 适配器(提交 / 轮询 / 报告 / 审查队列)。
worker 异步队列消费工作器,运行流水线以外的请求路径。
mcp 上述 MCP 服务器。
cli 本地运行器:离线、无网络演示和实时运行器,针对真实的 PubMed / Crossref / PMC 和判断模型。
web Next.js UI:作曲器、实时进度、逐声明报告和审查队列。
infra/
migrations 手动编写的、幂等的 SQL 模式迁移。
```
## 本地尝试
最快的验证保证的方法是离线 CLI 演示 — 确定性替身、无网络、无模型、无密钥:
```bash
uv sync
uv run python apps/cli/demo.py
```
它讲述并断言四种行为:伪造的引文强制红色,缺乏可定位支持的机械声明被黄色保留,安全关键剂量绝不能自动通过,以及判断模型通过单个配置值进行交换。要针对真实的文献和判断模型运行,请参见 `apps/cli/README.md`(需要 `ANTHROPIC_API_KEY`;基于数据库的 API、工作器和 MCP 接口还需要 PostgreSQL)。
## 使用 Claude 构建
为 **Built with Claude: Life Sciences** 黑客马拉松而构建。Claude 在流水线中进行语言级工作 — 将备忘录分解为原子声明,判断一个声明与一个段落的蕴涵关系,并对声明进行分类 — 而所有后果严重的门控(引文解析、安全上限、交通灯映射)都是确定性和模型无关的,因此判决是可审计的,否定保持诚实。
## License
MIT — 见 [LICENSE](LICENSE)。