刚过去的这个周末,和一位许久未见的朋友小A聚了聚。她在公司担任HR,近期正忙于大规模招聘,每天需要从各个渠道收集简历,处理起来相当头疼。
背景:用Excel维护候选人简历信息
公司规模不大,没有成熟的简历管理系统。为了省事,小A一直用Excel——每次收到新简历就在表格里新增一行,查找候选人时直接用Ctrl+F全文检索。不得不说,这确实是门槛最低的方案,插入成本极低。但随之而来的,是日常维护和信息检索的开销越来越大。至于Boss、猎聘这些在线系统,功能倒是齐全,可需要充值才能解锁完整功能,对于小团队来说也不太现实。
聊着聊着,小A就提到,要是能有一个搭建在本地、支持基本插入编辑搜索、最好能自动提取关键信息的简历维护系统就好了。这个需求听起来很实在,也恰好是个机会——之前的实践里,大部分时间都在自己熟悉的技术栈里用AI,后端和前端(特别是H5)这块儿还没怎么碰过。于是,趁着聊天,小A把日常工作的痛点细化了一遍,整理成了一份略带雏形的PRD。以此为起点,最终构建出了一套运行在本地、支持AI提取关键信息、能自动归档简历的系统。
前期工作:痛点梳理与需求澄清
在这个场景里,我暂时客串了一把功能开发,小A就是客户、是需求的提出方。以前没当过产品经理,但这次她相当投入,专门去学了PRD怎么写,并按照自己的理解写出了第一版需求文档。这点的确挺让人佩服的。
挖掘用户最本质的需求
核心需求其实很朴素:就是想要一个轻量、可控、贴近自己使用习惯的单机工具。市面上的同类系统要么太重,要么根本不适合个人单机场景。既然没有现成的,不如用AI Agent自己搭建一个。
典型的工作流长这样:
导入简历 -> 结构化抽取 -> 筛选检索 -> 行内审核编辑 -> 预览原始PDF
头脑风暴:痛点与解决方案
小A的痛点其实很清晰:
- 简历文件分散在不同的文件夹里,想检索或对比一下,成本极高。
- 教育背景、工作经历、目标城市、关键词这些字段,全靠人工整理,重复劳动量很大。
- 搜索需求是高度个性化的——比如按年龄、薪资、意向城市来匹配。
做个人项目最忌讳的,是功能一开始就发散。很多人不是不会写代码、不会用AI,而是总想着一步到位做一个公司级的完整平台。这样一来,项目边界只会不断膨胀。所以这次一开始就定了个明确的目标:做一个小A每天真的会打开使用的小工具,而不是一个完美的简历维护系统。
将问题收敛到周末可完成
为此做了三件事:
- 砍需求,只保留核心主链路。
- 明确边界:单机、单用户,先只支持
PDF/DOCX格式。 - 每完成一步都必须可验收,不搞“写完再看”这套。
实践阶段
设计技术方案
借力AI的建议,最终选用了一套朴素但高效的技术方案,兼顾开发速度和可维护性:
- 后端:FastAPI
- 存储:文件 + SQLite
- 前端:React + Vite
- 抽取层:
Extractor抽象接口(支持Codex CLI和本地Rule Extractor两种模式)
flowchart TBUser([用户浏览器])subgraph Frontend["前端 (React + Vite)"]SPA["App.jsx
单页应用"]ViteProxy["Vite Dev Proxy
/api/* → 127.0.0.1:8000"]endsubgraph Backend["后端 (FastAPI)"]direction TBRouter["main.py
路由与 Lifespan"]Imports["imports.py
导入 / 去重 / 归档"]Candidates["candidates.py
候选人合并"]TableView["table_view.py
表格视图 / 行编辑"]Review["review.py
字段审核"]ExtractionMod["extraction.py
抽取队列 + 后台 Worker"]Router --> ImportsRouter --> CandidatesRouter --> TableViewRouter --> ReviewRouter --> ExtractionModendsubgraph ExtractorLayer["抽取层 (Extractor 抽象)"]direction TBExtractorIface{{"Extractor
接口"}}RuleExtractor["RuleExtractor
本地 regex / 启发式"]CodexExtractor["CodexExtractor
Codex CLI 子进程"]ExtractorIface --> RuleExtractorExtractorIface --> CodexExtractorendsubgraph Storage["本地存储"]direction TBSQLite[("SQLite
data/db/*.sqlite3")]Archive[("Archive
data/archive/")]endLLM[/"外部 LLM API
(仅 Codex 模式)"/]User -->|HTTP| ViteProxyViteProxy --> SPASPA -->|REST /api/*| RouterImports --> ArchiveImports --> SQLiteCandidates --> SQLiteTableView --> SQLiteReview --> SQLiteExtractionMod --> SQLiteExtractionMod --> ExtractorIfaceCodexExtractor -.-->|网络外发
简历文本| LLMclassDef external fill:#fde68a,stroke:#b45309,color:#78350f;classDef storage fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a;classDef iface fill:#ede9fe,stroke:#6d28d9,color:#4c1d95;class LLM external;class SQLite,Archive storage;class ExtractorIface iface;
核心数据流大致是:
- 上传/导入简历
- 归档原件并提取文本
- 结构化字段写入数据库
- 前端高密度表格筛选与高亮
- 行内编辑、审核通过、删除、查看原文
在AI的辅助下,即便Python水平只是初级,也还是完成了这套MVP版本的简历管理工具。
周末两天具体做了什么
Day 1 上午:需求澄清与工程雏形建立
- 人与人之间的多轮沟通,确定页面形式、字段、搜索匹配条件。
- 人与AI协作搭建工程雏形,确保能运行并在浏览器里看到效果。
目标只有一个:能跑、能存、能查。
Day 1 下午:导入主链路
- 批量上传
- 提交历史页面
- 原始文件归档
- PDF/DOCX文本提取与失败状态记录
做到这一步,系统的主要数据和控制链路基本固定下来。
Day 2 上午:数据抽取与审核闭环
- 接入抽取器抽象层(支持在线Codex CLI接口和离线语义分析两种模式的切换)
- 支持手工修正与重新抽取
- 完善编辑、删除、审核功能
语义提取对大模型来说并不是特别复杂的任务。考虑到Codex CLI接口的用量限制与稳定性波动,后续计划接入国内的大模型。
Day 2 下午:UI优化,验收和微调
- 反复调整顶部的筛选区、数据分列的格式
- 关键词命中高亮
- 原始PDF弹窗预览
UI可视化效果是AI的弱项,在这上面花了不少时间,才把前端页面调整到信息密度适中、文字展示效果较好的程度。如果想做得更好看,可能就需要专业美工的介入了。也许已经有专攻前端UI效果的AI技能,只是目前还没了解到。
如何和AI协作的
这次最有价值的收获,其实不是代码和工程本身,而是协作方式的改变。AI完成的质量,很大程度上取决于使用者提问的质量。建议采用小步快跑的方式:
- 每次只交付一个闭环步骤(实现 + 验证 + 文档更新)。
- 如果失败,必须给出“原因 + 修复方案 + 验证命令”。
- 需求变更时必须回写任务文档,避免上下文漂移。
常用的Prompt可以分成三类:
1.需求开发类Prompt
目标:实现XXX功能,范围仅限A/B/C文件。验收标准:1) ... 2) ... 约束:1) 不改动无关模块 2) 变更后执行测试并反馈结果
2.问题修复类Prompt
问题:出现XXX异常,请给出:1) 根因 2) 最小修复方案 3) 验证步骤
3.UI调整类Prompt
UI调整:- 按“高密度、报表导向、筛选优先”改版,不要大圆角卡片风。- 将“备注”列的宽度减少2个汉字,加到“任职公司”列。
过程中最关键的4个坑
| 坑 | 现象 | 处理 | 结论 |
|---|---|---|---|
| 抽取质量不稳定 | 学历/任职公司可能为空或格式不统一 | 规则归一化 + 提示词约束 + 人工编辑兜底 | 自动抽取必须和人工修正并存 |
| UI“能看”不等于“能用” | 早期UI看起来还行,但筛选效率低 | 从卡片改成表格工作台,对列宽、换行、操作密度逐项打磨 | 工具型产品的UI指标应该是“信息吞吐量” |
| 导入与处理链路卡顿 | 导入后等待感明显 | 增加任务状态、进度反馈、日志定位;将抽取流程与导入流程解耦 | 可观测性几乎和功能同等重要 |
| 历史字段债务 | 早期字段定义和当前UI不一致,造成噪音 | 清理旧字段,只保留当前必需的字段集合 | 越早清债,后续迭代越稳 |
阶段总结与思考
这个项目目前算是告一段落。虽然还有很多可以打磨的地方,但还是决定先撤出来,把精力分配到更重要的事情上。
当前量化结果
目前项目已经进入“个人可持续使用”的状态,核心指标如下:
- 代码规模约
9,744行 - 后端模块
10个,前端核心模块3个,脚本模块3个 - API路由约
24个 - 回归测试
12项全部通过
功能上已覆盖:
- 批量导入、去重、归档
- 自动抽取(可切换本地/在线策略)
- 高密度筛选工作台
- 行内审核与编辑
- 操作日志与PDF预览
这个项目带来的真正价值
- 验证了“人+AI”在生产效率上的突破性提升——原本两周都不一定能完成的任务,压缩到了两天的工作量。
- 亲身实践并独立完成了一整套前后端闭环的系统,而且用的还不是最熟悉的开发语言。
- 更确信一点:个人工具最该追求的是闭环和迭代速度,而不是第一天就追求大而全。
- 也认识到,在能力范围内应该使用最强的大模型,因为大模型带来的效率提升是乘法而非加法。举个例子,模型A的准确率是90%,模型B是80%。当问题不断迭代到第5步时,模型A的累积准确率还有60%,而模型B已经降到了32%。虽然为更强的模型支付了更高的订阅费用,但同时也避免了较弱模型带来的时间和金钱上的浪费。
- 还学会了一个技巧:让两个模型之间交叉验证——模型A完成某个步骤后,交由模型B检查结果。即便只有一个模型,也可以让同一模型的不同Subagent来完成这种校验。
尚存不足之处
目前仍然有三类改进空间:
- 在线模型的稳定性问题。目前使用的是Codex,一方面需要科学上网才能访问,另一方面额度有限。后续考虑替换为国内大模型。
- 备份/恢复、诊断脚本等工程能力还可以继续补齐。
- 列表查询与分页性能还有优化空间。
不过对于小A的个人使用场景来说,这套工具基本已经进入了“可用生产工具”的阶段。
