
最近折腾了一个有点意思的项目——帮一个汽车和摩托车发烧友把脑子里那些零散的知识整理成一个能用的购车顾问。
我这朋友,能随口说出上个月发布的某款车型的扭矩数据,能讲清楚为什么某款两厢车开起来比配置表看起来好多了,你要是问他"预算两万的摩托车不想震动太大该买哪款",他也能给你列个一二三。他天天看测评、自己也骑也开,每款新车发布都兴奋得不行。
问题是这些知识全在他脑子里,散落在几百个浏览器标签页里。别人问他"我该买什么",他的回答完全取决于那天他恰好想起什么。
所以我做了 Garage Brain:一个基于他实际测试或研究过的车型库,加上一个购车顾问功能。
流程是这样的:他往里扔文字——视频字幕、文章、或者自己的笔记。模型把它转成一张结构化的规格卡:发动机、功率、扭矩、重量、价格、油耗、优缺点、适合人群。他逐一审核批准,所有进入顾问系统的内容都必须经过他的手。任何人可以问"城市通勤加周末偶尔跑高速,两万预算买什么摩托车",系统会给出排名推荐,每项选择都附带权衡分析。
顾问不会推荐他从没研究过的车型,也不会自己编造一个马力数据,因为所有答案都来自他审核通过的规格卡。

线上 Demo:garage-brain.onrender.com


整个系统基于开源权重模型 Qwen3-8B(用 LoRA 微调,Tinker 提供算力)、开源向量模型 bge-small、MongoDB Atlas、FastAPI,部署在 Render 上。
工作流程:
模型分工:
| 环节 | 模型 |
|---|---|
| 规格卡提取(admin) | Qwen3-8B + 自定义 LoRA(在 Tinker 上微调) |
核心理念:微调解决技能问题,检索解决知识问题。
一开始我想直接让模型学习朋友的观点。后来想明白了——小规模微调擅长学习格式和习惯,不擅长记忆事实。如果把事实直接训进模型,它分不清哪些是朋友说的、哪些是自己从网上半记半猜的,更新也得重新训练。
所以我把流程拆开了:
提取阶段(微调)。用 LoRA 在 Tinker 上微调 Qwen3-8B,让它把乱七八糟的文本转成严格的 JSON 卡片。最重要的调教目标:文本里没说的参数必须输出 null,绝不能瞎猜。自动字幕会把车型名搞错("Duke" 变成 "duck"),价格可能是 lakh 单位,测评文章又比较散漫——微调后的模型得能 handle 这些。
存储。审核通过的卡片连同其优缺点和评价的向量一起存入 MongoDB。
建议阶段(基础模型 + 检索)。用户提问时,基础模型先解析出筛选条件(车型类型、预算)。MongoDB 用这些做硬过滤,因为数值范围判断不应该丢给大模型来决定。向量相似度对剩余候选车型排序,模型再基于被检索出来的卡片内容给出推荐解释。
反馈闭环。朋友在审核页面做的每一次修正都可以导出成新的训练数据,用于下一版本模型的训练。


1. 可以针对具体需求微调模型行为。
这个应用最重要的特性是模型不知道就说不知道(输出 null),而不是编一个数字出来。靠 prompt 调教闭源模型只能靠运气。用开源权重模型,在大量"正确答案就是 null"的样本上微调,这会变成模型的本能习惯,而且效果可以量化对比。
2. 他的知识存在自己可控的数据库里。
卡片集合存在我的 MongoDB 里,是他能读、能导出、能修正的明文 JSON。不是锁在某个聊天机器人的记忆里。顾问的回答基于这些卡片,所以他要是不同意某个建议,直接去改卡片就行。
3. 换模型不丢失之前的工作。
训练数据、数据 schema、评估脚本都不依赖某个特定模型。哪天出现了更好的开源模型,我可以用同样的样本重新训练,在同一张表格里跟现在的结果对比。
4. 权重是自己的。
Tinker 处理 GPU 算力,但产出的是一个开源模型的 LoRA 适配器,不是租来的 API 行为。
Render:Web 应用和 API 作为 Render Web Service 运行,通过 render.yaml Blueprint 部署。
Tinker:Qwen3-8B 的 LoRA 微调和推理客户端都在 Tinker 上运行。
MongoDB Atlas:所有已批准的规格卡、向量数据、审核状态都存在 Atlas 里。顾问用它做硬过滤(车型类型和预算),再做向量相似度排序。修正记录也会保存下来,作为下一版本模型训练的样本。