88
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2601_FZU_SE社区-CSDN社区云 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践第二次作业——与AI结对编程(顶会热词统计)-CSDN社区 |
| 这个作业的目标 | 做一个能看 CVPR、ICCV、ECCV 近几年在研究什么的网站:论文能进库,前十、关系图和多年走势都能点 |
| 其他参考文献 | 邹欣《构建之法》第3章、第4章、第8章;数据来源见 data/public/SOURCES.md |
交出去的是四样:能点的原型、五个功能的网站、这篇博客、学号命名的仓库。成品首页和原型播放页在这张表的前两行。克隆地址打不开论文和走势。
| 还要一起看的 | 在哪 |
|---|---|
| 成品网站 | http://159.75.211.145:8080/#/hot。浏览器里要能看见论文、前十和走势 |
| 原型网站 | 不用登录。从热门方向点进去,走势、论文、导入、说明都能跟着走。https://www.figma.com/proto/KAm85sJhaB1sxRUTYcPteR/%E9%A1%B6%E4%BC%9A%E7%83%AD%E8%AF%8D%E5%8E%9F%E5%9E%8B%E8%AE%BE%E8%AE%A1?page-id=0%3A1&starting-point-node-id=3%3A8&scaling=min-zoom&content-scaling=fixed&node-id=3-7&show-proto-sidebar=1 |
| 代码仓库,名字就是学号 102400307 | https://codehub-cn-south-1.devcloud.huaweicloud.com/0d6f3fb7704b4025b9c9388b55cc0980/102400307.git |
| 代码规范 | 仓库根目录 codestyle.md。Python 跟 PEP 8,Vue 跟 官方 Style Guide |
| 写草稿的助手 | DeepSeek Hy4 preview。场景、表和页面是 DeepSeek 起的稿,五个功能怎么切、三套数字怎么算,是我定的 |
| 论文元数据 | data/public/SOURCES.md,主路是 Hugging Face 上的公开会议数据 |
| 课程页、作业要求 | 文首第一张表,两条链接还空着 |
代码在华为云 CodeArts,仓库名是学号 102400307。下面这条是克隆地址,浏览器打开它看不到论文和走势。
https://codehub-cn-south-1.devcloud.huaweicloud.com/0d6f3fb7704b4025b9c9388b55cc0980/102400307.git
规范在仓库根目录 codestyle.md,没有另起一套。Python 跟 PEP 8,Vue 跟 官方 Style Guide。我额外钉了三条,是怕统计写乱:入库只走 PaperRepo.create,接口都是 code、message、data,数据库和密钥不进 Git。
论文从哪来写在 data/public/SOURCES.md,主路是 Hugging Face 上的公开会议数据。课程页和作业要求还空着,在文首第一张表。学号在仓库名里。README.md 的中文乱成问号了,发布前要重写。
过程和规范都在仓库根目录,和代码同一处:
| 写的是什么 | 文件 |
|---|---|
| 需求 | NABCD.md |
| 时间 | PSP.md |
| 信息架构 | 原型信息架构.md |
| 原型细化 | 原型设计细化.md |
| 原型发布 | 原型设计结果.md |
| 实现设计 | 阶段二-实现设计.md |
| 编码步骤 | 阶段二-编码协作.md |
| 骨架与入库 | 阶段二-编码-01-骨架与入库.md |
| 论文列表 | 阶段二-编码-02-论文列表.md |
| 导入接口 | 阶段二-编码-03-导入接口.md |
| 热门方向 | 阶段二-编码-04-热门方向.md |
| 关键词图谱 | 阶段二-编码-05-关键词图谱.md |
| 热度走势 | 阶段二-编码-06-热度走势.md |
| 前端五页 | 阶段二-编码-07-前端五页.md |
| 查无则抓 | 阶段二-编码-08-查无则抓.md |
| 代码规范 | codestyle.md |
只用 DeepSeek Hy4 preview。没有第二个助手。
用法就一条。我先写不许做什么,DeepSeek 出草稿,我对照五个功能留下或划掉,划掉的写进下一轮提示。五个功能的边界,以及前十用 TF-IDF、图谱用篇内共现、走势用按年篇数,这三套数字定了就不再交回去改。不把范围写进提示时,阅读清单和以后再做图谱会自己长出来。
flowchart LR
bound[我先写不许做什么] --> draft[DeepSeek 出草稿]
draft --> check[我对照五个功能]
check --> keep[留下]
check --> cut[划掉]
cut --> bound
需求、原型、每一个功能里 DeepSeek 起草了什么、我留下什么、划掉什么,整张表在后面的结对一节。这里不先铺一遍。前端五页的取舍也在那张表里,原文在 阶段二-编码-07-前端五页.md。
开工前我把整件事拆进 PSP.md。表上的合计写成 2210 分钟,大约 37 小时。那一格把「计划」和「估计」各加了 40,其实是同一件拆表的事。下面合计只加一次计划,预估是 2170 分钟。
实际按 9 月 20 日建仓库、22 日定需求、23 日画原型并起骨架、24 日把五个功能和这篇博客收尾来记,大约 35 小时。总账差不多。省下来的和超时的不在同一行。
| 阶段 | 预估(分钟) | 实际(分钟) | 相差 |
|---|---|---|---|
| 计划 | 40 | 25 | 少 15 |
| 估计这个任务要多久 | 40 | 25 | 和计划是同一件事,合计不加 |
| 需求分析,含学新技术 | 360 | 260 | 少 100 |
| 设计文档 | 120 | 150 | 多 30 |
| 设计复审 | 40 | 80 | 多 40 |
| 代码规范 | 50 | 20 | 少 30 |
| 具体设计 | 280 | 340 | 多 60 |
| 编码 | 900 | 680 | 少 220 |
| 代码复审 | 90 | 160 | 多 70 |
| 测试 | 150 | 100 | 少 50 |
| 测试报告 | 40 | 90 | 多 50 |
| 计算工作量 | 20 | 10 | 少 10 |
| 事后总结 | 80 | 200 | 多 120 |
| 合计 | 2170 | 2115 | 少 55 |
省在起稿。DeepSeek 先把六个场景、表和五页骨架写出来,我不用对着空白从零列。学 Flask 和 Vue 也没有单独坐满预估里的两小时,是边写边问,时间进了编码。代码规范是 DeepSeek 按 PEP 8 和 Vue 官方指南收成一份,我只加了三条:入库只走一个函数,接口都是 code、message、data,数据库和密钥不进 Git。测试里不少 pytest 是 DeepSeek 起草、我改断言,所以测试行比 150 分钟短。提交次数和 tag 用 git 就能数,工作量那一行也就十几分钟。
超时花在把 DeepSeek 的草稿拉回来。需求第 1 轮人写对了,功能写多了,阅读清单出现了,图谱和动图被放到以后。复审就不能只花 40 分钟,要把这两块捞回基础功能,再改一版问法。原型按墨刀估了 280 分钟,底图定了以后改到 Figma 盖热区,具体设计多出来的就是换工具;热区怎么连是 DeepSeek 列的,我对着按钮画,所以没有把五个页面重描一遍。编码表面少了 220 分钟,是因为骨架不用手打。停用词把 Deep Learning 切没、更新用 or 把空年份当成没填、大库统计要等几十秒,这些进了代码复审,那一行从 90 加到 160。每步协作记录和这篇博客比「事后总结 80 分钟」长,DeepSeek 写的摘要不能直接交,我要留下采纳了什么、划掉了什么。编码这 680 分钟是本机五个功能加上后来的浏览和演变。华为云能打开的首页还空着,上线后再点的那一遍还没记进来。
Git 这边,当前分支 108 次提交,远程 dev 109 次,main 还停在阶段一,只有 7 次。tag 有 v1.0.0 和 v1.1.1。提交说明写功能,不写工具名。Issue 和 PR 的网页编号我还没抄下来。数据库、依赖和生成的大 CSV 都在 .gitignore 里。
读完《构建之法》第 3 章和第 8 章,我按五个字母把平台定下来。作业让做法和对比写长,需求和好处、怎么交出去也不能空着。定稿在 NABCD.md 前半,下面就是那一版。后半是四轮对话和截图,这里不重贴,结对那一节只留把范围拉回来的那一次。
需求是痛点,不是功能清单。做法要做得成。好处是用户愿不愿意离开现在的工具。竞争里还有一种对手:用户干脆不做统计。交付是原型、网站和仓库交到别人手里,还要能指出哪三条场景走得通。
动手前我已经钉死、后面没再让 DeepSeek 推翻的有这些:五个功能都做,图谱和动图不能放到以后;Flask、Vue 3、SQLite、ECharts,部署华为云;批量以课程或公开数据为主,单篇才用 DBLP;前十用 TF-IDF 和横条,不用词云当主图;走势只数某年有多少篇,不把权重画进去;transformer、NeRF、diffusion 保持论文里的写法,不合并。了解更多、年度热词演变是附加。关于页必须有,了解更多写在关于页里。
CV 三大顶会一年录取就能到几千篇。刚入门的人靠逐篇阅读摸热点几乎做不到。翻官网、用学术搜索、自己用表格数,都慢,换一批人或换一年,热门也对不上。
我把需求收成一句话:先维护一份三个顶会的论文库,再在这份库上给出热词、图谱和走势。库里有什么,统计就认什么。这不是再做一个论文检索网站。
三类用户是我定的:刚入门的本科生、实验室研究生、导师和助教。每类两条。谁、在什么情况、痛点、希望平台怎样,这个格式用了 DeepSeek 的建议。希望平台怎样只许落到五个功能和两项附加。
| 场景 | 谁,卡在哪 | 希望平台怎样 |
|---|---|---|
| 1 前沿调研,还没选方向 | 学过机器学习和数字图像处理,还没进组。网上多是二手转述,官网标题里的缩写看不懂。分不清哪个词在变热、哪个已经过时,论文散在官网、DBLP 和下载站 | 首页看前十,点进图谱,再点关键词列出论文。走势页看这个词近几年出现得勤不勤。要读某篇时回列表查摘要和原文链接。关于页可以写进报告 |
| 2 读指定的一篇 CVPR,术语卡住 | 方法名一个接一个。每次只查到一个孤立的词,看不出和谁经常一起出现,也不知道近几年还有没有人做,自己不会写爬虫 | 入口是列表:题目精确查,或编号、关键词模糊查。DeepSeek 写成在图谱上搜词,我改掉了,图谱只负责看关系。在不在前十都可以看图谱和走势。库里没有就按这句话抓取并写入,下次走库 |
| 3 开题,方向还值不值得做 | 导师给了大致方向,自己到会议列表里数、往表格里贴,换个词就重做。缺的是趋势,不是当年有多少篇。机构名不统一只当痛点,平台不做合并 | 走势页把多个词、多个年份、三个会画在一张动图上,纵轴是按年篇数。回首页看在不在前十,用图谱找邻近词。缺数据去导入。附加看往年谁新进、谁掉出去 |
| 4 写综述,文献来源杂 | PDF、自己下的、别人导出的混在一起,写到一半又多一篇。补字段耗时间,还容易重复。计算方法对不上,热度图不敢放进论文 | 先批量导入,单篇再补。列表上改错、删重复。库里没有就按查询抓。整理完在这批数据上出前十。计算方法写进脚注 |
| 5 给定方向,给新生出题 | 主要靠读论文的印象,印象会偏,会守着熟悉的方向。三个会几千篇没人愿意数一遍 | 从热词点到图谱再点到论文。走势看跨年和三个会的差别。定了方向后列出带原文链接的论文。组里用同一个网址 |
| 6 维护课程论文库 | 每届重新统计,文件留在自己电脑里,学生报告里的热词各说各的。DeepSeek 写过课程名单和防撞题,我删了 | 导入课程数据集,缺的按查询去抓。本届前十当题目和评分参照。学生点关键词读论文,再做多年、三个会的对比 |
| 场景 | 主要用到 | 页面怎么走 |
|---|---|---|
| 1 本科生做调研 | 前十、图谱、走势、列表 | 首页到走势,需要时去说明页 |
| 2 本科生卡术语 | 列表是入口,再用图谱和走势 | 列表到首页,再到走势 |
| 3 研究生开题 | 走势为主,加上前十、图谱、导入 | 走势到首页,缺数据去导入 |
| 4 研究生写综述 | 导入、列表、前十 | 导入到列表,再到首页 |
| 5 导师定方向 | 前十、图谱、走势、列表 | 首页到走势 |
| 6 助教维护课程库 | 五个功能都用 | 导入到列表,到首页,再到走势 |
相对第一稿,我删掉的是:词云当主图、热词白话解释、引用量排序、机构分布、BibTeX 和 DOI 自动补全、个人阅读档案、课程名单、选题撞题、按学期做快照,以及把图谱和动图放到以后。导入页上后来的粘贴只是把已有材料喂进库,不是这里划掉的自动补全。
页面上怎么点,DeepSeek 帮我写细了。算法和边界是我定的。先写清要分开算的三件事,再对齐到五个功能。
库如果不先固定,换一个人、换一年,热门就对不上。所以第一件事不是画图,是有一份能改、能删、能再导入的三会论文库。图都从这张库里长出来。
前十要回答的是:在当前这批论文里,哪个词更能代表方向,而不是哪句话每篇都出现。标题、摘要、关键词放在一起,小写、去掉 using、method 这种停用词。transformer、NeRF、diffusion 保持论文里的写法,不做词干合并。权重是这批论文里的 ∑(1+log tf)×log(N/df)。一个词出现的文档越多,log(N/df) 越小,泛词会被压下去。首页用横条,能数出 1 到 10。换会议或年份,就按新的这一批重算。DeepSeek 建议过词云当主图,我没留。词云看不出名次。
走势要回答的是另一件事:这个词在某一年、某一个会上,有多少篇提到。纵轴只用篇数。DeepSeek 写成篇数或权重都可以,我改成只留篇数。两套数画在一条线上,上升到底是公式变了还是录用变了,看不出来。少于两个词没有对比,不画线。ICCV 奇数年才开,ECCV 偶数年才开,某年是 0 是没开会,不是这个方向凉了。
已经知道题名的人,不该先去点云图。列表先查库:题目精确,编号或关键词模糊。库里没有,就把这句话拿去联网,摘要、关键词、原文链接同一趟写回,页面上用绿条说明这次是补录。再搜走库。导入是另一条路:手里已经有课程数据或公开数据,批量或单篇喂进去。两条路都进同一张库,不合成一页。机构名合并、DOI 自动补全,这次不做。
榜上是十个分开的词,仍然不知道该读哪篇。所以图谱的点是这十个词,同一篇里一起出现就连一条边,次数多线就粗。必做是点节点,列出题名、会议、年份、摘要和原文链接。DeepSeek 还建议点边、只看两个词同时出现的论文,我标成可选。
| 功能 | 上面要解决的事 | 页面上怎么做 |
|---|---|---|
| 1 导入 | 没有统一的库,后面的图都画不出来 | 导入页批量上传,或单篇先查库、没有再走 DBLP。写入摘要、关键词、原文链接,返回成功条数和失败原因 |
| 2 列表 | 已知题名却被带到图上;库里没有,这个词进不了榜 | 精确或模糊先查库。没有就抓,同一次把结果送回并入库,绿条提示补录。同一页改错、删重复。再搜走库 |
| 3 前十 | 泛词每篇都有,只数篇数会把 method 送上榜 | 当前筛选下的论文算 TF-IDF,横条取前十。悬停能看到权重和大约多少篇。换筛选就重算 |
| 4 图谱 | 词分开了,不知道该读哪篇 | 节点是关键词,篇内共现连边。点节点列出论文和原文链接。点边不是必做 |
| 5 走势 | 看方向在涨还是已经见顶,三个会是否一样热 | 多个词、年份,勾上 CVPR、ICCV、ECCV。三条线按年播放,能暂停并读出那一年各自的篇数。可以从某一年回到列表 |
两个附加不进这五格。说明页写三个会是什么,以及前十和走势各自怎么算、数据到哪一年。年度热词演变把每年的前十排在一起,看谁新进、谁掉出去、名次怎么变。
DeepSeek 按场景把五个功能又写了一遍。我压成对谁有用,没有把引用量和个人文献库写成卖点。
| 谁 | 离开现在的工具之后得到什么 |
|---|---|
| 本科生做调研 | 首页就有三个会上的前十,点进图谱落到论文,走势用来避开已经过时的方向 |
| 本科生卡术语 | 从列表搜,不在前十里也能看图谱和走势。查不到由平台去抓 |
| 研究生开题 | 看谁在涨、谁已经见顶。前十和图谱用来看这个方向站在哪、和谁交叉 |
| 研究生写综述 | 导入和增删改少填字段。前十是在自己这批数据上算的,图能说清数字从哪来 |
| 导师和助教 | 定完方向能拉出带链接的论文。各届在同一份库、同一个网址上出前十 |
| 这门课 | 库建一次还能改。两套算法分开写在说明页。不用每换一个词就重爬、重画 |
本科生做调研时,首页就能拿到三个会上的前十,点进图谱落到具体论文,走势页用来避开已经过时的方向。列表能查,查不到由平台去抓。读论文卡住时从列表搜术语,不在前十里也能看图谱和走势。
研究生开题主要看谁在涨、谁已经见顶。前十和图谱用来看这个方向站在哪、和谁交叉。写综述时用导入和增删改少填字段。前十是在自己这批数据上算的,图放进论文能说清数字从哪来。
导师定方向可以看图,定完能拉出带链接的论文给新生。助教导入课程数据后,各届在同一份库上出前十。同一个网址,组里看到同一批数据。
库建一次还能改、还能用。前十和走势的算法分开写在说明页上。榜上的词和库里的词对得上。不必每换一个关键词就重爬、重画。
对比对象是我选的:Google Scholar、Semantic Scholar,会议官网和 DBLP,Connected Papers、ResearchRabbit,自己用 Excel 或 Python,还有公众号、博客上的年度盘点。DeepSeek 起草了表。我删掉了用引用次数和机构排行去比的句子,数据里不一定有这些字段,这次也不做。
书里还写了一种对手:用户干脆不做统计。翻两篇官网就交作业,或者组里每人一份表格,热门永远对不齐。所以交付不能只停在本机脚本。
| 现成工具 | 能做什么 | 对这三个会的热词帮不上什么 | 这里怎么做 |
|---|---|---|---|
| Google Scholar、Semantic Scholar | 检索论文,提供摘要、链接和相关推荐 | 结果是一长串命中,看不出该词在三个会上现在热不热、近几年怎么变。里面还混着预印本和其他会议 | 功能 3 给出三个会、可选年份的前十。功能 5 把多年对比放在同一张动图上 |
| 会议官网、DBLP 列表 | 数据全,可按年、按会浏览 | 仍是列表。热词要自己导出再算,换一批人或换一年,算法容易对不上 | 功能 1 先建成自己的库。功能 3、4、5 直接给出统计,不把计算丢回给读者 |
| Connected Papers、ResearchRabbit | 按引用关系画论文关系图 | 节点是论文,不是关键词。没有三个会的热度榜,也没有按年走势 | 功能 4 是关键词共现,点词看到相关论文和原文链接。功能 3、5 补热度和时间。功能 2 搜不到会抓取入库 |
| 自己用 Excel 或 Python | 计算方法完全自己定 | 要自己爬、洗、画。换词、换年就重做。结果在本机,导师、助教和学生各看各的 | 页面上改筛选就重算。部署到华为云后,组里打开同一个地址 |
| 公众号或博客的年度热词盘点 | 读着轻松,适合先建立印象 | 说不清数据从哪来、怎么算、截止到哪一天。想接着读原文,还要自己再搜 | 图都从自己的库里算。从榜点到图谱,再点到原文。说明页写清口径和年份 |
这五条是现成工具不容易一起给到的:
| 留在这个平台上的 | 现成工具里缺的那一块 |
|---|---|
| 只在 CVPR、ICCV、ECCV 上算的前十,用横条,不是搜出一堆论文 | Scholar 给的是命中列表,盘点文章给的是结论,都不是可筛选的榜 |
| 关键词共现图,点一个词就能看到相关论文和原文链接 | 论文关系图的节点是论文,点开仍不知道该词和谁一起出现 |
| 多年、三个会画在同一张动图上,能暂停,暂停时读得出那一年各自的篇数 | 官网和 DBLP 是按年的名单,没有这张可暂停的对比 |
| 库里没有就按查询抓取,写入后再查还在,并带摘要、关键词、原文链接 | 搜不到就结束的检索,不会把这篇补进后面的榜和图谱 |
| 能批量导入,能改能删,云上同一个地址,前十和走势的算法分开写清 | 私人脚本换一台电脑就没了,盘点文章不能改库 |
交付按作业写,不是另拟一套范围:能点的原型网页、五个功能的实现、华为云上能打开的地址、学号命名的仓库。仓库在 dev 上开发,基本功能完成后有 v1.0.0。数据以课程或公开数据集为主。DBLP 只补单篇,以及列表里搜不到的时候。爬取只用于这门课。
做完以后怎么说明它能用,我从已经定的场景里挑了三条。细的截图放在成品那一节,以项目交付那张表里的网站地址为准。
能点的原型和项目交付那张表是同一条,原型那一节开头再放一次。后面的动图是按它的分镜接出来的。
正式能点的是 Figma,不用登录。和项目交付那张表是同一条:
三份文档都还在:原型信息架构.md 写为什么这么分,原型设计细化.md 写每一页怎么排,原型设计结果.md 写 Figma 上点到哪。下面按这个顺序把三份都写进博客。画板和热区是我盖的。网站代码不是从原型导出来的。
NABCD 定稿之后先分页面,当时按墨刀来写。五个功能不能挤在一页上,也不能做成又一个检索站。首页要同时看见前十和词和词的关系,走势单独一页是因为纵轴是另一套数,列表负责改库和查无则抓,导入负责把已有材料喂进来,说明页把算法写给人看。详情不占顶栏,从题名点进去。
顶栏五个名字一开始写成首页、热度走势、论文列表、论文导入、关于。细化时改成热门方向、热度走势、论文、导入、说明,功能没变,只是和页面上的话对齐。当前页高亮。右上角小字:数据来自当前库,统计随库变化。后来细化改成直接显示库内篇数。
筛选条只放在热门方向和走势。会议是 CVPR、ICCV、ECCV,可多选,默认三个都勾。年份是起止,默认近若干年,细化定成 2022 至 2025。点更新统计之后,当前页的图按新范围重算。这两页之间沿用已经应用的会议和年份。点进论文、导入、说明,不带这套筛选,否则人会以为列表也被同一组勾限制住了。列表只用自己的查询框。从图表点进列表,才出现一条只读的附带条件,那是这一次跳转带上的词、年、会,不是全局筛选。
走势如果年份只选了一年,提示需要多年区间,不播放。少于两个词也不画对比线。空库时热门方向和走势都给出去导入,本页不写库。
flowchart TD
open[打开热门方向] --> see[看见前十和词云,论文区还空着]
see --> word[点一个词]
word --> papers[论文仍出现在这一页]
papers --> title[点题名]
title --> detail[详情]
see --> trend[顶栏去热度走势]
trend --> year[读某一年]
year --> list[论文列表带上只读条件]
list --> miss{库里有没有这句话}
miss -->|没有| green[顶部绿条,写入后再显示]
miss -->|有| table[直接列出]
| 页 | 为什么单独成页 | 这一页不许做的事 |
|---|---|---|
| 热门方向 | 前十和共现回答的是「现在热什么、和谁一起出现」 | 不写库,不做搜词框。点词先在本页出论文 |
| 热度走势 | 纵轴是篇数,和权重不是同一件事 | 少于两个词不画线。本页不补库 |
| 论文 | 已知题名的人从这里进,查不到要能补进库 | 批量导入不放这里。补录不用弹窗 |
| 导入 | 手里已经有文件或一篇题名 | 不做查无则抓。那是列表的事 |
| 说明 | 算法和三个会要能被引用 | 只读,没有空库态 |
| 详情 | 读完一篇再决定要不要打开原文 | 不占顶栏。原文链接后来改成弹窗,播放时不离开原型 |
DeepSeek 写过点图谱节点就跳进列表。我没留。图谱要是变成另一张检索表,功能 4 就没了。进列表只有三条路:点题名,从走势带词、年、会进去,从演变带词和年进去。演变不带会议,因为那一行是这一年的榜,不是某一个会的折线。
算法没改。前十仍是 TF-IDF,走势仍是按年篇数。改的是人怎么读这些数。
信息架构里前十是单独一条大横条。画出来之后,一条长条读不出第 1 名到第 10 名,也分不清三个会各占多少。细化改成十行表:方向名、一句白话、蓝色权重条、绿色约篇数条、较上年、三会分布。旁注写明横轴是权重,不是篇数。词云和共现合成一块,圆的大小是权重,细弧线是同一篇里一起出现。点一行或点一个圆,该词描边,其余变淡,右侧写这个方向在做什么、常和谁一起出现,下方列出题名、会议、年份、摘要。点两个词之间的边只列同时出现的论文,标成可选。首页改成上下长页,不再左右硬挤。
走势页上,折线区不再单独放一个播放键。鼠标在图上左右移动,或拖滑块,竖线停在某年,下面三张卡读出当年三个会各多少篇。只选一个词时用黄条,不画对比图。作业要求的动图放在页面下方的历年前十:按年播放、暂停,一次只看一年。播放用分年画板加延时,不是真的物理动画。
论文页的检索和添加在同一行。结果用文字卡片:会议色标、年份、题名、两行摘要、关键词,不加封面图。查无则抓是表上方的绿条。同一句再查改黄条,表示不再补录。添加打开空表,手填,不查库、不走 DBLP。
说明页改名之后,最上放三张会卡,这是了解更多。接着用四句人话对应五个功能,不写功能编号。计算方法分成左右两格,写明两套数不能比大小。最后写数据从哪来,以及 ICCV 只在奇数年、ECCV 只在偶数年,某年落到 0 不是这个方向凉了。
| 信息架构里的样子 | 细化之后 | 为什么改 |
|---|---|---|
| 导航叫首页、论文列表、关于 | 热门方向、论文、说明 | 和页面上的话一致,五页功能不变 |
| 按钮叫应用筛选 | 更新统计 | 语气和「库会变」一致 |
| 前十是一条大横条 | 十行表加迷你条 | 要能数出名次,还要看见三个会 |
| 共现是单独一块关系图 | 和词云画在一起 | 两块都是「词和词」,再拆会重复 |
| 原文链接新开一页 | 弹窗里给地址 | 播放原型时不离开这一站 |
| 浅灰底加稳蓝 | 深色顶栏、白卡片、三会三种颜色 | 三个会要能靠颜色认出来 |
| 补录用过弹窗的草稿 | 绿条、再查变黄条 | 不要挡住表格 |
配色留浅底白卡片。顶栏 #161b22,主按钮 #0969da。CVPR 绿、ICCV 蓝、ECCV 琥珀只给会议标签、走势线和说明页的会卡,不再加第五种颜色。删除用描边,不做成大红主按钮。页脚三列:站点一句话、五个导航、两套数字不要混比。底行写数据来自当前论文库,只用于课程。顶栏下面不再画一条蓝绿红装饰线。
连线时我改到 Figma。各页长什么样已经在细化里定了,我按那些画面做成画板底图,锁定之后盖透明热区,比在墨刀里重描一遍快。DeepSeek 列过热区可以连到哪,位置是我对着按钮、表格行和题名画的。每个弹窗的关闭单独对,没有共用一套坐标。演变按年播放是画板载入后大约 1.1 秒跳到下一张。暂停落在 2025 那张,那张不加延迟,不然停不住。词云选中、气泡挤开、竖线跟年,都是换一张底图,不是真的动画引擎。发布之后不用登录就能点。换的是连线工具,五个功能和文案没换。
下面五段是分镜接成的动图,鼠标没有画进去。每一段先写清画面上有什么、点的是哪一个控件、点完变成什么,再放图。运行中的网站在下一节,两套画面不要看成同一张。
热门方向。一开始没点任何词:权重前十的十行和词云都在,相关论文那一块是空的,空状态写着要点上面的热词。第一下点的是前十第一行 diffusion,或词云里最大的那个圆。画面换成选中:diffusion 描边,其余变淡,论文还不一定列出。第二下点的是第二行 transformer。词云改围着 transformer,下方出现该词的论文,有题名、会议、年份、摘要。最后把 CVPR、ICCV、ECCV 三个勾都去掉,再点更新统计。数卡、榜和词云隐去,出现恢复默认范围、去导入论文。去导入连到导入页的批量。

热度走势。从顶栏点热度走势进来,或在热门方向已选词时点看近几年还热不热。第一张是 2025:竖线在最右,下面三张卡是当年三个会的篇数。接着点折线最左一段,或把滑块拖到最左,切到 2022,竖线和三张卡一起变。再点滑块偏左到 2023,偏右到 2024。然后点掉 transformer 那个胶囊,只剩一个词,出现黄条:至少再选一个,折线不画。再点回 transformer,回到两个词的 2025。最后点页面下方的按年播放,从 2022 开始大约每 1.1 秒翻到 2023、2024、2025。暂停停在 2025 那张。

论文列表。顶栏点论文,或从详情点返回,进入默认表。检索框和添加在同一行。先输入一句库里没有的话,点检索。整页顶部换成绿条,写的是已按这句话补录并入库,表格里出现这条,不是弹出一个窗口。同一句再点检索,绿条换成黄条,篇数不增加。再点添加论文,弹出空表,字段是手填的,点取消或遮罩回到列表。点某一行的编辑,弹出已经填好的表,保存或取消都回列表。点删除,弹出确认,确认或取消都回列表。若是从走势点了查看当年论文进来,列表上方有一条只读条件,例如词、年、会,点取消限定之后条件消失,回到默认表。

导入。顶栏点导入,默认是批量页签:说明每条至少要有题名、摘要、关键词、原文链接,中间是一块虚线区域。点开始导入,弹出导入完成,上面是成功篇数和失败篇数。查看论文会去列表,回热门方向会到未选词的首页,点遮罩关掉这个窗口。再点单篇补录页签,题名是必填。点查找并写入,弹出写入成功。查看该论文进详情。这一页没有绿条。绿条只在论文列表。

详情和说明。在热门方向已经列出的论文里,点第一篇题名,进入详情:大标题、会议色标、年份、编号、摘要、关键词。点返回论文列表,回到默认表。点原文链接,弹出地址,点关闭或遮罩回到详情,原型里不打开外站。详情上的编辑、删除和列表是同一套弹窗。顶栏点说明:最上三张卡片是 CVPR、ICCV、ECCV,下面左右两格是两套算法,再下面是数据从哪来。这一页没有按钮会改库。

成品网站:http://159.75.211.145:8080/#/hot。浏览器里要能看见论文、前十和走势。克隆地址不算这一条。
打开网站,顶栏从左到右是热门方向、热度走势、论文、浏览、导入、说明。左上角是「顶会热词」,下面一行小字是 CVPR、ICCV、ECCV。右边是库里现在有多少篇。第一次进来停在热门方向。地址用 hash,刷新不会丢页。下面按这个顺序看。顶栏里的浏览,以及说明页上的三个会、走势页下方的历年前十,写在目录最后的 附加。和原型不一致的地方,走到那一页再写。
热门方向,还没点词。画面上有筛选条,三个会默认勾着,年份是 2020 到 2026,按钮是更新统计。下面是库内总数、当前范围、关键词数。再下面是十行前十,每行一条权重条和大约多少篇。再往下是一块「关键词工作台」:左边是词列表,中间写这个词经常和谁一起出现,右边是词云图。最底下「相关论文」还是空的,写着先选一个关键词。鼠标只是停着,没有点。这一段证明榜来自当前库,论文要等你点词。

热门方向回答两件事:当前这批论文里哪十个词更能代表方向,这些词经常和谁写在同一篇里。点更新统计会先出现「正在统计…」。库大的时候要等一会儿,等的是按当前会议和年份重算,不是页面卡死。三个勾都去掉再更新,榜会空,和原型里隐去榜、出现去导入是同一件事。筛选只在这一页和走势页之间带着走,点进论文或导入不会把这三个勾带过去。
热门方向,点一个词。鼠标先停在前十里的 3d 那一行再点。地址带上这个词,下面列出相关论文,能看见题名、会议、年份、摘要。再点一篇的原文链接,新开的是论文自己的页面。这一段要看见鼠标先停在那一行上再点,证明点词不是装饰。

点词之后论文留在这一页,和原型一样,不会立刻跳进列表。点题名才离开。点关键词可以去浏览,这是原型上没有的入口。原型里词云和共现画在同一块;网站上前十仍是能数出 1 到 10 的表,关系画在旁边,侧栏上的邻近词也限制在这十个里,避免点进去的数字和线上的篇数不是一回事。
论文详情。这一篇是 Ultra3D,上面有 ECCV、2026、编号、摘要和关键词。鼠标点原文链接,新开的是论文自己的页面,不是站内再套一个地址弹窗。回到详情后点编辑,弹出已经填好的题名、关键词和摘要。关掉之后回到论文列表。

详情和原型的差别在原文链接。原型为了不离开播放页,用弹窗把地址摆出来。网站上原文链接直接打开那篇论文。编辑和删除仍是弹窗,和列表同一套。
论文列表。顶栏停在论文。查询方式是综合,框里已经是 diffusion。鼠标先去掉 CVPR,再去掉 ICCV,只留 ECCV,然后点检索。列表从最近入库改成找到 8254 篇,都是 ECCV。每条有编号、会议色标、年份、题名、摘要和关键词,右边是编辑和删除。

论文页是已知题名的入口。从走势或演变进来时,列表上方有一条只读条件,写着词、年、会,点取消就回到默认表。添加是空表,手填,不访问外面。删一篇要确认,词会跟着删,榜按剩下的库重算。
论文列表,绿条。输入一句库里没有的话,鼠标点检索。顶部先出绿条,写明这次是联网补录,摘要、关键词、原文链接出现在结果里。库内篇数增加。不要用弹窗。

论文列表,黄条。鼠标再点一次检索。绿条换成黄条,说明这句话已经抓过,篇数不增加。这一段证明不会插两次。

绿条和黄条的位置与原型一致,都在列表顶部,不挡成窗口。外面也没有时,页面明说没有,不假装补录成功。
导入,批量。顶栏停在导入,页签停在批量导入。中间是文件区,还没选文件,可以拖进去,支持 PDF、CSV、JSON、TXT、BIB,以及这些文件打成的 ZIP。下面是开始导入和下载 CSV 模板。右边写着 CSV 表头、会议只认 CVPR / ICCV / ECCV、同一题名不会重复入库。最近一次批量记录是空的,单篇补录不会出现在这里。

导入,单篇。鼠标点单篇,填一个题名,点查找并写入。弹出写入成功。若再切到粘贴,把一条 BibTeX 贴进去写入,也进同一张库。粘贴是网站多出来的入口。

导入页不做查无则抓。手里已经有材料才走这里。列表上那句库里没有的话,才走绿条。两条路写进同一个库。
说明。顶栏停在说明。最上三张卡片是 CVPR、ICCV、ECCV,绿、蓝、琥珀。CVPR 每年都办,ICCV 只在奇数年,ECCV 只在偶数年。下面是按你现在的任务选页,再往下是统计口径:抽词、热门方向的 TF-IDF、走势按年篇数、图谱篇内共现。鼠标继续往下,能看见数据从哪里来,以及附加的历年 Top 10 演变和开题浏览。这一页没有会改库的按钮。

说明页写给第一次看的人:ICCV 只在奇数年开,ECCV 只在偶数年开,折线掉到 0 是这年没开会,不是这个方向凉了。走势纵轴是篇数,不是权重,也不是引用。浏览页在说明里被写成附加,挂了也不影响另外五页。
热度走势。顶栏停在热度走势。统计范围是 CVPR、ICCV、ECCV,年份从 2020 到 2026。参与对比的方向还是空的,旁边写着至少选择两个。逐年收录走势只有图例和空坐标,下面三张卡都是 0。鼠标在筛选附近移动,没有点播放,三条线没有画出来。

这是功能 5 要的动,必须是运行中的网站,不能用上一节的原型翻页代替。原型靠换四张底图表示年份,网站是同一张图上的线在画,播放时在已经拿到的点上移动年份。接口一次把全部年份和三个会都返回,没有届次的年份是 0。
浏览。顶栏点浏览。输入一个方向,停大约 0.2 秒,下面出现建议。点一个建议,能看见简介和相关论文。没有匹配时是中文,不是英文报错。这一页是原型没有的。
【动图】
浏览用来开题时把方向收成一页。它不替代绿条,不替代前十的算法,也不替代走势那条正在画的线。
作业这一节要看见的不是最后一版,而是来回:关键提示、DeepSeek 回了什么、我为什么留下、为什么划掉、改的时候动了哪一句。同一件事要有第二轮,不能只贴终稿。下面按作业来排。先写协作怎么进行。再把每一份记录里的轮次摊开,这是提示迭代。然后单写调试,也就是 DeepSeek 写错、我指出来并改掉的地方。最后是案例。作业点名三则:需求与原型、编码、调试。编码里来回长的步骤各自再写一则,所以不止三则。原文和截图在对应 md,这里把判断写全。
工具是 DeepSeek,版本 Hy4 preview。每一处都是同一套,不是一次生成交上去。
我先把已经钉死的边界写进提示:五个功能都做,图谱和动图不能放到以后;Flask、Vue 3、SQLite、ECharts,部署华为云;前十用 TF-IDF,走势用按年篇数;查无则抓在列表页,用提示条,不用弹窗。DeepSeek 出草稿。我对照作业和已经定稿的文档看,留下能对上的,划掉加戏和会算错的。下一轮提示只改划掉的那几条,不让整篇重写。本机再跑一遍,对上了才进仓库。
flowchart LR
bound[我先写边界] --> ask[这一轮只问一件事]
ask --> draft[DeepSeek 出草稿]
draft --> check[我对照五个功能]
check --> keep[留下]
check --> cut[划掉并写原因]
cut --> ask
keep --> run[本机跑通再入库]
分工可以讲清。场景结构、五页怎么分、配色和按钮文案、表和接口清单、前端目录、走势折线怎么逐年露出来,这些草稿来自 DeepSeek。作者标签怎么躲开停用词、更新字段不用 or 回退、图谱侧栏只留当前前十、空库也返回三个会的线、词云不用那版图、测试在创建应用之前关掉抓取,这些是我改的。草稿对不上五个功能,或者我讲不清这行会让榜、线、绿条错在哪,就不进仓库。
作业要的迭代是问法变过,结果跟着变。不是换标题重贴终稿。需求从四个字母拆开问。信息架构先出五页,再只改四条规则,最后才问配色和文案。参考图七轮,每一轮只改上一版被我指出的问题。编码则是先出一截能跑的,再拿去审查,错的改代码,口径类只写清不改。每一则都写在后面的案例里:我问什么,DeepSeek 给了什么,发生在哪一份文档,我留下什么,我划掉什么,为什么,怎么改。
调试分要看见的是:DeepSeek 的代码或结论有错,我指出来,改完再验。不是换个说法重写一遍。下面这些都进过仓库或测试,每一条都写了错在哪、我怎么改。
| 发生在哪 | DeepSeek 当时那样 | 错在哪 | 我怎么改 | 怎么知道改对了 |
|---|---|---|---|---|
骨架,阶段二-编码-01 | 作者关键词和摘要走同一套停用词 | deep、learning 把 Deep Learning 切没,导入成功但榜是空的 | 标签整段留下,能切开的单词再计一份 | 样例里 Vision Transformer 既能看见短语,也能用 transformer 搜到 |
| 同一处更新 | data.get("year") or old["year"] | 空字符串被当成没填,旧年份被吞掉 | 键不在请求里才用旧值;撞上唯一键报重复 | 把年份改成空再读,旧值还在 |
| 同一处 seed | 空文件、缺年份直接往下跑 | 导入崩,不是给出失败行 | 先查空文件和题名、会议、年份;CVPR 2024、2024-06 也能收 | 空 CSV 不崩,返回 0 |
前十,阶段二-编码-04 | 某年的文档数被当成全库文档数来算 idf | 较上年和洞察全空 | idf 用当前筛选的全集合,再乘各年的 tf | 各年权重加总等于总权重 |
| 前十 | 较上年用全库年份 | 这个词没出现的年份也参加环比 | 只取这个词自己有权重的年份;没有上一年就空 | 单年的词,较上年是空,表上是横线 |
图谱,阶段二-编码-05 | 侧栏列出前十以外的词 | 点了侧栏,图上没有对应的圆 | 邻近词用当前前十的节点过滤 | 侧栏词都能在图上找到 |
走势,阶段二-编码-06 | 空库时序列是空数组 | 有数据时永远三条线,空库变成零条,前端要写特殊分支 | 空库也返回 CVPR、ICCV、ECCV 三条,值为 0,并给出无数据提示 | 空库和有库的序列条数一样 |
五页,阶段二-编码-07 | 三万篇库上一次把好几条接口绑在一起,没有等待 | 页面像卡死 | 改成按顺序请求,并写出正在统计 | 大库上能看见等待,不是白屏 |
查无则抓,阶段二-编码-08 | 缺会议时默认写成 CVPR | 别的会的论文被算进 CVPR | 会议必须是 CVPR、ICCV、ECCV 之一,否则跳过 | 缺会议的行不进库 |
| 同一处测试 | 测试没在创建应用前关掉抓取 | 用例互相污染,绿条是假的 | HOTWORDS_CRAWL=none 写在建应用之前,不写进业务代码 | 抓取相关测试 16 条通过 |
截图留在对应 md。骨架那一轮和查无则抓的审查,下面案例 C 里贴了。
作业点名的三则都在这一节。A 是需求和原型,含头脑风暴、信息架构、文案。B 是编码,含入库、前十、图谱、热度动图和五页。C 是调试。每一则都写提示、回复摘要、留下、划掉、为什么、怎么改。
发生在 NABCD.md。我已经定了三类用户、技术栈、功能 1 到 5 都做。不确定的是每人卡在哪一步、和 Scholar、DBLP、Connected Papers 比差在哪。所以让 DeepSeek 先写场景,由我按作业收。
第 1 轮。我的提示是:固定平台、技术、数据、部署,三种角色各写 2 个场景,每条按谁、在什么情况、痛点、希望平台怎样。
DeepSeek 回了六条场景,角色也对。希望平台怎样写得很满:词云、白话解释、引用量、机构分布、BibTeX、DOI、阅读档案、课程名单。文末建议先做搜索和趋势,图谱和跨年对比以后再说。


留下:三类人、每类两条、四个要素;本科生要先看全貌,研究生要看趋势,助教要一份能共用的数据。
划掉:引用量、机构、阅读档案这些加戏。数据里不一定有这些字段。尤其划掉把功能 4、功能 5 放到以后。这两项各占基础分。
为什么这样改:不限定范围,DeepSeek 会按理想产品写。作业要的是能做完、能讲清的五个功能。
怎么改:没有在第 1 轮的稿上补句子,而是改了第 2 轮的问法。
第 2 轮。我把五个功能原文贴进去,写明前十用 TF-IDF 和条形图,数据以课程或公开数据集为主,并列出不要写进期望的内容。只改六条里的希望平台怎样,加一张场景、功能、页面表。不写做法和对比,不写代码。
DeepSeek 把六条希望收到功能 1 到 5 上,附加单独标了。
留下:这张对应表。
我自己改了三处,没有再让 DeepSeek 重写:场景 2 的入口改到列表页;清洗只当痛点,不单独做一页;导入和查不到再抓分成两页。
第 3 轮只写做法和竞争。提示写明场景已定,不要再加功能。做法按问题、功能、怎么点来写。竞争至少四个,只能用已定功能比较。
DeepSeek 把入库、查不到就抓、前十、图谱、动图分开写了,竞品表里没有再写引用量和 BibTeX。
留下这个骨架。必须我拍板的三处:动图只用按年词频;方法名不做词干合并;点边看两词同时出现的论文标成可选。不拍板,后面实现会对不上。
第 4 轮只写好处和交付,对应原型链接、实现、华为云、仓库。验收从已定场景里挑。
留下好处和三条验收。我补进定稿的两处:场景 2 仍从列表搜,不从图谱搜;关于页必做,了解更多才是附加。交付里的逐步点击改成实现后的自检,避免需求文档写成测试手册。
| 轮次 | DeepSeek 草稿 | 我交上去的 |
|---|---|---|
| 1 | 功能可以以后再做,并加上阅读清单 | 五个功能都在这一版 |
| 2 | 六条希望已经对上功能 | 入口改到列表,导入和抓取分两页 |
| 3 | 做法和竞品表可用 | 动图只用篇数,词不合并 |
| 4 | 好处和交付可用 | 关于页必做,了解更多才是附加 |
发生在 原型信息架构.md。对应原型评分里和 AI 一起定页面、以及跳转、弹窗、查询联动、数据怎么流。五页、图谱不做搜词框、导入和列表分开,这些我已经定了。不确定的是每页怎么排、筛选会不会串到列表、列表新增和导入单篇会不会画成同一个表。
第 1 轮。提示只给已定功能和五页,按页写区块、按钮文案、空状态,再给跳转、弹窗、数据流转三张表。不许加功能,不许写代码。后来补了一句:两个附加都要画进对应页,并标附加。
DeepSeek 把五页都写了。筛选只放首页和走势;图谱无搜词框;写库只有导入和列表里的查无则抓。三张表齐全。
留下:五页划分;首页点词出论文;查无则抓放列表;两种算法分开写在关于页。
还不能定稿的原因:筛选写成切页都沿用,会把年份套到列表上;走势空状态写成至少选 1 个词,对不上两个词才有对比;列表新增和导入单篇可能画成一个表单。所以进入第 2 轮,不重写五页。
第 2 轮只改四点:走势至少两个词;筛选只在首页和走势之间沿用;列表新增手填,导入单篇才走 DBLP;附加演变允许点词进列表;补录用表内提示条,不要弹窗挡住表格。
DeepSeek 四条都改了。未选词和只选 1 个词分开写了提示。演变点某年某词只带词和年。补录改为表上方提示条。
四条全部留下,写进定稿。
我补了两处,没有再要一版:点图谱节点必须仍在首页论文面板里出论文。DeepSeek 把节点和边点进列表写进例外,这和功能 4 打架,所以没采纳。进列表只走点题名、点走势数据、点演变里的词。另一处是附加两块要画出来,不要只用灰色未实现。
第 3 轮只问布局、配色、文案。架构不许改。不要改导航五个名字。
DeepSeek 给了四色:底、白卡片、主色、强调色。首页论文面板放图谱右侧。列表把新增一篇放在查询行最右。
留下这套排法。划掉或改写的:有些句子太像套话,改成按当前库、当前筛选,用 TF-IDF 取前十。不增加第五种颜色。删除键用橙色描边,不当填充主按钮。窄屏把面板挪到下面只当备注,正式画板按宽屏。
| 项目 | DeepSeek 第一稿 | 我定稿 |
|---|---|---|
| 筛选 | 切页都带着走 | 只在热门方向和走势之间 |
| 走势选词 | 写过至少一个 | 至少两个才画,只选一个要提示 |
| 点节点 | 可以进列表 | 仍在本页出论文 |
| 查无则抓 | 可以用弹窗 | 表上方提示条 |
发生在 原型设计细化.md。信息架构已经定了规则,这一份是把每页的样子收紧,一共七轮。每一轮都是我指出上一版哪里不对,DeepSeek 改一版,我再决定留还是划。
| 轮次 | 我问什么 | DeepSeek 给了什么 | 我留下 | 我划掉 | 为什么、怎么改 |
|---|---|---|---|---|---|
| 1 | 按作业和已定文档出各页参考 | 五页齐套的初版 | 这套底,后面只在上面改 | 不另起一套页面 | 有底才能对,重画会把已经定的规则画丢 |
| 2 | 初版太挤,导航对不上 | 导航收成同一套 | 五个名字和顶栏一致 | 和作业无关的页面名 | 助教按五个功能找页,多出来的名字对不上 |
| 3 | 按架构重做,论文用文字卡片 | 深色顶栏、白卡片 | 论文只留文字 | 装饰图标和假缩略图 | 数据里没有图,原型假装有图,后面实现会对不上 |
| 4 | 说明页往前,走势跟年,词云要醒目 | 逐年读数、三会卡片、页脚 | 词云和共现放在一起看 | 没有单独再开一页讲关系 | 功能 4 留在热门方向本页 |
| 5 | 数字用图,词云和关系合成一块 | 权重前十表、气泡、纵向长页 | 前十仍是能数出 1 到 10 的表 | 不用词云代替前十 | 前十要能复核名次 |
| 6 | 按原型评分通查 | 数卡、洞察、列表提示条的改法 | 三会颜色用在标签和折线上 | 列表上啰嗦的状态行 | 改成表上方一条提示,和查无则抓同一位置 |
| 7 | 这些参考怎么进原型工具 | 正式原型用专用工具,参考图只当底 | 热区自己连 | 把参考图链接当成正式原型 | 作业要的是能点的原型网页,不是一张效果图 |
按钮文案我改了一处:信息架构里叫应用筛选,页面上改成更新统计,和后来网站上的按钮一致。
发生在 原型设计结果.md。底图定了以后,正式可点的原型做在 Figma。这一轮问的是注册好之后怎么铺、怎么连,不是再改需求。
留下的连法:五页顶栏;筛选只在热门方向和走势之间;点权重前十或词上的圆,论文留在本页,点题名才进详情;列表绿条、再查黄条,不用弹窗;走势至少两个词才画线;演变按年翻,大约 1.1 秒一张,暂停那张不加延迟,否则停不下来;导入批量以及单篇各有结果弹窗;说明页只读。
划掉的是用原型工具导出代码。作业禁止用原型工具生成实现。热区是我对着按钮连的,DeepSeek 只列出从哪一块连到哪一块。
发生在 阶段二-实现设计.md。提示写明只要设计,不要整站代码。
留下:先库后页面;四张表和唯一键;接口按五个功能拆;查无则抓的状态放在列表响应里;部署放在功能能在本机跑通之后。
划掉一上来就上云。本机对不齐的接口,放到云上只是把同一处错误放到远处。筛选、绿条、两套数字这些边界,设计稿里写死,不让编码那一步再发明一套。
发生在 阶段二-编码协作.md 第 1 步,详文在 阶段二-编码-01-骨架与入库.md。这一则同时是编码和后面案例 C 的起点。
我已经定了四张表、写库只走 PaperRepo.create、两套数不混、库文件不进仓库。提示只要第 1 步:表结构、应用入口、建库、样例导入、健康检查。不要整站代码。去重用题名、会议、年份。样例和以后的导入页走同一条写库函数。
DeepSeek 回了四张表和索引,统一的返回形状,命中唯一键不覆盖,健康检查返回篇数。并说在沙盒里导入 12 条,再导一次跳过 12 条。
留下:目录拆开、四张表、唯一键、健康检查、写库只走 create。本机按这个顺序跑:建库、导入、再导入一次。第一次新增 12,第二次新增 0、跳过 12。
划掉并改了五处,原因都是草稿会让统计变空、导入崩掉,或者仓库缺作业要的文件。
codestyle.md。
发生在第 2 步、第 3 步,详文在 阶段二-编码-02-论文列表.md、阶段二-编码-03-导入接口.md。做法是 DeepSeek 出一版,我先反问,本机跑测试,再决定进不进仓库。
列表。留下五个论文路由,以及库里没有、刚抓到、已经抓过、外面也没有这几种状态。划掉缺会议时默认 CVPR。抓到的行一旦进库就会进前十和走势,默认会议会把别的会算成 CVPR。也划掉用容易撞车的历史键,同一句话和另一句话可能被当成同一次抓取,黄条会错。第 3 轮把键改成能分开两次查询。
导入。留下批量、单篇、批次记录这三条;批次记录只记批量;样例导入和批量导入共用同一套写入。审查列出的空文件要拒绝、成功篇数要带回编号、测试互相隔离,这些我按清单改了代码再请复验。嵌套的抓取状态被展平,避免页面读到两层不一致的结果。
为什么不原样提交:第 1 轮里的默认会议和历史键若直接粘贴,后面前十的三会分布和列表黄条都会错。我讲得清后果,所以拒绝原样进仓库。
发生在 阶段二-编码-04-热门方向.md。我先按设计写了概览和前十两个接口,再把文件交给 DeepSeek 对照功能 3 审查,不是让 DeepSeek 从零写算法。
审查指出两处算错,我都改了代码。某年的文档数被当成全库文档数,较上年和洞察是空的。改成 idf 来自当前筛选的全集合。较上年用了全库年份,这个词没出现的年也参加比较。改成只看这个词自己的年份。
作者标签里的 the、of 会进榜。整段标签若是停用词就丢掉。滤完以后一篇里没有词,就回退到题名和摘要再切,否则有的样例会从榜上消失。
有一条没改代码:短语整段保留的同时再拆词,审查担心共现把 image 这类词放大。我暂不改切词。切词一改,列表搜索会跟着变,范围过大。图谱用当前前十限制节点,放在下一步处理。
测试里洞察用例原先绑着样例年份,换一份数据结论就漂。改成测试自带一份趋势数据。这样通过与否不靠审查附件和我本机是不是同一份库。
发生在 阶段二-编码-05-关键词图谱.md。提示写明对照功能 4:篇内共现、节点用当前前十、点词出论文、不要写页面。
DeepSeek 审查出两处,我都改了。侧栏出现不在前十里的词,点下去图上没有圆。邻近词改成只用这十个节点。另一处是约篇数和下面列表的命中数混用。不在前十时,权重和约篇数应为空,列表条数仍可以大于 0。两个数字不是同一件事。
性能上再缓存一层,我暂缓。先保证点进去的词和图上的圆是同一批。
发生在 阶段二-编码-06-热度走势.md,页面动画在 阶段二-编码-07-前端五页.md。作业要的动是运行中的网站上线在画,不是换几张底图。
后端。提示是对照设计里的走势一节审查。留下按年、按会数篇数,一次把全部年份返回,没有届次的年是 0。少于两个词不画对比。
改了两处。空库时序列是空的,有数据时却永远是三个会。空库也返回三条,值为 0。空库还要有无数据的提示,不能和词数不够混成没有提示。
有一条没改接口:筛选到一个没有论文的遥远年份,横轴还在、值是 0。这和库是空的不是同一种情况。我在取年份的地方写清,页面用提示或合计为 0 来分支,不为这一种再改返回形状。
前端。折线用已经拿到的点,按年份逐段露出来,播放时不再请求。播放大约 0.8 秒一步。这一段我整段留下,因为和第 6 步的接口是同一套。少于两个词时页面自己提示,不画线。
我问过较上一年用权重还是篇数。走势纵轴用篇数。较上一年留在前十那张表上,用的是权重变化,不画进折线。
发生在 阶段二-编码-07-前端五页.md。DeepSeek 出了前端草稿:目录、请求、走势动画、绿条和黄条。我在本机三万篇的库上对过再改。


留下:按资源拆的目录、地址用 hash、开发时代理、筛选状态单独放、弹窗不进路由、走势只请求一次、列表按抓取状态出条、上传和普通请求分开设置、顶栏篇数来自健康检查。
划掉的每一条都有页面上的后果。
| 草稿 | 我交上去的 | 为什么 |
|---|---|---|
| 词的关系用现成图组件 | 自己画圆和线 | 要能点、能只强调一个词,现成那版对不上本页交互 |
| 热门方向一次请求多条,没有等待 | 按顺序请求,并写正在统计 | 库大时像卡死 |
| 两张卡都用当前范围的篇数 | 库内总数、当前范围、关键词数分开 | 三套数不能看成同一个数 |
| 进论文页就先拉一整表 | 有条件才拉,摘要收起 | 从别的页带条件进来时,不该先闪一版默认表 |
| 筛选年份一开始为空,再靠健康检查填 | 默认一段年份 | 第一次进来就要能看见榜 |
| 把压缩包原样盖住仓库 | 只合并对过的文件 | 一份草稿覆盖会把已经改过的后端冲掉 |
删除确认草稿里用浏览器自带的确认。我留下弹窗方案,和列表、详情同一套,不另做一种。
发生在 阶段二-编码-08-查无则抓.md。列表上的绿条、黄条要真的走通。DeepSeek 做了审查,不是再写一套抓取。
审查把问题分成必须改和只需写清的。我留下并改了这些:建应用之前把抓取设为不访问外面,避免上一个测试的开关漏到下一个;外面抛错时列表仍要正常返回,不能变成服务器错误;补一条真正关掉抓取时的测试,证明不会假绿。演示用的临时数据和怎么清掉,写进说明,不装成生产数据。
划掉的是把测试开关写进正式的启动过程。测试归测试,业务代码不该认识测试框架。缺会议时默认 CVPR 也在这一步再次拒绝,和第 2 步是同一条理由。

改完之后,抓取相关测试 16 条通过。通过的意思是四种状态分得开,不是页面截了一张绿条就算完。
发生在编码协作记录第 9 步。这不是五个基础功能的替代。提示是:关键词要有短简介,优先外面的百科,失败就用本地词条;热门页要能按前缀搜库里的词。
留下:失败就降级、搜索防抖、浏览页和热门方向共用筛选、短清单最多 20 条而且只在浏览器里。
划掉英文的没有匹配。我改成中文,并留一组热门词当兜底。会议名之间的分隔曾经是乱码,改成正常的标点。新页面加在地址里,不替换原来的五页。
这一节写每个功能当初怎么想、数据从哪来、页面上点一下之后发生什么。记录文件里有审查过程,这里不把读者再送去翻文件。
五个功能对应五种动作,不是五个互相独立的小网站。
| 功能 | 用户当时要做的事 | 页面 | 不做什么 |
|---|---|---|---|
| 1 入库 | 手里已经有一批或一篇材料,放进库 | 导入 | 不在这一页搜库里没有的题 |
| 2 列表 | 找一篇、改错、删重复;没有就按这句话去外面抓 | 论文、详情 | 不在这里重算前十 |
| 3 前十 | 当前这批论文里,哪十个词更能代表方向 | 热门方向上半 | 不用篇数当名次 |
| 4 图谱 | 这十个词里,谁经常写在同一篇 | 热门方向下半 | 不把前十以外的词画进来 |
| 5 走势 | 这几个词这几年、三个会各有多少篇 | 热度走势 | 不把权重画进折线 |
顺序是先有库,再列表,再导入接口,再三种统计,然后五页接上。绿条补在列表查询上,不单独做第六个功能。部署放在本机能点通之后。DeepSeek 建议过更早上云,我没听。环境会把对功能的时间吃掉,云上对不齐的接口只是同一处错误放到远处。
flowchart TB
subgraph pages [页面]
hot[热门方向]
trend[热度走势]
papers[论文和详情]
imp[导入]
about[说明]
end
hot --> api[Flask 接口]
trend --> api
papers --> api
imp --> api
about --> api
api --> create[唯一的写入]
create --> db[(SQLite)]
api --> tfidf[前十:TF-IDF]
api --> co[图谱:篇内共现]
api --> cnt[走势:按年篇数]
tfidf --> db
co --> db
cnt --> db
一篇论文进库之后,三条统计都只读库,不在浏览器里现切词。
flowchart LR
file[文件或单篇或粘贴] --> check[校验题名会议年份]
check --> create[同一条写入]
create --> papersT[论文表]
create --> words[词表]
words --> top10[前十]
words --> graph[共现]
papersT --> trendN[按年篇数]
数据主路是公开元数据:CVPR 每年,ICCV 奇数年,ECCV 偶数年,大约 2020 到 2026。原文优先开放获取页面。大表不提交进仓库。单篇在库里找不到,才用 DBLP。2026-09-24 那天批量访问 DBLP 被挡成验证页面,本机用 12 条样例把接口跑通。前端联调时用过更大的本地库,那不是现在公网上的数字。这些材料只用于这门课。
清洗先做题名、会议、年份,再抽词,再入库。会议写成 CVPR 2024 收成 CVPR,年份写成 2024-06 收成 2024。同一题名、同一会议、同一年再来一次,不变成两行。
热门页上能看见权重、约篇数、较上一年,走势页上又能看见篇数。这三套数回答的不是同一个问题,放在一条轴上会把人带偏。说明页把这件事写给第一次看的人。
| 数 | 问的是 | 用在哪 | 不用在哪 |
|---|---|---|---|
| TF-IDF 权重 | 这个词有多能代表当前这批论文 | 前十的名次和横条 | 走势纵轴 |
| 约篇数 | 这个词出现在多少篇里 | 前十表里的一列 | 不拿它排序 |
| 按年篇数 | 这一年、这个会,有多少篇提到这些词 | 走势折线 | 不拿它和权重比高低 |
前十用 TF-IDF,是为了压住每篇都有的泛词。图用篇内共现,是为了看哪些词经常一起出现。走势数篇,是为了看这一年还有多少篇。换筛选,三套数一起按当前会议和年份重算。刚抓进来或删掉的论文,不能还显示旧榜,所以缓存带着篇数和更新时间。
想法:手里已经有材料,才走导入。课程或公开数据是一大批,不能一篇篇手打。单篇是补一条已经知道的记录。后来加的粘贴,是把一条 BibTeX 喂进同一张库。需求定稿时我拒绝过自动补 DOI、阅读清单那种文献工具。粘贴只是多一个入口,写库仍然一条路。
这一页不做查无则抓。库里没有、要按查询语句去外面抓,那是论文列表上的绿条。两条路如果都做在导入页,用户会分不清自己是在交材料,还是在搜。
flowchart TD
start[打开导入] --> tab{哪一个页签}
tab -->|批量| file[选文件]
tab -->|单篇| one[填题名]
tab -->|粘贴| bib[贴一条 BibTeX]
file --> empty{文件是空的吗}
empty -->|是| reject[先拒绝,不入库]
empty -->|否| rows[拆成一行行]
one --> rows
bib --> rows
rows --> valid[题名必须有,会议和年份收成标准写法]
valid --> create[同一条写入]
create --> dup{题名加会议加年份已经有了吗}
dup -->|有| skip[跳过,不算新的一篇]
dup -->|没有| ok[写入论文和词]
ok --> done[弹出成功篇数和失败篇数]
skip --> done
摘要、关键词、原文链接都进论文表。没有摘要时,词从题名来。作者写的关键词整段留下,里面还能切开的单词再计一份,避免 Deep Learning 这种标签被停用词切没。空文件先拒绝,不让导入直接崩。上传体积没有先写一个固定上限,留到和服务器的实际限制一起看。
想法:论文页是已知题名的入口,也是改错和删重复的地方。添加是空表,手填,不访问外面。编辑时,请求里没带的字段保持旧值,不能因为空字符串把年份吞掉。删一篇要确认,词跟着删,前十和走势按剩下的库重算。
查找分两种。题名精确:大小写和标点不同,仍算同一篇,比的是整理过的题名。编号、关键词模糊:对得上就出列表。从走势或演变进来时,地址上带着词、年、会,列表上方一条只读条件,点取消就回到默认表。这条条件是跳转带来的,不是热门页那三个勾跟着过来。
库里没有这句话,才按查询语句去外面抓。抓取只补摘要、关键词、原文链接,结果就在列表顶部,不用弹窗。
flowchart TD
q[点检索] --> db{库里有吗}
db -->|有| hit[直接列出,不提示补录]
db -->|没有| crawl{允许到外面抓吗}
crawl -->|关掉| none[页面明说没有]
crawl -->|开着| net{外面有吗}
net -->|没有| none
net -->|有,而且这句话没抓过| green[绿条:这次联网补录,并写入]
net -->|这句话已经抓过| yellow[黄条:不再插第二次]
green --> list[摘要、关键词、原文链接出现在结果里]
yellow --> list
四种状态由接口说明,页面只显示人话,不把后端的英文句子贴出来。模拟、真的访问 DBLP、完全关闭,用环境变量切换。测试必须在创建应用之前关掉抓取,否则上一个用例的开关会让下一个用例假绿。
想法:用户打开首页,要先回答当前这批论文里哪十个方向更突出。范围只认已经入库、并且落在所选会议和年份里的论文。三个勾都去掉再更新,榜就空,和没有数据时去导入是同一件事。换年份或换会议,榜应该变,这是故意的。
名次用 TF-IDF,不用篇数。一篇里到处都有的词,篇数很高,但不代表方向。权重高的排前面。排完丢掉 which、image 这种不能当方向的词,再取十个。表上仍能看见大约多少篇、较上一年、三个会各占多少,这些是辅助,不是排序依据。横条的长度是权重。
点某一行,就是选中这个词,页面滚到下面的工作台,并准备列出相关论文。点更新统计时先出现正在统计。库大的时候要等一会儿,等的是按当前范围重算,不是页面卡死。
较上一年比的是这个词自己的权重,只看它出现过的相邻年份。这个词只有一年,较上一年就是空,表上画横线,不拿全库的日历硬比。
想法:前十回答谁更突出,图谱回答他们经常和谁写在同一篇。两件事都留在热门方向这一页。点了词立刻跳进列表,图谱就变成另一张检索表,功能 4 的点击就没了。
节点就是当前这十个,不多画。每一篇论文里,落在这十个里的词两两搭配,一起出现一次,线就粗一点。所以线的粗细是篇数,不是引用,也不是权重。
flowchart LR
top[当前前十] --> nodes[只画这十个点]
paper[每一篇论文] --> pair[篇内的词两两搭配]
pair --> edge[同时落在这十个里才连线]
click[点一个词] --> stay[仍在这一页]
stay --> side[侧栏只显示这十个里的邻居]
stay --> rel[下面列出相关论文]
title[点题名] --> leave[才离开,进详情]
侧栏上的邻近词也限制在这十个里。如果侧栏冒出前十以外的词,点下去图上没有对应的圆,线上的数字和表上的篇数就不是一回事。前十仍是能数出 1 到 10 的表。关系画在旁边,不用词云代替名次。
想法:看一个方向这几年还在不在做,看的是篇数随年份怎么走,并且 CVPR、ICCV、ECCV 要能放在一起比。少于两个词没有对比,不画线,只提示再选一个。
接口一次把全部年份和三个会都返回。播放时只是在已经拿到的点上移动年份,不再请求。没有届次的年份是 0。ICCV 只在奇数年开,ECCV 只在偶数年开,折线掉到 0 是这年没开会,不是这个方向凉了。
数的是论文篇数,同一篇在同一年同一个会只数一次,按年和会议分组。三个会都会出现在结果里。空库也返回三条线,值为 0,并说明库是空的。这样页面不用猜某个会是没返回,还是篇数为 0。
flowchart TD
words{至少两个词吗}
words -->|否| hint[提示,不画对比]
words -->|是| api[一次取回全部年份和三个会]
api --> draw[折线大约 0.7 秒自己画完]
draw --> play[点播放,年份往前走]
play --> pause[点暂停,读出这一年三个会各自的篇数]
pause --> jump[查看当年论文,带到列表,上方出现只读条件]
动有两层,都必须是运行中的网站。一层是线自己画出来。一层是播放时年份往前走,大约 0.8 秒一年。博客里的录屏放在成品那一节,这里不重复贴两份不一样的文件。原型靠换底图表示年份,不能代替这一条。
五页只是把上面五条路放进导航,不另做一套算法。地址用 hash,刷新不会丢页。筛选的三个勾和年份只在热门方向和走势之间带着走。点进论文或导入,不把这三个勾带过去。
| 页面 | 打开时读什么 | 点一下之后 |
|---|---|---|
| 热门方向 | 篇数、前十、共现 | 点词留在本页;点题名去详情 |
| 热度走势 | 至少两个词的按年篇数 | 播放只移动年份;查看当年论文去列表 |
| 论文 | 库里的表,或一次抓取的结果 | 绿条或黄条在表上方;编辑和删除是弹窗 |
| 导入 | 不先查库 | 写入成功后,前十和走势按新库重算 |
| 说明 | 三个会、两套算法、数据从哪来 | 没有会改库的按钮 |
详情不占顶栏一项。从论文点进去时,顶栏论文仍是当前页。原文链接直接打开那篇论文的地址。原型里用弹窗把地址摆出来,是为了播放时不离开原型,网站上不需要再套一层。
代码已经在 腾讯云。运行时由 Flask 托管前端打包结果,数据库放在服务器的数据目录,不进仓库。克隆地址打开看不到论文和走势。
能打开的首页和项目交付那张表、成品那一节是同一行:
上线后要再点一遍:导入、列表和绿条、前十、点词、走势播放。本机的样例和测试不能代替这一遍。
上一节是数据怎么走。这一节把十份记录里的关键代码按功能放在一起。阶段二-实现设计.md 先定了表、接口和页面,里面没有可运行的函数,所以不贴代码。阶段二-编码协作.md 是总目录,第 1 步到第 8 步对应下面各段,第 9 步浏览放在附加。能讲清的才贴。草稿里后来被改掉的写法,贴的是改完进仓库的那一版。
实现的是后面五个功能共用的底,记在 阶段二-编码-01-骨架与入库.md。同一题名、同一会议、同一年再导一次,不该变成两行。词挂在论文上,论文删了,词一起走。
CREATE UNIQUE INDEX IF NOT EXISTS ux_papers_dedup
ON papers (title_norm, venue, year);
CREATE TABLE IF NOT EXISTS paper_keywords (
paper_id INTEGER NOT NULL REFERENCES papers (id) ON DELETE CASCADE,
keyword TEXT NOT NULL,
tf INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (paper_id, keyword)
);
导入、手填、抓取都进 create。插入论文和重写关键词在同一个事务里。撞上唯一键时,返回已经有的那篇,写成功后清掉统计缓存,新论文才会出现在前十里。
def create(data: dict) -> tuple[int, bool]:
title_norm = normalize_title(title)
try:
with conn:
cur = conn.execute(sql, params)
paper_id = cur.lastrowid
PaperRepo._replace_keywords(conn, paper_id, title, abstract, keywords)
except sqlite3.IntegrityError:
row = conn.execute(
"SELECT id FROM papers WHERE title_norm = ? AND venue = ? AND year = ?",
(title_norm, venue, year),
).fetchone()
if row is None:
raise
return int(row["id"]), False
invalidate_stats_cache()
return paper_id, True
作者给了关键词,就整段留下,再把切得开的单词补一份。字段是空的,才去切题名和摘要。这是我改过的。草稿把关键词和摘要走同一套停用词,Deep Learning 会被切空。
def keyword_source(title: str, abstract: str, raw_keywords: str) -> dict[str, int]:
tags = split_keywords(raw_keywords)
if tags:
merged: Counter = Counter()
for tag in tags:
merged[tag] += 1
for token, tf in tokenize(tag).items():
if token != tag:
merged[token] += tf
if merged:
return dict(merged)
return keywords_of(title, abstract)
更新不能用 or。请求里没带的字段才沿用旧值,空字符串和 0 都算用户填过。
def _pick(data: dict, key: str, fallback):
if key not in data or data[key] is None:
return fallback
return data[key]
会议写成 CVPR 2024、年份写成 2024-06,先收成 CVPR 和四位年。空文件在写入前就返回 0 条,不往下崩。
def validate_venue(venue: str) -> str:
text = (venue or "").strip().upper()
for name in VENUES:
if name in text:
return name
raise ValueError(f"会议必须是 {', '.join(VENUES)} 之一,收到:{venue!r}")
def seed(path: Path, source: str = "batch_import") -> dict:
rows = read_rows(path)
if not rows:
return {"total": 0, "success": 0, "duplicated": 0, "failed": 0, "failures": []}
实现的是把已经有的材料写进库,记在 阶段二-编码-03-导入接口.md。批量和样例导入数的是新增还是重复,都只调用上面的 create,不另写一套插入。
paper_id, created = PaperRepo.create(normalized_row)
if created:
success += 1
else:
duplicated += 1
单篇如果填了会议和年份,就当手填直接写入。没填齐,才走抓取。导入页不把黄条那句话原样拿来用:这句话已经抓过,在单篇上改报成没有新的一篇,避免导入页冒出列表上的黄条。
_SINGLE_FETCH_MAP = {
STATUS_CRAWLED: "crawled",
STATUS_NOT_FOUND: "not_found",
STATUS_ALREADY_CRAWLED: "not_found",
}
def resolve_single_import(data: dict) -> dict:
if _has_venue_year(data):
paper_id, created = PaperRepo.create(payload)
...
return _run_fetch_path(data, title)
批量接口只负责拆文件、调用写入、记一批日志。空批次不记日志。
实现的是列表上的查找、增删改,以及库里没有就按这句话去外面抓。查找和四种状态记在 阶段二-编码-02-论文列表.md,抓取开关和入库校验记在 阶段二-编码-08-查无则抓.md。
精确查找比的是整理过的题名。大小写和标点不同,仍算同一篇。模糊查找走题名、编号或关键词。从走势进来的词、年、会是地址上的只读条件,取消之后才回到默认表。
库里已经有结果,状态是命中,不去外面。没有结果时,先看这句话抓过没有。抓过就是黄条。没抓过、并且外面的源关着,就明说没有。开着才去抓,抓到再查一次库,让刚写入的行出现在同一次响应里。
def resolve_fetch(query: str, mode: str, has_result: bool) -> dict:
if not query or has_result:
return {"status": STATUS_HIT, "inserted": 0, "skipped": [], "message": ""}
key = history_key(query, mode)
if CrawlHistoryRepo.exists(key):
return {"status": STATUS_ALREADY_CRAWLED, "inserted": 0, "skipped": [],
"message": "这个查询之前已经抓过,本次不再补录"}
if not has_enabled_provider():
return {"status": STATUS_NOT_FOUND, "inserted": 0, "skipped": [],
"message": "库内没有找到相关论文,外部数据源尚未接入,本次未补录"}
return _fetch_and_store(query, mode, key)
同一次请求里,刚抓到要立刻再搜一遍,否则绿条说补录了,表还是空的。
items, total = _search(**search_kw)
fetch = resolve_fetch(query, mode, bool(total))
if fetch["status"] == STATUS_CRAWLED:
items, total = _search(**search_kw)
两次查询不能因为空格或大小写被当成同一次。键用检索方式加整理过的句子,中间用分隔符隔开。
def history_key(query: str, mode: str) -> str:
normalized_mode = (mode or "fuzzy").strip().lower()
return f"{normalized_mode}{UNIT_SEP}{_loose_query(query)}"
抓回来的一条,会议必须是 CVPR、ICCV、ECCV,年份必须能转成整数。缺了就丢掉,不默认写成 CVPR。默认会议会把别的会的论文算进 CVPR,前十和走势都会偏。
def _normalize_item(item: dict) -> tuple[dict | None, str]:
title = (data.get("title") or "").strip()
if not title:
return None, "题名为空"
venue = (data.get("venue") or "").strip().upper()
if venue not in VENUES:
return None, f"会议缺失或非法:{venue or '空'}"
每一条通过校验的才调用 create。插进几篇,这次就是绿条。环境变量决定这次是模拟、去 DBLP,还是关闭抓取。测试要在创建应用之前设成关闭,不然用例互相补录,看起来像通过了。
STATUS_HIT = "hit"
STATUS_CRAWLED = "crawled"
STATUS_ALREADY_CRAWLED = "already_crawled"
STATUS_NOT_FOUND = "not_found"
实现的是热门方向的名次,记在 阶段二-编码-04-热门方向.md。N 是当前筛选里的论文数,df 是出现过这个词的论文数。换会议、换年份,N 变了,榜就变。各年的权重用同一个 idf,加总应当等于这个词的总权重。草稿曾把某一年的文档数当成全库文档数,较上年和洞察会全空。
df = len({int(r["paper_id"]) for r in kw_rows})
idf = math.log(n_papers / df) if df else 0.0
weight = sum((1.0 + math.log(max(int(r["tf"]), 1))) * idf for r in kw_rows)
for year, year_rows in by_year.items():
year_weights[year] = sum(
(1.0 + math.log(max(int(r["tf"]), 1))) * idf for r in year_rows
)
较上一年只看这个词自己出现过的年份。没有上一年,或上一年权重是 0,就返回空,表上画横线。
def _yoy_rate(year_weights: dict[int, float], years: list[int]) -> float | None:
ref = max(years)
prev = ref - 1
cur_w = year_weights.get(ref)
prev_w = year_weights.get(prev)
if cur_w is None or prev_w is None or prev_w <= 0:
return None
return (cur_w - prev_w) / prev_w
排序先比权重,权重相同再按词的字母序,名次才能复现。取前十。which、image 这种不能当方向的词,在这一步丢掉,不让它们占名额。
实现的是关键词之间的关系,记在 阶段二-编码-05-关键词图谱.md。节点先取当前这十个词,不另算一套榜。每一篇论文里,落在这十个里的词两两搭配,一起出现一次,边加一。一篇里同一对词只贡献一次。
def build_cooccurrence(paper_filter: PaperFilter) -> dict:
top_items = build_top10(paper_filter)
node_ids = {item["keyword"] for item in top_items}
by_paper = _by_paper_keywords(paper_filter, allowed=node_ids)
for keywords in by_paper.values():
for a, b in combinations(sorted(keywords), 2):
link_counts[(a, b)] += 1
侧栏上的邻近词也必须在这十个里。点到前十以外的词,图上没有圆,线上的数字和表上的篇数就不是一回事。不在前十时,约篇数为空,下面列表的命中数仍可以大于 0。
def _co_neighbors(..., node_ids: set[str], ...):
for other in keywords:
if other == target or other not in node_ids:
continue
counts[other] += 1
点词之后的论文,复用列表那套按词、会议、年份的查询,不另写一条搜索。
实现的是多年、三个会的篇数对比,后端记在 阶段二-编码-06-热度走势.md,线上的动画记在 阶段二-编码-07-前端五页.md。
数的是论文篇数。同一篇在同一年、同一个会只数一次。三个会都按年分组,没有届次就是 0,页面不用猜某个会是没返回还是篇数为 0。
sql = (
"SELECT p.year, p.venue, COUNT(DISTINCT p.id) AS cnt"
" FROM papers p"
" INNER JOIN paper_keywords pk ON pk.paper_id = p.id"
f"{where}"
" GROUP BY p.year, p.venue"
)
少于两个词,不画对比,给出再选一个的提示。库是空的,仍返回三个会的空序列,并说明没有数据。这两种提示不能合成一种。
if not years:
hint = HINT_NEED_KEYWORDS if len(normalized) < 2 else HINT_NO_DATA
return {"years": [], "series": _series_shell(), "keywords": normalized, "hint": hint}
if len(normalized) < 2:
return {"years": years, "series": _empty_series(years), "hint": HINT_NEED_KEYWORDS}
接口一次把全部年份返回。多个词用重复的参数传,和列表的关键词参数同一风格。
def parse_keywords(request: Request) -> list[str]:
words: list[str] = []
for item in request.args.getlist("keyword"):
for part in item.split(","):
word = part.strip().lower()
if word and word not in words:
words.append(word)
return words
@bp.get("")
def trend():
return ok(build_trend(PaperFilter(**parse_filter(request)), parse_keywords(request)))
页面上的动不请求新数据。折线大约 0.7 秒自己画完。播放大约 0.8 秒走一年,只是把已经拿到的点逐段露出来。
.draw-line {
stroke-dasharray: 1200;
stroke-dashoffset: 1200;
animation: draw 0.7s ease forwards;
}
五页接到这些接口上时,地址用 hash,刷新和静态托管不会丢页。三个勾和年份只在热门方向和走势之间带着走。进论文、导入、说明不带这套筛选。从走势跳到论文,用地址上的查询带词和年。详情不占顶栏一项,论文仍高亮。顶栏篇数来自健康检查,导入、删除、补录之后再读一次。
表、唯一写入、一次返回全部年份,设计稿里就有。标签怎么留、更新不用 or、纵轴用篇数、侧栏只留前十、空库也返回三条线,是对照记录改过的。讲不清的文件没有贴在这里。
有三件我记得很具体。
直连 DBLP 的那天,返回的页面在问我是不是机器人。我盯了一会才确定不是证书坏了。如果把这次失败写成库已经满了,后面的前十就是假的。所以主数据改走公开数据集,本机先用 12 条把健康检查跑通。
另一件更阴。导入显示 12 篇,榜却是空的。我先怀疑 CSV 被逗号切坏,对过停用词才发现 deep 和 learning 自己把标签吃了。从那以后我先看词表里有没有行,再相信榜。
第三件是原型。信息架构按墨刀写完,连线时我改去 Figma,等于页面要再摆一次。摆完才发现热区可以盖在已经定稿的底图上。工具换了,五个功能没换。
构建之法第 4 章把结对写成一个开、一个看。这里多数时候我在开。DeepSeek 写得快,也会在同一段里加上书单。人很少这么稳定地加戏,但人可以指着屏幕问绿条在哪。没有同学坐在旁边,我就把拒绝写进 md。不然一周后仓库里只剩最终代码,看起来像我一开始就写对了。
相同的地方是:都要有人看另一份产出,都要把没看懂的留下。不同的地方是速度和加戏。好处是表和工厂一轮就有,需求可以按字母拆开问。坏处是 DeepSeek 会把基础功能写成以后再做。DBLP 被挡是网站当时的行为,我没把它算成模型过期。密钥和数据库没有进仓库,没看懂的抓取也不会直接开到公网。
结对伙伴只有 DeepSeek,版本是 Hy4 preview。DeepSeek 适合起稿:三个角色、五页怎么分、表和接口、工厂和目录,一轮就能铺开。我留得最多的是结构。走势一次把全部年份和三个会拿回来,播放只在已经拿到的点上移动,这个结构我留了。入库走唯一的写入,撞上同一题名、同一会议、同一年就跳过,不覆盖,这个我也留了。列表上库里没有就抓、再查不再插第二次,绿条和黄条的位置我留了。
DeepSeek 定不了哪一套数算分。前十是 TF-IDF,图谱是当前这十个词的篇内共现,走势纵轴是按年篇数。这三套如果混成一个权重,横条、线和点就不是一回事。热区画在哪、绿条放在列表而不是弹窗、BibTeX 粘贴算交材料不算推荐,这些是我定的。DeepSeek 写得出函数,写不出这一刀该切在哪。
加戏是稳定的。阅读清单、把图谱写成以后再做、把权重当成走势纵轴、作者标签和摘要共用同一套停用词,这几件如果原样留下,网站会缺基础功能,或者图的意思是错的,或者榜是空的。切词那种错更麻烦:样例还是 12 篇,接口像成功,词却被切没了。所以我只提交检索对过、词还在的版本。测试要在创建应用之前关掉抓取,不然上一个用例的开关漏过来,绿条是假的。缺会议时不要默认 CVPR。更新字段不能用 or 把空值填回旧的。这些都是草稿里看起来能跑、对上页面就错的地方。
局限我也碰到了。DeepSeek 会把基础功能写成以后再做,会在同一段里加上书单。人很少这么稳定地加戏,但人可以指着屏幕问绿条在哪。知识不是实时的:2026-09-24 那天批量访问 DBLP 被挡成验证页面,这是网站当时的行为,我没把它算成模型过期。DeepSeek 也不知道我这台机器上的库有多少篇,本地联调时的篇数不能写成公网上的数字。密钥和数据库没有交给 DeepSeek,也没有进仓库。没看懂的抓取不会直接开到公网。
用法就固定成两轮。第一轮让 DeepSeek 起草,第二轮写明不许加功能、不许换口径,再对页面和测试。拒绝写进对应的 md,博客从那些 md 里摘,不从记忆里重写。没有同学坐在旁边的时候,这些 md 就是那个看的人。DeepSeek 写得快,收得慢的是我。快的部分我用了,讲不清会让榜、线或绿条错在哪的部分,不进仓库。
五个基础功能走通之后才做这三块。它们挂在说明页、走势页下方和顶栏的浏览上,不替换入库、列表、前十、图谱和走势折线。挂了也不影响另外五页。成品里浏览那一段动图在 成品展示,历年前十要单独录,不要和三个会的折线放进同一个文件。
打开说明。最上是三张卡片,颜色和走势图例一致:CVPR 绿,ICCV 蓝,ECCV 琥珀。卡片上写开会的年份。CVPR 每年都开。ICCV 只在奇数年,近年是 2021、2023、2025。ECCV 只在偶数年,近年是 2020、2022、2024、2026。点卡片去官网,本站不改库。
再往下是按任务选页:还没定方向去热门方向,想看近几年还做不做去热度走势,手里有题名去论文,已经有文件去导入。左右两格把两套算法分开写:前十是 TF-IDF 权重,走势纵轴是当年篇数,两者不混看。再下面写数据从公开元数据来,单篇找不到才用 DBLP,只用于这门课。
设计上,了解更多不单独做第六个导航。三个会的背景和统计口径以说明页为准,点一个词时的短简介放在热门方向和浏览页。前十是当前筛选下这批库里的权重,不是整个领域的结论。样例库上的名次不能写成计算机视觉这两年的定论。
实现几乎是静态页。奇数年、偶数年写在卡片文案里,和走势接口里「没有届次就是 0」是同一句话,避免说明页说没开会、折线却空着不解释。点词简介不在这一页请求。
热度走势页上方仍是功能 5:至少两个词,三条线,播放时年份在已经拿到的点上走。页面向下另有一块历年前十。一次只看一年,榜上是这一年的权重前十。点按年播放,大约 1.1 秒换一年,名次重排。每一行能看出相对上一年是上升、下降还是没动,页上有一句人话:新进了哪些词,退出了哪些词。最早的一年没有上一年,句子写成对照起点,不假装有升降。
设计和折线分开。折线回答选定的几个词各有多少篇。演变回答这一年的前十换了谁。两套接口,两套播放。折线大约 0.8 秒走一年,演变大约 1.1 秒换一帧。筛选仍是走势页上的会议和年份,换筛选两块一起重算。
flowchart TD
filter[当前会议和年份] --> once[一次取出这些年的词]
once --> year[按年切开]
year --> rank[每一年单独算 TF-IDF 前十]
rank --> diff[和上一年的名单比:新进、退出、升降]
diff --> frame[收成一帧]
frame --> play[播放只切换帧,不再请求]
名次用的仍是前十那套权重,不是篇数,也不是折线上的数。具体哪个词从第几到第几,要在当前库上看,这里不写一个没对着页面核对过的名次。
后端一次把筛选里的行取回来,在内存里按年算,避免每一年重扫全库。算完放进缓存,键里带着筛选。
def build_evolution(paper_filter: PaperFilter) -> dict:
years = _resolve_years(paper_filter)
all_rows = _fetch_rows(paper_filter) if years else []
by_year = {}
for row in all_rows:
by_year.setdefault(int(row["year"]), []).append(row)
prev_top = []
for year in years:
_, stats = _aggregate(by_year.get(year, []))
items = _top10_from_stats(stats)
top = [item["keyword"] for item in items]
entered = [word for word in top if word not in prev_top] if prev_top else []
left = [word for word in prev_top if word not in top] if prev_top else []
prev_top = top
接口是 GET /api/trend/evolution,和 GET /api/trend 不是同一个。页面播放只改当前帧的下标。
function playEvo() {
evoPlaying.value = true
evoTimer = setInterval(() => {
evoIdx.value = (evoIdx.value + 1) % evoFrames.value.length
}, 1100)
}
顶栏点浏览,标题是开题浏览。这一页把四件事收在一起:当前范围有多大规模,哪些词在升温或降温,选中的方向是什么意思,相关论文能不能先圈几篇。右边是短清单。页头写明名次是 TF-IDF,值不值得做要去走势页看当年篇数。
输入一个方向,手离开大约 0.2 秒,下面出现库里的建议,每条带相关篇数。点一条建议,中间出现简介和相关论文。简介优先是英文维基的短摘要,并带来源。失败、断网或测试关掉外部请求时,用本地词条,不编一段看起来像维基的话。没有匹配时是中文,旁边仍留着一组热门词,不是英文报错。
从热门方向点开题浏览,地址会带上当前这个词,浏览页直接打开它。短清单点加入才出现,最多 20 篇,只存在这台浏览器里,刷新还在,不写回数据库。同一篇再点,提示已经在清单里。
flowchart TD
type[输入一个词] --> wait[停大约 0.2 秒]
wait --> sug[按当前会议和年份在词表里找]
sug --> pick[点一条建议]
pick --> wiki{维基摘要拿到了吗}
wiki -->|拿到| show[显示摘要和链接]
wiki -->|失败或关掉| local[显示本地词条]
show --> papers[下面是库里的相关论文]
local --> papers
papers --> save[加入短清单,只写在浏览器]
建议接口按当前筛选查词表。一个字母只匹配前缀,更长的词再做包含匹配,完全相等的排在前面,然后按篇数。
@bp.get("/suggest")
def suggest():
filt = parse_filter(request)
query = (request.args.get("q") or "").strip()
items = PaperRepo.suggest_keywords(
query,
venue=filt["venue"],
year_from=filt["year_from"],
year_to=filt["year_to"],
)
return ok({"items": items, "q": query, "filter": filt})
简介先查别名,例如 diffusion 去对扩散模型,再请求英文维基。摘要截到大约 420 字。任何网络错误都退回本地说明。测试用环境变量跳过外部请求。
def fetch_keyword_intro(keyword: str) -> dict:
word = (keyword or "").strip()
if os.environ.get("HOTWORDS_SKIP_WIKI") == "1":
return _local(word)
query = ALIASES.get(word.lower(), f"{word} computer vision")
try:
search = _get("https://en.wikipedia.org/w/api.php?" + urllib.parse.urlencode({
"action": "query", "list": "search", "srsearch": query, "srlimit": 1, "format": "json",
}))
hits = (search.get("query") or {}).get("search") or []
if not hits:
return _local(word)
title = hits[0]["title"]
summary = _get(
"https://en.wikipedia.org/api/rest_v1/page/summary/" + urllib.parse.quote(title)
)
extract = (summary.get("extract") or "")[:420]
page_url = ((summary.get("content_urls") or {}).get("desktop") or {}).get("page") or ""
if not extract:
return _local(word)
return {"keyword": word, "extract": extract, "url": page_url, "source": "wikipedia"}
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, KeyError, OSError):
return _local(word)
前端停 200 毫秒再请求,避免每敲一个字母打一次库。短清单用浏览器本地存储,超过 20 条就丢掉最旧的。
const MAX = 20
function add(paper, keyword = '') {
if (!paper?.id || has(paper.id)) return false
items.value = [{ id: paper.id, title: paper.title, venue: paper.venue, year: paper.year, keyword }, ...items.value].slice(0, MAX)
localStorage.setItem('cv-shortlist-v1', JSON.stringify(items.value))
return true
}
DeepSeek 草稿里没有匹配是英文,引用行的分隔还乱过码。我改成中文提示,分隔改成正常标点,并留热门词当兜底。浏览不替代绿条,不替代前十的算法,也不替代走势那条正在画的线。
浏览页挂了,热门方向、走势、论文、导入、说明仍从顶栏进去。它不替代绿条,不替代前十的算法,也不替代热度走势那条线。