Skip to main content
Glama
README.md
# dsh-research-check

**在交论文、交文档、交数据之前,先让它替你检查一遍。**

它专门抓那种"明明检查过、却还是被抓出来"的低级错误:数字前后不一致、改了标题忘了改正文、
文档属性里藏着你的名字和学校、少交了一个文件、页数超了一页。

适用于任何"有要求、要交付"的活儿:论文投稿、软件交付验收、数据交付、标书申报、技术文档。
**它不是写手,是校对员**——不帮你写内容,只帮你把该对的地方对上。

```bash
dsh plugin --profile web add dsh-research-check      # 装进 DeepSeek Harness
```

> 也支持 MCP:Claude Code、Codex、Cursor 等任何支持 MCP 的工具都能用(见 [第 3 步](#第-3-步用其他-ai-工具claude-code--codex--cursor))。

---

## 目录

- [它能帮你什么(先看这个)](#它能帮你什么先看这个)
- [三步用起来](#三步用起来)
- [四个工具的用法](#四个工具的用法)
- [支持哪些交付物](#支持哪些交付物)
- [它不会做什么](#它不会做什么)
- [规格文件与台账](#规格文件与台账)
- [验证与开发](#验证与开发)
- [常见问题](#常见问题)

---

## 它能帮你什么(先看这个)

下面每一条,都是**真实发生过**、并且被这个工具抓出来的问题:

| 你可能犯的错 | 具体长这样 | 后果 |
|---|---|---|
| **数字改了正文没改** | 图表里写 4761,正文还写着 4883 | 评审一眼看出你没核对 |
| **两个口径混用** | 一张表用"计划量"算,另一张用"实际量"算,两个总数对不上 | 被认为数据不可信 |
| **比率说错** | 写"两个方案都只有 1/565",其实一个 565、一个 583 | 严谨性打折 |
| **旧版本残留** | 摘要写 1536.4,定稿是 1521.6(改稿时漏了摘要) | 摘要与正文矛盾 |
| **文档属性泄露身份** | 你的 Excel/Word 里存着账号名、学校名(**肉眼看不见**) | **匿名评审直接违规** |
| **页数超限** | 要求正文 ≤30 页,你交了 31 页 | 可能直接被拒 |
| **清单对不上** | 论文附录列 11 个文件,压缩包里实际 12 个 | **被视为材料不实** |
| **交付物里有报错** | 交付日志里留着 `Traceback`、`ERROR:` | 显得没测过 |
| **数据不合规** | 交付数据里混进了手机号字段 | 合规风险 |
| **占位符没删** | 对外文档里留着 `TODO`、`待补充` | 非常尴尬 |

**核心价值一句话**:这些错误自己很难查(尤其是元数据里的身份信息——**你根本看不见**),
但被发现一次,代价往往是一整轮返工甚至取消资格。让工具在交之前替你过一遍。

---

## 三步用起来

### 第 1 步:把"要求"喂给它(只做一次)

你手上一定有一份要求文件:赛会格式规范、期刊投稿须知、甲方验收标准、导师给的模板。
把这个文件交给它,它会变成一份**能自动核对的清单**:

```bash
python python/spec_build.py --requirements 格式规范.doc --out specs/我的要求.json \
    --profile academic --name "竞赛论文格式规范"
```

它会告诉你提取出了多少条规则,**每条规则都带出处**(引用要求文件里的原话),例如:

```json
{
  "id": "doc.body_pages",
  "title": "正文页数上限",
  "check": "max_pages",
  "severity": "hard",
  "params": { "limit": 30 },
  "source": "…论文从第四页开始是正文内容(不要目录,不超过30页)…"
}
```

⚠️ **然后花两分钟人工看一眼**:确认数字对不对(30 页?20 MB?)。
有些规则需要你填参数(比如"必须提交哪些文件"),空着的规则它会如实报"没检查"——**不会假装通过**。

### 第 2 步:交之前,一句命令自查

```bash
python python/check_spec.py --spec specs/我的要求.json --root . \
    --pdf 我的论文.pdf --files 交付文件1.xlsx 交付文件2.docx --archive 材料.zip
```

输出是中文表格,通过的打 ✓,违反的标 ✗ 并告诉你**为什么**(引用要求原文):

```
[ok  ] hard  正文页数上限          正文 30 页 / 上限 30 页
[FAIL] hard  文档属性不得含身份信息   属性命中:{'creator': '张三'}
[ok  ] soft  图表必须被引用          全部被引用
```

### 第 3 步:用其他 AI 工具(Claude Code / Codex / Cursor)

如果你不用 DeepSeek Harness,可以用 MCP 接入——在工具的 MCP 配置里加一段:

```json
{
  "transport": "stdio",
  "serverName": "research-check",
  "command": "node",
  "args": ["<插件目录>/mcp/server.mjs"]
}
```

之后你的 AI 就能调用 `spec_build` / `spec_check` / `audit` / `numbers` / `ledger` 五个能力。

> 📖 **想跟着做一遍?** 完整教程(从零开始、每步都有命令与预期输出):
> [中文](docs/TUTORIAL.zh.md) · [English](docs/TUTORIAL.md)

---

## 四个工具的用法

装好之后,你**用大白话让 AI 去用**就行,不用背命令:

| 你可以这样说 | 实际调用的工具 | 它会做什么 |
|---|---|---|
| "帮我按格式要求检查一下论文" | `research_spec` | 按要求清单逐条核对 |
| "检查我的论文和压缩包,交之前看一眼" | `research_audit` | 查页数、空白页、图表引用、身份信息、清单一致性 |
| "我改了结果,帮我核对论文里的数字有没有漏改" | `research_numbers` | 找出前后矛盾的数字 |
| "把这个数字和它的来源记下来,以后好核对" | `research_ledger` | 建立"数字—出处"台账 |

**推荐的工作流**(顺序很重要):

```
① 先建台账,记下关键数字和它们的来源
② 跑程序,刷新台账里的数值
③ 改正文和图表(两个地方都要改!)
④ 核对:台账里的数字在正文里还找得到吗?
⑤ 交之前跑一遍格式检查
```

命令行直接用法:

```bash
# 建台账:从文稿里自动找出带单位的数字,登记成候选
node lib/ledger-cli.js teach --ledger paper-ledger.json --paper 论文.tex \
    --unit-filter 万元 --min-abs 100

# 手动登记一条(把"这个数字从哪来"钉住)
node lib/ledger-cli.js add --ledger paper-ledger.json \
    --key q3.total_cost --value 1521.6 --unit 万元 \
    --source code/q3.py --anchor 全年费用

# 核对:台账里的值还在文稿里吗?(--near 会额外提示"同位置的相似数值")
node lib/ledger-cli.js verify --ledger paper-ledger.json --paper 论文.pdf --near

# 体检:页数、摘要、空白页、身份信息、压缩包清单
python python/audit_paper.py --paper 论文.pdf --archives 材料.zip
```

---

## 支持哪些交付物

同一个工具,换一份要求文件,就能用在完全不同的场景。`--profile` 决定用哪套规则:

| 场景 | `--profile` | 它重点查什么 |
|---|---|---|
| 论文 / 学位论文 / 期刊投稿 | `academic` | 页数、摘要单页、图表引用、页边距、行距字号 |
| 软件交付 / 项目验收 / 发版 | `software` | 必备文件、LICENSE、变更记录、日志里的报错 |
| 数据交付 / 数据集 | `dataset` | 字段齐备、样本量、隐私字段、数据字典 |
| 说明书 / 技术文档 / 手册 | `docs` | 段落长度、版本号、联系方式、TODO 残留 |
| 标书 / 申报书 | `tender` | 章节齐备、逐条响应 |
| 任何交付物 | `generic` | 体积、命名、身份信息、清单一致性、占位符 |

**共 37 个可判定的检查项**。查看每类的规则数:

```bash
python python/spec_build.py --list-profiles
```

**跨场景实测**(`python tests/test_generality.py`,自动化跑,每次发布前都会验证):

| 交付物 | 干净版本 | 故意做坏的版本 → 被抓住的问题 |
|---|---|---|
| 软件包 | 0 错误 | 缺 `README.md`;日志里有 `Traceback` |
| 数据集 | 0 错误 | 缺字段;样本量不足;混入手机号 |
| 技术文档 | 0 错误 | 段落超长;`TODO` 没删;Word 属性里有作者名 |

---

## 它不会做什么

先说清楚,免得你误会:

- ❌ **不帮你写内容**——它只核对,不生成
- ❌ **不判断两个数字谁对**——它会告诉你"这里有两个互相矛盾的数"以及它们在哪,
  由你来决定改哪个(只有你知道哪个是新的)
- ❌ **不重新推导公式**——数字是"比对"而不是"重算"。**如果程序算错了、正文照抄了这个错值,
  两边一致就会通过**。所以对结论起决定作用的数字,仍然要自己独立复算
- ❌ **不评价方法好坏、不做法律意见**

---

## 规格文件与台账

### 规格(spec):把要求变成清单

```json
{
  "id": "doc.body_pages",
  "title": "正文页数上限",
  "check": "max_pages",
  "scope": "body",
  "severity": "hard",
  "params": { "limit": 30, "appendix_marker": ["附录"] },
  "why": "页数是评委最先感知的硬约束",
  "source": "…要求文件里的原话…"
}
```

- `check`:用哪个检查项,必须是 37 个之一,或 `manual`(人工清单)
- `scope`:`document`(整份)/ `body`(附录前)/ `per-file` / `bundle`
- `severity`:`hard` 违反即报错 / `soft` 只提醒 / `info` 只记录

**三条纪律**(决定了它不会变成"只会说通过"的花架子):

1. **每条规则必须指定检查项**,否则加载就报错。机器判断不了的(比如"测试是否覆盖需求")
   写成 `manual`,进入人工清单——**绝不假装能查**。
2. **没检查 = 没检查**:缺输入、缺参数时如实报 `skipped`,**绝不混进"通过"里**。
3. **每条判定都带出处**,你可以拿着它去答辩、回复审稿意见。

### 台账(ledger):把数字和出处钉在一起

```json
{
  "entries": [
    {
      "key": "q3.total_cost",
      "value": 1521.6,
      "unit": "万元",
      "source": "code/q3.py",
      "anchor": "全年费用"
    }
  ]
}
```

- `key` 用**语义名**(`q3.total_cost`),不要用数字本身当 key——程序重跑后只改 `value`,正文不用动
- `unit` 单独放,避免把「1.5 万元」和「15000 元」当成两件不同的事
- `anchor` 是正文里的固定短语,用来定位并检查"同一位置是否出现了量级相近的异值"

---

## 验证与开发

九段验证,全部可以在本地复现:

```bash
npm test                 # 语法 + 上架清单 + 打包清单 + 契约 + MCP + 规格 + 跨领域
npm run test:generality  # 跨场景(软件/数据/文档,含"故意做坏"的负例)
npm run test:pack        # 检查 npm 实际会打包什么
npm run mcp:smoke        # MCP 协议层(真实报文)
npm run boot             # 真启动一个临时实例,确认不会把 DSH 启动搞崩
```

关键设计:**每个功能都配了"负例"**——故意写错的东西必须被抓出来。
只会说"通过"的检查器是装饰品,所以每个功能都有对应的"必须抓住"测试。

在 CI(`.github/workflows/verify.yml`)上跑 Node 22/24 + Python 3.13。

---

## 常见问题

**Q:装完没反应 / 工具不出现?**
A:DSH 插件在**启动时**注册,需要重启 profile。MCP 方式则要重启你的 AI 工具。

**Q:报 `NO_PYTHON`?**
A:检查核心需要 Python 3.10+。装了还是报,就设环境变量 `DSH_RESEARCH_PYTHON` 指向解释器路径。

**Q:报 `skipped` 是什么意思?**
A:**"这条我没查"**——通常缺输入(比如没给 PDF、没给文件清单)或规则参数没填。
它**不等于通过**,请补齐输入或转人工确认。

**Q:中文输出乱码?**
A:1.4.1 起已修复(脚本强制 UTF-8 输出)。旧版本请升级。

**Q:会不会把我的论文上传到什么地方?**
A:不会。所有检查都在你本机命令行完成,插件不联网、不收集任何数据。

**Q:能检查 Word 文档吗?**
A:能。段落长度、身份元数据类规则直接读 `.docx`;页数、摘要、图表引用类规则需要 PDF
(Word 转 PDF 后即可)。

**Q:我的要求文件格式很乱(表格、编号、中英混排)能识别吗?**
A:能解析 `.doc` / `.docx` / `.md` / `.txt`。它用的是**模板匹配**而不是自由发挥——
只收录要求文件里明确出现、且机器能判定的条款,其余进入"需要人工确认"清单,不会瞎猜。

---

## License

MIT

---

## 由来

最初是给一篇真实的竞赛论文写检查脚本,写着发现**每一个检查都对应一个真实犯过的错**,
而这类错误与学科无关——凡是"数据 → 图表 → 结论"的活儿都会犯。
于是把"要求"抽象成规格、把"数字"绑回台账、把检查项做成词汇表,
让它能用在论文、文档、软件、数据、标书五类交付上。