Skip to content

将编号抽取接进一轮 - #96

Merged
JunieXD merged 7 commits into
mousebaoyan:mainfrom
Xi1495:extraction-round-integration
Nov 9, 2026
Merged

JunieXD merged 7 commits into
mousebaoyan:mainfrom
Xi1495:extraction-round-integration

Conversation

@Xi1495

@Xi1495 Xi1495 commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

关联

Closes #44。按 #18 选定的 DeepSeek 官方 deepseek-flash 编号方案实现 Steps.extract,支持已确认的 unresolved_dates 交接与 unparsed_date 审核。入口组装和部署归 #45。

改了什么

  • 一轮通过 steps.extract(notice.title, page, context) 传标题,NoticePage.hyperlinks 为必填;正文和附件统一编号。抽取从同校学院和近期项目清单中选编号,校验已有项目的学院、届、类型,再还原日期、入口和证据。延期只交本次原文明确写出的端点,不抄旧时间。
  • 正式抽取与评估共用 prepare、Responses、resolve_programs 和同一份提示词。学院候选独立来自 sources.csv。缓存只存调用结果,恢复编号每次重算,请求失败不缓存。
  • 官方 Responses 请求固定模型、思考强度、温度、输出上限与严格 JSON Schema。Extract、Responses 使用 async with;仅本地环境变量 LLM_API_KEY 放密钥。未知学院或项目编号明确报错。
  • 未识别日期保留原文和证据,网站验证后送审核,不覆盖旧时间;合并重复项目时,将尚未处理的日期审核项改挂原项目,继续待审。审核页明确提示到项目页补充时间,按钮为“已补充,关闭此条”。
  • 初始表结构支持 unparsed_date 审核原因;设计文档定义编号选择、未识别日期交接与缓存组织,§8.5 提供复现命令、结果和局限。

怎么验证的

  • 浏览器抓取使用 Browser、PageSession,评估取正文复用正式解析。
  • Python 3.14:格式、lint、严格类型、生成契约检查通过,235 项离线测试通过。
  • Node 26:类型、lint、格式检查及 100 项网站测试通过,使用本机 PostgreSQL 17.6;新增回归测试覆盖“可能重复”与“日期待确认”并存时的归并。
  • GitHub CI 的 crawler、web 两项均通过:运行结果。
  • 正式评估命令在线跑完 50 条,再用 --reuse-only 离线复算;网站实际 evidenceFound 核验 832 条证据,未找到 0 条。

同一标注集复验

在 crawler/ 中设置本地 LLM_API_KEY 后运行:

uv run python -m crawler.extraction.evaluate --output extraction-results/official --off-peak
uv run python -m crawler.extraction.evaluate --output extraction-results/official --off-peak --reuse-only

--off-peak 只用于原调用处于官方空闲计费时段的费用估算;在线与复算使用同一计费口径。固定模型 deepseek-flash、非思考、温度 0、8192 输出 token,时间和报名入口按标注有值的数量固定分母,失败不排除,多抽单列。

指标 官方接入版结果
报名截止 正确 27、错误 0、漏抽 1、多抽 1;27/28(96.43%)
报名开始 正确 22、错误 0、漏抽 1、多抽 1;22/23
活动开始 正确 21、错误 2、漏抽 2、多抽 0;21/25
活动结束 正确 18、错误 1、漏抽 5、多抽 2;18/24
报名入口 正确 26、错误 2、漏抽 1、多抽 2;26/29
学院、届、类型、作用 36/37、36/37、36/37、35/37
学位 19/20,另多抽 14 项
是否相关 36/36,无漏判或误报
整条全对、失败、证据未找到 25/50、0/50、0/832
输入、输出、缓存命中 token 251415、22848、144512
输入加权缓存命中率 57.4795%
实际命中量估算费用、平均耗时 0.20118524 元(空闲时段,未核对账单)、2.58 秒

同一 50 条通知按首次收录评估,学院候选来自 sources.csv,近期项目和附件为空;已有项目更新、附件和未识别日期交接由固定响应及数据库测试验证。仅 28 个标注截止,无独立测试集且缓存受此前请求影响,不能据一两条差异断言稳定优势,扩充后须重跑。活动结束仍只有 18/24,证据精确匹配不能代替语义判断,网站校验和人工审核须保留。

日期候选覆盖为报名开始 23/23、截止 28/28、活动开始 25/25、结束 24/24。本轮未识别日期列表为空。截止漏抽为东南大学多项目通知的第二个项目未抽出,正确截止候选已存在;多抽为厦大人工智能研究院通知中的另一个截止。

费用按官方空闲时段单价和返回的实际缓存命中量估算,未核对账单。

影响

Steps.extract 的首参数新增标题,NoticePage.hyperlinks 必填,已对接 #98 合入的正式正文读取步骤。抓取接口新增可选 unresolved_dates,初始表结构新增审核原因;审核关闭本身不写时间。MCP 工具无变更,无新依赖或环境变量。

unparsed-date-review unparsed-date-review-mobile

@Xi1495
Xi1495 marked this pull request as ready for review November 9, 2026 08:26
@Xi1495 Xi1495 mentioned this pull request Nov 9, 2026
3 tasks

@JunieXD JunieXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢!这一版把 #44 的几项都接上了:学院和已有项目按编号选、选错就失败;延期通知只交新的那一端;正文和附件共用一套编号;认不出的日期交给网站,网站核对原文后进审核页。标注集上截止 27/28,证据 0/826 找不到,PR 里把活动结束偏低这些局限也写清楚了。

我们本地跑了 ./check.sh,211 项都通过;网站的类型检查、lint 和构建也都通过;网站和抓取节点两边的 diff 逐个文件读了一遍。

方向:认不出的日期按你在 #44 提的方式交接,也就是 ExtractedProgram.unresolved_dates 加上 unparsed_date 这种审核原因,我们同意。

必须改

  1. 重复项目并进原项目时,「日期待确认」的条目会丢:审核页确认一个新项目是重复的,review-items.ts 会把它的各个版本按 fields 重新归并到原项目(:395、:421),不带 unresolved_dates;然后把这个项目上其他待审的条目全部关掉(:426)。认不出的时间就这样悄悄没了。请改成:合并时把重复项目上还没处理的 unparsed_date 条目改挂到原来的项目上,不关掉;再加一个测试:一个项目同时有「可能重复」和「日期待确认」两个条目,并进原项目后,「日期待确认」还在、挂在原项目上。

  2. 标注集的评估要跑正式的抽取:仓库里的 extraction/evaluate.py 还是用 llm.py 调硅基流动的 Chat Completions,用的也是实验的提示词和按学院名称输出的结构(evaluate.py:14、:260)。PR 里贴的接入版结果来自没提交的脚本,别人照 §8.5 的命令跑出来的是旧方案。以后改提示词或者换模型,都要靠这个脚本重跑(AGENTS.md「用实验说话」),所以它要跑的就是一轮里用的那一套。请改成:

    • evaluate.py 用 extract.py 的 prepare、Responses 和 resolve_programs,学院从 data/sources.csv 生成,也就是你这次在本地的做法;缓存命中数直接从 Responses 返回的 usage 里取;
    • 删掉只给硅基流动用的 llm.py,以及只对它有用的 --models、--modes;
    • §8.5 的命令改成新的,结果用提交的脚本重跑一次贴出来。#40 里我们也会把实验那份取正文的代码换成正式的,道理一样:一件事只有一种实现。
  3. 提示词只留一份,不要前后矛盾:ROUND_INSTRUCTIONS(extract.py:21)是在实验的 INSTRUCTIONS 后面追加一段「替代上面 college 的规则」。结果同一份提示词里,前面说「这是正文抽取实验,不处理附件」「直接写学院全称,不对照院校清单」,后面又说「编号覆盖正文和附件」「college_id 必须选编号」。模型可能因此忽略只写在附件里的截止时间。请把 prompt.py 直接改写成一轮用的那一份(正文加附件、学院和项目选编号、unresolved_dates),删掉 ROUND_INSTRUCTIONS,评估也用这一份。改完提示词按第 2 条重跑。

  4. 改表直接改 0001_init.sql:上线前没有历史包袱,表结构直接改(AGENTS.md「写代码」),之前改表的 PR(#90、#95)都是这么改的。请删掉 0002_unparsed_date.sql,把 unparsed_date 加进 0001_init.sql:248 的 reason 约束。

  5. 截图不提交进仓库:请删掉 docs/images/ 下的两张 PNG,截图在网页上直接拖进 PR 描述。现在描述里引用的是你 fork 上分支的地址,分支删了图也就没了。

  6. NoticePage 的新字段不要带默认值:title: str = ""、hyperlinks = () 加上一轮里的 replace(page, title=notice.title)(rounds.py:56-57、:175),是为了让原来的写法不改也能跑,属于兼容的写法(AGENTS.md「不留旧东西」)。请改成:

    • 标题不放进 NoticePage,作为 Steps.extract 的参数交给抽取:steps.extract(notice.title, page, result.extraction);
    • hyperlinks 去掉默认值,测试里构造 NoticePage 时写 hyperlinks=()。读正文那一步填这个字段,由 #40 来做,我们正在写,它会加同一个字段,合并时由后合并的那边调整。
  7. 设计文档只写结论:

    • §8.5 的「官方 Responses 迁移基线」是过程,请删掉;「接入版抽取复验」按第 2 条重跑后,只留结果表和两三句局限。
    • §8.2 新加的第二条,只留设计(学院和已有项目选编号、元数据要和对照一致、对照里的旧时间不当成候选),类名和调用方怎么读密钥这些是代码的事,请删掉(AGENTS.md「写文档」)。

建议一起改

  • Extract 和 Responses 照 Fetcher、Website 的写法用 async with,不单独写 close()(extract.py:152)。
  • ResponseUsage.valid_cached_tokens(responses.py:28)删掉:接口不会返回命中数比输入还多的用量,这是不可能出现的检查。
  • resolve_programs 用一整段 except KeyError 把「编号不存在」转成 SelectionError(extract.py:143),resolve 内部真有 bug 也会被当成模型选错。请在查学院、查项目的地方各自判断,查不到就抛 SelectionError。
  • 审核页的提示里写「补充时间后标记已核对」(review.tsx:31),「已核对」在项目上是另一个意思(项目的 verified)。建议写成「到项目页补充时间后关闭此条」,按钮也和「缺少截止时间」那种对齐,用「已补充,关闭此条」。

说明

  • #77(浏览器抓取)刚合并,它把 Steps 的参数从 Fetcher 换成了 PageSession,crawl_source 也改成接收 Browser。改的时候请先基于最新的 main。
  • 改完转回待审,或者在这里说一声,我们来复审,推了新提交以后我们再批准 CI。#44 已经指派给你了。

@Xi1495

Xi1495 commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor Author

已推送审查修复 a97c110,并合入最新 main 的 #77 与 #98,已解决正文/附件读取接口冲突。

  • 重复项目合并时,将尚未处理的 unparsed_date 改挂到原项目,继续待审;补了“可能重复”和“日期待确认”并存的真实数据库回归测试。
  • 正式抽取和评估共用 prepare、Responses、resolve_programs、同一份提示词,学院来自 sources.csv;旧客户端、--models、--modes、追加覆盖提示词均删除。
  • 标题改为抽取步骤首参数,hyperlinks 必填;客户端改为 async with,去掉不必要用量检查及整段 KeyError 捕获,审核提示和按钮按建议调整。
  • 审核原因直接改初始迁移;额外迁移、仓库 PNG、分支图片链接及文档迁移过程已删除,§8.5 只保留正式命令、结果表和局限。

本地全部检查通过:Python 235 项、网站 100 项测试,格式、lint、严格类型和生成契约检查通过。使用仓库里的正式评估命令在线跑完 50 条并离线复算,截止 27/28(96.43%),0 条失败,整条全对 25/50;网站实际 evidenceFound 核验 0/832 找不到。缓存命中 57.4795%,按实际命中量及官方空闲价估算 0.20118524 元,未核对账单;活动结束仅 18/24,局限已保留。

首次统一提示词复验出现 3 条空字段结构错误,仍计入分母,截止 24/28、估算 0.20274188 元;显式开启严格 schema 并明确空字段整体为 null 后,固定方案完整重跑得到上述结果,两轮用量均保留,没有重发失败响应挑分。

第 5 条还差 GitHub 附件上传:内置浏览器页面控制超时,本机 Chrome 的应用控制授权也超时。没有换图床或再引用分支地址。GitHub CI 的 crawler、web 两项均已通过。

@Xi1495
Xi1495 marked this pull request as draft November 9, 2026 14:21
@Xi1495
Xi1495 marked this pull request as ready for review November 9, 2026 14:22

@JunieXD JunieXD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢!上次的意见都改到了:重复项目合并时「日期待确认」改挂到原项目并补了测试,评估脚本跑的就是正式的抽取,提示词只留一份,改表直接改初始迁移,NoticePage 不再带兼容的默认值,文档只留结论。我们把这个分支和最新的 main(含 #99)合在一起跑了 ./check.sh(235 项)和网站的类型检查、lint、格式检查,都通过,生成的 OpenAPI 文档也对得上。

截图传不上来没关系,审核页我们在接入口(#100)联调时对着开发版看。接入口时我们会顺手调两处:抽取失败的原因改成中文、带上具体的错误码(比如 unknown_college),方便在抓取看板上查;调用 LLM 的并发上限整个进程共用,和初筛一样。

@JunieXD
JunieXD merged commit 915b89b into mousebaoyan:main Nov 9, 2026
2 checks passed
JunieXD added a commit that referenced this pull request Nov 9, 2026
#96 合并后,入口里占位的抽取换成正式的 Extract,抓取节点就能从头走到尾。

- 启动时一起校验 `LLM_API_KEY`,`Extract` 和其他客户端一样用 `async with` 打开
- 同时在调用的 LLM 请求整个进程共用一个上限,和初筛一样
- 抽取失败的原因写成「抽取失败: unknown_college」这样,带上具体的错误码,抓取看板和日志里能看出模型错在哪
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[任务] 抽取接进一轮

2 participants