<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>大模型应用 on 老朱的赛博工位</title><link>https://blog.zjkun.top/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BA%94%E7%94%A8/</link><description>Recent content in 大模型应用 on 老朱的赛博工位</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Wed, 21 Oct 2026 16:52:00 +0800</lastBuildDate><atom:link href="https://blog.zjkun.top/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BA%94%E7%94%A8/index.xml" rel="self" type="application/rss+xml"/><item><title>前端老鸟手搓企业知识库：RAG 全链路落地实录</title><link>https://blog.zjkun.top/posts/rag-fulllink-handcraft/</link><pubDate>Wed, 21 Oct 2026 16:52:00 +0800</pubDate><guid>https://blog.zjkun.top/posts/rag-fulllink-handcraft/</guid><description>RAG 系列总开篇：一个做了12年前端的人，怎么按「存」和「找」两条线亲手跑通企业知识库问答——bge-m3+Chroma 向量化、手写 BM25+RRF 混合粗召、bge-reranker 精排、DeepSeek 流式生成。不讲概念，只记链路、参数和踩过的坑，每个环节附对应的深拆文章。</description><content:encoded><![CDATA[<p>先交代身份：我叫老朱，33 岁，写了 12 年前端为主的全栈（Vue/React/TS 为主），最近在做求职方向转换，主攻 AI 应用。手上这个企业知识库问答系统（RAG），前端是我写的，Node 服务端（Nuxt 的 Nitro server）也是我写的，整条链路从头手搓到尾。</p>
<p>外面讲 RAG 的文章一开口就是「检索增强生成」的三段论定义，我不重复这个。<strong>这篇只回答一个问题：这套系统是怎么跑通的。</strong> 全文按两条线走——「存」和「找」。每个环节末尾有一句话小结，链到我已经写好的深拆文章，想抠细节的点开看，不想抠的看完这条线也够你搭出同款。</p>
<h2 id="定调rag-就两件事存和找">定调：RAG 就两件事，存和找</h2>
<p>一句话把 RAG 说尽：把公司文档喂给大模型之前，先检索出最相关的几段，让模型只根据资料回答。所以系统的上限在哪里？<strong>我的结论是：RAG 的上限在检索，不在大模型。</strong> 模型只负责把资料组织成人话，检索决定它能看到什么。资料给错了，再贵的模型也只是把错误说得更流畅。</p>
<p>顺带回答一个选型问题：为什么是 RAG 而不是微调？这是整个系列的前置判断，我在 <a href="/posts/rag-vs-finetune-3-modes/">「RAG 还是微调？大模型应用 3 种模式别选错」</a> 里拆过——企业知识库这种「资料持续更新、答案必须可溯源」的场景，RAG 是默认答案，不在这里展开。</p>
<p>下面进入正题。</p>
<h2 id="存这条线让知识库有进有出">存这条线：让知识库有进有出</h2>
<h3 id="环节一文档入库本质是一套状态机">环节一：文档入库，本质是一套状态机</h3>
<p>第一版我图省事，上传即入库，很快就后悔了——知识库变成垃圾堆，什么过期报价单、写了一半的文档都在里面被检索出来。现在的设计：</p>
<ul>
<li><strong>格式</strong>：支持 txt / docx / pdf / xlsx，上传后统一解析成纯文本；</li>
<li><strong>元数据</strong>：文档表带完整生命周期字段——密级、访问范围、归属部门、版本号，改了哪版一清二楚；</li>
<li><strong>状态机</strong>：草稿 → 入库 → 已发布 → 下架。只有 published 状态的文档块参与检索；发布后锁死，想改必须先下架，重新入库会覆盖旧向量。</li>
</ul>
<p>这个状态机不值钱吗？太值钱。密级和访问范围让「哪个部门能问什么」有了落点，这是企业知识库和个人玩具的分水岭。</p>
<p>一句话小结：<strong>入库不是把文件塞进数据库，是给它上户口。</strong> 至于文档怎么切块、切完检索到小块要不要回查整篇，我另外写过 <a href="/posts/rag-parent-document-retrieval/">「RAG 父页面召回？这 3 种场景不值得做」</a>，先把边界讲清楚，别盲目上父文档召回。</p>
<h3 id="环节二切块--向量化三个坑替你们踩了">环节二：切块 + 向量化，三个坑替你们踩了</h3>
<p>长文档切成小 chunk，用 <strong>bge-m3</strong> 转成向量，存进 <strong>Chroma</strong> 向量库（我库里从头到尾就一个 collection，没必要整花活）。提问时把问题也转向量，找距离最近的片段——这就是向量检索的全部原理，剩下的全是工程细节。</p>
<p>踩坑实录，三条都真金白银花时间：</p>
<ol>
<li>Chroma 查询的 <code>include</code> 参数要传<strong>字符串数组</strong>，随手传个字符串就报错；</li>
<li>metadata 字段名<strong>统一用下划线</strong>，混用驼峰和下划线会让你查库时一脸懵；</li>
<li><strong>query 和入库必须用同一个 embedding 模型</strong>——换模型不是升级，是两个宇宙的对不上，换模型就得全量重灌，新旧向量绝不混库（同维度也不行）。</li>
</ol>
<p>一句话小结：<strong>向量检索翻车，八成是入库和查询不同路。</strong></p>
<h2 id="找这条线四步把答案捞上来">找这条线：四步把答案捞上来</h2>
<h3 id="环节三双路粗召向量--手写-bm25">环节三：双路粗召——向量 + 手写 BM25</h3>
<p>纯向量检索有个死穴：<strong>专有名词、缩写、产品型号会翻车</strong>，向量对这种精确匹配就是不准。所以关键词这一路必须有。</p>
<p>我没上 ES 这种现成轮子，BM25 引擎是自己手写的（这大概是最能体现「手搓」的一个环节）：</p>
<ul>
<li>jieba 中文分词 + 倒排索引，核心参数 k1=1.5、b=0.75，没有引第三方 BM25 库；</li>
<li>Nitro 服务启动时从数据库里 published 状态的 chunks 预热建索引，<strong>零冷启动</strong>，预热失败也不阻塞服务启动；</li>
<li>召回后回查数据库补全文，向量库只管「找到哪块」，不管「给出全文」。</li>
</ul>
<p>检索时两路并行：向量和 BM25 <strong>各粗召 top-20</strong>，用 <code>Promise.allSettled</code> 包着——单路挂了，另一路自动兜底，服务不停摆。</p>
<p>一句话小结：<strong>混合检索 = 广撒网两路并行 + 一路挂掉另一路兜底。</strong> 四步怎么搭、每步参数怎么定，看 <a href="/posts/rag-hybrid-search-4-steps/">「RAG 检索总跑偏？混合检索四步直接抄」</a>。</p>
<h3 id="环节四rrf-融合扔掉分数只认名次">环节四：RRF 融合——扔掉分数，只认名次</h3>
<p>两路都召回了，分数没法直接比：向量给的是余弦相似度，BM25 给的是另一套量纲，硬加权平均是玄学。</p>
<p>我的方案是 <strong>RRF（Reciprocal Rank Fusion），k=60</strong>：分数全部扔掉，只用名次。两路都认的片段自然浮到前面，融合后取 top-20。每一路的名次都记进日志，后面调试全靠它。</p>
<p>一句话小结：<strong>量纲不同别硬融合，名次是唯一通用货币。</strong> k 值为什么取 60、融合细节，同样在 <a href="/posts/rag-hybrid-search-4-steps/">混合检索四步</a> 里拆过。</p>
<h3 id="环节五query-改写用户不会提问系统替他补全">环节五：Query 改写——用户不会提问，系统替他补全</h3>
<p>真实用户的提问又短又含糊：「那个多少钱」「上次说的发货呢」。直接拿这种话去检索，神仙难救。</p>
<p>所以在粗召前面加了一步改写：规则 6 条、类型 6 种，输出 JSON，temperature 压到 0.1；两道成本短路（首条消息无历史直接跳过、启发式指代词判断），失败全部降级用原话。<strong>最关键的一条设计：改写只用于检索，生成时模型看到的还是用户原话</strong>——改写错了不污染答案。</p>
<p>一句话小结：<strong>改写是给检索器用的，不是给用户看的。</strong> 四步流程看 <a href="/posts/rag-query-rewrite-4-steps/">「RAG 怕问得含糊？Query 改写四步直接抄」</a>，规则和类型清单看 <a href="/posts/rag-rewrite-6rules-6types/">「RAG 改写没头绪？6 规则配 6 类型直接抄」</a>。</p>
<h3 id="环节六精排--生成最后五段决定答案质量">环节六：精排 + 生成——最后五段决定答案质量</h3>
<p>粗召是广撒网，top-20 里良莠不齐。最后一步用 <strong>bge-reranker-v2-m3</strong> 做 cross-encoder 精排：把问题和每条候选成对过一遍模型，逐条打分重排，<strong>只取 top-5</strong> 注入上下文，交给 DeepSeek 流式输出（SSE，打字机效果）。rerank 服务不可用时自动降级：按 RRF 分或召回原始序截断——精度掉一点，服务不断。</p>
<p>生成侧的 system prompt 上了硬约束：<strong>只根据资料回答，找不到就说找不到，不许编。</strong> 配合前端把命中片段的名次、来源全部展示出来，答错了能追责到具体文档。</p>
<p>一句话小结：<strong>精排是唯一「问题+资料同场打分」的环节，值得为它留降级通道。</strong></p>
<h2 id="全链路可观测效果差的时候先知道差在哪">全链路可观测：效果差的时候，先知道差在哪</h2>
<p>最后一环不是某个算法，是工程习惯：</p>
<ul>
<li>混合 / 纯关键词 / 纯向量<strong>三种模式页面一键切换</strong>，同一个问答抽屉按 mode 切换，切模式时强制重建组件清掉历史答案，防止新旧混着看；</li>
<li>单独的关键词检索、向量检索调试接口，支持单路对比；还有个 <code>retrieveOnly</code> 开关——只看检索结果，不触发生成；</li>
<li>每一路的名次、得分全部打日志。</li>
</ul>
<p>这套 observability 的回报巨大：用户反馈「答得烂」，我十分钟就能定位是检索没捞到、还是捞到了模型没用好。排查思路我写成了一篇 <a href="/posts/rag-debug-playbook/">「RAG 效果差怎么调试？面试官要听的排查思路」</a>，面试和干活都用得上。</p>
<h2 id="说点效果也说实话">说点效果，也说实话</h2>
<p>链路参数都在明处：双路各 top-20、RRF 融合取 top-20、精排取 top-5、k=60、k1=1.5、b=0.75。但你要问我「准确率提升多少个百分点」——<strong>说实话没有脸说，因为我没建标准评估集，上线前后的评测数字我没记下来</strong>，这是这期的欠账，补上评测再写。我唯一敢负责的话是：加精排和改写之前，专有名词类问题经常答不上来；之后这类问题基本能稳定命中——这是体感，不是数据。</p>
<p>手搓一圈最大的收获不在算法，在心态：<strong>RAG 系统 80% 的工程问题，最后都归结为「数据进库时没管好，查询进来时没接住」。</strong> 状态机、日志、降级通道，这些最土的工程实践，恰恰是 AI 应用最缺的东西。</p>
<p>这是「老朱的 RAG 系列」总开篇，六个环节各有一篇深拆，链接都在文中。下一篇不在 RAG 系列里，但和这套系统的账单直接相关：<strong>四个 AI API 平台的价格/速度/稳定性实测对比</strong>——手搓很爽，token 很贵，怎么选不花冤枉钱，下篇见。</p>
]]></content:encoded></item><item><title>RAG 还是微调？大模型应用 3 种模式别选错</title><link>https://blog.zjkun.top/posts/rag-vs-finetune-3-modes/</link><pubDate>Fri, 09 Oct 2026 09:30:00 +0800</pubDate><guid>https://blog.zjkun.top/posts/rag-vs-finetune-3-modes/</guid><description>给大模型加能力有三层：Prompt 上下文、RAG、微调。我用 Nuxt3 企业知识库的真实选型讲清边界——为什么知识类需求 RAG 完胜微调：SFT 教的是行为不是知识，知识更新频率、可溯源、成本三笔账怎么算。</description><content:encoded><![CDATA[<p>做 AI 应用绕不开这道选择题：让模型「懂你的业务」，到底靠提示词、RAG，还是微调？</p>
<p>我把自己的企业知识库项目（Nuxt3 my-nuxt-app：私有制度文档问答，检索走向量+BM25 混合，生成走 deepseek-chat API）从头选到尾，这篇文章把三层的边界和我的决策过程讲清楚——这也是面试被问最多的一题。</p>
<h2 id="先摆认知地基训练范式在干嘛">先摆认知地基：训练范式在干嘛</h2>
<p>一张表说人话（来自我系统补的《语言模型训练范式》课件，老师一句话总结：<strong>大模型 = 会接话茬的机器（Pretrain）+ 会做任务的机器（SFT）+ 会自我评判的机器（RM/PPO/DPO）</strong>）：</p>
<table>
	<thead>
			<tr>
					<th>阶段</th>
					<th>在干嘛</th>
					<th>人肉类比</th>
					<th>数据量</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Pretrain</td>
					<td>海量文字学「预测下一个字」</td>
					<td>儿童大量阅读</td>
					<td>Llama3 用了 15 万亿 tokens</td>
			</tr>
			<tr>
					<td>SFT</td>
					<td>带标注问答，从接话茬变做任务</td>
					<td>老师布置带答案的作业</td>
					<td>几十万条 ≈ 预训练的 1/5000</td>
			</tr>
			<tr>
					<td>RM + PPO/DPO</td>
					<td>学会「说人该说的话」</td>
					<td>按批改意见反复改写</td>
					<td>较小</td>
			</tr>
	</tbody>
</table>
<p>硬核数字记一个就够：Llama 3.1 405B 预训练 = 16384 块 H100 跑 54 天，亿级人民币。所以<strong>普通企业和个人根本不碰预训练</strong>，选择题只在「现成模型 + 什么方式喂能力」之间做。</p>
<p>面试加分项也在这：<strong>SFT/微调 ≠ 给模型喂知识，是教模型「做任务」。</strong> 知识靠 Pretrain（通用）+ RAG（私有/新）供给。很多人把微调当成「把公司文档灌进模型」，方向从根上就错了。</p>
<h2 id="三层模式按成本从低到高">三层模式，按成本从低到高</h2>
<h3 id="第一层prompt--上下文工程">第一层：Prompt / 上下文工程</h3>
<p>把关键信息直接写进 system prompt。适合：全局规则、输出格式、角色设定。</p>
<p>局限：窗口有限，塞不进几十份制度文档；每轮都重复计费。我的项目里它只负责「只用资料作答、不编造、标注来源页码」这类约束。</p>
<h3 id="第二层rag知识在模型外面">第二层：RAG——知识在模型外面</h3>
<p>文档入库切块 → 向量化（我用 Chroma + bge-m3）→ 提问先检索（混合检索：BM25+向量，RRF 融合，reranker 精排取 top5）→ 检索结果拼进 prompt → 生成。</p>
<p><strong>知识更新 = 重新入库，分钟级。</strong> 文档改了版本，知识库表里状态机走一遍（草稿→发布→下架），不动模型一根手指。回答能带来源文件和页码——<strong>可溯源</strong>，这是企业场景的硬需求，微调给不了。</p>
<h3 id="第三层微调把能力焊进权重">第三层：微调——把能力焊进权重</h3>
<p>拿几百到几万条「输入→期望输出」对模型做 SFT。适合什么？<strong>固定的输出格式、领域语气、术语习惯</strong>——也就是「行为方式」，不是「知识内容」。</p>
<h2 id="我的真实三笔账为什么-my-nuxt-app-选了-rag微调碰都不碰">我的真实三笔账（为什么 my-nuxt-app 选了 RAG，微调碰都不碰）</h2>
<p><strong>1. 知识更新频率。</strong> 部门制度文档是活的：新考勤规则、修订的报销流程。RAG 重新入库就完事；微调要攒数据、排队训练、验证回归——为「上周改的那条婚假规定」重训一次模型，纯属行为艺术。</p>
<p><strong>2. 机器现实。</strong> 我的生产环境是 99 元/年的 2核2G 阿里云 ECS，内存预算排完（nginx 64MB + node-app 384MB + 系统 + swap）只剩 1G 出头。<strong>这台机器连一个 7B 模型都推理不动</strong>，更别说训练。我架构里所有重活全在云 API：Embedding 走 bge-m3、精排走硅基流动 bge-reranker-v2-m3、生成走 deepseek-chat。微调需要训练数据+算力+评测集三件套，对个人项目是三重成本。</p>
<p><strong>3. 可回滚性。</strong> RAG 答错了，改文档、调分块、调检索参数，改完立刻生效，还能追溯「当时引用了哪一页」。微调把错误知识焊进权重后，你连它为什么胡说都查不出来，回滚要重训。</p>
<h2 id="什么时候我才会上微调">什么时候我才会上微调</h2>
<p>预判一下自己：等业务问答稳定，出现这类需求再考虑——「回答必须严格按公司工单模板的语气和结构输出」这种<strong>格式/风格</strong>问题。届时也是 LoRA 这类轻量方案 + 现成 API 模型，依旧不碰预训练。知识类需求，永远 RAG。</p>
<h2 id="速查表">速查表</h2>
<table>
	<thead>
			<tr>
					<th>需求</th>
					<th>选择</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>全局规则/格式约束</td>
					<td>Prompt</td>
			</tr>
			<tr>
					<td>私有知识、时效知识、要溯源</td>
					<td>RAG</td>
			</tr>
			<tr>
					<td>固定输出风格/任务行为</td>
					<td>微调（最后才考虑）</td>
			</tr>
			<tr>
					<td>「把文档灌进模型」的想法</td>
					<td>❌ 方向错了，那是 RAG 的活</td>
			</tr>
	</tbody>
</table>
<p>一句话收束：<strong>知识在模型外面解决（RAG），行为在模型权重里解决（微调）——分不清这两件事，选型必错。</strong></p>
<p>下一篇讲 RAG 效果差怎么调试。</p>
<blockquote>
<p>本篇最早于 2026 年 10 月 9 日发布于小红书，此为博客完整版。</p>
</blockquote>
]]></content:encoded></item></channel></rss>