Repository navigation
Description
关联与状态
路线图 #2,一期。状态:进行中,@Xi1495 认领,PR #96 按审查意见修改中。认不出的日期按评论里提的 unresolved_dates 交接,已在 #96 确认。有问题直接在这里留言。
目标
把抽取接进一轮:通知有了新快照,网站会给出这所学校的学院和近期的项目做对照,抓取节点让 LLM 读通知,输出它涉及哪些项目、每个项目的各字段和证据(总体设计 §8.2)。这个任务写一轮里的 Steps.extract。
已经有的
- 项目骨架:
crawler/是 Python 3.14 + uv 的项目,装依赖uv sync,跑全部检查./check.sh(格式、lint、Pyright 严格模式、测试)。命令见 AGENTS.md。 - 一轮的流程:
crawl/rounds.py的crawl_source已经把一个来源的一轮串好:读列表 → 初筛 → 读正文和附件 → 抽取 → 交结果。四步作为Steps传进来,签名和交接的数据写在那里,测试的写法见rounds_test.py。抓不成时抛FetchError或StepError,流程会把原因记进抓取记录(开发约定 §2)。 - 请求层:所有对来源网站的请求都经过
crawl/fetch.py的Fetcher,它管 UA、同一网站一次一个请求和间隔,不要自己用 httpx 发请求。 - 和网站交接的类型:
crawl/contract.py由网站的接口定义生成,不手改(接口 §6)。 - 输入:
NoticePage(正文和附件的文字),以及网站给的ExtractionContext:发布日期、学校的学院、近期的项目(编号、学院、届、类型、名称、报名时间)。 - 要返回的:
ExtractedProgram的列表和用量。网站收到后检查证据能不能在快照里找到、置信度够不够、日期合不合理,拿不准的进审核页(§8.3、§8.4)。 - 编号:[任务] 句子、日期和链接的编号 #43 的函数把句子、日期、链接编好号。
范围
-
按 [任务] 抽取的实验 #18 选定的 LLM 和提示词实现
Steps.extract:- 模型从学院清单里选学院、从近期的项目里认出是哪个项目(给编号,没有就新建),从编号里选日期和证据的句子;
- 程序按编号取出原文、换算出时间值,拼成
ExtractedProgram。 - 只给这条通知写了的部分:延期通知只写了新截止的,报名时间只给
end,不从近期项目里抄开始时间。网站按部分合并,没给的部分保持原样(§8.4)。
-
模型的输出按固定的结构解析,解析不了的当失败处理(开发约定 §2);通知里的文字只当要读的材料(AGENTS.md · 可靠与安全)。
-
LLM 用 DeepSeek 官方接口的 V4.1 Flash(
deepseek-flash)。[任务] 抽取的实验 #18 的比较是在硅基流动上做的,官方的 Chat Completions 不支持json_schema,要用 Responses API(text.format设成json_schema),见总体设计 §8.5。key 从环境变量LLM_API_KEY读(补充基于编号的抽取评估和实验结果 #81 里定的);模型名、思考强度、温度和接口地址都写成常量,换模型要在标注集上重跑。 -
程序认不出的日期不能悄悄漏掉:模型看到原文写了截止这类时间,编号里却没有对应的日期时,照抄日期的原文、附上句子编号,这一项送审核页由人判断。这条规则写进 §8.2;怎么交给网站(契约里怎么表示),先在这里和维护者商定。
-
提示词按缓存命中组织:DeepSeek 的接口自动缓存请求开头相同的部分,命中的部分按缓存价计费,便宜得多(硅基流动上 V4.1 Flash 输入 2 元、命中 0.04 元每百万 token)。所以:
- 固定不变的放最前面:规则和输出的结构;
- 同一所学校共用的其次:院校清单里的学院、近期的项目;
- 每条通知自己的放最后:标题、发布日期、编号后的正文;
- 前面的部分每次都要一字不差:不放当天日期、请求编号这类每次都变的东西,结构化数据的字段顺序固定。
这条规则写进总体设计 §8.6,以后所有调 LLM 的地方(比如试抓 [任务] 试抓:抓取节点这边 #46)都照着组织。
-
用固定的模型响应写测试;效果用 [任务] 抽取的实验 #18 的标注集和评估脚本重跑,结果贴在 PR 里,并写明缓存命中率(用量里的
prompt_cache_hit_tokens)和按实际命中算的费用。
建议的第一步
在本 Issue 留言认领。#18 的评估脚本在 crawler/src/crawler/extraction/,先读懂它怎么调模型、怎么还原编号。
验收标准
- 一轮里要抽取的快照都交了抽取结果,网站那边证据找不到的比例和实验里一致
- 标注集上的结果贴在 PR 里,报名截止的准确率不低于实验的结论,并写明缓存命中率
- 测试用固定的响应,
./check.sh通过
Activity
实现原文句子日期和链接编号 (#75)
补充基于编号的抽取评估和实验结果 (#81)
JunieXD commentedon Nov 9, 2026
@Xi1495 #18 的实验和 #42 的初筛都是你做的,抽取接进一轮接着做最顺。有兴趣的话在这里留言认领,范围和第一步见正文;不方便也没关系,说一声就好。
Xi1495 commentedon Nov 9, 2026
我来接着做 #44,按已合并的 #18 结论使用 DeepSeek 官方
deepseek-flash,通过 Responses API 的text.format: json_schema输出,密钥仍只从本地LLM_API_KEY读取,不沿用硅基流动接口。实现会复用现有编号与还原函数,按学院编号、近期项目编号交接,延期通知只提交原文新增的部分;提示词按固定规则与结构 → 同校学院和近期项目 → 单条通知编号正文排列,固定字段顺序。测试先使用固定响应,官方接口标注集重跑时保留失败分母、缓存命中 token 和实际用量,结果贴到 PR。
范围第 4 项的契约想先确认:建议在每个
ExtractedProgram上增加可选的unresolved_dates,每项包含目标字段(如apply.end)、模型照抄的日期文字text及由句子编号还原的evidence。网站验证原文后将该项存入审核队列,原因用unparsed_date;这一项不生成推测时间、不更新相应时间字段,原来已知的时间保持不变,其他可确定字段继续按现有规则处理。即使只是漏了活动时间或延期截止,也不能因为已有项目带着旧时间而忽略这项。请确认这种交接方式,或指定更合适的契约表示。我先做官方 Responses 调用与固定响应测试,契约及网站审核分支等这里商定后再实现,不会先手改生成类型。
Xi1495 commentedon Nov 9, 2026
#96 已完成并处于待审状态。本轮按 CONTRIBUTING.md 的「自查」、PR 模板和 AGENTS.md 对照 #44 全部范围重新读了差异,补齐了数据表与审核文档,并将未识别日期结构集中到同模块 schemas,避免服务层反向依赖接口层。没有改动提示词和模型请求参数。
正文、原始响应、密钥、测试会话和临时材料均未提交。本轮自查未新增模型或高校网站请求。#45 的入口组装和部署不混入本 PR;本 Issue 待维护者确认契约并验收后关闭。
将编号抽取接进一轮 (#96)