Skip to main content
Glama

kaoyan-matrix · 考研数学真题定位 MCP

给一段考研数学题干,返回它是哪一年、数学几、第几题。

站点本身没有搜索接口,题干只散落在 69 张按年份和科目组织的试卷页里。本工具把这一整套试卷抓成本地索引,查询在本地完成,并把带 LaTeX 的原文与凭记忆手打的纯文本折叠成同一种可比形式。

实测精度(见下文「精度数据」):320 次真实输入查询中,目标题出现在前 5 个候选的比例为 100%,首位命中或落在同题孪生上的比例为 99.7%。

功能

  • 按题干反查出处:年份、科目(数学一/二/三)、题号、题型、分值、详情页链接。

  • 四种输入形态都可用:站点原文(含 LaTeX)、手打纯文本(λ、∫、x^2)、连 A/B/C/D 选项一起粘贴、只记得前半句。

  • 诚实的不确定标注:每条候选带 match(exact / fuzzy / weak)与 0—1 的 confidence;题干太短或没有可靠匹配时明确说"未找到",给出候选但不冒充结论。

  • 离线可用:首次查询联网建库(70 个请求:1 个清单页 + 69 张试卷页,实测 5—20 秒,取决于网络)。索引默认 7 天内直接复用、不联网;超期的首次查询会尝试重建,重建失败则沿用旧索引并在结果里标 degraded: true。

  • 改版防护:逐卷核对解析题量与站点声明题量,任一卷不符即中止建库并保留旧索引。

Related MCP server: University Content MCP

环境要求

  • Python 3.11+

  • mcp >= 2.2, < 3(其余依赖全部来自 Python 标准库)

pip install "mcp>=2.2,<3"

安装

git clone <本仓库地址>
cd kaoyan-matrix
python server.py        # 以 stdio 启动 MCP 服务

接入 MCP 客户端

在客户端的 MCP 配置里加入(Claude Code、Codex、opencode、Cursor 等 stdio 客户端通用):

{
  "mcpServers": {
    "kaoyan-matrix": {
      "command": "python",
      "args": ["/绝对路径/kaoyan-matrix/server.py"]
    }
  }
}

用法

find_problem(stem, subject=None, year=None, top_k=3)
refresh_index()

stem 为题干文本;subject 可选「数学一/二/三」缩小范围;year 可选限定年份。

输出示例(查询为 2024 年一道指数分布的选择题):

{
  "verdict": "题干精确匹配到 2 道题(同一道题常同时出现在数学一/二/三中,需一并参考):2024 年数学一第10题、2024 年数学三第10题",
  "reason": "ok",
  "hits": [
    {"year": 2024, "subject": "数学一", "num": 10, "type": "选择题", "score": "5分",
     "url": "https://mathabc.xyz/kaoyan/problem/458/",
     "match": "exact", "confidence": 1.0, "ratio": 1.0,
     "stem_preview": "题目原文前 90 字,超出部分以省略号收尾…"}
  ],
  "index": {"built_at": 1793958624, "records": 1569, "pages": 69,
            "stale": false, "degraded": false}
}

reason 取 ok / too_short(题干信息量不足,拒答)/ no_match(无可靠匹配,候选仅供参考)。

find_problem 返回五个顶层字段:

字段

说明

verdict

一句话结论,规则模板拼接,无命中时明确写"未找到可靠匹配"

reason

ok / too_short / no_match

query_len

查询归一化后的字符数,用于判断是不是题干太短导致拒答

hits

候选列表,每项含 year subject num type score url match confidence ratio stem_preview

index

索引元数据:built_at records pages stale degraded

top_k 会被钳制到 1—10;分数并列的同题孪生不受 top_k 截断影响,会一并返回。

refresh_index() 返回 records / pages / built_at / seconds / cache / index_version。

精度数据

标定脚本 tests/calibrate.py 从索引随机抽 80 道题,每题生成四种真实输入形态(原文、转纯文本、连选项、前半句),共 320 次查询;另造 30 条跨题拼接的假题干做反例。随机种子 20261106,索引 1569 条,全部 69 卷解析条数与站点声明逐卷相符。

指标

值

含义

recall@5

1.000

目标题出现在前 5 候选内

top1 或同题孪生

0.997

首位即答案,或首位是同一道题在另一张卷上的副本

exact 标签准确率

0.959

被标为精确命中的候选中确实是目标或其孪生的比例

目标被判为精确命中

1.000

四种输入形态下,目标本身都被判 exact

假题干误标 exact

0 / 30

拼接出的不存在的题不会被判成精确命中

测量环境:Windows 11 26100 / Python 3.13.9 / Intel64 Family 6 Model 183。基线是同一份索引上的自身命中,未与其他检索方案对比——站点没有搜索接口可供对照,因此这里只报告本工具的绝对表现。

"同题孪生"是语料本身的性质:1569 道题里有 583 道(37.2%)的归一化文本与另一张卷的题目完全相同——数学一/二/三大量共用同一道题。此时首位落在孪生题上,返回结果依然正确,因此指标以"目标或孪生"计。

检索延迟为百毫秒量级,且同机重复采样就会明显波动:tests/bench.py 每次跑两轮采样,实测分布在约 110—220 ms 之间,跨环境复测到过 274 ms。因此这里只给量级,具体数字请在你的机器上跑该脚本,它会同时打印测量环境。在 1569 条这个规模下,倒排索引和向量检索换不到可感知的收益,所以没有引入;题目总量增长到十倍量级时应重新评估该取舍。

工作原理

关键在归一化。站点题干形如 $\displaystyle\int_{0}^{x} \mathrm{e}^{\cos t} \mathrm{~d} t$,用户手打形如 ∫₀ˣ e^cos t dt。不做命令映射时,同一道真题的匹配分会退化到与错误候选不可分(实测 0.491 对 0.216);映射后能精确包含。

  1. normalize.py:NFKC 折叠全角与上下标 → 展开 \frac{a}{b} → 删除 \begin{...}/\end{...} 环境声明 → 按 222 项命令映射表(其中 151 项映射到非空内容,其余为直接丢弃的装饰命令)还原符号与希腊字母、同时剥掉 \displaystyle/\mathrm 这类装饰 → 删除定界符、标点、运算符与空白 → 小写 → 再迭代到不动点。

    最后一步是必需项:NFKC 会把部分符号继续分解(∬→∫∫、∭→∫∫∫、…→...)。只归一化一次时,语料里存的是单字符 ∭,而用户手打的三个 ∫ 归一化后是 ∫∫∫,两者永远无法精确包含,三重积分类题目会漏命中。改为不动点后,标定里「目标被判为精确命中」从 0.991 升到 1.000。

  2. match.py:score = 4×开头命中 + 2×精确命中 + 覆盖度 + 0.5×相似度。开头命中单独加权,因为题型相同的题目常有完全相同的开头短句,同一句开头能落进几十道题的题干里;精确命中还要求查询覆盖该题干长度的 50% 以上,否则 exact 标签的准确率从 0.96 掉到 0.60。

  3. index.py:从 matrix 页链接发现试卷清单(不硬编码年份,站点新增年份后无需改代码),并发抓取、逐卷核对题量、原子写缓存。

配置项

环境变量

作用

默认

KAOYAN_MATRIX_CACHE

覆盖索引缓存文件路径

<代码所在目录>/.cache/index.json(随 server.py 的位置走,与启动时的当前工作目录无关)

缓存目录建议加入 .gitignore:其中是站点题目的全文快照,属运行时数据。

测试与验证

python -m pytest tests/ -q        # 59 个测试函数,参数化展开后 73 个用例
python tests/bench.py             # 实测检索延迟、建库耗时与测试计数
python tests/calibrate.py         # 精度标定,写 .cache/calibration.json
python tests/smoke_stdio.py       # 完整 MCP stdio 协议端到端
python tests/acceptance.py        # 逐项验收账本
python tests/scan_stems.py        # 片段级真题残留检测
python tests/check_docs.py        # 文档与代码口径一致性

tests/acceptance.py 的六项验收全部通过:测试全绿、建库条数与站点声明题量总和逐卷相符、两种输入形态首位精确命中、编造题干不出现 exact、MCP 协议子进程实跑退出码 0。

测试文件不保存任何站点真题原文,需要真实题干时一律运行时从索引读取;tests/scan_stems.py 用最长公共子串复核这条声明,阈值 16 字。

故障排查

现象

原因与处理

启动报 ModuleNotFoundError: mcp.server.mcpserver

mcp 版本低于 2.2。pip install "mcp>=2.2,<3"

首次查询慢

正常,正在抓取 69 张试卷页建库,实测 5—20 秒

返回 degraded: true

重建失败,本次结果来自旧缓存。检查网络,或调 refresh_index()

建库报"解析题量与声明题量不一致"

站点结构变化。旧缓存仍可用,需按新结构更新 parser.py

reason: too_short

题干太短。补充到至少 6 个归一化字符,理想是一整句

结果有多条

同一道题出现在多张卷上,属正常结果,应一并参考

数据来源与使用边界

题目内容来自 https://mathabc.xyz/kaoyan/public/matrix/(页面标注 © mathabc.xyz 杨世显,渝ICP备2026008344号)。本仓库只在运行时抓取,不分发题目全文:.cache/ 不进版本控制,唯一入库的测试素材 tests/fixtures/synthetic_paper.html 是手写的 DOM 骨架,文本全部自造。使用时请遵守目标站点的使用条款。

License

GPL-3.0-or-later,见 COPYING。

下一步

这个工具解决的是一个具体缺口:题目查不到出处,就只能靠人工翻 69 张试卷。当前索引覆盖 2004—2026,遇到多条同题孪生时会把并列结果一并交给你判断。若你希望它把同题合并成一条多出处结果、或接入 2004 年之前的真题,欢迎提 issue 或 PR,parser.py 与 normalize.py 的边界都为此留得很清楚。

Related MCP Connectors

Related MCP Servers