D-academic-agent — 学术证据核验工作区
- TypeScript
- Electron
- MCP
- ONNX / HuggingFace Transformers
- DeBERTa-NLI
- SQLite / better-sqlite3
- React
- AI SDK
- Vitest
- scite / Consensus APIs
- OAuth 2.1 PKCE
- Zod
一个本地优先的学术证据核验工作区,用确定性 NLI judge 对照来源证据审查论点与引用。它提供 Electron 桌面 app 与 MCP-capable core,外部 scite / Consensus 证据检索保持明确 opt-in。
一眼看懂
角色 独立开发者 — 架构、核心管线、Electron app、MCP server、评测框架
问题
大规模文献审查容易出错:LLM 会虚构 supports 关系;词汇 judge 无法识别矛盾,所谓 'supports' 毫无意义。每条 claim 都需要可溯源的定位器,而不是模型猜测。
解法
我做了一条本地优先流程:导入论文、检索证据、用确定性 NLI 判断论点,并把裁定、片段与定位器一起呈现。外部证据检索保持 opt-in,私有草稿与文献库默认不外发。
成果
317 个离线测试 · 42 条人工标注 gold set · 最新打包版本覆盖 macOS arm64 / x64 / universal
我的工作
- Draft Citation Audit — 逐句审查:每条引用解析到来源、判定(supports / weakly_supports / unsupported / contradicts / unclear),附原文片段、字符跨度定位器、置信度与建议改写;语料库反例会自动浮现
- Thesis Review — 论文拆解 → 检索 → 逐条判断 → 共识裁定(supported / contested / refuted / insufficient),附比例、决定性指数和支持/矛盾证据图
- 混合检索 — BM25 + 稠密向量(本地 ONNX:all-MiniLM / bge-small / nomic,角色感知)+ reciprocal-rank fusion;定位器从页面追踪到字符跨度
- 离线优先 judge — 默认使用确定性 DeBERTa-NLI(零外呼);OpenAI-compatible 与 Ollama 一键预设支持云端/本地 LLM judge
- 持久化文献库 — PDF 拖入(GROBID 章节感知 + unpdf fallback);本地 SQLite(Source / Chunk / vectors);切换 embedder 自动重索引
- 迭代 agent-loop checker — 顶层证据不确定时,审查次级候选并追踪步骤;failure mode 量化后作为诊断指标暴露
- 三轴评测:准确率(macro-F1 + 分类 + 混淆矩阵)、忠实度(answer_groundedness)和治理(policy_compliance:grounded-locator 率 + snippet-only 率 + 外发字符数)
- Provider ablation runner — 横向对比 hash+mock / all-MiniLM+NLI / agentic 在三轴上的表现;本地 vs 云端、单轮 vs agentic 的取舍,有数字为证
- 写作台(Writing Desk)— 贴入一个段落:拆分并分类每条 claim(因果 / 比较 / 方法等),对照文献库标记 needs_citation / overclaimed / contradicted,并给出更稳妥的改写;可针对每条论点,用一段可编辑、已告知的查询词查找外部证据
- 外部研究与引用健康(可选)— 在 app 内检索 scite / Consensus 以获取候选外部证据;并把 scite 的引文分析呈现为风险徽章(无明显问题 / 存在一些反驳 / 值得注意的问题 / 已撤稿或严重通告),附支持 / 反驳 / 提及计数与撤稿通告,覆盖三处:检索结果、已导入的文献库论文(在引用前抓出被撤稿的文献),以及一篇论文经 GROBID 解析出的参考文献
- 可复用的 MCP OAuth — 基于 MCP SDK 的 OAuth 2.1 PKCE + 动态客户端注册(DCR)浏览器登录,用于需 OAuth 的 MCP server:127.0.0.1 回环流程,token 只存于钥匙串,worker 除一个 bearer access token 外什么都拿不到
- 桌面发布管线 — 最新公开 release 已打包 macOS arm64 / x64 / universal,让审查者可直接以真实 app 方式检视,而不只依赖 source checkout
技术证据
- 共享核心,两个接口:纯 TS 引擎在 src/(ingest · retrieve · check · plan · eval · trace);MCP server 与 Electron app 是薄适配层,src/ 不 import Electron
- 确定性来自离线 mock(HashEmbedder / MockJudge / MockPlanner);模型加载测试加 env gate,CI 全程离线
- Gold set:42 条人工标注 claims,附 integrity-gated sources.lock.json;gold 必须人工标注,绝不让被测模型自评
- 可选的出站,且边界严格 — 外部调用仅在明确动作下触发,只送出查询词或公开 DOI,绝不送出语料库或草稿(唯一例外:写作台的证据检索,送出一段可编辑、已告知的查询词);OAuth token 留在钥匙串,只以一个 bearer 标量抵达 worker,并在每个边界被脱敏
- 密钥存 OS keychain(Electron safeStorage),不进 config/git/trace/日志;317 个测试覆盖 retrieval / check / eval / trace 与外部研究层,全程离线(live 网络冒烟测试以 env gate 隔离)
- Electron 打包覆盖最新 packaged release 的 macOS arm64 / x64 / universal 产物,同时保留非 macOS 目标的 source-run fallback
管线:计划 → 检索 → 判断 → 报告
核心循环是计划 → 检索 → 判断 → 报告,通过版本化 trace event 端到端可观测。论文被拆成子查询;混合检索在语料库中收集证据;judge 一次只依据检索到的片段判定一条 claim;报告则是一张带裁定的 grounded 证据图。
三个入口共享引擎:逐句的 Draft Citation Audit 把每条引用解析到来源并判断被引片段;Thesis Review 拆解论文,在支持/矛盾证据图上给出共识裁定;Writing Desk 把段落拆成 claim,对照文献库标记未引用和过度宣称之处,并能为其中任一条拉取外部证据。
为什么用确定性 NLI judge
裁定的强度取决于 judge。词汇 judge 无法识别矛盾,所有东西看起来都像 'supported'。确定性 DeBERTa-NLI judge 能揭示支持与反驳之间真实的分裂,而且完全离线。这一点是用 gold set 测出来的,不是假设。
共享核心,两个接口
一个纯 TypeScript 引擎(ingest · retrieve · check · plan · eval · trace)被两个薄适配层包裹:MCP server 与 Electron 桌面 app。铁律是 src/ 绝不 import Electron,因此核心保持无头可测。确定性来自离线 mock(HashEmbedder / MockJudge / MockPlanner),模型加载测试加 env gate,CI 全程离线。
外部研究:可选且受控
在设置中绑定 scite 或 Consensus,agent 就能伸出文献库之外,但只在你明确触发时执行。每次外部调用都需明确动作触发,且只送出已提交的查询词或公开 DOI;语料库绝不离开本机。在写作台(Writing Desk)中,由 claim 衍生的查询词可编辑,并会在送出前明示。scite 的引文分析以“引用健康”的形式返回:一个风险徽章,加上支持 / 反驳 / 提及计数与撤稿通告,呈现在检索结果、已导入论文(让你在引用前抓出撤稿文献),以及论文经 GROBID 解析出的参考文献上。Consensus 负责候选证据检索,不参与引用健康。
连接需 OAuth 的 MCP server 沿用同一套纪律:标准 OAuth 2.1 PKCE + 动态客户端注册浏览器流程(基于 MCP SDK),以 127.0.0.1 回环捕获回调,token 只存于系统钥匙串,worker 只会收到一个 bearer access token。外部结果始终被定位为候选,而不是 app 自己的裁定。
评测,以及一个诚实的失败模式
一个冻结的 gold set(42 条人工标注 claim,并对 sources lock 文件做完整性验证)驱动三轴报告:准确率(macro-F1 加混淆矩阵)、忠实度(answer groundedness)和治理(grounded-locator 率、snippet-only 率、外发字符总数)。provider-ablation runner 量化本地 vs 云端、单轮 vs agentic 的取舍。
agent-loop checker 有一个被测量到的失败模式:在 noisy judge 下会过度提交。它没有被藏起来,而是作为诊断指标暴露。全程保持诚实口径:这是 seed set,不是权威 benchmark。
界面截图