v3.0 跑通的第二天,我发现它只有我自己能用 —— ai_collector v3.1 把求职 Agent 接进 MCP 生态的两个晚上
项目:AI Collector v3.1(在 v3.0 LangGraph Agent 基础上加 MCP Server + Bad Case 闭环)
仓库:https://github.com/nakajimamiyuki/ai_collector_project
作者:minjie / @nakajimamiyuki
标签:MCPLangGraphAgent生态Bad Case闭环转型
楔子:我造了个 Agent,然后把它锁在了硬盘里
v3.0 完工那晚我跑通了第一条端到端:
python scripts/find_jobs.py"杭州 AI Agent 1-3 年 15K+"# → 反思一次 → 6 条命中 → Top 3 推荐合上电脑的那一刻挺爽的。「我用 LangGraph 写了一个能自主决策的 Agent,它在帮我找工作。」
第二天早上一打开终端,我盯着 README 看了几分钟,意识到一个尴尬的事实:
这个 Agent 只有我自己能用。
它躲在scripts/find_jobs.py里,必须cd到项目目录、激活 venv、敲一行 CLI。Hermes Agent 不知道它存在,Claude Desktop 不知道它存在,Cursor 也不知道。我造了一个能力很强的工具,然后把它锁在了自己的硬盘里。
更扎心的事在下午。我数了一下 6 月份采集到的 192 条 Boss 直聘 AI 岗位 JD ——
14 条明确写了「熟悉 MCP」「基于 MCP 封装业务工具」。
杭州 2 条、郑州 1 条,正好是我心仪的目标城市。
这 14 条 JD 在向我喊:会 MCP 的人很稀缺。
而我手里的 v3.0 项目,恰好就应该是一个 MCP server,但它现在不是。
这就是 v3.1 的起点 ——
把 v3.0 从「自己能跑的 demo」,升级成「AI 生态原生公民」。
时间:两个晚上。昨晚摸黑装 mcp SDK + 写第一个 Tool,今晚一口气扩到 3 个 Tool、加 Bad Case 闭环、画架构图、写简历级 README。
一、为什么是 v3.1,不是 v4.0?
打开 v3.0 的代码画一遍当前架构,盯着图看了 10 分钟:
Boss / Bilibili / arXiv 采集 → SQLite + Milvus ↓ LangGraph Agent(5 节点 DAG) ↓ find_jobs.py CLI ↓ 只有我自己能用得出一个对我自己有点反直觉的结论:
v3.0 已经是一个能跑通的 Agent,没必要推倒重来。
我要做的是让它「长出三根新手脚」,分别接进三个新维度。
v3.1 要给 v3.0 加的三件事 ① MCP Server 化 → 让 AI 生态调用我(核心) ② Bad Case 闭环 → 让 Agent 自己变好(工程闭环) ③ 简历级打磨 → 让 HR 30 秒看懂我(求职闭环)v4.0 留给真正改架构的版本——多 Agent supervisor、商用 LLM 路由、端到端微调。
v3.1 是「加肉」,v4.0 才是「换骨」。
这个决策今天看起来理所当然,但实际上是花了 20 分钟自己说服自己的——转型期手痒,总想堆更多新功能,越堆越没法收尾。
一个能投出去的 demo,比五个半完成的功能值钱。
二、F1:让 MCP Server 从「只有一个工具」长出灵魂
2.1 昨晚的起点
昨天晚上 9 点开始动手。装mcpSDK、写最小 server 骨架:
frommcp.server.fastmcpimportFastMCP mcp=FastMCP("ai-collector")@mcp.tool()defsearch_jobs(keyword:str,city:str="",top_k:int=5)->str:"""搜索已采集的 Boss 直聘岗位(字面匹配)。"""...if__name__=="__main__":mcp.run(transport="stdio")接进 Hermes Agent,能跑:
我:杭州有哪些提到 MCP 的岗位? Hermes:(自动调用 mcp_ai_collector_search_jobs)返回 5 条匹配爽,但简历摆出来就尴尬:
「我自研了一个 MCP Server,只有一个工具。」
面试官追问「就一个?」我能讲什么?必须扩。
2.2 该扩什么?三条标准
候选列了 5 个工具,我用三条标准筛:
- 面试演示价值——能不能在面试现场跑一段 demo 让面试官点头?
- 跟现有工具的对照关系——能不能形成「为什么需要这个」的故事?
- 真实数据卖点——能不能输出一段让我背下来当谈资的数字?
按这三条标准选出 2 个:
| 工具 | 演示价值 | 对照关系 | 真实数据 |
|---|---|---|---|
query_rag | ⭐⭐⭐ | 跟 search_jobs 形成「字面 vs 语义」对照 | bge-m3 + Milvus 召回 |
get_skill_gap | ⭐⭐⭐ | 跟 search_jobs 形成「找岗 vs 找方向」对照 | 192 条 JD 跑出的市场热度 |
2.3query_rag—— 让 MCP server 听人话
字面匹配的弱点很明显:
查:"会用 LangGraph 做反思决策的 Agent 岗位" search_jobs → 0 条(没人 JD 里写「反思决策」四个字)但实际数据里有一条 JD 写了:
“基于 LangGraph、LangChain、AutoGen 等框架开发 Agent 应用,运用 CoT(Chain-of-Thought)实现复杂业务流程的自动化”
「运用 CoT」就是反思决策的语义近义词。这种召回,只有向量检索能做到。
query_rag直接复用 v3.0 已有的vector_search_jobs:
@mcp.tool()defquery_rag(question:str,top_k:int=5)->str:"""用自然语言对 Boss 岗位做向量检索(bge-m3 + Milvus)。"""fromsrc.agent.toolsimportvector_search_jobstry:records=vector_search_jobs(question,top_k=top_k)exceptExceptionase:# 关键设计:基础设施故障要翻译成 JSON 错误,不能让异常穿透returnjson.dumps({"error":f"{type(e).__name__}:{e}","hint":"确认 ollama 在运行(ollama serve),bge-m3 已 pull","hits":[],})...这里有一个细节藏着设计取舍:
基础设施故障(ollama 没起 / vector.db 不存在)要翻译成 JSON 错误,不能抛异常。
为什么?MCP 客户端拿到isError=True的 content 时,用户体验是「工具坏了不知道为啥」。返回带hint的 JSON,客户端能直接告诉用户「去启动 ollama」。
跑出来的真实例子:
query_rag("会用 LangGraph 做反思决策的 Agent 岗位", top_k=3) [0.7974] Python(LangChain/LightRAG/AI Agent) | 深圳中迈 | 20-25K | 杭州 [0.7872] 高级 AI Agent 开发工程师 | 堃垚集团 | 13-25K | 郑州 [0.7851] agent开发-乾健科技 | 武汉添才 | 20-35K | 济南第三条 JD 写了「基于 LangGraph、LangChain、AutoGen 框架开发 Agent + 运用 CoT」 ——字面搜不到「反思决策」,但语义检索召回了。这一条数据本身就是面试现场最好的 demo。
2.4get_skill_gap—— 让 Agent 告诉我该学什么
这个工具的核心是三个步骤:
- 在 192 条 Boss 真实 JD 上做技能词频聚合
- 对照
my_profile.yaml里我标的 already_have / learning / want_to_avoid - 输出三张表:市场 Top N / 我的缺口 / 我学习方向的市场热度
跑出来的结果给我看傻了:
total_jobs_analyzed = 192 market_top_skills: skill_gap (我没掌握但市场要的): Python 108 Agent 106 Agent 106 Prompt 72 RAG 82 Java 48 ← 转 Java 系会大幅扩盘 Prompt 72 微调 43 LangChain 66 LlamaIndex 32 learning_hits (我正在学的市场热度): 多模态 41 ← 学这个 ROI 最高 Dify 25 Coze 16 LangGraph 16 MCP 14 ← 学这个稀缺度最高 LoRA 11 QLoRA 5 RLHF 3这张表的杀伤力在哪?
它告诉我:
- 接下来 3 个月应该先攻多模态(市场热度 41 > LangGraph 16 > MCP 14),而不是凭感觉学
- MCP 虽然只有 14 条但稀缺度极高,学它简历差异化大
- Agent 这个标签市场上 106 条——这就是为什么我要把项目名从「AI Collector」改叫「求职 Agent」
面试时这张表是金句:
「我接下来三个月学什么?我让自己的 Agent 在 192 条真实 JD 上跑了一遍技能聚合,结果告诉我先攻多模态,其次是 LangGraph 和 MCP——你看,我连「我该学什么」都是数据驱动的。」
三、两个让我抓狂的报错
中间踩了两个非常有代表性的坑。值得单独写——因为它们能讲清楚一件事:
写代码的工程师能力分水岭,不在算法,在能不能 5 分钟内把故障定位到这一层。
3.1 坑 1:ModuleNotFoundError: No module named 'src'
写完三个 Tool,单元测试全绿。跑真实的 stdio MCP client smoke test:
✅ search_jobs 跑通 (5 matches) ❌ get_skill_gap Error executing tool: No module named 'src' ❌ query_rag Error executing tool: No module named 'src'单元测试全绿、其中一个工具能跑、另外两个就死。
抓出来看,挂掉的两个工具里有这一行:
fromsrc.agent.toolsimportcompute_skill_gap,load_profile而 search_jobs 没用这种延迟 import。
为什么 venv 里直接 import 是好的,子进程里就不行?
python src/mcp_server/ai_collector_mcp.py当脚本运行时,Python 默认只把脚本所在目录(src/mcp_server/)加进sys.path。顶层src包根本不在 import path 里。
单元测试不暴露这个坑,是因为 pytest 启动时会把项目根加进 sys.path。只有真实子进程模式才会原形毕露。
修复一行:
PROJECT_ROOT=Path(__file__).resolve().parents[2]ifstr(PROJECT_ROOT)notinsys.path:sys.path.insert(0,str(PROJECT_ROOT))教训:写 MCP server / cron 脚本 / 任何「会被以子进程启动」的模块,永远要在文件顶部手动注入项目根。
3.2 坑 2:macOS 上的 OpenMP 双库 abort
修完坑 1,再跑:
✅ search_jobs OK ✅ get_skill_gap OK(纯 SQLite,不走 milvus) ❌ query_rag McpError: Connection closed为什么连接被关掉?看 stderr:
INFO Loading faiss. INFO MilvusLite server started on port 61160 OMP: Error #15: Initializing libomp.dylib, but found libomp.dylib already initialized. OMP: Hint This means that multiple copies of the OpenMP runtime have been linked into the program.这个错我 v2.1 时就踩过,所以 v3.0 的scripts/*.py顶部都设了KMP_DUPLICATE_LIB_OK=TRUE。但今晚新写的 MCP server 入口我忘了设。
根因:
macOS 上
milvus-lite内嵌的faiss链了一份libomp.dylib,numpy/torch各自再链一份。同一进程加载多份会触发 OpenMP 的运行时安全检查,直接 abort。abort 之后 stdout 断了,stdio MCP client 看到的就是Connection closed。
根本想不到是 OpenMP 的问题,只能从 stderr 一行一行往回追溯。
修复就一行,但必须在import faiss/milvus之前:
importos os.environ.setdefault("KMP_DUPLICATE_LIB_OK","TRUE")# 必须在所有 import 之前3.3 这两个坑想说的事
转型期最浪费时间的不是「不知道答案的问题」,是「答案被 5 层抽象隐藏的问题」:
- MCP client 报
Connection closed,怎么也想不到是 OpenMP 库 No module named 'src',怎么也想不到跟 pytest rootdir 机制有关
「懂 LangGraph 框架」对面试有用,但「能 5 分钟从 stderr 追到第三层抽象底下的根因」对真实工作更值钱。
这两个坑都是我从 stderr 一行一行往回追的,每一步都没快捷方式。我把它们写下来,不是炫"我修好了",是想告诉转型路上的同行人:这种排错能力没法速成,只能在踩坑中长出来。
四、F2:Bad Case 闭环 —— 让 Agent 自己变好
4.1 这个想法是怎么来的
v3.0 跑通那天我跑了两个 query:
Run 1: "LangChain RAG 15K+ 1-3 年" → 6 条命中 ✅ Run 2: "郑州 25K+ LangGraph" → 3 轮反思后 0 命中(故意稀有)❌Run 2 看着很有「反思决策」的戏剧感(错配诊断 + 替代方案)。但是 ——
这两次跑完,下次我打开项目就忘了。没有任何持久化记录。
下次再跑同一个 query 时:
- 我不知道上次跑过没
- 我不知道上次结果是不是空
- 我不知道上次空了之后我有没有修过什么
- 我不知道这次修完之后是不是真的修好了
这就是典型的没有 bad case 闭环的 Agent 项目。
市面上 90% 的 LangGraph demo 都死在这一步——能跑一次给视频录屏用,但不能形成「跑 → 复盘 → 修 → 验证」的工程闭环。
而 bad case 闭环这一项,是我最不该缺的能力——我做了 2 年第三方测评,军工/航天级 CNAS+CMA 测评的核心就是这套。
只是之前对象是硬件,现在换成 LLM。
4.2 三个关键设计决策
决策 1:bad case 库要不要塞进collector.db?
不。两个 SQLite,物理隔离:
collector.db → v1/v2 pipeline 状态机(采集 / 清洗 / 入库) agent_runs.db → v3+ Agent 维度数据(运行记录 / bad case)为什么?因为 v1/v2 的 schema 已经稳定,加新表会污染语义边界。两个库都是 SQLite,但解耦——未来想把 agent_runs 单独导出 / 迁移 / 对外暴露,零成本。
决策 2:零结果是不是自动 bad?
是。
ifresult_count==0andstatus=="unreviewed":status="bad"auto_bad_reason="zero_result"为什么?因为「什么算 bad case」这个问题,大部分情况下不需要人来判断。Agent 跑出来 0 条结果,95% 的概率就是 bad case。
让机器先标,人只复核。这跟我以前做测评写自动化测试用例的习惯一模一样:先让测试用例自己判生死,人只看 fail 的。
决策 3:必须有 replay 命令
python scripts/agent_runs.py replay--statusbad--limit5这一条是整个闭环的灵魂——你以为修好的 bad case,必须用同一份 query 重跑一遍验证。
不重跑就不算修好。
这条经验也是从测评工作里被反复教育出来的:「regress 不是说说的,是要重新跑测试用例的。」
4.3 CLI 长这样
$ python scripts/agent_runs.py list ID|when|cnt|sec|refl|status|cause|query3|2026-06-2622:49|2|4.1|0|unreviewed||AI 测试 大模型评估 北京2|2026-06-2622:49|0|12.5|3|bad|zero_result|郑州 50K AGI 内核岗1|2026-06-2622:49|4|3.2|0|unreviewed||杭州 AI Agent MCP1-3年 $ python scripts/agent_runs.py mark2--root-cause filter_too_strict\--fix-commit abc1234 --fix-notes"把 salary_min 从 20K 降到 15K"✅ Run#2 已更新$ python scripts/agent_runs.py replay--statusbad--limit5🔁 Replay Run#2: "郑州 50K AGI 内核岗"旧结果:result_count=0status=bad 新结果:id=4result_count=2(↑2)status=unreviewed ✅1条之前零结果的 query 这次有命中了。4.4 它在简历上能讲什么
「我自研的 Agent 项目里实现了完整 bad case 闭环:每次跑自动落库,零结果自动 bad,replay 做回归。
这个习惯是从 2 年军工/航天级 CNAS+CMA 测评带过来的。
同样的字段在投流广告场景里就是 ROI 反馈闭环,在对话陪练里就是 bad case 回流,在评测体系里就是回归测试套件。」
—— 这就是为什么前一篇博客里我说的:
我的测评经验不是包袱,是独特竞争力。
15 个新单元测试,全绿。
五、F3:让 HR 30 秒看懂你
代码再好,HR 30 秒扫不到关键词就废了。今晚最后一段时间,做了三件给 HR 看的工程——README 顶部电梯版、架构图、JD 对照表。
我想单独讲其中一个观点:
大部分技术人最大的认知盲区,是以为「项目做完」=「项目能投」。
不是。
「项目做完」是工程能力的终点,但是「项目投得出去」的起点。中间还隔着——
- README 顶部 5 行能不能让 HR 30 秒拿到「是什么/怎么做/数据/可调用方」
- 简历附件里能不能有一张能直接用的架构图 PNG
- 面试前能不能 5 分钟过一遍「JD 关键词 → 我项目能力」的映射表
这三件事每一件都不写新代码,但每一件都决定项目能不能用进求职。
我今晚做了:
F3 README 30 秒电梯版 标题下加 5 行关键词高密度框 F4 架构图 SVG docs/assets/architecture.svg 矢量图 F6 JD 对照表 docs/JD_MAPPING.md 三家 JD vs 我的项目能力F4 这里值得多说一句。第一直觉是用 mermaid-cli 出图,但它要拉 Chromium 内核,国内拉镜像可能要几分钟。避坑思路:手写 SVG。GitHub README 原生支持 SVG,矢量、文件 14KB、简历用时一行qlmanage -t -s 1920命令导 PNG。
能让自己 5 分钟内可重做的方案,永远优先于「装套工具一劳永逸」的方案。
写文档、画图、做小工具,这条原则比技术选型重要得多。
六、写在合上电脑前
再看一眼 git log:
v3.0 (2026-06-24) 一天 5 阶段写完 LangGraph Agent v3.1 (2026-06-26) 两个晚上加 3 Tool + Bad Case 闭环 + 简历级打磨但我心里清楚——
v3.0 → v3.1 这一步的工作量,跟 v0 → v3.0 几乎一样大。
v3.0 之前我做的是「让项目长出能力」:采集层、RAG 层、Agent 层、反思循环。每一步都看得见、摸得着。
v3.1 这两个晚上我做的是:
- MCP server 让别的 AI 客户端能调用我
- Bad case 闭环让我能持续把 Agent 调得更准
- 简历级 README + 架构图 + JD 对照表让 HR 30 秒看懂
这些工作没有一行代码「能跑出新功能」,但它们决定了项目能不能用进面试、用进简历、用进真实求职。
这个对照让我想起一句话:
95% 的开源项目死在「能跑但讲不清楚」。
而我现在 26 岁,从测评转 AI 应用开发,时间窗口只有今年下半年。项目骨架在 v3.0 已经建完,v3.1 这两个晚上做的就是把它打磨到「投得出去」的程度。
接下来要做的事,我的 Agent 已经告诉我了
get_skill_gap跑出来的真实数字:
学习方向 ROI 排序: 多模态 41 ← 市场最大 LangGraph 16 ← 项目已用,需深入 MCP 14 ← 项目已用,稀缺度最高 LoRA 11三个月学完这四样,我手里就有四张牌:
- v3.0/v3.1能讲(已完成)
- LangGraph深讲(v3.0 已用,需深入)
- MCP稀缺讲(v3.1 已用,市场会的人少)
- 多模态市场讲(市场最缺)
够了。
9 月份去新城市投简历,这四张牌打出去,AI 应用 / Agent 开发岗能进面试。
简历金句(v3.1 完工版)
自研一个端到端的 AI 求职 Agent:用LangGraph编排 5 节点 DAG(含反思循环),
用bge-m3 + Milvus做 RAG 语义检索(192 条真实 Boss 直聘 JD),
用CDP 接管真实 Chrome绕过 Canvas 反爬。
把核心能力封装成3 个 MCP Tool,Hermes Agent / Claude Desktop / Cursor 三个客户端均可调用。
每次运行自动落 Bad Case 库,形成「跑 → 复盘 → 修 → replay」闭环。
81 个 pytest 全绿 + GitHub Actions CI。代码:github.com/nakajimamiyuki/ai_collector_project
项目演进时间轴
| 版本 | 日期 | 主题 | 一句话 |
|---|---|---|---|
| v1.0 | 2026-06-16 | 能跑 | 基础采集 + LLM 清洗 |
| v1.1 | 2026-06-17 | 稳定 | 反爬 + 重试 + 调度 |
| v2.0 | 2026-06-20 | 可扩展 | 插件式多源(B 站 + arXiv) |
| v2.1 | 2026-06-23 | 能查询 | + RAG 语义检索(Milvus + bge-m3) |
| v3.0 | 2026-06-24 | 真 Agent | + LangGraph + 自主决策 + 反思循环 |
| v3.1 | 2026-06-26 | ★ 生态原生 | + MCP Server + Bad Case 闭环 |
给读到这里的你
如果你也在转型 AI 应用开发,记一句话:
别等项目「完美」再投简历——让项目「投得出去」,本身就是一项工程能力。
把 v3.0 打磨成 v3.1 这两个晚上,跟从 0 写到 v3.0 一样难,也一样值。
Repo: github.com/nakajimamiyuki/ai_collector_project
Cheers,
@nakajimamiyuki / minjie