-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrss.xml
More file actions
251 lines (115 loc) · 216 KB
/
Copy pathrss.xml
File metadata and controls
251 lines (115 loc) · 216 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>RyanCoreAI</title>
<link>https://yanxai.com/</link>
<atom:link href="https://yanxai.com/rss.xml" rel="self" type="application/rss+xml"/>
<description>记录可信 AI 应用工程、项目复盘与长期成长</description>
<pubDate>Sun, 28 Jun 2026 12:51:10 GMT</pubDate>
<generator>http://hexo.io/</generator>
<item>
<title>一文讲透 RAG 核心术语:Embedding、Chunk、Vector DB、BM25、Reranker 到底是什么</title>
<link>https://yanxai.com/p/rag-core-terms/</link>
<guid>https://yanxai.com/p/rag-core-terms/</guid>
<pubDate>Sun, 28 Jun 2026 13:00:00 GMT</pubDate>
<description><p>上一篇我写 RAG,不想把它讲成“给 AI 接一个知识库”。</p>
<p>因为知识库只是资料放在哪里,RAG 真正要解决的是:当 AI 给出一个答案时,我们能不能知道它依据了哪段材料、有没有遗漏限制条件、能不能在证据不足时拒答。</p>
<p>但如果继续往下讲 RAG,很快就会遇到一堆术语:</p>
<p>Embedding、Chunk、Vector DB、BM25、Reranker、Ci</description>
<content:encoded><![CDATA[<p>上一篇我写 RAG,不想把它讲成“给 AI 接一个知识库”。</p><p>因为知识库只是资料放在哪里,RAG 真正要解决的是:当 AI 给出一个答案时,我们能不能知道它依据了哪段材料、有没有遗漏限制条件、能不能在证据不足时拒答。</p><p>但如果继续往下讲 RAG,很快就会遇到一堆术语:</p><p>Embedding、Chunk、Vector DB、BM25、Reranker、Citation、Faithfulness、RAG Eval……</p><p>这些词如果直接堆出来,文章会很像技术名词表。读者看完还是不知道:它们到底在 RAG 系统里解决什么问题?</p><p>所以这一篇不写代码,不写复杂架构,只做一件事:</p><p><strong>把 RAG 里的核心术语翻译成人话,并说明每个组件在“证据链”里负责什么。</strong></p><p>如果把 RAG 看成一条可信回答链路,它大概是这样:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">原始文档</span><br><span class="line"> ↓</span><br><span class="line">清洗 / 解析</span><br><span class="line"> ↓</span><br><span class="line">Chunking</span><br><span class="line"> ↓</span><br><span class="line">Embedding</span><br><span class="line"> ↓</span><br><span class="line">向量检索 / BM25 检索</span><br><span class="line"> ↓</span><br><span class="line">Hybrid Search</span><br><span class="line"> ↓</span><br><span class="line">RRF 融合排序</span><br><span class="line"> ↓</span><br><span class="line">Reranker 精排</span><br><span class="line"> ↓</span><br><span class="line">Final Evidence</span><br><span class="line"> ↓</span><br><span class="line">LLM 基于证据生成回答</span><br><span class="line"> ↓</span><br><span class="line">Citation / Faithfulness / Abstention</span><br><span class="line"> ↓</span><br><span class="line">RAG Eval</span><br></pre></td></tr></tbody></table></figure><p>这条链路看起来很长,但它其实只服务一个目标:</p><p><strong>让 AI 的回答从“看起来对”,走向“有证据、可追溯、可验证”。</strong></p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/rag-core-terms/rag-core-terms-overview.webp" alt="image.png"></p><h2 id="1-Document:原始资料"><a href="#1-Document:原始资料" class="headerlink" title="1. Document:原始资料"></a>1. Document:原始资料</h2><p>Document,就是进入知识库的原始资料。它可以是公司制度 PDF、产品说明书、客服 SOP、GitHub README、技术文档、Markdown 博客、简历、订单规则、FAQ,也可以是数据库里的业务记录。</p><p>Document 解决的是 RAG 里最基础的问题:</p><p><strong>系统到底从哪里拿事实?</strong></p><p>如果没有外部资料,模型只能依赖参数记忆。它可能知道一些通用知识,但不知道你的公司政策、项目文档、订单状态、内部流程、用户简历。</p><p>所以 Document 是 RAG 的原料。</p><p>但原始文档不能直接塞给模型。原因很简单:</p><ul><li>太长。 </li><li>太乱。 </li><li>格式复杂。 </li><li>版本不清楚。 </li><li>里面可能有页眉、页脚、目录、广告、水印、重复段落。 </li><li>有些信息已经过期。 </li><li>有些信息不属于当前用户或当前租户。</li></ul><p>所以进入 RAG 系统的第一步,不是 embedding,也不是向量库,而是文档治理:这份文档是谁的、从哪里来、什么时候更新、属于哪个业务线、是否有效、用户有没有权限看。</p><p>一句话总结:<br><strong>Document 是 RAG 的事实来源,但原始文档本身还不是可用证据。</strong></p><h2 id="2-Chunk:证据片段"><a href="#2-Chunk:证据片段" class="headerlink" title="2. Chunk:证据片段"></a>2. Chunk:证据片段</h2><p>Chunk,就是把大文档切成一小段一小段,方便检索和引用。</p><p>很多人会把 chunking 理解成“把长文切短”,比如每 500 tokens 切一段,重叠 50 tokens。</p><p>这个理解太粗。</p><p>真正好的 chunk,不只是短,而是要成为一个“证据单元”。</p><p>比如一份退款政策里有这样一段:</p><blockquote><p>定制商品非质量问题不支持七天无理由退货。若商品存在质量问题,用户需在签收后 7 日内提交图片或视频证据,客服审核后进入售后流程。</p></blockquote><p>如果你把它切成两段:</p><ul><li>第一段只保留“定制商品非质量问题不支持七天无理由退货”。 </li><li>第二段只保留“用户需在签收后 7 日内提交证据”。</li></ul><p>那模型可能在回答时丢掉条件,或者误解适用范围。</p><p>所以 chunk 的目标不是平均切短,而是:</p><p><strong>把文档切成能被检索、被引用、被验证的最小证据单元。</strong></p><p>好的 chunk 不只包含正文,还应该带上能追溯和过滤的信息,比如标题、章节、来源、页码、版本、所属租户、更新时间和权限范围。</p><p>比如:</p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"content"</span><span class="punctuation">:</span> <span class="string">"定制商品非质量问题不支持七天无理由退货..."</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"source"</span><span class="punctuation">:</span> <span class="string">"refund_policy_v3.pdf"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"section"</span><span class="punctuation">:</span> <span class="string">"定制商品售后限制"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"page"</span><span class="punctuation">:</span> <span class="number">7</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"tenantId"</span><span class="punctuation">:</span> <span class="string">"merchant_001"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"v3"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"updatedAt"</span><span class="punctuation">:</span> <span class="string">"2026-06-01"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>这才是一个对 RAG 友好的 chunk。</p><p>一句话总结:</p><p><strong>Chunk 不是把文档切短,而是把文档切成 AI 能使用、用户能追溯的证据片段。</strong></p><h2 id="3-Embedding:把文本变成语义坐标"><a href="#3-Embedding:把文本变成语义坐标" class="headerlink" title="3. Embedding:把文本变成语义坐标"></a>3. Embedding:把文本变成语义坐标</h2><p>Embedding 是 RAG 里最常被提到的词。</p><p>简单说,Embedding 就是把一段文本变成一串数字,也就是向量。</p><p>为什么要这么做?</p><p>因为计算机不能直接理解“这两句话意思很接近”。它需要一种可以计算的表示方式。</p><p>比如用户问:</p><blockquote><p>出差住酒店最多能报销多少钱?</p></blockquote><p>文档里写的是:</p><blockquote><p>差旅住宿报销标准:一线城市 600 元/晚,其他城市 400 元/晚。</p></blockquote><p>这两句话用词不完全一样。</p><p>用户说的是“出差”“住酒店”“最多能报销多少钱”,文档写的是“差旅”“住宿”“报销标准”。如果只靠关键词,系统不一定能稳定命中。但从语义上看,它们问的其实是同一类问题。</p><p>Embedding 的作用,就是把这些文本映射到一个向量空间里。你可以把它理解成“语义坐标”。</p><ul><li>意思接近的文本,坐标距离更近。 </li><li>意思不相关的文本,坐标距离更远。</li></ul><p>所以在 RAG 里,Embedding 解决的是:</p><p><strong>让系统可以计算用户问题和文档片段之间的语义相似度。</strong></p><p>具体流程分成两个阶段。</p><p>第一阶段是文档入库,也就是提前处理知识库:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">原始文档</span><br><span class="line"> ↓</span><br><span class="line">切成 chunk</span><br><span class="line"> ↓</span><br><span class="line">每个 chunk 做 embedding</span><br><span class="line"> ↓</span><br><span class="line">得到每个 chunk 的向量</span><br><span class="line"> ↓</span><br><span class="line">把 chunk 原文 + 向量 + metadata 存入数据库</span><br></pre></td></tr></tbody></table></figure><p>比如文档被切成三个 chunk:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">chunk_001:差旅住宿报销标准:一线城市 600 元/晚,其他城市 400 元/晚。</span><br><span class="line">chunk_002:交通费报销需提供发票。</span><br><span class="line">chunk_003:餐饮补贴按城市等级计算。</span><br></pre></td></tr></tbody></table></figure><p>系统会把每个 chunk 都转成向量。真实的向量通常有几百到几千维,这里只用简化数字演示:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">chunk_001 embedding = [0.12, -0.44, 0.87, ...]</span><br><span class="line">chunk_002 embedding = [0.31, 0.22, -0.10, ...]</span><br><span class="line">chunk_003 embedding = [-0.18, 0.62, 0.40, ...]</span><br></pre></td></tr></tbody></table></figure><p>这些数字不是给人看的,而是给系统计算相似度用的。</p><p>第二阶段是用户提问,也就是在线检索:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">问题也做 embedding</span><br><span class="line"> ↓</span><br><span class="line">得到 query 向量</span><br><span class="line"> ↓</span><br><span class="line">拿 query 向量去数据库里查相似 chunk</span><br><span class="line"> ↓</span><br><span class="line">返回最相似的 topK 个 chunk</span><br><span class="line"> ↓</span><br><span class="line">把这些 chunk 原文交给 LLM 生成回答</span><br></pre></td></tr></tbody></table></figure><p>比如用户问:</p><blockquote><p>出差住酒店最多能报销多少钱?</p></blockquote><p>系统会把这个问题也转成向量:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">query embedding = [0.10, -0.41, 0.83, ...]</span><br></pre></td></tr></tbody></table></figure><p>然后系统会比较:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">query embedding 和 chunk_001 embedding 距离很近</span><br><span class="line">query embedding 和 chunk_002 embedding 距离较远</span><br><span class="line">query embedding 和 chunk_003 embedding 距离较远</span><br></pre></td></tr></tbody></table></figure><p>所以它会优先召回 chunk_001:</p><blockquote><p>差旅住宿报销标准:一线城市 600 元/晚,其他城市 400 元/晚。</p></blockquote><p>最后,LLM 不是凭空回答,而是基于召回的 chunk 生成:</p><blockquote><p>根据差旅住宿报销标准,一线城市住宿最高可报销 600 元/晚,其他城市最高可报销 400 元/晚。</p></blockquote><p>所以 Embedding 不是直接回答问题,它只负责把“可能相关的证据”找出来。</p><p>你可以把它理解成 RAG 检索层的第一步:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">文档 chunk 先变成向量</span><br><span class="line">用户问题也变成向量</span><br><span class="line">系统比较两边向量的距离</span><br><span class="line">距离越近,说明语义越相关</span><br><span class="line">然后取回对应 chunk 原文</span><br><span class="line">再交给大模型生成回答</span><br></pre></td></tr></tbody></table></figure><p>但这里有一个非常重要的边界:</p><p><strong>Embedding 解决的是语义相似,不等于事实正确。</strong></p><ul><li>相似,不代表能回答。 </li><li>相关,不代表完整。 </li><li>语义接近,不代表证据足够。</li></ul><p>比如用户问:</p><blockquote><p>SKU-A1937 是否支持德国仓发货?</p></blockquote><p>这个问题最关键的是两个精确信息:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">SKU-A1937</span><br><span class="line">德国仓</span><br></pre></td></tr></tbody></table></figure><p>系统必须找到同时和这两个条件相关的内容。</p><p>如果只靠 Embedding,它可能召回这些看起来相近的内容:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">SKU-A1938 支持德国仓发货。</span><br><span class="line">SKU-A1937 支持法国仓发货。</span><br><span class="line">德国仓发货规则说明。</span><br></pre></td></tr></tbody></table></figure><p>这些内容都“相关”,但不一定能回答用户的问题。</p><p>因为用户问的不是“德国仓规则是什么”,也不是“类似 SKU 怎么发货”,而是:</p><blockquote><p>SKU-A1937 这个具体商品,是否支持德国仓发货?</p></blockquote><p>这种问题不能只靠语义相似,还需要 BM25 / 关键词检索、metadata filter,甚至直接查业务数据库。</p><p>所以不要把 Embedding 当成语义魔法。它只是 RAG 检索系统的一部分。</p><p>一句话总结:</p><p><strong>Embedding 是 RAG 检索层的语义匹配器:它把文档片段和用户问题都变成向量,通过相似度找到可能相关的候选证据。但它只能说明“语义接近”,不能保证证据完整、事实正确、权限合规,也不能替代关键词检索和业务数据查询。</strong></p><h2 id="4-Vector-DB:存向量、查相似"><a href="#4-Vector-DB:存向量、查相似" class="headerlink" title="4. Vector DB:存向量、查相似"></a>4. Vector DB:存向量、查相似</h2><p>有了 embedding 之后,需要一个地方存这些向量,并支持按相似度检索。</p><p>这就是 Vector DB。</p><p>常见选择包括 PGVector、Milvus、Qdrant、Weaviate、Pinecone,也可以使用 Elasticsearch / OpenSearch 的向量检索能力。</p><p>Vector DB 的基本工作方式是:</p><ul><li>文档被切成 chunk。 </li><li>每个 chunk 被 embedding 成向量。 </li><li>向量和 chunk 一起存入数据库。 </li><li>用户提问时,问题也被 embedding 成向量。 </li><li>系统查找和问题向量最相似的 chunk。 </li><li>把这些 chunk 交给大模型生成答案。</li></ul><p>基本流程:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">chunk 文本</span><br><span class="line"> ↓</span><br><span class="line">embedding 模型</span><br><span class="line"> ↓</span><br><span class="line">向量</span><br><span class="line"> ↓</span><br><span class="line">Vector DB</span><br><span class="line"> ↓</span><br><span class="line">相似度检索</span><br><span class="line"> ↓</span><br><span class="line">topK chunks</span><br></pre></td></tr></tbody></table></figure><p>如果你用 Java / Spring Boot / PostgreSQL 技术栈,PGVector 是一个很适合入门和工程落地的选择。它是 PostgreSQL 的向量相似搜索扩展,可以让你在 PostgreSQL 里存向量、建索引、做相似度查询。</p><p>对个人项目和中小型应用来说,PGVector 的优势是工程成本低:document、chunk、metadata、tenant_id 可以和业务数据放在同一个 PostgreSQL 里,也方便接入 Spring AI 的 VectorStore 抽象,不需要一开始就维护单独的向量数据库集群。</p><p>但 Vector DB 不是 RAG 的全部。</p><p>很多人做 RAG 的第一个误区就是:</p><blockquote><p>我接了向量数据库,所以我有 RAG 了。</p></blockquote><p>不一定。</p><p>如果你没有好的 chunk,没有 metadata filter,没有 citation,没有拒答,没有 eval,那它只是一个“向量检索 + Prompt”demo。</p><p>用一个生活类比:</p><p>假设你有一个图书馆。</p><ul><li>Document 是一本本书。 </li><li>Chunk 是书里被裁出来的一小段内容。 </li><li>Embedding 是给每一段内容贴一个“语义坐标”。 </li><li>Vector DB 是图书馆的“语义索引系统”。 </li><li>用户问题也会被贴一个“语义坐标”。 </li><li>系统就看:用户问题这个坐标,离哪些内容的坐标最近。 </li><li>最近的几段,就是候选证据。</li></ul><p>所以 Vector DB 不是“知识库本身”的全部,它只是知识库里的一个检索组件。</p><p>一句话总结:</p><p><strong>Vector DB 负责把 chunk 的语义坐标存起来,并在用户提问时找出最相似的候选证据;但证据是否完整、是否有权限、是否支持答案,还要靠 chunk 质量、metadata filter、reranker、citation、faithfulness 和 eval。</strong></p><h2 id="5-topK:到底取多少条证据"><a href="#5-topK:到底取多少条证据" class="headerlink" title="5. topK:到底取多少条证据"></a>5. topK:到底取多少条证据</h2><p>topK 是一个很小但很重要的参数。</p><p>它表示:检索时取前 K 条结果。</p><p>比如:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">topK = 5</span><br></pre></td></tr></tbody></table></figure><p>意思是:系统会取最相似的 5 个 chunk 放进上下文。</p><p>很多人会以为 topK 越大越好,因为取更多证据似乎更安全。</p><p>但真实情况不是这样。</p><ul><li>topK 太小,可能漏掉关键证据。 </li><li>topK 太大,可能引入噪声,污染上下文。 </li><li>无关 chunk 进入 prompt 后,模型可能把错误信息也编进答案。 </li><li>上下文越长,成本越高,延迟越大。</li></ul><p>比如用户问住宿报销标准,topK 太小可能只拿到金额,漏掉“必须提供发票”;topK 太大又可能把交通费、餐饮补贴、加班餐费一起塞进上下文,污染回答。</p><p>如果 topK 太小,比如 topK = 1:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">只取第 1 条 chunk</span><br></pre></td></tr></tbody></table></figure><p>好处是上下文很干净,成本低,速度快。</p><p>但风险是:如果第 1 条不完整,系统就漏掉关键条件。</p><p>比如第 1 条只说:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">一线城市住宿最高 600 元/晚。</span><br></pre></td></tr></tbody></table></figure><p>但第 4 条才说:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">报销时必须提供住宿发票,且需经过部门负责人审批。</span><br></pre></td></tr></tbody></table></figure><p>如果 topK = 1,模型只能看到第一条,就可能回答不完整。</p><p>这叫:<strong>漏召回</strong>。</p><p>如果 topK 太大,比如 topK = 20:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">取前 20 条 chunk</span><br></pre></td></tr></tbody></table></figure><p>看起来证据更多,好像更安全。</p><p>但问题是,前 20 条里很可能混进很多“看起来相关但其实没用”的内容:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">差旅住宿标准</span><br><span class="line">交通费规则</span><br><span class="line">餐饮补贴</span><br><span class="line">出差审批</span><br><span class="line">酒店协议</span><br><span class="line">员工福利</span><br><span class="line">加班餐费</span><br><span class="line">团队建设报销……</span><br></pre></td></tr></tbody></table></figure><p>这些内容一起塞进 Prompt,模型可能被干扰。</p><p>用户问的是“住宿最高报销多少”,但上下文里混入“餐饮补贴”“交通费”“加班餐费”,模型就可能把不相关规则也带进答案。</p><p>这叫:<strong>噪声污染上下文</strong>。</p><p>所以 topK 的本质是一个平衡:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">topK 太小:容易漏掉关键证据</span><br><span class="line">topK 太大:容易引入无关噪声</span><br></pre></td></tr></tbody></table></figure><p>所以 topK 不是越大越好,而是要和 chunk 质量、检索质量、reranker、上下文窗口一起调。</p><p>比如:</p><ul><li>naive RAG 可能直接取 top5。 </li><li>Hybrid Search 可能先召回 top50。 </li><li>Reranker 再从 top50 里选 top5。 </li><li>最终只把最可靠的 3–5 个 chunk 放进 prompt。</li></ul><p>topK也分两种,第一种是:<strong>召回阶段的 topK</strong>。</p><p>它的目标是“不要漏”。</p><p>比如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">Vector Search 先召回 top50</span><br><span class="line">BM25 也召回 top50</span><br></pre></td></tr></tbody></table></figure><p>这时候 topK 可以大一点,因为只是候选池,还没直接塞进 Prompt。</p><p>第二种是:<strong>最终上下文的 topK</strong>。</p><p>它的目标是“少噪声”。</p><p>比如 reranker 从 top50 里重新排序,最后只选:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">final topK = 3 或 5</span><br></pre></td></tr></tbody></table></figure><p>这几条才会真正进入 Prompt,让 LLM 生成答案。</p><p>所以完整流程是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">向量检索 / BM25 检索</span><br><span class="line"> ↓</span><br><span class="line">先多召回一些候选证据,比如 top50</span><br><span class="line"> ↓</span><br><span class="line">Reranker 重排</span><br><span class="line"> ↓</span><br><span class="line">最终只选 top3~top5</span><br><span class="line"> ↓</span><br><span class="line">放进 Prompt</span><br><span class="line"> ↓</span><br><span class="line">LLM 生成答案</span><br></pre></td></tr></tbody></table></figure><p>一句话总结:</p><p><strong>topK 决定有多少候选证据进入下一步,太少会漏召回,太多会污染上下文。</strong></p><h2 id="6-Similarity-Threshold:相似度阈值"><a href="#6-Similarity-Threshold:相似度阈值" class="headerlink" title="6. Similarity Threshold:相似度阈值"></a>6. Similarity Threshold:相似度阈值</h2><p>Similarity Threshold,就是相似度低于某个分数的结果不要。</p><p>比如:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">similarityThreshold = 0.75</span><br></pre></td></tr></tbody></table></figure><p>意思是:相似度低于 0.75 的 chunk 不进入上下文。</p><p>它的作用是减少无关内容进入 prompt。</p><p>但这个参数也不能迷信。</p><ul><li>阈值太高,可能把有用证据过滤掉。 </li><li>阈值太低,可能放进很多噪声。 </li><li>不同 embedding 模型、不同业务问题、不同 query 长度都会影响分数分布,所以 threshold 不能照抄,需要靠 eval 调。</li></ul><p>Similarity Threshold 可以理解成:<strong>检索结果的最低及格线</strong>。</p><p>Similarity Threshold 的本质是一个平衡:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">阈值太高:结果更干净,但可能漏掉有用证据</span><br><span class="line">阈值太低:结果更多,但可能引入噪声</span><br></pre></td></tr></tbody></table></figure><p>它和 topK 的区别也要分清。</p><p>topK 控制的是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">最多取几条</span><br></pre></td></tr></tbody></table></figure><p>Similarity Threshold 控制的是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">低于多少分不要</span><br></pre></td></tr></tbody></table></figure><p>比如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">topK = 5</span><br><span class="line">similarityThreshold = 0.75</span><br></pre></td></tr></tbody></table></figure><p>意思不是“无论如何取 5 条”,而是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">先按相似度排序最多取前 5 条但低于 0.75 的不要</span><br></pre></td></tr></tbody></table></figure><p>假设结果是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">0.91:chunk A</span><br><span class="line">0.86:chunk B</span><br><span class="line">0.79:chunk C</span><br><span class="line">0.68:chunk D</span><br><span class="line">0.62:chunk E</span><br></pre></td></tr></tbody></table></figure><p>虽然 topK = 5,但因为 threshold = 0.75,最后只保留:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">chunk A</span><br><span class="line">chunk B</span><br><span class="line">chunk C</span><br></pre></td></tr></tbody></table></figure><p>所以可以这样理解:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">topK 是数量上限</span><br><span class="line">threshold 是质量底线</span><br></pre></td></tr></tbody></table></figure><p>一句话总结:</p><p><strong>topK 决定“最多拿几条”,Similarity Threshold 决定“太不像的不要”。</strong></p><h2 id="7-BM25-Lexical-Search:关键词检索不是过时技术"><a href="#7-BM25-Lexical-Search:关键词检索不是过时技术" class="headerlink" title="7. BM25 / Lexical Search:关键词检索不是过时技术"></a>7. BM25 / Lexical Search:关键词检索不是过时技术</h2><p>BM25 或 lexical search,可以简单理解成关键词检索。</p><p>它不像 embedding 那样理解语义,而是更关注词面命中。</p><p>比如:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">SKU-A1937</span><br><span class="line">HTTP 401</span><br><span class="line">退款政策第 7 条</span><br><span class="line">tenant_id</span><br><span class="line">RetrievalAugmentationAdvisor</span><br><span class="line">PGVector</span><br></pre></td></tr></tbody></table></figure><p>这些查询不需要先“理解语义”,而是要精确命中。</p><p>如果用户问“SKU-A1937 是否支持德国仓发货”,系统就应该优先找到包含 <code>SKU-A1937</code> 的文档或表格。</p><p>如果用户问“Spring AI 的 RetrievalAugmentationAdvisor 怎么用”,系统就应该优先命中官方文档里包含这个类名的片段。</p><p>所以 BM25 / lexical search 没有过时。</p><p>它解决的是向量检索不擅长的问题:</p><ul><li>编号。 </li><li>错误码。 </li><li>字段名。 </li><li>类名。 </li><li>方法名。 </li><li>产品型号。 </li><li>政策条款。 </li><li>固定术语。 </li><li>短 query。 </li><li>中英混合表达。</li></ul><p>这也是为什么很多生产 RAG 系统会做 Hybrid Search,而不是只做向量检索。</p><p>BM25 / Lexical Search 这段的核心意思是:</p><p><strong>不要以为 RAG 只靠 Embedding。Embedding 擅长找“意思相近”的内容,但 BM25 擅长找“字面上必须命中”的内容。</strong></p><p>你不需要记 BM25 的公式,只要理解它比普通关键词匹配更会衡量词的重要性和匹配强度,特别适合编号、类名、字段名、错误码、政策条款这类必须精确命中的内容。</p><p>你只要理解:<strong>BM25 更重视词面命中,尤其适合查精确词。</strong></p><p>如果用 Embedding,系统可能觉得这些内容相关:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">HTTP 403 权限错误</span><br><span class="line">HTTP 401 未认证</span><br><span class="line">HTTP 500 服务异常</span><br></pre></td></tr></tbody></table></figure><p>但如果用户明确问 <code>HTTP 401</code>,那系统就必须优先命中包含 <code>HTTP 401</code> 的片段。</p><p>这就是 BM25 的价值。</p><p>它不是过时技术,而是在 RAG 里补 Embedding 的短板。</p><p>更具体一点,Embedding 容易在这些场景失手:</p><p>比如用户问“退款政策第 7 条怎么说?”,Embedding 可能召回退款政策总则、退款流程、售后说明,但不一定精确命中“第 7 条”。BM25 会更关注“第 7 条”这个词面。</p><p>一句话总结:</p><p><strong>Embedding 找语义相似,BM25 / lexical search 找精确命中。</strong></p><h2 id="8-Hybrid-Search:两路一起找证据"><a href="#8-Hybrid-Search:两路一起找证据" class="headerlink" title="8. Hybrid Search:两路一起找证据"></a>8. Hybrid Search:两路一起找证据</h2><p>Hybrid Search,就是混合检索。</p><p>最常见的是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">Vector Search + BM25 / Lexical Search</span><br></pre></td></tr></tbody></table></figure><p>也就是:</p><p>向量检索负责找语义相关。<br>关键词检索负责找精确命中。<br>两路结果合并后再排序。</p><p>它解决的是一个非常现实的问题:</p><p><strong>单一路检索器容易漏证据。</strong></p><p>比如:</p><p>用户问:</p><blockquote><p>我的简历里有没有能支撑 RAG evaluation 的证据?</p></blockquote><p>简历里可能没写 “RAG evaluation”,但写了:</p><blockquote><p>设计了 evidence guard、citation check、unsupported claim detection,用于限制 AI 生成中的无证据扩写。</p></blockquote><p>这个时候向量检索有价值。</p><p>但如果用户问:</p><blockquote><p>我的项目有没有写 PGVector?</p></blockquote><p>那就应该精确命中 <code>PGVector</code> 这个词,关键词检索更重要。</p><p>所以 Hybrid Search 不是炫技,而是降低漏召回。</p><p>基本流程是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">Vector Recall:找语义相关</span><br><span class="line"> +</span><br><span class="line">Lexical Recall:找精确命中</span><br><span class="line"> ↓</span><br><span class="line">融合排序/去重/重排</span><br><span class="line"> ↓</span><br><span class="line">候选证据</span><br></pre></td></tr></tbody></table></figure><p>Vector Search 像一个懂意思的人:<br>“你虽然没说 evidence guard,但我知道它和 RAG evaluation 有关。”</p><p>BM25 像一个认真查关键词的人:<br>“你问 PGVector,我就必须找到 PGVector 这个词。”</p><p>一句话总结:</p><p><strong>Hybrid Search = 语义检索负责“意思相近”,关键词检索负责“原词命中”。两路一起召回,可以降低关键证据被漏掉的概率。</strong></p><h2 id="9-RRF:把多路检索结果合并"><a href="#9-RRF:把多路检索结果合并" class="headerlink" title="9. RRF:把多路检索结果合并"></a>9. RRF:把多路检索结果合并</h2><p>Hybrid Search 会产生两张排行榜,RRF 的作用就是把这两张排行榜合并成一张总榜。</p><p>做完 Hybrid Search 后,会出现一个新问题:</p><ul><li>Vector Search 有一组结果。 </li><li>BM25 / Lexical Search 有一组结果。 </li><li>这两组结果怎么合并?</li></ul><p>最直接的想法是把分数加起来。</p><p>但这通常不严谨。</p><p>因为向量相似度和 BM25 分数不是同一种分数。它们来自不同算法,尺度不同,不能简单相加。</p><p>这时候可以用 RRF。</p><p>RRF,全称 Reciprocal Rank Fusion,中文可以理解成“倒数排名融合”。</p><p>它不直接比较原始分数,而是看排名。</p><p>公式大概是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">RRF score = Σ 1 / (k + rank)</span><br></pre></td></tr></tbody></table></figure><p>不用被公式吓到,它的意思很简单:</p><ul><li>如果一个 chunk 在向量检索里排得很靠前,在关键词检索里也排得靠前,那它更可能重要。 </li><li>如果一个 chunk 只在某一路检索里非常靠前,它也有机会被保留下来。 </li><li>RRF 关心的是“排第几”,不是原始分数是多少。</li></ul><p>举个简单例子:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">Chunk A:向量检索第 1,关键词检索第 5</span><br><span class="line">Chunk B:向量检索第 20,关键词检索第 1</span><br><span class="line">Chunk C:向量检索第 3,关键词检索没有召回</span><br></pre></td></tr></tbody></table></figure><p>RRF 会综合这些排名,而不是强行比较“向量相似度 0.82”和“BM25 分数 12.7”谁更大。</p><p>RRF 最适合解决的问题是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">Vector Search 和 BM25 的分数不能直接加,但它们的排名可以融合。</span><br></pre></td></tr></tbody></table></figure><p>你可以把它放回整个 RAG 流程:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">Vector Search 返回一组排行榜</span><br><span class="line"> ↓</span><br><span class="line">BM25 返回一组排行榜</span><br><span class="line"> ↓</span><br><span class="line">RRF 根据“排名”融合两组结果</span><br><span class="line"> ↓</span><br><span class="line">得到候选证据总榜</span><br><span class="line"> ↓</span><br><span class="line">Reranker 再精排</span><br><span class="line"> ↓</span><br><span class="line">选出 Final Evidence</span><br></pre></td></tr></tbody></table></figure><p>这里还要分清 RRF 和 Reranker:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">RRF:用排名规则快速融合多路检索结果</span><br><span class="line">Reranker:用模型进一步判断 query 和 chunk 是否真的相关</span><br></pre></td></tr></tbody></table></figure><p>一句话总结:</p><p><strong>RRF 是把 Vector Search 和 BM25 的结果合并成一张更合理的候选证据排行榜。</strong></p><h2 id="10-Reranker:最终复核候选证据"><a href="#10-Reranker:最终复核候选证据" class="headerlink" title="10. Reranker:最终复核候选证据"></a>10. Reranker:最终复核候选证据</h2><p>Retriever 是粗筛,Reranker 是复核。</p><ul><li>检索器先找出一批候选 chunk,比如 top50 或 top100。 </li><li>Reranker 再判断这些 chunk 和当前问题到底有多相关。 </li><li>最后只选最适合回答问题的几个 chunk 进入 prompt。</li></ul><p>为什么要这样做?</p><p>因为召回阶段追求“不要漏”。<br>重排阶段追求“更准确”。</p><ul><li>如果一开始就只取 top5,很可能漏掉关键证据。 </li><li>如果把 top50 全部塞进 prompt,又会污染上下文。 </li><li>所以常见做法是:先多召回,再精排。</li></ul><p>典型流程:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">Vector Search / BM25 先召回 top50</span><br><span class="line"> ↓</span><br><span class="line">RRF 把多路结果融合成候选列表</span><br><span class="line"> ↓</span><br><span class="line">Reranker 逐条判断:这个 chunk 到底能不能回答这个问题?</span><br><span class="line"> ↓</span><br><span class="line">重新排序</span><br><span class="line"> ↓</span><br><span class="line">只选 top3 / top5 放进 Prompt</span><br><span class="line"> ↓</span><br><span class="line">LLM 基于最终证据生成答案</span><br></pre></td></tr></tbody></table></figure><p>Reranker 和 Embedding 的区别非常重要。</p><p>Embedding 的做法一般是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">把 query 单独变成向量</span><br><span class="line">把 chunk 单独变成向量</span><br><span class="line">然后计算两个向量距离</span><br></pre></td></tr></tbody></table></figure><p>它更像是快速粗筛:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">这个问题和这个 chunk 的语义大概像不像?</span><br></pre></td></tr></tbody></table></figure><p>Reranker 的做法通常是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">把 query 和 chunk 放在一起让模型直接判断它们的相关性</span><br></pre></td></tr></tbody></table></figure><p>它问的是更细的问题:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">这个 chunk 是否真的能回答当前 query?</span><br><span class="line">这个 chunk 是强相关、弱相关,还是只是沾边?</span><br><span class="line">这个 chunk 是否包含用户问题需要的关键条件?</span><br></pre></td></tr></tbody></table></figure><p>举个例子。</p><p>用户问:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">退款政策第 7 条怎么说?</span><br></pre></td></tr></tbody></table></figure><p>前面检索可能召回:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">A:退款政策总则</span><br><span class="line">B:退款申请流程</span><br><span class="line">C:第 7 条:定制商品非质量问题不支持七天无理由退货</span><br><span class="line">D:售后审核要求</span><br><span class="line">E:退货运费说明</span><br></pre></td></tr></tbody></table></figure><p>Embedding 可能觉得 A、B、D、E 都和“退款政策”相关。</p><p>但 Reranker 会更容易判断:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">用户问的是“第 7 条”</span><br><span class="line">C 直接包含“第 7 条”</span><br><span class="line">所以 C 应该排第一</span><br></pre></td></tr></tbody></table></figure><p>这就是 reranker 的价值:<strong>它不是只看大概相关,而是更细地判断“能不能支撑这个问题”。</strong></p><p>一句话总结:</p><p><strong>Reranker 是 final context 之前的最后一道证据复核:它决定哪些候选 chunk 真正有资格进入大模型上下文。</strong></p><h2 id="11-Metadata-Filter:只在正确范围里检索"><a href="#11-Metadata-Filter:只在正确范围里检索" class="headerlink" title="11. Metadata Filter:只在正确范围里检索"></a>11. Metadata Filter:只在正确范围里检索</h2><p>很多 RAG demo 会直接在所有文档里检索,但生产系统不能这样做。</p><p>Metadata,就是文档或 chunk 附带的信息。</p><p>比如:</p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"tenantId"</span><span class="punctuation">:</span> <span class="string">"merchant_001"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sourceType"</span><span class="punctuation">:</span> <span class="string">"refund_policy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"language"</span><span class="punctuation">:</span> <span class="string">"zh"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"v3"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status"</span><span class="punctuation">:</span> <span class="string">"active"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"updatedAt"</span><span class="punctuation">:</span> <span class="string">"2026-06-01"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>Metadata Filter 就是按这些字段过滤。</p><p>为什么它重要?</p><p>因为不是所有文档都应该被检索。</p><p>用户问退款政策时,系统要先判断:</p><ul><li>是不是当前商家的政策? </li><li>是不是当前语言? </li><li>是不是当前版本? </li><li>是不是 active 状态? </li><li>是不是当前用户有权限访问? </li><li>是不是对应业务线?</li></ul><p>如果不做 metadata filter,系统可能召回:</p><ul><li>旧版本政策。 </li><li>其他租户文档。 </li><li>测试文档。 </li><li>英文文档。 </li><li>无权限文档。 </li><li>已废弃规则。</li></ul><p>这不是回答不准的问题,而是系统边界问题。</p><p><strong>RAG 检索时,不能在所有文档里乱找。它必须先限定范围,只在“当前用户应该看的、当前业务应该用的、当前版本有效的文档”里检索。</strong></p><p>Chunk 本身是正文,比如:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">定制商品非质量问题不支持七天无理由退货。</span><br></pre></td></tr></tbody></table></figure><p>Metadata 是这段 chunk 身上的标签,比如:</p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"tenantId"</span><span class="punctuation">:</span> <span class="string">"merchant_001"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sourceType"</span><span class="punctuation">:</span> <span class="string">"refund_policy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"language"</span><span class="punctuation">:</span> <span class="string">"zh"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"v3"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status"</span><span class="punctuation">:</span> <span class="string">"active"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"updatedAt"</span><span class="punctuation">:</span> <span class="string">"2026-06-01"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>这些标签不是给大模型看的废信息,而是给系统检索时做过滤用的。</p><p>也就是说,系统不是直接问:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">哪些 chunk 和用户问题最相似?</span><br></pre></td></tr></tbody></table></figure><p>而应该先问:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">在当前商家 merchant_001 的 active 中文退款政策里,哪些 chunk 和用户问题最相似?</span><br></pre></td></tr></tbody></table></figure><p>这就是 Metadata Filter。</p><p>一句话总结:</p><p><strong>Metadata Filter 决定“哪些资料有资格参与检索”;Embedding / BM25 决定“这些资料里哪些最相关”。</strong></p><h2 id="12-Tenant-Filter:多租户系统的硬隔离"><a href="#12-Tenant-Filter:多租户系统的硬隔离" class="headerlink" title="12. Tenant Filter:多租户系统的硬隔离"></a>12. Tenant Filter:多租户系统的硬隔离</h2><p>Tenant Filter 可以理解成 Metadata Filter 里最不能出错的一种。</p><p>Metadata Filter 是范围控制,Tenant Filter 是权限隔离。</p><p>如果一个系统服务多个公司、多个商家、多个团队,每个组织就是一个 tenant。</p><p>比如:</p><ul><li>商家 A 有自己的退款政策。 </li><li>商家 B 有自己的退款政策。 </li><li>商家 C 有自己的商品知识库。</li></ul><p>用户来自商家 A,那么系统只能检索商家 A 的文档。</p><p>不能靠 prompt 说:</p><blockquote><p>请你只回答当前商家的内容。</p></blockquote><p>这不够。</p><p>真正的 tenant filter 应该在数据库查询、检索条件、权限系统里生效。</p><p>比如:</p><figure class="highlight sql"><table><tbody><tr><td class="code"><pre><span class="line"><span class="keyword">WHERE</span> tenant_id <span class="operator">=</span> <span class="string">'merchant_001'</span></span><br></pre></td></tr></tbody></table></figure><p>如果 tenant filter 漏了,后果不是“回答不够好”,而是“数据越权”。</p><p>一句话总结:</p><p><strong>Tenant Filter 不是 Prompt 约束,而是数据访问控制。</strong></p><h2 id="13-Citation:答案来自哪里,但不代表答案一定被支持"><a href="#13-Citation:答案来自哪里,但不代表答案一定被支持" class="headerlink" title="13. Citation:答案来自哪里,但不代表答案一定被支持"></a>13. Citation:答案来自哪里,但不代表答案一定被支持</h2><p>Citation,就是引用来源。</p><p>它告诉用户:</p><ul><li>这个答案来自哪篇文档。 </li><li>来自哪个 chunk。 </li><li>来自哪一页。 </li><li>来自哪个章节。 </li><li>原文片段是什么。</li></ul><p>比如:</p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"answer"</span><span class="punctuation">:</span> <span class="string">"一线城市住宿最高可报销 600 元/晚。"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"citation"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"document"</span><span class="punctuation">:</span> <span class="string">"2026 差旅报销制度"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"section"</span><span class="punctuation">:</span> <span class="string">"住宿标准"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"page"</span><span class="punctuation">:</span> <span class="number">3</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"quote"</span><span class="punctuation">:</span> <span class="string">"一线城市 600 元/晚,其他城市 400 元/晚"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>Citation 的价值是让答案可追溯。</p><p>但要注意:</p><p><strong>有引用不等于可信。</strong></p><p>引用只能说明系统给了来源。<br>它不能自动说明答案真的被来源支持。</p><p>比如原文说:</p><blockquote><p>定制商品非质量问题不支持七天无理由退货。</p></blockquote><p>模型回答:</p><blockquote><p>所有跨境商品都不支持退货。</p></blockquote><p>这个答案可能也带了引用,但它扩大了原文范围,所以不可信。</p><p>一句话总结:</p><p><strong>Citation 是可信的起点,不是终点。</strong></p><h2 id="14-Faithfulness-Groundedness:答案是否忠于证据"><a href="#14-Faithfulness-Groundedness:答案是否忠于证据" class="headerlink" title="14. Faithfulness / Groundedness:答案是否忠于证据"></a>14. Faithfulness / Groundedness:答案是否忠于证据</h2><p>Faithfulness 和 Groundedness 经常一起出现。</p><p>你可以先不用纠结二者的边界,它们都在问一个核心问题:</p><p><strong>模型说的话,证据里到底有没有?</strong></p><p>如果证据说:</p><blockquote><p>候选人参与了 Spring Boot 后端开发,并接入了 Redis 限流。</p></blockquote><p>模型改写成:</p><blockquote><p>主导设计高并发 AI Agent 平台,使用 Redis、RocketMQ、PGVector 构建生产级 RAG 系统。</p></blockquote><p>这就是典型不 faithful。</p><p>因为模型加了很多证据里没有的东西。</p><p>这在 AI 简历、项目包装、求职材料里尤其危险。它不是简单“润色”,而是把没有证据的经历写成了事实。</p><p>所以 Faithfulness / Groundedness 不是看答案是否流畅,也不是看答案是否有引用,而是看:</p><p>答案里的每个关键 claim 是否被证据支持。</p><p>你可以把它拆成 claim 检查:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">Claim 1:主导设计高并发 AI Agent 平台证据支持?没有。</span><br><span class="line">Claim 2:使用 Redis证据支持?有。</span><br><span class="line">Claim 3:使用 RocketMQ证据支持?没有。</span><br><span class="line">Claim 4:使用 PGVector证据支持?没有。</span><br><span class="line">Claim 5:构建生产级 RAG 系统证据支持?没有。</span><br></pre></td></tr></tbody></table></figure><p>所以 Faithfulness 检查的不是“这句话写得好不好”,而是:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">每一个关键 claim 是否有证据支撑?</span><br><span class="line">有没有夸大?</span><br><span class="line">有没有扩展范围?</span><br><span class="line">有没有补充证据里没有的技术?</span><br><span class="line">有没有把参与说成主导?</span><br><span class="line">有没有把 demo 说成生产级?</span><br></pre></td></tr></tbody></table></figure><p>这也是为什么 Faithfulness 不能只看语言是否流畅,也不能只看答案是否带了引用。</p><p>因为模型最容易出现的问题就是:<strong>它可以把错误答案说得非常自然。</strong></p><p>一个不 faithful 的答案可能具备这些表象:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">语言很流畅结构很清楚看起来专业甚至带了引用</span><br></pre></td></tr></tbody></table></figure><p>但只要答案里的关键结论没有被证据支持,它仍然不可信。</p><p>一句话总结:</p><p><strong>Citation 问的是“答案挂了哪个来源”,Faithfulness 问的是“这个来源到底支不支持答案”。有引用只是把答案和资料连起来;Faithfulness 才是在检查这条连接是不是真的成立。</strong></p><h2 id="15-Abstention:证据不足时,系统应该拒答"><a href="#15-Abstention:证据不足时,系统应该拒答" class="headerlink" title="15. Abstention:证据不足时,系统应该拒答"></a>15. Abstention:证据不足时,系统应该拒答</h2><p>Abstention,就是拒答。</p><p>但这里的拒答不是“模型不会”,而是“系统知道不该答”。</p><p>什么时候应该拒答?</p><ul><li>没有检索到证据。 </li><li>证据之间互相冲突。 </li><li>用户没有权限访问相关资料。 </li><li>问题超出知识库范围。 </li><li>答案需要实时订单状态,但当前只有静态政策文档。 </li><li>检索结果只支持一部分结论,不支持完整回答。</li></ul><p>比如用户问:</p><blockquote><p>这个订单还能退吗?</p></blockquote><p>如果系统只检索到通用退款政策,但没有订单状态、商品类型、物流节点、是否定制商品这些信息,就不应该直接回答“可以退”或“不可以退”。</p><p>更好的回答是:</p><blockquote><p>当前知识库只包含通用退款政策,无法确认该订单是否符合退款条件。需要查询订单状态、商品类型和物流信息后才能判断。</p></blockquote><p>这就是可信 RAG 的边界感。</p><p>一句话总结:</p><p><strong>可信 RAG 不应该永远给答案。它必须能识别证据不足、权限不足、信息缺失和结论无法成立的情况。</strong></p><h2 id="16-RAG-Eval:证明系统真的变好了"><a href="#16-RAG-Eval:证明系统真的变好了" class="headerlink" title="16. RAG Eval:证明系统真的变好了"></a>16. RAG Eval:证明系统真的变好了</h2><p>RAG Eval,就是评测 RAG 系统是不是真的有效,要用一组可重复的测试问题,检查系统到底坏在哪个环节。</p><p>很多 demo 的评测方式是:</p><ul><li>问几个问题。 </li><li>看起来回答不错。 </li><li>觉得系统可以上线。</li></ul><p>这很危险。</p><p>因为 RAG 的错误可能发生在多个环节:</p><ul><li>文档解析错。 </li><li>chunk 切坏了。 </li><li>embedding 召回错。 </li><li>BM25 没命中。 </li><li>RRF 排序不好。 </li><li>reranker 选错证据。 </li><li>final context 被噪声污染。 </li><li>模型没有忠实使用证据。 </li><li>引用不支持答案。 </li><li>该拒答时没有拒答。</li></ul><p>所以 eval 的价值不是打一个漂亮分数,而是定位系统坏在哪里。</p><p>常见评测维度包括:</p><ul><li>retrieval recall:该召回的证据有没有召回。</li><li>context precision:召回的上下文有多少真的有用。</li><li>faithfulness:答案是否忠于证据。</li><li>answer relevance:答案是否回应问题。</li><li>citation accuracy:引用是否真的支持答案。</li><li>abstention accuracy:该拒答时有没有拒答。</li></ul><p>RAG 不是一个单点功能,而是一条链路:</p><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">文档解析</span><br><span class="line"> ↓</span><br><span class="line">chunk 切分</span><br><span class="line"> ↓</span><br><span class="line">embedding / BM25 召回</span><br><span class="line"> ↓</span><br><span class="line">RRF / Reranker 排序</span><br><span class="line"> ↓</span><br><span class="line">final context 选择</span><br><span class="line"> ↓</span><br><span class="line">LLM 生成回答</span><br><span class="line"> ↓</span><br><span class="line">citation 引用</span><br><span class="line"> ↓</span><br><span class="line">faithfulness 检查</span><br><span class="line"> ↓</span><br><span class="line">abstention 拒答</span><br></pre></td></tr></tbody></table></figure><p>这条链路任何一环出问题,最后答案都可能错。</p><p>一句话总结:</p><p><strong>没有 Eval,你只能凭感觉调 RAG。<br>有了 Eval,你才能知道问题到底出在检索、排序、上下文选择、生成、引用,还是拒答边界。<br>到这一步,RAG 才开始从 demo 接近工程系统。</strong></p><h2 id="17-把这些词放回一条证据链里"><a href="#17-把这些词放回一条证据链里" class="headerlink" title="17. 把这些词放回一条证据链里"></a>17. 把这些词放回一条证据链里</h2><p>现在再回头看这些术语,它们其实不是孤立概念。</p><ul><li>Document 是原料。 </li><li>Chunk 是证据单元。 </li><li>Embedding 是语义坐标。 </li><li>Vector DB 是语义检索工具。 </li><li>BM25 / Lexical Search 是精确词面检索工具。 </li><li>Hybrid Search 是双路召回。 </li><li>RRF 是多路排序融合。 </li><li>Reranker 是候选证据复核。 </li><li>Metadata Filter 是检索范围控制。 </li><li>Tenant Filter 是数据隔离。 </li><li>Citation 是来源标记。 </li><li>Faithfulness / Groundedness 是答案是否忠于证据。 </li><li>Abstention 是边界控制。 </li><li>RAG Eval 是质量证明。</li></ul><p>它们共同组成的是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">问题</span><br><span class="line"> ↓</span><br><span class="line">检索正确范围内的证据</span><br><span class="line"> ↓</span><br><span class="line">选择最能支撑答案的证据</span><br><span class="line"> ↓</span><br><span class="line">基于证据生成回答</span><br><span class="line"> ↓</span><br><span class="line">标记引用来源</span><br><span class="line"> ↓</span><br><span class="line">检查答案是否忠于证据</span><br><span class="line"> ↓</span><br><span class="line">证据不足时拒答</span><br><span class="line"> ↓</span><br><span class="line">用 eval 证明系统是否变好</span><br></pre></td></tr></tbody></table></figure><p>这就是我理解的可信 RAG。</p><ul><li>它不是“向量库 + Prompt”。 </li><li>也不是“把 PDF 丢进去让 AI 聊天”。 </li><li>它是一套把问题、证据、生成、引用、拒答、评测串起来的工程系统。</li></ul><h2 id="18-下一篇:从-0-到-1-搭一个个人技术资产知识库"><a href="#18-下一篇:从-0-到-1-搭一个个人技术资产知识库" class="headerlink" title="18. 下一篇:从 0 到 1 搭一个个人技术资产知识库"></a>18. 下一篇:从 0 到 1 搭一个个人技术资产知识库</h2><p>下一篇,我会开始写保姆级实战:</p><p><strong>从 0 到 1 搭一个个人技术资产知识库:Spring Boot + Spring AI + PGVector 保姆级教程。</strong></p><p>这次不做泛泛的“PDF 聊天机器人”,而是用自己的真实技术资产作为知识库来源:</p><ul><li><p>个人博客文章</p></li><li><p>GitHub README</p></li><li><p>项目文档</p></li><li><p>技术复盘</p></li><li><p>Markdown 笔记</p></li></ul><p>目标也不是一上来做完整企业级平台,而是搭出一个最小可信 RAG 骨架:</p><ul><li><p>文档从哪里来</p></li><li><p>chunk 怎么切</p></li><li><p>embedding 怎么入库</p></li><li><p>PGVector 怎么检索</p></li><li><p>metadata 怎么过滤</p></li><li><p>回答怎么带 citation</p></li><li><p>证据不足怎么拒答</p></li><li><p>retrieval trace 怎么记录</p></li><li><p>最小 eval case 怎么设计</p></li></ul><p>它还不是完整企业级知识库,但会保留企业级系统最关键的边界意识:来源、权限、版本、证据、引用、拒答和评测。</p><p>最后回到一句话:</p><p><strong>RAG 的目标不是让 AI 更会说,而是让 AI 的回答更值得被信任。</strong></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><p>Patrick Lewis et al. <strong>Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks</strong>. NeurIPS 2020 / arXiv:2005.11401.<br><a href="https://arxiv.org/abs/2005.11401">https://arxiv.org/abs/2005.11401</a></p></li><li><p>Spring AI Reference Documentation. <strong>Retrieval Augmented Generation</strong>.<br><a href="https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html">https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html</a></p></li><li><p>pgvector. <strong>Open-source vector similarity search for Postgres</strong>. GitHub.<br><a href="https://github.com/pgvector/pgvector">https://github.com/pgvector/pgvector</a></p></li><li><p>Gordon V. Cormack, Charles L. A. Clarke, Stefan Büttcher. <strong>Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods</strong>. SIGIR 2009.<br><a href="https://research.google/pubs/reciprocal-rank-fusion-outperforms-condorcet-and-individual-rank-learning-methods/">https://research.google/pubs/reciprocal-rank-fusion-outperforms-condorcet-and-individual-rank-learning-methods/</a></p></li><li><p>Shahul Es et al. <strong>RAGAS: Automated Evaluation of Retrieval Augmented Generation</strong>. EACL 2024 / arXiv:2309.15217.<br><a href="https://arxiv.org/abs/2309.15217">https://arxiv.org/abs/2309.15217</a></p></li></ul><p>我是 Ryan,一个专注于可信 AI 应用工程的开发者,技术博客:<a href="https://yanxai.com/">yanxai.com</a>。相比让 AI 生成更多内容,我更关心它的回答是否有证据,过程是否可追溯,结果是否经得起验证。</p>]]></content:encoded>
<category domain="https://yanxai.com/categories/AI%E5%BA%94%E7%94%A8%E5%B7%A5%E7%A8%8B/">AI应用工程</category>
<category domain="https://yanxai.com/categories/AI%E5%BA%94%E7%94%A8%E5%B7%A5%E7%A8%8B/RAG%E4%B8%93%E9%A2%98/">RAG专题</category>
<category domain="https://yanxai.com/tags/RAG/">RAG</category>
<category domain="https://yanxai.com/tags/LLM/">LLM</category>
<category domain="https://yanxai.com/tags/PGVector/">PGVector</category>
<category domain="https://yanxai.com/tags/Evals/">Evals</category>
<category domain="https://yanxai.com/tags/Embedding/">Embedding</category>
<category domain="https://yanxai.com/tags/Vector-DB/">Vector DB</category>
<category domain="https://yanxai.com/tags/BM25/">BM25</category>
<category domain="https://yanxai.com/tags/Reranker/">Reranker</category>
<comments>https://yanxai.com/p/rag-core-terms/#disqus_thread</comments>
</item>
<item>
<title>RAG 不是外挂知识库,而是一套证据链系统</title>
<link>https://yanxai.com/p/rag-evidence-chain/</link>
<guid>https://yanxai.com/p/rag-evidence-chain/</guid>
<pubDate>Sat, 27 Jun 2026 13:00:00 GMT</pubDate>
<description><p>你有没有发现,现在很多 AI 回答最大的问题不是“答不上来”,而是“答得太像真的”。</p>
<p>它语气很稳定,结构很完整,甚至还会列出步骤、注意事项和看起来很专业的判断。</p>
<p>可真正危险的地方在于:你不知道它到底是根据什么说出这句话的。</p>
<p>很多人第一次听到 RAG,会把它理解成“给 AI 接一个知识库”。</p>
<p>这个理解不算错,但太浅。</p>
<p>知识库</description>
<content:encoded><![CDATA[<p>你有没有发现,现在很多 AI 回答最大的问题不是“答不上来”,而是“答得太像真的”。</p><p>它语气很稳定,结构很完整,甚至还会列出步骤、注意事项和看起来很专业的判断。</p><p>可真正危险的地方在于:你不知道它到底是根据什么说出这句话的。</p><p>很多人第一次听到 RAG,会把它理解成“给 AI 接一个知识库”。</p><p>这个理解不算错,但太浅。</p><p>知识库只是资料放在哪里。RAG 真正要解决的是另一个问题:</p><p><strong>当 AI 给你一个答案时,你能不能判断这个答案到底靠不靠谱?</strong></p><p>比如你在电商平台问客服:</p><blockquote><p>我这个商品还能退吗?</p></blockquote><p>一个普通大模型可能会根据常见售后规则回答:</p><blockquote><p>一般情况下,签收 7 天内可以申请退货。</p></blockquote><p>这句话听起来很正常,甚至很像客服话术。</p><p>但问题是:它真的知道你的订单状态吗?知道你买的是不是定制商品吗?知道商品有没有拆封吗?知道是否超过签收时间吗?知道当前店铺的退货政策是不是最新版吗?</p><p>如果这些信息都没有,它直接回答“可以退”,其实就是在没有证据的情况下装作知道。</p><p>RAG 真正要解决的,不只是让 AI 多读几份文档,而是让它在回答前先去找证据:订单状态是什么,商品类型是什么,售后政策怎么写,是否存在例外条款。只有这些证据足够支持结论,它才能回答;如果证据不够,它就应该说“不确定,还需要查询订单状态”。</p><p>所以我现在更愿意这样理解 RAG:</p><p><strong>RAG 不是让模型知道更多,而是让模型的回答有证据、有边界、可追溯、可评测。</strong></p><h2 id="什么是-RAG:让模型先找证据,再回答问题"><a href="#什么是-RAG:让模型先找证据,再回答问题" class="headerlink" title="什么是 RAG:让模型先找证据,再回答问题"></a>什么是 RAG:让模型先找证据,再回答问题</h2><p>RAG = Retrieval-Augmented Generation,检索增强生成。</p><p>这个概念来自 2020 年 Lewis 等人的论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》。这篇论文提出的核心思路,是把预训练模型的参数化记忆和外部检索得到的非参数化记忆结合起来,让模型在知识密集型任务中利用外部资料生成回答。但在今天的 AI 应用工程里,如果我们要把 RAG 用到客服、企业知识库、求职材料、内部系统,就不能只停留在“检索 + 生成”,还要继续解决引用、权限、拒答、评测和可追溯问题。</p><p>用更简单的话说:</p><p><strong>RAG 不是让大模型只靠“脑子里记住的参数”回答,而是在回答前先去外部知识源检索相关资料,再把检索到的内容作为上下文交给模型生成答案。</strong></p><p>基本流程可以理解成:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">用户问题</span><br><span class="line"> ↓</span><br><span class="line">检索相关文档 / 数据 / 片段</span><br><span class="line"> ↓</span><br><span class="line">把检索结果作为上下文</span><br><span class="line"> ↓</span><br><span class="line">交给大模型生成回答</span><br><span class="line"> ↓</span><br><span class="line">返回答案 + 来源</span><br></pre></td></tr></tbody></table></figure><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/rag-evidence-chain/rag-typical-flow.webp" alt="image.png"></p><p>更准确地说,RAG 不等于知识库。</p><p>知识库是数据。<br>Embedding 是表示方式。<br>Vector DB 是存储和检索工具。<br>RAG 是把检索结果接入生成过程的架构。<br>可信 RAG 还要有 citation、faithfulness、abstention、eval。</p><p>所以:</p><p><strong>有知识库 ≠ 有 RAG。</strong><br><strong>有向量库 ≠ 有可信 RAG。</strong></p><p>很多“AI 知识库聊天机器人”只是做到了文档检索 + 模型回答,但还没有真正解决证据是否可靠、引用是否准确、回答是否忠实、证据不足时是否拒答这些问题。</p><h2 id="RAG-的真正价值:不是知识更多,而是答案更可追溯"><a href="#RAG-的真正价值:不是知识更多,而是答案更可追溯" class="headerlink" title="RAG 的真正价值:不是知识更多,而是答案更可追溯"></a>RAG 的真正价值:不是知识更多,而是答案更可追溯</h2><p>大模型的参数里确实存了大量知识,但这种知识有几个天然问题。</p><p>第一,它不一定新。</p><p>模型训练完成后,外部世界继续变化。政策会改,商品库存会变,订单状态会更新,公司内部文档会迭代。如果只依赖模型参数,知识更新成本很高。</p><p>第二,它不一定属于你的业务。</p><p>企业内部 SOP、用户简历、订单物流、客户服务记录、项目 README、私有代码文档,都不可能稳定存在于通用模型参数里。</p><p>第三,它很难给出 provenance,也就是可追溯的依据。</p><p>模型生成一个结论时,你很难知道它到底依据了哪份文档、哪段材料、哪条规则。</p><p>RAG 的核心价值就在这里:它把生成过程从纯参数记忆拉回到外部证据上。LangChain 文档把 retrieval 描述为在查询时获取相关外部知识,以增强 LLM 对上下文特定信息的回答能力;Spring AI 的 RAG 文档里,QuestionAnswerAdvisor 会查询向量数据库,把相关文档追加到用户文本中,为模型生成提供上下文。</p><p>这也是为什么 RAG 很适合作为 AI 应用工程里的长期专题。它不是一个单点技术,而是一条系统链路:</p><p>文档进入系统之前,要清洗、切块、加元数据。<br>问题进入系统之后,要改写、检索、混合召回、重排。<br>答案生成之前,要选择证据、控制上下文、约束输出。<br>答案生成之后,要检查引用、忠实度、拒答、可追溯性。<br>系统上线之后,还要持续评测、监控、回放、修复。</p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/rag-evidence-chain/rag-system-chain.webp" alt="image.png"></p><p>如果只写“Embedding 是什么”“Vector DB 怎么用”,你只是在写 RAG 的零件。</p><p>真正值得写的是:这些零件如何共同决定一个 AI 应用是否可信。</p><h2 id="第一个误区:只要接了知识库,就算有-RAG-了"><a href="#第一个误区:只要接了知识库,就算有-RAG-了" class="headerlink" title="第一个误区:只要接了知识库,就算有 RAG 了"></a>第一个误区:只要接了知识库,就算有 RAG 了</h2><p>很多人做 RAG 的第一个动作,是接一个向量数据库。</p><p>这当然重要。</p><p>有了 embedding 之后,需要一个地方存这些向量,并支持按相似度检索。这就是 Vector DB。</p><p>比如 pgvector 是 PostgreSQL 的开源向量相似搜索扩展,它可以让你在 PostgreSQL 里存向量,并支持精确或近似最近邻搜索、余弦距离、内积、L2 距离等能力。</p><p>如果你用 Java / Spring Boot / PostgreSQL 技术栈,PGVector 很适合作为入门和工程落地选择。因为你可以把 document、chunk、embedding、metadata、tenant_id 放在同一个 PostgreSQL 体系里,而不是一开始就维护单独的向量数据库集群。</p><p>但 Vector DB 不是 RAG 的全部。</p><p>Vector DB 解决的是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">这些 chunk 里,哪些和用户问题的向量最接近?</span><br></pre></td></tr></tbody></table></figure><p>它不解决:</p><p>这个 chunk 是否来自最新版文档?<br>这个 chunk 是否属于当前用户或当前租户?<br>这个 chunk 是否完整包含限制条件?<br>这个 chunk 是否真的能支撑答案?<br>模型是否忠实使用了这个 chunk?<br>证据不足时系统是否拒答?</p><p>所以不要把“接入向量库”误认为“完成了 RAG”。</p><p>如果你没有好的 chunk,没有 metadata filter,没有 citation,没有拒答,没有 eval,那它只是一个“向量检索 + Prompt”demo。</p><p>更准确地说:</p><p><strong>Vector DB 负责存向量和查相似,但它不能自动保证答案可信。</strong></p><h2 id="第二个误区:只靠向量检索,就能找到正确证据"><a href="#第二个误区:只靠向量检索,就能找到正确证据" class="headerlink" title="第二个误区:只靠向量检索,就能找到正确证据"></a>第二个误区:只靠向量检索,就能找到正确证据</h2><p>很多 RAG 教程会从 Embedding 开始。</p><p>Embedding 的确重要。OpenAI 文档把 embedding 定义为向量表示,并指出可以通过向量之间的距离衡量文本之间的相关程度。</p><p>简单说,Embedding 可以把一段文本变成一串数字,让系统按语义相似度检索内容。</p><p>比如用户问:</p><blockquote><p>出差住酒店最多能报销多少钱?</p></blockquote><p>文档里写的是:</p><blockquote><p>差旅住宿报销标准:一线城市 600 元/晚,其他城市 400 元/晚。</p></blockquote><p>这两句话词不完全一样,但意思相关。Embedding 能把它们映射到相近的向量空间里,让系统知道它们语义接近。</p><p>但真实系统里,只靠 Embedding 很容易出问题。</p><p>比如用户问:</p><blockquote><p>退款政策第 7 条怎么说?</p></blockquote><p>向量检索可能召回“售后政策总则”“退货流程说明”“跨境商品说明”,但漏掉真正包含“第 7 条”的精确条款。</p><p>用户问:</p><blockquote><p>SKU-A1937 是否支持德国仓发货?</p></blockquote><p>这里的关键不是语义相似,而是精确编号、地区、仓库、商品属性。BM25 / lexical search 这类词面检索反而更稳。</p><p>OpenSearch 文档把 BM25 描述为一种基于关键词的 lexical search 算法,会考虑词频、逆文档频率等因素来计算文档相关性。</p><p>所以,RAG 检索的第一条原则是:</p><p><strong>不要迷信单一检索器。</strong></p><p>Embedding 解决的是语义相似,不等于事实正确。<br>BM25 / lexical search 擅长词面匹配、编号、术语、错误码、固定表达。<br>Hybrid Search 把稀疏检索和稠密检索结合起来,降低漏召回风险。<br>RRF 可以把多个检索器的排序结果融合起来。<br>Reranker 再对候选结果做更精细的 query-document 匹配。</p><p>Qdrant 的 hybrid queries 文档也把 dense、sparse、多向量检索,以及 RRF、DBSF 等融合方式放在同一个检索框架下讨论。</p><p>一个最简 RAG demo 可能是:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">query → embedding → vector db → answer</span><br></pre></td></tr></tbody></table></figure><p>但一个更接近生产的 RAG,应该更接近:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">query</span><br><span class="line"> ↓</span><br><span class="line">query rewrite</span><br><span class="line"> ↓</span><br><span class="line">BM25 / lexical recall + vector recall</span><br><span class="line"> ↓</span><br><span class="line">RRF fusion</span><br><span class="line"> ↓</span><br><span class="line">rerank</span><br><span class="line"> ↓</span><br><span class="line">evidence selection</span><br><span class="line"> ↓</span><br><span class="line">answer generation</span><br><span class="line"> ↓</span><br><span class="line">citation / faithfulness check</span><br></pre></td></tr></tbody></table></figure><p>这条链路看起来复杂,但它解决的是同一个问题:</p><p><strong>先尽可能不要漏掉证据,再尽可能把真正相关的证据排到前面。</strong></p><h2 id="第三个误区:把文档切碎,就等于做好了知识库"><a href="#第三个误区:把文档切碎,就等于做好了知识库" class="headerlink" title="第三个误区:把文档切碎,就等于做好了知识库"></a>第三个误区:把文档切碎,就等于做好了知识库</h2><p>Chunking 是 RAG 里最容易被低估的环节。</p><p>很多人会直接用固定长度,比如 500 tokens 一个 chunk,50 tokens overlap。这个做法能跑,但不一定能用。</p><p>因为真实文档不是纯文本流。真实文档有标题、层级、表格、FAQ、代码块、步骤、异常说明、法律条款、商品参数、订单字段、版本记录。</p><p>如果你把一个退款规则从条件和例外之间切开,模型可能只看到“支持退款”,看不到“定制商品除外”。</p><p>如果你把项目架构图的说明和模块列表切散,模型可能知道用了 Redis,却不知道 Redis 是用于限流、缓存还是会话。</p><p>如果你把简历项目经历按固定长度切开,模型可能看到“AI 应用开发”,却看不到对应技术栈和结果证据。</p><p>所以 Chunking 的本质不是“把长文变短”,而是:</p><p><strong>把文档切成适合被检索、被引用、被验证的证据单元。</strong></p><p>好的 chunk 至少要满足几个条件:</p><p>它要足够小,小到能被精确召回。<br>它要足够完整,完整到能支撑一个回答 claim。<br>它要保留结构,例如标题、章节、来源、页码、版本、所属租户。<br>它要可追溯,生成答案时能反查原文位置。<br>它要可评测,能判断某个 query 是否应该召回它。</p><p>这也是为什么“RAG 做不好”很多时候不是模型问题,而是知识工程问题。</p><h2 id="第四个误区:检索到相似内容,就等于找到了证据"><a href="#第四个误区:检索到相似内容,就等于找到了证据" class="headerlink" title="第四个误区:检索到相似内容,就等于找到了证据"></a>第四个误区:检索到相似内容,就等于找到了证据</h2><p>相似,不等于正确。</p><p>这个问题在 RAG 里非常常见。</p><p>比如用户问:</p><blockquote><p>这个订单还能退吗?</p></blockquote><p>系统召回了一段“通用退款政策”,这当然相关。但它是否足够回答这个订单能不能退?</p><p>不一定。</p><p>因为这个问题还需要订单状态、商品类型、签收时间、是否定制商品、是否有质量问题、是否拆封等信息。</p><p>也就是说,检索到“相似内容”只说明系统找到了一些可能相关的文本,不代表它已经找到了足够支撑结论的证据。</p><p>这里至少有三层过滤。</p><p>第一层是 topK:最多取多少条候选 chunk。topK 太小,可能漏掉关键证据;topK 太大,可能把噪声塞进上下文。</p><p>第二层是 similarity threshold:相似度低于某个分数的结果不要。阈值太高,可能过滤掉有用证据;阈值太低,又会放进太多无关内容。</p><p>第三层是 metadata filter / tenant filter:只在正确范围里检索。</p><p>Metadata,就是文档或 chunk 附带的信息。</p><p>比如:</p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"tenantId"</span><span class="punctuation">:</span> <span class="string">"merchant_001"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sourceType"</span><span class="punctuation">:</span> <span class="string">"refund_policy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"language"</span><span class="punctuation">:</span> <span class="string">"zh"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"v3"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"status"</span><span class="punctuation">:</span> <span class="string">"active"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"updatedAt"</span><span class="punctuation">:</span> <span class="string">"2026-06-01"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>用户问退款政策时,系统要先判断:</p><p>是不是当前商家的政策?<br>是不是当前语言?<br>是不是当前版本?<br>是不是 active 状态?<br>是不是当前用户有权限访问?<br>是不是对应业务线?</p><p>如果不做 metadata filter,系统可能召回旧版本政策、其他租户文档、测试文档、英文文档、无权限文档、已废弃规则。</p><p>这不是回答不准的问题,而是系统边界问题。</p><p>尤其在多租户系统里,tenant filter 不是 Prompt 约束,而是数据访问控制。你不能靠一句“请只回答当前商家的内容”来防止越权检索。真正的过滤应该发生在数据库查询、检索条件和权限系统里。</p><p>一句话总结:</p><p><strong>RAG 不是在全库里找最像的内容,而是在正确范围里找能支撑答案的证据。</strong></p><h2 id="第五个误区:有引用,就说明答案可信"><a href="#第五个误区:有引用,就说明答案可信" class="headerlink" title="第五个误区:有引用,就说明答案可信"></a>第五个误区:有引用,就说明答案可信</h2><p>普通大模型胡说,用户至少还会怀疑:</p><blockquote><p>它是不是编的?</p></blockquote><p>但 RAG 系统一旦答错,问题反而更隐蔽。因为它通常会带着文档名、引用片段、相似度分数和来源链接,看起来像是经过检索验证的结果。</p><p>真正危险的不是 AI 没有依据,而是它拿着不完整、错误或不相关的依据,生成了一个看起来很确定的答案。</p><p>很多 RAG 系统会在回答后面附上引用来源,看起来很专业。但 citation 不等于 faithfulness。</p><p>引用只能说明:系统给了一个来源。<br>它不能自动说明:答案中的每一句都被这个来源支持。</p><p>举个例子。</p><p>原文说:</p><blockquote><p>跨境定制商品非质量问题不支持七天无理由退货。</p></blockquote><p>AI 回答:</p><blockquote><p>所有跨境商品都不支持退货。</p></blockquote><p>这个回答可能带着引用,但它把“定制商品”“非质量问题”“七天无理由”这些限定条件丢掉了。它不是没引用,而是引用不忠实。</p><p>所以 RAG 的可信问题至少要拆成三层:</p><p>Context relevance:检索到的上下文是否真的相关。<br>Groundedness / Faithfulness:回答是否被上下文支持。<br>Answer relevance:回答是否真正回应用户问题。</p><p>TruLens 的 RAG Triad 使用 context relevance、groundedness、answer relevance 三个维度来检查 RAG 应用;DeepEval 的 faithfulness 指标也强调要判断生成结果是否与 retrieval context 中的内容事实一致。</p><p>这三件事不能混在一起。</p><p>检索相关不代表生成忠实。<br>生成忠实不代表回答完整。<br>回答完整也不代表应该回答。</p><p>有些问题在证据不足时就应该拒答。</p><p>一句话总结:</p><p><strong>Citation 问的是答案来自哪里,Faithfulness 问的是来源到底支不支持答案。</strong></p><h2 id="第六个误区:AI-应该永远给出答案"><a href="#第六个误区:AI-应该永远给出答案" class="headerlink" title="第六个误区:AI 应该永远给出答案"></a>第六个误区:AI 应该永远给出答案</h2><p>真正成熟的 RAG 系统必须有 abstention,也就是拒答能力。</p><p>当证据不足时,它应该说“不确定”。<br>当检索结果冲突时,它应该暴露冲突。<br>当问题超出知识库范围时,它应该说明边界。<br>当用户试图诱导它越权访问其他租户数据时,它应该拒绝。<br>当答案需要实时数据库状态,而当前只检索到静态文档时,它应该提示需要调用订单系统或业务 API。</p><p>很多 AI 应用失败,不是因为它答不出来,而是因为它在不该回答时回答了。</p><p>对客服系统来说,这可能是错误承诺。<br>对求职材料来说,这可能是简历造假。<br>对医疗、法律、金融来说,这可能是高风险误导。<br>对企业内部知识库来说,这可能是权限越界。</p><p>所以 RAG 不应该只追求“回答率”,还要追求“正确拒答率”。</p><p>一个可信 RAG 的目标不是让模型每次都有话说,而是让系统在证据不足、权限不足、上下文冲突或需要实时数据时,能主动暴露边界。</p><p>一句话总结:</p><p><strong>可信 RAG 不应该永远回答,它应该知道什么时候不该回答。</strong></p><h2 id="第七个误区:问几个问题感觉不错,就可以上线"><a href="#第七个误区:问几个问题感觉不错,就可以上线" class="headerlink" title="第七个误区:问几个问题感觉不错,就可以上线"></a>第七个误区:问几个问题感觉不错,就可以上线</h2><p>RAG demo 最危险的地方是:你问三五个问题,感觉它回答不错,就以为系统可用了。</p><p>但 RAG 的质量不能靠肉眼感受。你至少要把评测拆成两部分。</p><p>第一部分评测检索:</p><p>该召回的证据有没有召回?<br>相关 chunk 是否排在前面?<br>无关 chunk 是否污染上下文?<br>BM25、Embedding、Hybrid、RRF、Reranker 哪个环节带来了提升?<br>metadata filter 和 tenant filter 是否真的生效?</p><p>第二部分评测生成:</p><p>答案是否只基于证据?<br>引用是否准确?<br>是否遗漏关键限定条件?<br>是否在证据不足时拒答?<br>是否把多个来源中的冲突信息混成一个确定结论?<br>是否能把“不确定”说清楚,而不是装作知道?</p><p>RAGAS 论文把 RAG 评测拆成多个维度,包括检索系统能否识别相关且聚焦的上下文、LLM 是否忠实利用这些上下文、生成质量如何等;LangSmith 的 RAG 评测教程也会评估 RAG 应用;OpenAI 的评测最佳实践也强调,evals 应该是面向具体应用的测试,用来衡量 LLM 应用表现。</p><p>这就是 RAG Eval 的价值。它不是为了做漂亮分数,而是为了定位系统坏在哪里。</p><p>如果 retrieval recall 低,说明证据根本没进上下文。<br>如果 context precision 低,说明召回内容太脏。<br>如果 faithfulness 低,说明模型拿到了证据但没有忠实使用。<br>如果 answer relevance 低,说明回答没有解决用户问题。<br>如果 citation accuracy 低,说明引用链不可信。<br>如果 abstention accuracy 低,说明系统不知道什么时候不该回答。</p><p>从这个角度看:</p><p><strong>RAG Eval 不是上线后的评分表,而应该是 RAG 开发的导航系统。</strong></p><p>没有 eval 的 RAG,只能叫 demo;有 eval 的 RAG,才开始接近工程系统。</p><h2 id="重新定义-RAG:从“能回答”到“可验证”"><a href="#重新定义-RAG:从“能回答”到“可验证”" class="headerlink" title="重新定义 RAG:从“能回答”到“可验证”"></a>重新定义 RAG:从“能回答”到“可验证”</h2><p>以前我会说:</p><blockquote><p>RAG = Retrieval-Augmented Generation,检索增强生成。</p></blockquote><p>现在我更愿意说:</p><p><strong>RAG 是一种把外部证据引入生成过程,并通过检索、重排、引用、拒答和评测机制约束模型输出的 AI 应用架构。</strong></p><p>这个定义更长,但更接近真实工程。</p><p>因为真实业务要的不是“AI 能说”,而是:</p><p>它引用的是不是正确来源?<br>它有没有越权拿到别人的数据?<br>它有没有遗漏关键例外条款?<br>它能不能承认证据不足?<br>它的回答能不能被回放、检查和复盘?<br>当知识库更新后,它能不能及时反映变化?</p><p>如果这些问题解决不了,RAG 就只是一个看起来更专业的聊天机器人。</p><p>如果这些问题解决了,RAG 才真正成为可信 AI 应用工程的底座。</p><p>接下来这个专题,我会按工程链路继续拆开写:</p><p>Embedding / Vector DB:语义表示和向量召回。<br>BM25 / Hybrid Search / RRF:降低只靠向量检索带来的漏召回。<br>Reranker:对候选证据重新排序。<br>Chunking:决定证据单元是否完整。<br>Metadata Filter / Tenant Filter:控制版本、权限和租户边界。<br>Citation / Faithfulness / Groundedness:判断答案是否真的被证据支持。<br>Abstention / RAG Eval:决定系统什么时候该拒答,以及如何证明它变好了。</p><p>但我不会把它们写成孤立概念。</p><p>每一个技术点都要回答同一个问题:</p><p><strong>它能不能帮助 RAG 从“能回答”走向“可验证”?</strong></p><p>我的目标不是证明“AI 能回答文档问题”,而是回答一个更硬的问题:</p><p><strong>这个答案为什么值得被信任?</strong></p><p>最后,用一张图把这篇文章的核心逻辑收束一下:</p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/rag-evidence-chain/rag-one-page-summary.webp" alt="image.png"></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks, NeurIPS 2020.<br>[2] LangChain Docs, Retrieval / RAG.<br>[3] Spring AI Reference, Retrieval Augmented Generation.<br>[4] OpenAI Docs, Vector embeddings.<br>[5] pgvector GitHub, Open-source vector similarity search for Postgres.<br>[6] OpenSearch Docs, Keyword search / BM25.<br>[7] Qdrant Docs, Hybrid Queries.<br>[8] Cormack et al., Reciprocal Rank Fusion, SIGIR 2009.<br>[9] RAGAS, Automated Evaluation of Retrieval Augmented Generation.<br>[10] TruLens Docs, RAG Triad.<br>[11] DeepEval Docs, Faithfulness Metric.<br>[12] LangSmith Docs, Evaluate a RAG application.<br>[13] OpenAI Docs, Evaluation best practices.</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>下一篇,我会用类比讲透 RAG 的核心术语:Embedding、Chunk、Vector DB、BM25、Reranker 到底是什么。</p><p>我是 Ryan,一个专注于可信 AI 应用工程的开发者,个人技术博客:<a href="https://yanxai.com/">yanxai.com</a>,研究如何让 AI 生成从“看起来对”走向“有证据、可追溯、可验证”。</p>]]></content:encoded>
<category domain="https://yanxai.com/categories/AI%E5%BA%94%E7%94%A8%E5%B7%A5%E7%A8%8B/">AI应用工程</category>
<category domain="https://yanxai.com/categories/AI%E5%BA%94%E7%94%A8%E5%B7%A5%E7%A8%8B/RAG%E4%B8%93%E9%A2%98/">RAG专题</category>
<category domain="https://yanxai.com/tags/RAG/">RAG</category>
<category domain="https://yanxai.com/tags/LLM/">LLM</category>
<category domain="https://yanxai.com/tags/PGVector/">PGVector</category>
<category domain="https://yanxai.com/tags/Evals/">Evals</category>
<category domain="https://yanxai.com/tags/Guardrails/">Guardrails</category>
<comments>https://yanxai.com/p/rag-evidence-chain/#disqus_thread</comments>
</item>
<item>
<title>小白用 Codex 和 Claude Code 也能做产品,程序员的出路在哪里?</title>
<link>https://yanxai.com/p/ai-coding-trustworthy-delivery/</link>
<guid>https://yanxai.com/p/ai-coding-trustworthy-delivery/</guid>
<pubDate>Sat, 27 Jun 2026 12:00:00 GMT</pubDate>
<description><p>最近用 Codex 和 Claude Code 写项目时,我越来越明显地感受到一种割裂:一边是效率真的变高了,一个想法可以很快变成能跑的产品;另一边是焦虑也变强了,因为“能跑”开始变得不再稀缺。真正刺痛我的问题不是 AI 能不能写代码,而是当一个项目大部分代码都由 AI 生成后,我还能不能解释它、修改它、验证它、回滚它,并在它出问题时真正负责?如果不能,这个项目看起来再完整,也更像是 AI </description>
<content:encoded><![CDATA[<p>最近用 Codex 和 Claude Code 写项目时,我越来越明显地感受到一种割裂:一边是效率真的变高了,一个想法可以很快变成能跑的产品;另一边是焦虑也变强了,因为“能跑”开始变得不再稀缺。真正刺痛我的问题不是 AI 能不能写代码,而是当一个项目大部分代码都由 AI 生成后,我还能不能解释它、修改它、验证它、回滚它,并在它出问题时真正负责?如果不能,这个项目看起来再完整,也更像是 AI 的产物,而不是我的工程能力。</p><h2 id="1-能做出产品,但不等于完全具备工程能力"><a href="#1-能做出产品,但不等于完全具备工程能力" class="headerlink" title="1.能做出产品,但不等于完全具备工程能力"></a>1.能做出产品,但不等于完全具备工程能力</h2><p>小白能用AI做产品,但开发的时候他是否知道什么场景用什么技术栈,架构如何设计,如何用工作树和Git提交或回滚代码,如果一个系统真的上线,并发怎么处理?数据库事务边界在哪里?缓存失效怎么办?权限绕过怎么防?日志怎么设计?成本异常怎么定位?AI 改了鉴权逻辑但测试没覆盖怎么办?线上出错谁回滚?这些问题不是“生成代码”本身,而是<strong>工程责任</strong>。</p><p>未来最危险的岗位不是“程序员”,而是“只会接明确需求、写普通 CRUD、不会判断架构、不懂测试、不懂安全、不懂业务的人”。因为这类工作最适合被AI批量生成。</p><p>AI 让代码产出变快,必然让review、测试、安全和治理变成瓶颈。</p><p>AI 时代的高阶程序员价值链变成:定义问题 → 拆解任务 → 设计架构 → 给 AI 上下文 → 审查 diff → 写测试和评测 → 控制权限和安全边界 → 监控线上行为 → 复盘成本和质量 → 长期维护系统。</p><p><strong>AI 时代越往后,“能生成代码”越不稀缺,“能证明代码可信”越稀缺。</strong></p><p>我所做的项目:</p><p>PatchBrake 不是为了再做一个代码扫描器,而是盯住 AI-generated diff 里的工程风险:删测试、放大权限、弱化鉴权、修改规则文件、引入危险脚本。</p><p>Token Studio ROI 不是为了记 token,而是把 AI 编程从“感觉效率很高”变成可复盘的工作证据:不同项目、不同模型、不同任务到底消耗了多少,产出了什么,值不值得继续。</p><p>OmniMerchant 不是客服 demo,而是在跨境电商场景里处理 RAG、tool calling、多租户、流式响应、fallback、成本和权限边界。</p><p>简喵不是泛泛的简历评分器,而是证据约束型岗位竞争力诊断:JD里哪些能匹配,哪些有证据,哪些不能硬补,改写后能不能经得起面试追问。</p><p>这些项目表面不同,底层是同一个问题: AI 生成之后,如何留下证据、边界和责任? 我把它叫作可信交付。 它至少包含五件事:</p><ul><li>生成结果有证据。</li><li>系统行为有边界。</li><li>质量风险可验证。</li><li>线上问题可追溯。</li><li>长期演进可复盘。</li></ul><p>在AI应用工程中:</p><p>RAG系统不是接一个向量库。 它要评估retrieval recall、context precision、faithfulness、answer relevance。</p><p>Agent系统不是让模型调用工具。 它要设计tool permission、approval flow、audit log、failure fallback。</p><p>AI observability不是看一眼 token 数。 它要记录模型、延迟、成本、trace、tool call、错误、用户反馈和版本变化。 这些才是AI应用开发的真实壁垒。</p><p>所以AI时代真正的出路是:<strong>能把AI生成的软件纳入一套可验证、可审计、可交付的工程流程,建立一套自己的AI工作流。AI时代不缺代码生成,缺的是可信交付。</strong></p><h2 id="2-小白也能做产品,程序员的出路在哪里?"><a href="#2-小白也能做产品,程序员的出路在哪里?" class="headerlink" title="2.小白也能做产品,程序员的出路在哪里?"></a>2.小白也能做产品,程序员的出路在哪里?</h2><h3 id="(1)做-AI-应用工程,而不是“套壳应用”。"><a href="#(1)做-AI-应用工程,而不是“套壳应用”。" class="headerlink" title="(1)做 AI 应用工程,而不是“套壳应用”。"></a>(1)做 AI 应用工程,而不是“套壳应用”。</h3><p>用上Codex和Claude Code确实可以做一个“看起来不错”的工具,但多数人做不出稳定的 AI 应用。因为 AI 应用真正难的地方不是页面,也不是调 API,而是这些问题:</p><ul><li>模型输出不稳定怎么办?</li><li>结构化 JSON 解析失败怎么办?</li><li>RAG 检索到错误证据怎么办?</li><li>用户输入包含 prompt injection 怎么办?</li><li>tool calling 调错工具怎么办?</li><li>多租户数据串了怎么办?</li><li>token 成本失控怎么办?</li><li>流式响应中途失败怎么办?</li><li>模型降级后质量怎么保证?</li></ul><p><strong>AI 生成内容如何可追溯、可解释、可复盘?</strong> 这些才是AI应用开发的真实壁垒。</p><p>普通小白能上线 demo,但很难建立一套 evidence、eval、observability、guardrail、fallback、audit trail。 出路不是“我也会用 Claude Code 做项目”,而应该是:“<strong>我能把 Claude Code、Codex 生成的软件纳入一套可验证、可审计、可交付的工程流程。”</strong></p><ul><li>不只是我的项目能跑,而是我能解释核心链路和关键实现。</li><li>不只是我的技术栈很丰富,而是我选择了有取舍、有边界、有替代方案。</li><li>不只是我用了 Claude Code / Codex,而是我能审查、约束、验证 AI 产物。</li><li>不只是我做了几个单元测试,而是我的测试覆盖了异常、边界、回归和 CI。</li><li>不只是我实现了登录鉴权,而是我能处理越权、注入、敏感信息和多租户隔离。</li><li>不只是我本地运行成功,而是我有日志、错误码、重试、回滚和监控。</li><li>不只是我的README很漂亮,而是我有 changelog、benchmark、failure cases 和复盘。</li><li>不只是我会背项目介绍,而是我能现场改需求、debug、解释取舍。</li></ul><h3 id="(2)掌握领域,而不是只掌握工具。"><a href="#(2)掌握领域,而不是只掌握工具。" class="headerlink" title="(2)掌握领域,而不是只掌握工具。"></a>(2)掌握领域,而不是只掌握工具。</h3><p>AI 最擅长的是通用模式。越通用,越容易被生成;越需要领域判断,越不容易被替代。</p><p>比如跨境电商客服系统,不只是写一个聊天框。它涉及订单、物流、售后、退换货政策、多语言、商家规则、平台合规、用户情绪、风控和成本。求职材料生成也不是“润色简历”,而是 JD → 证据 → 改写 → 风险控制 → 面试可追问。AI 可以帮你写代码,但它不知道你的产品到底要对谁负责。</p><p>所以程序员的长期出路之一,是变成“某个领域里的工程师”,而不是“只会某个技术栈的人”。Java 后端、Spring AI、RAG、Agent 都只是武器;真正值钱的是你能把它们用于一个真实问题,并且知道哪些地方不能乱生成。</p><p>AI 可以生成代码、README、测试样例、架构图,但很难伪造你对系统的真实理解、取舍过程、排障能力和长期维护能力。</p><h2 id="3-AI-时代,项目本身会贬值,工程证据会升值"><a href="#3-AI-时代,项目本身会贬值,工程证据会升值" class="headerlink" title="3.AI 时代,项目本身会贬值,工程证据会升值"></a>3.AI 时代,项目本身会贬值,工程证据会升值</h2><h3 id="(1)设计取舍证据。"><a href="#(1)设计取舍证据。" class="headerlink" title="(1)设计取舍证据。"></a>(1)设计取舍证据。</h3><p>面试官问:</p><ul><li>“为什么用这个架构?”</li><li>“为什么不用更简单的方案?”</li><li>“这个模块的边界在哪里?”</li><li>“如果用户量扩大10倍,哪个地方先出问题?”</li><li>“这个设计最失败的地方是什么?”</li></ul><p>真正做过工程的人,回答里会有取舍:性能、复杂度、开发时间、可维护性、安全、成本、团队能力。只靠 AI 堆出来的人,通常只能复述架构名词,比如“用了 Redis、用了 MQ、用了 RAG、用了微服务”,但说不出为什么。</p><h3 id="(2)debug-证据。"><a href="#(2)debug-证据。" class="headerlink" title="(2)debug 证据。"></a>(2)debug 证据。</h3><p>AI 能生成新代码,但真实工程能力很大一部分体现在出问题后能不能定位。</p><p>面试官会问:</p><ul><li>“线上接口变慢,你怎么排查?”</li><li>“Redis 缓存命中率下降,你看什么指标?”</li><li>“数据库偶发死锁,你怎么复现?”</li><li>“用户说 AI 回答错了,你怎么判断是 prompt 问题、检索问题、模型问题还是数据问题?”</li><li>“流式响应中途断了,前后端分别怎么处理?”</li></ul><p>这类问题很难靠背诵解决。因为它考的是排查路径:日志、traceId、metrics、SQL explain、异常栈、请求链路、复现条件、最小化变量、回滚策略。</p><p>所以项目里要有日志、错误码、traceId、监控截图、故障复盘。哪怕是个人项目,也可以写一份“线上事故排查文档”。</p><h3 id="(3)测试和验证证据。"><a href="#(3)测试和验证证据。" class="headerlink" title="(3)测试和验证证据。"></a>(3)测试和验证证据。</h3><p>未来 AI 生成代码越多,测试越重要。面试官会越来越看重你有没有能力验证 AI 产物。</p><p>他会问:</p><ul><li>“你怎么证明这个功能是对的?”</li><li>“边界条件测了哪些?”</li><li>“单测、集成测试、端到端测试分别覆盖什么?”</li><li>“AI 生成代码你怎么 review?”</li><li>“你怎么防止修改 A 功能时破坏 B 功能?”</li></ul><p>小白做项目通常是“能跑就行”。工程师要回答的是“为什么我相信它长期能跑”。</p><p>项目需要的东西:</p><p>单元测试、集成测试、异常测试、边界测试、benchmark、CI、测试覆盖范围说明、失败用例说明。</p><p>除此之外还应该有 AI eval:比如 RAG 回答是否引用了正确证据、简历改写是否夸大事实、客服 Agent 是否越权调用工具、AI 生成 diff 是否删除测试或放大权限。</p><h3 id="(4)代码审查证据。"><a href="#(4)代码审查证据。" class="headerlink" title="(4)代码审查证据。"></a>(4)代码审查证据。</h3><p>面试官不一定只让你写代码,可能直接给你一段AI生成代码,让你 review。</p><p>他会看你能不能发现: 并发问题。 权限绕过。 事务边界错误。 N+1 查询。 空指针和异常吞噬。 SQL 注入。 缓存穿透/击穿/雪崩。 日志泄露敏感信息。 测试只测 happy path。 代码过度抽象。</p><p>AI 最容易写出“看起来完整,但边界很脆”的代码。会工程的人,能看出这些脆点。</p><p>我会把 PatchBrake 这类项目继续往“AI diff 风险审查”方向做。它不只是一个工具,而是能力定位的证据:你不是只会用 AI 写代码,你还能审计 AI 写出来的代码。</p><h3 id="(5)系统边界和安全证据。"><a href="#(5)系统边界和安全证据。" class="headerlink" title="(5)系统边界和安全证据。"></a>(5)系统边界和安全证据。</h3><p>AI 写代码后,最容易被忽视的是边界。 面试官问:</p><ul><li>“用户输入恶意内容怎么办?”</li><li>“多租户数据怎么隔离?”</li><li>“普通用户能不能越权访问别人的资源?”</li><li>“tool calling 有没有权限控制?”</li><li>“模型输出不合法怎么办?”</li><li>“敏感信息会不会进日志?”</li><li>“AI 生成内容错了,系统怎么兜底?”</li></ul><h3 id="(6)长期维护证据。"><a href="#(6)长期维护证据。" class="headerlink" title="(6)长期维护证据。"></a>(6)长期维护证据。</h3><p>很多 AI 项目是一次性 demo。面试官会越来越警惕这种“短期堆出来”的项目。 更关注:</p><ul><li>有没有版本演进记录。</li><li>有没有 changelog。</li><li>有没有 issue/roadmap。</li><li>有没有重构记录。</li><li>有没有测试随功能增长而增长。</li><li>有没有配置说明。</li><li>有没有部署说明。</li><li>有没有已知问题。</li><li>有没有从 v1 到 v2 的设计变化。</li></ul><p>真正的工程能力不是“做出来”,而是“维护得住”。一个项目连续迭代 3 个月,比 5 个一次性AI demo更有说服力。</p><h3 id="(7)表达和复盘证据。"><a href="#(7)表达和复盘证据。" class="headerlink" title="(7)表达和复盘证据。"></a>(7)表达和复盘证据。</h3><p>AI 能帮你写项目介绍,但很难替你讲清楚真实经历。面试官会通过追问判断你是不是 owner。 面试官问:</p><ul><li>“这个项目里你最难的一个 bug 是什么?”</li><li>“你做过最错误的设计是什么?”</li><li>“哪一部分后来推翻了?”</li><li>“你最不满意的地方是什么?”</li><li>“如果再做一遍,你会怎么改?”</li><li>“这个项目真正服务了谁?”</li><li>“有没有用户反馈?”</li></ul><p>这些问题非常关键。因为没真正做过的人,通常只会讲正面包装,不会讲失败、返工、妥协和权衡。</p><p>所以文章里可以考虑主动写失败点。不是自曝缺点,而是证明你真的经历过工程过程。</p><table><thead><tr><th>能力</th><th>低质量证据</th><th>高质量证据</th></tr></thead><tbody><tr><td>写代码</td><td>项目能跑</td><td>能解释核心链路和关键实现</td></tr><tr><td>架构设计</td><td>技术栈很丰富</td><td>有取舍、有边界、有替代方案</td></tr><tr><td>AI 使用</td><td>用 Claude Code/Codex 做项目</td><td>能审查、约束、验证 AI 产物</td></tr><tr><td>测试</td><td>有几个 happy path 单测</td><td>覆盖异常、边界、回归、CI</td></tr><tr><td>安全</td><td>登录鉴权</td><td>越权、注入、敏感信息、多租户隔离</td></tr><tr><td>可靠性</td><td>本地运行成功</td><td>日志、错误码、重试、回滚、监控</td></tr><tr><td>项目深度</td><td>README 很漂亮</td><td>有 changelog、benchmark、复盘、失败案例</td></tr><tr><td>面试可信度</td><td>会背项目介绍</td><td>能现场改需求、debug、解释取舍</td></tr></tbody></table><p>这些都是我后面做内容和产品会坚持的方向。</p><h2 id="4-关于我个人对AI时代的思考"><a href="#4-关于我个人对AI时代的思考" class="headerlink" title="4.关于我个人对AI时代的思考"></a>4.关于我个人对AI时代的思考</h2><p>我不会把自己的产品和文章做成“手把手复制一个爆款项目”的流量模板。</p><p>更不会承诺“学完某个项目就能找到工作”。</p><p>这种话听起来很有吸引力,但很多时候只是另一种自我安慰。</p><p>AI 应用开发不是背几个框架名词,也不是照着教程部署一个项目。</p><p>真正重要的是:<strong>你有没有在一个具体问题里做过判断,踩过坑,推翻过方案,修过错误,理解过边界,并且能把这些过程沉淀成自己的工程认知。</strong></p><p>我更在意长期品牌,而不是短期热点。</p><p>热点可以带来流量,但很难带来信任。</p><p><strong>真正能让别人相信你的,不是你追上了多少话题,而是你是否长期围绕一个方向持续输出,是否能在项目、代码、文章和复盘里看见稳定的判断力。</strong></p><p>我现在做的事情很简单:</p><p>记录自己从 0 到 1 学习 AI 应用开发的过程。</p><p>记录项目踩坑、架构取舍、错误判断、重构过程和复盘。</p><p><strong>记录我如何从一个学生慢慢建立起对 AI 应用工程、可信交付、证据链、评测、安全和长期产品判断的理解。</strong></p><p>这不是速成路线。</p><p>也不是求职捷径。</p><p>它更像是一条长期训练路径。</p><p><strong>我希望我的内容不是让人看完以后产生虚假的兴奋,而是让真正愿意思考的人看到:一个项目为什么这样做,哪里容易错,哪些地方不能骗自己,什么才算真正有工程价值。</strong></p><p>我相信,<strong>一个人需要在某个领域建立足够稳定的判断,才不会被网上的情绪、热点和速成叙事随意带偏。</strong></p><p>我也相信,<strong>真正有判断力的人,最终会通过长期内容找到彼此。</strong></p><p>不是因为标题足够刺激。</p><p>而是因为<strong>文字里有真实经历,有取舍,有问题意识,有持续迭代的痕迹。</strong></p><p>我不会只做“看起来很热”的内容。</p><p>我会继续做有我自己工程路径、成长痕迹和判断密度的深度内容。</p><p>短期流量会过去。</p><p>长期品牌会留下。</p><p>我是 Ryan,一个专注于可信 AI 应用工程的开发者,个人技术博客:<a href="https://yanxai.com/">yanxai.com</a>,研究如何让 AI 生成从“看起来对”走向“有证据、可追溯、可验证”。</p>]]></content:encoded>
<category domain="https://yanxai.com/categories/AI%E6%97%B6%E4%BB%A3%E6%80%9D%E8%80%83/">AI时代思考</category>
<category domain="https://yanxai.com/tags/LLM/">LLM</category>
<category domain="https://yanxai.com/tags/Evals/">Evals</category>
<category domain="https://yanxai.com/tags/Guardrails/">Guardrails</category>
<category domain="https://yanxai.com/tags/Observability/">Observability</category>
<category domain="https://yanxai.com/tags/Agent/">Agent</category>
<comments>https://yanxai.com/p/ai-coding-trustworthy-delivery/#disqus_thread</comments>
</item>
<item>
<title>我做了 Token Studio:AI 编程花掉的 Token,到底换来了什么?</title>
<link>https://yanxai.com/p/token-studio-roi/</link>
<guid>https://yanxai.com/p/token-studio-roi/</guid>
<pubDate>Sun, 21 Jun 2026 12:30:00 GMT</pubDate>
<description>Token Studio ROI 是一个本地 AI 编程投入复盘系统:采集 Claude Code、Codex CLI 等工具的结构化 token 元数据,按官方公开价换算成本,并把 token 消耗连接到项目、任务、产出证据、模型策略和行动报告。</description>
<content:encoded><![CDATA[<p>项目地址:<a href="https://github.com/RyanCoreAI/token-studio-roi">GitHub - Token Studio ROI</a><br>npm registry:<a href="https://registry.npmjs.org/token-studio/latest">token-studio</a><br>Release:<a href="https://github.com/RyanCoreAI/token-studio-roi/releases/tag/v6.0.8">v6.0.8</a><br>动图体验:<a href="https://yanxai.com/images/token-studio-roi/token-studio-walkthrough.gif">Token Studio Walkthrough</a></p><p>Windows 用户可以直接用第 3 节里的固定版本命令下载并启动。完整 PowerShell 命令放在“下载和使用全流程”,可以整段复制运行。</p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/dashboard.webp" alt="Token Studio Dashboard"></p><blockquote><p>这张图回答的问题:本期 AI 编程 token 花在哪些模型、来源和趋势上,哪些消耗最值得先复盘。</p></blockquote><p>我以前只想知道自己 AI 编程花了多少 token。</p><p>后来发现,这个数字本身没什么用。</p><p>真正的问题是:<strong>这些 token 到底换来了什么?</strong></p><p>如果一天用了几千万 token,但我不知道它们是花在功能开发、调试修复、上下文整理,还是无效试错上,那 token 总数只是一个越来越大的数字。它既不能告诉我哪个项目值得继续投,也不能告诉我下周应该少用重模型,还是该先压缩上下文。</p><p>所以我做了 Token Studio。它不是一个普通 token meter,而是一个本地 AI 编程 ROI 复盘系统:先可信地采集本机结构化 token 元数据,再把 token 连接到项目、任务、产出证据、模型策略和行动报告。</p><span id="more"></span><h2 id="1-普通-token-看板少回答了一个关键问题"><a href="#1-普通-token-看板少回答了一个关键问题" class="headerlink" title="1. 普通 token 看板少回答了一个关键问题"></a>1. 普通 token 看板少回答了一个关键问题</h2><p>市面上的 token 工具大多能回答“用了多少”。这当然重要,但对高频 AI 编程用户来说还不够。</p><p>我真正想回答的是:</p><ul><li>这些 token 是花在探索、实现、验证、发布,还是重复上下文里?</li><li>哪些项目消耗最高,最后有没有 PR、commit、文档、部署或可展示产出?</li><li>重模型是不是被用在了测试验证、上下文整理这种低 ROI 场景?</li><li>哪些 session 只是在烧 token,却没有形成任何可复盘证据?</li><li>下周我应该继续用同样的模型策略,还是先把低价值任务切到轻量模型?</li></ul><p>这就是 Token Studio 的核心差异:</p><blockquote><p>统计工具告诉你用了多少;Token Studio 追问这些 token 换来了什么。</p></blockquote><p>这个定位也影响了整个设计。它不追求做云端 SaaS,不追求团队排行榜,也不为了覆盖数量去伪造 token。它先做一件事:把本地真实 token 变成可信工作证据。</p><h2 id="2-Token-Studio-的闭环"><a href="#2-Token-Studio-的闭环" class="headerlink" title="2. Token Studio 的闭环"></a>2. Token Studio 的闭环</h2><p>Token Studio 当前围绕一条闭环设计:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">真实本地 token</span><br><span class="line"> -> 可信覆盖</span><br><span class="line"> -> 自动证据</span><br><span class="line"> -> ROI 复盘</span><br><span class="line"> -> 模型策略</span><br><span class="line"> -> 行动报告</span><br></pre></td></tr></tbody></table></figure><p>这条链路里,每一步都要回答一个实际问题。</p><h3 id="2-1-真实本地采集:先确认数据是真的"><a href="#2-1-真实本地采集:先确认数据是真的" class="headerlink" title="2.1 真实本地采集:先确认数据是真的"></a>2.1 真实本地采集:先确认数据是真的</h3><p>Token Studio 会读取本机仍然存在、且带有可靠 token 字段的结构化日志。核心可信来源是 Claude Code 和 Codex CLI。</p><p>它采集的是结构化使用元数据,例如 source、model、session、timestamp、input/output/cache/reasoning tokens。它不保存 prompt、response、transcript、diff、文件内容或完整本机路径。</p><p>这个边界很关键:如果连采集数据本身都不可信,后面的 ROI 判断就只是漂亮图表。</p><h3 id="2-2-Coverage-Trust:解释为什么某些工具没有数据"><a href="#2-2-Coverage-Trust:解释为什么某些工具没有数据" class="headerlink" title="2.2 Coverage Trust:解释为什么某些工具没有数据"></a>2.2 Coverage Trust:解释为什么某些工具没有数据</h3><p>很多工具会在本机留下目录或日志,但检测到目录不等于拿到了可靠 token。</p><p>所以 Token Studio 把来源拆成几类:原生可信采集、ccusage 可导入、实验采集、仅检测到、无可靠 token 字段 / 不支持。</p><p>这不是为了保守而保守,而是为了避免一种很危险的假象:<strong>覆盖名单很好看,但实际 token 是估出来的。</strong></p><p>我宁愿页面告诉用户“这个来源现在没有可靠 token 字段”,也不希望它用文本长度估算 token,然后生成看似完整但不能复盘的数据。</p><h3 id="2-3-Evidence-Flywheel:把消耗数字变成工作证据"><a href="#2-3-Evidence-Flywheel:把消耗数字变成工作证据" class="headerlink" title="2.3 Evidence Flywheel:把消耗数字变成工作证据"></a>2.3 Evidence Flywheel:把消耗数字变成工作证据</h3><p>采到 token 之后,还不够。</p><p>真正有价值的是这些 token 对应了什么工作:项目、任务类型、工作目的、阶段、产出状态、产出价值、产出链接,以及这些字段是人工确认、自动推断,还是待确认草稿。</p><p>比如一个 session 可以从“某模型消耗了 200 万 token”,变成:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">项目:Token Studio ROI</span><br><span class="line">任务:功能开发</span><br><span class="line">阶段:实现</span><br><span class="line">产出状态:已完成</span><br><span class="line">产出:commit / PR / 文档</span><br><span class="line">价值:中 / 高</span><br><span class="line">证据来源:自动高置信或人工确认</span><br></pre></td></tr></tbody></table></figure><p>只有走到这一步,<code>/review</code> 才能继续判断:哪些 token 有产出,哪些高成本 session 还没归因,哪些模型策略值得调整。</p><h3 id="2-4-ROI-Advisor:不给玄学建议,只给可解释规则"><a href="#2-4-ROI-Advisor:不给玄学建议,只给可解释规则" class="headerlink" title="2.4 ROI Advisor:不给玄学建议,只给可解释规则"></a>2.4 ROI Advisor:不给玄学建议,只给可解释规则</h3><p>Token Studio 不调用 LLM 生成建议。原因很简单:这个工具本身就是为了帮我少花 token,不应该为了分析 token 再消耗一轮 token。</p><p>它用本地规则做判断:</p><ul><li>高成本未归因 session:先补证据。</li><li>测试验证、探索、上下文整理使用重模型:建议切到轻量模型。</li><li>低价值或废弃任务使用高成本模型:建议先轻量试错。</li><li>高 input / low output:建议压缩上下文。</li><li>cache 复用低且 input 高:建议沉淀项目上下文。</li><li>未定价模型:不参与美元节省判断。</li></ul><p>Savings Simulator 也不声称真实节省金额,只做官方价换算下的策略模拟。</p><h3 id="2-5-Desktop-Pulse:给实时状态一个轻量入口"><a href="#2-5-Desktop-Pulse:给实时状态一个轻量入口" class="headerlink" title="2.5 Desktop Pulse:给实时状态一个轻量入口"></a>2.5 Desktop Pulse:给实时状态一个轻量入口</h3><p>浏览器里的 Dashboard、Trust、Review 是完整复盘入口。Desktop Pulse 只是伴侣。</p><p>它解决的是另一个问题:我不想一直开着浏览器 tab,但想随时知道今天 token 有没有失控。</p><p>所以 Pulse 重点展示近 24 小时 token、官方价换算、请求数、输入/输出/cache、模型消耗、来源分布、预算 guardrail 和当前建议。它只读本地 API / SQLite 聚合数据,不上传,也不重新实现采集。</p><h2 id="3-下载和使用全流程"><a href="#3-下载和使用全流程" class="headerlink" title="3. 下载和使用全流程"></a>3. 下载和使用全流程</h2><p>这里给 Windows 用户两份命令:<strong>PowerShell 用 PowerShell 版,CMD 用 CMD 版</strong>。不要混用。</p><p>PowerShell 版:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line"># 前提:已安装 nvm-windows</span><br><span class="line">nvm install 24.17.0</span><br><span class="line">nvm use 24.17.0</span><br><span class="line"></span><br><span class="line">node.exe -v</span><br><span class="line">npm.cmd -v</span><br><span class="line"></span><br><span class="line">New-Item -ItemType Directory -Force D:\TokenStudio | Out-Null</span><br><span class="line">Set-Location D:\TokenStudio</span><br><span class="line"></span><br><span class="line">$pkg = 'token-studio' + '@' + '6.0.8'</span><br><span class="line">npx.cmd -y $pkg</span><br></pre></td></tr></tbody></table></figure><p>CMD 版:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">REM 前提:已安装 nvm-windows</span><br><span class="line">nvm install 24.17.0</span><br><span class="line">nvm use 24.17.0</span><br><span class="line"></span><br><span class="line">node -v</span><br><span class="line">npm -v</span><br><span class="line"></span><br><span class="line">mkdir D:\TokenStudio</span><br><span class="line">cd /d D:\TokenStudio</span><br><span class="line"></span><br><span class="line">set "pkg=token-studio"</span><br><span class="line">set "at=@"</span><br><span class="line">set "ver=6.0.8"</span><br><span class="line">npx -y %pkg%%at%%ver%</span><br></pre></td></tr></tbody></table></figure><p>如果已经有 Node 24,也可以只运行最后三行。它做的是完整本地真实模式,不是 demo:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">1. npx 从 npm 下载并运行 token-studio</span><br><span class="line">2. Token Studio 做只读 coverage,检查本机是否有 Claude Code / Codex CLI 结构化 token 日志</span><br><span class="line">3. 通过可信 gate 后,把 event 级 token 元数据写入本地 SQLite</span><br><span class="line">4. 启动 127.0.0.1 上的本地 Web/API 服务</span><br><span class="line">5. 自动打开浏览器 Dashboard</span><br><span class="line">6. 后台定时刷新可信 Claude/Codex token 元数据</span><br><span class="line">7. 在 Trust 页面确认数据可信度</span><br><span class="line">8. 在 Review 页面生成证据队列、模型策略和行动报告</span><br><span class="line">9. 如需实时观察,再打开 Desktop Pulse 或 /live</span><br></pre></td></tr></tbody></table></figure><p>第一次使用可以按这个顺序看:</p><table><thead><tr><th>步骤</th><th>你要做什么</th><th>页面会告诉你什么</th></tr></thead><tbody><tr><td>1</td><td>运行第 3 节固定版本命令</td><td>下载包、启动本地服务、打开浏览器</td></tr><tr><td>2</td><td>看 Dashboard</td><td>近周期 token、官方价换算、模型和趋势</td></tr><tr><td>3</td><td>看 Trust</td><td>当前数据是否是真实 event 级采集,哪些来源可信</td></tr><tr><td>4</td><td>看 Review</td><td>哪些 token 有证据,哪些 session 需要补归因</td></tr><tr><td>5</td><td>点生成证据队列</td><td>把最贵、最值得处理的 session 推到前面</td></tr><tr><td>6</td><td>导出复盘材料</td><td>复制复盘证据包、技术博客草稿、简历项目描述</td></tr><tr><td>7</td><td>开 Desktop Pulse</td><td>实时看 24 小时 token、成本、请求数和模型消耗</td></tr></tbody></table><p>几个常用命令也可以分开用:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">$pkg = 'token-studio' + '@' + '6.0.8'</span><br><span class="line"></span><br><span class="line"># 真实模式:下载、采集可信 token、启动本地看板</span><br><span class="line">npx.cmd -y $pkg</span><br><span class="line"></span><br><span class="line"># 只做只读检查,不写 SQLite</span><br><span class="line">npx.cmd -y $pkg --dry-run-only --no-open</span><br><span class="line"></span><br><span class="line"># 只打开已有本地数据库,不重新采集</span><br><span class="line">npx.cmd -y $pkg --no-collect</span><br><span class="line"></span><br><span class="line"># 查看本地实时状态摘要</span><br><span class="line">npx.cmd -y $pkg statusline --format=text</span><br><span class="line"></span><br><span class="line"># 如果你只想看产品界面,可以用合成数据</span><br><span class="line">npx.cmd -y $pkg demo</span><br></pre></td></tr></tbody></table></figure><p>注意:第 3 节里的真实模式命令会读取本机结构化 token 元数据,但不会读取 prompt、response、transcript、diff 或完整本机路径。<code>demo</code> 是合成数据,只用于看界面,不代表真实采集成功。</p><h3 id="3-1-Dashboard:先看成本、模型和趋势"><a href="#3-1-Dashboard:先看成本、模型和趋势" class="headerlink" title="3.1 Dashboard:先看成本、模型和趋势"></a>3.1 Dashboard:先看成本、模型和趋势</h3><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/dashboard.webp" alt="Token Studio Dashboard"></p><blockquote><p>这张图回答的问题:当前周期 token、官方价换算、模型消耗和趋势是否异常。</p></blockquote><p>Dashboard 的首屏不应该先解释产品理念,而应该先让用户看到真实数据:总 token、输入、输出、缓存、推理 token、官方价换算、模型分布和趋势。</p><p>因为用户打开看板的第一反应通常不是“这个工具有什么功能”,而是:</p><blockquote><p>我的 token 到底花在哪里?哪个模型最贵?今天有没有异常?</p></blockquote><h3 id="3-2-Trust:确认数据是否能用于复盘"><a href="#3-2-Trust:确认数据是否能用于复盘" class="headerlink" title="3.2 Trust:确认数据是否能用于复盘"></a>3.2 Trust:确认数据是否能用于复盘</h3><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/trust.webp" alt="Token Studio Trust"></p><blockquote><p>这张图回答的问题:当前数据是 demo、旧聚合库,还是可信 event 级真实采集;哪些来源能用于 ROI 判断。</p></blockquote><p>Trust 页面是我认为最容易被低估的一页。</p><p>它负责回答:当前是 demo、空库、旧聚合库,还是真实 event 级数据?Claude / Codex / Cursor 等来源分别是什么状态?daily、session、event 之间能不能对齐?哪些来源只是 detected-only,不能当成真实覆盖?</p><p>如果这一步不清楚,用户就会把“看起来有数据”误当成“可以做决策”。</p><h3 id="3-3-Review:把-token-变成-ROI-证据"><a href="#3-3-Review:把-token-变成-ROI-证据" class="headerlink" title="3.3 Review:把 token 变成 ROI 证据"></a>3.3 Review:把 token 变成 ROI 证据</h3><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/review.webp" alt="Token Studio Review"></p><blockquote><p>这张图回答的问题:哪些 token 已经有产出证据,哪些 session 仍缺项目、任务、阶段、价值或产出链接。</p></blockquote><p>Review 不是普通报表,而是复盘页。</p><p>它关心:哪些 token 产出了东西,哪些高成本 session 仍缺证据,哪些模型策略可以调整,哪些建议应该进入下周行动清单。</p><p>我还加了三个专业可复制输出包:</p><ul><li>复盘证据包</li><li>技术博客草稿</li><li>简历 / 面试项目描述</li></ul><p>这些内容不是静态 bullet,而是基于当前结构化数据生成的 Markdown,方便直接复制到周报、博客或简历项目说明里。缺人工确认或产出链接时,它会明确写限制,不会编造成果。</p><h3 id="3-4-Desktop-Pulse:桌面实时伴侣"><a href="#3-4-Desktop-Pulse:桌面实时伴侣" class="headerlink" title="3.4 Desktop Pulse:桌面实时伴侣"></a>3.4 Desktop Pulse:桌面实时伴侣</h3><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/pulse.webp" alt="Token Studio Pulse"></p><blockquote><p>这张图回答的问题:过去 24 小时 token、成本、请求、缓存复用和模型消耗是否处在可控范围内。</p></blockquote><p>Desktop Pulse 是 v6 的桌面端补强。它不是主产品,也不替代浏览器复盘页。</p><p>它更像一个本地实时仪表盘:当我正在高频使用 Claude Code 或 Codex 时,它能快速告诉我今天 token 是否失控、重模型是否用得过多、是否有预算风险、是否该停下来处理 open actions。</p><h3 id="3-5-动图体验"><a href="#3-5-动图体验" class="headerlink" title="3.5 动图体验"></a>3.5 动图体验</h3><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/token-studio-roi/token-studio-walkthrough.gif" alt="Token Studio Walkthrough"></p><blockquote><p>这张动图回答的问题:从 Dashboard 到 Trust、Review、Live,完整复盘流是怎么走的。</p></blockquote><p>动图地址:<a href="https://yanxai.com/images/token-studio-roi/token-studio-walkthrough.gif">https://yanxai.com/images/token-studio-roi/token-studio-walkthrough.gif</a></p><h2 id="4-和竞品相比,Token-Studio-的差异在哪里"><a href="#4-和竞品相比,Token-Studio-的差异在哪里" class="headerlink" title="4. 和竞品相比,Token Studio 的差异在哪里"></a>4. 和竞品相比,Token Studio 的差异在哪里</h2><p>我查过同类项目后,结论比较明确:竞品很强,但强项不一样。</p><p>ccusage 的优势是多来源覆盖和 JSON 报表。它能统一读取 Claude Code、Codex、OpenCode、Amp、Goose、Kimi、Qwen、Copilot CLI、Gemini CLI 等本地数据源,并提供 daily、weekly、monthly、session 等视图。</p><p>CodeBurn 的优势是 TUI 和快速成本观测,强调按 task、model、project、provider 拆分 AI spend。</p><p>TokenTracker 的优势是覆盖工具数量、桌面组件、菜单栏和去重能力。</p><p>tokscale 的优势是 CLI/TUI、贡献图、leaderboard 和更强的可视化传播感。</p><p>Token Studio 没有选择去硬拼这些方向。它的差异是:</p><table><thead><tr><th>维度</th><th>常见 token 工具</th><th>Token Studio</th></tr></thead><tbody><tr><td>核心问题</td><td>我用了多少 token</td><td>这些 token 换来了什么</td></tr><tr><td>覆盖策略</td><td>尽量多源统计</td><td>可信覆盖 + ccusage bridge,不伪造 token</td></tr><tr><td>复盘粒度</td><td>日期、模型、项目</td><td>项目、任务、阶段、产出、价值、证据来源</td></tr><tr><td>建议方式</td><td>cost hotspot / tips</td><td>本地规则 ROI Advisor + 行动清单</td></tr><tr><td>输出形态</td><td>报表或 TUI</td><td>周报、博客、简历项目描述、模型策略</td></tr><tr><td>隐私边界</td><td>多数本地优先</td><td>明确不保存正文、diff、完整路径</td></tr></tbody></table><p>所以它不是 ccusage 的替代品,也不是 CodeBurn 的 TUI 复制品。更准确地说,它是本地复盘层:把可信 token 进一步变成工作证据、策略判断和下周行动。</p><h2 id="5-架构设计"><a href="#5-架构设计" class="headerlink" title="5. 架构设计"></a>5. 架构设计</h2><p>Token Studio 的架构很简单,但每一层都保持边界清楚。</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">Claude Code / Codex CLI 本地日志</span><br><span class="line"> |</span><br><span class="line"> v</span><br><span class="line">结构化 collector</span><br><span class="line"> |</span><br><span class="line"> v</span><br><span class="line">SQLite: token_events / session_usage / daily_usage</span><br><span class="line"> |</span><br><span class="line"> v</span><br><span class="line">Coverage Trust / Reconciliation</span><br><span class="line"> |</span><br><span class="line"> v</span><br><span class="line">Evidence Flywheel</span><br><span class="line"> |</span><br><span class="line"> v</span><br><span class="line">Dashboard / Trust / Review / Live / Desktop Pulse</span><br></pre></td></tr></tbody></table></figure><h3 id="5-1-为什么用-SQLite"><a href="#5-1-为什么用-SQLite" class="headerlink" title="5.1 为什么用 SQLite"></a>5.1 为什么用 SQLite</h3><p>因为这是个人本地工具。</p><p>SQLite 不需要部署数据库,容易备份,适合单用户本地场景,也方便把数据文件明确排除在 npm 包和 Git 仓库之外。</p><h3 id="5-2-为什么不用云端"><a href="#5-2-为什么不用云端" class="headerlink" title="5.2 为什么不用云端"></a>5.2 为什么不用云端</h3><p>AI 编程数据很敏感。即便不保存正文,session、模型、时间、项目名、产出链接也可能透露工作节奏和项目方向。</p><p>所以默认选择是:数据留在本地,复盘也在本地完成。</p><h3 id="5-3-为什么不估算所有工具"><a href="#5-3-为什么不估算所有工具" class="headerlink" title="5.3 为什么不估算所有工具"></a>5.3 为什么不估算所有工具</h3><p>覆盖数量本身不是目标。可信覆盖才是目标。</p><p>如果某个工具没有稳定 token 字段,Token Studio 不会用文本长度估算 token,也不会把 detected-only 写成采集成功。它会建议用 ccusage bridge 导入结构化 JSON,或者明确标记“当前无可靠 token 字段”。</p><h2 id="6-隐私和安全边界"><a href="#6-隐私和安全边界" class="headerlink" title="6. 隐私和安全边界"></a>6. 隐私和安全边界</h2><p>这部分很重要,但不需要在每一段重复。</p><p>Token Studio 的边界集中在几条:</p><ul><li>不保存 prompt、response、transcript、diff、文件内容或完整本机路径。</li><li>Web API 默认绑定本机地址。</li><li>普通写接口要求 local Origin + JSON。</li><li><code>/api/ingest</code> 默认关闭,只有设置 <code>INGEST_TOKEN</code> 后才启用 Bearer token 写入。</li><li>Desktop Pulse 只读本地聚合数据,不上传,不做远程控制面板。</li><li>官方价换算不是供应商账单,只用于复盘和策略模拟。</li></ul><p>这也是为什么我没有把它做成云端 SaaS。对这个产品来说,隐私不是附加卖点,而是系统边界。</p><h2 id="7-我怎么判断这个项目是否有价值"><a href="#7-我怎么判断这个项目是否有价值" class="headerlink" title="7. 我怎么判断这个项目是否有价值"></a>7. 我怎么判断这个项目是否有价值</h2><p>我不想把 Token Studio 写成“又一个全能平台”。</p><p>它解决的是一个很具体的问题:</p><blockquote><p>对高频 AI 编程用户来说,token 已经变成持续投入,但缺少一个本地、可信、可复盘的投入产出系统。</p></blockquote><p>如果只看 token 数,意义有限。<br>如果 token 能连接到项目、任务、阶段、产出和模型策略,它才开始变成可管理的工作资产。</p><p>这个判断也符合软件工程研究里常见的问题-方法-证据-评价结构:先定义真实问题,再做可运行系统,然后用真实数据、限制条件和验收门来评价,而不是只做一个漂亮页面。</p><p>Token Studio 不会自动证明“ROI 提升了 37%”。它更保守,也更可信:</p><ul><li>哪些数据可信。</li><li>哪些只能看趋势。</li><li>哪些不能下结论。</li><li>哪些证据缺口最值得补。</li><li>哪些模型策略值得下周试。</li></ul><h2 id="8-当前局限"><a href="#8-当前局限" class="headerlink" title="8. 当前局限"></a>8. 当前局限</h2><p>这个项目有明确边界。</p><p>第一,它不能恢复所有历史。它只能覆盖本机仍然保留、且日志里有可靠 token 字段的数据。</p><p>第二,它不是供应商账单。官方价换算适合做趋势和策略复盘,不适合做财务对账。</p><p>第三,它不会读取正文,所以不会自动理解任务质量。产出价值仍然需要人工确认或结构化证据支撑。</p><p>第四,它不会为了覆盖面伪造 token。没有可靠 token 字段,就不会写成“已采集成功”。</p><p>第五,桌面 Pulse 只是伴侣。真正的复盘仍然在浏览器 Review 和 Trust 页面里完成。</p><h2 id="9-后续是否继续做"><a href="#9-后续是否继续做" class="headerlink" title="9. 后续是否继续做"></a>9. 后续是否继续做</h2><p>v6 之后,我不打算继续无限追竞品功能。</p><p>后续只维护几类事情:</p><ul><li>上游日志格式变化</li><li>官方价格变化</li><li>真实 bug</li><li>隐私和安全问题</li><li>release / npm / 桌面打包问题</li></ul><p>明确不做:云同步、账号体系、多用户、leaderboard、大型 TUI、伪造 collector、文本长度估算 token。</p><p>因为再往后加功能,收益已经不如把当前闭环打磨稳定。</p><h2 id="10-排错命令速查"><a href="#10-排错命令速查" class="headerlink" title="10. 排错命令速查"></a>10. 排错命令速查</h2><p>完整下载和启动命令见第 3 节。下面是几个排错或进阶命令。</p><p>如果你只想确认它能不能看到你的本机日志,可以先运行:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">$pkg = 'token-studio' + '@' + '6.0.8'</span><br><span class="line">npx.cmd -y $pkg --dry-run-only --no-open</span><br></pre></td></tr></tbody></table></figure><p>如果你已经有本地 SQLite,只想打开看板,不想重新扫描日志:</p><figure class="highlight text"><table><tbody><tr><td class="code"><pre><span class="line">$pkg = 'token-studio' + '@' + '6.0.8'</span><br><span class="line">npx.cmd -y $pkg --no-collect</span><br></pre></td></tr></tbody></table></figure><p>如果你要看桌面端实时伴侣,当前推荐先从本地源码或 GitHub Release asset 体验;npm 主入口仍是 Web/CLI:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/RyanCoreAI/token-studio-roi.git</span><br><span class="line"><span class="built_in">cd</span> token-studio-roi</span><br><span class="line">npm install</span><br><span class="line">npm run desktop</span><br></pre></td></tr></tbody></table></figure><p>项目地址:</p><ul><li>GitHub:<a href="https://github.com/RyanCoreAI/token-studio-roi">https://github.com/RyanCoreAI/token-studio-roi</a></li><li>npm registry:<a href="https://registry.npmjs.org/token-studio/latest">https://registry.npmjs.org/token-studio/latest</a></li><li>Release:<a href="https://github.com/RyanCoreAI/token-studio-roi/releases/tag/v6.0.8">https://github.com/RyanCoreAI/token-studio-roi/releases/tag/v6.0.8</a></li><li>动图体验:<a href="https://yanxai.com/images/token-studio-roi/token-studio-walkthrough.gif">https://yanxai.com/images/token-studio-roi/token-studio-walkthrough.gif</a></li></ul><p>如果你想验证它是否真的能采到你本机的数据,最直接的方式还是在本机运行真实模式,而不是看 demo。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li>GitHub README 文档:<a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes">About READMEs</a></li><li>OpenAI Prompt Caching:<a href="https://developers.openai.com/api/docs/guides/prompt-caching">Prompt caching</a></li><li>OpenAI Reasoning models:<a href="https://developers.openai.com/api/docs/guides/reasoning">Reasoning models</a></li><li>Anthropic Prompt Caching:<a href="https://platform.claude.com/docs/en/build-with-claude/prompt-caching">Prompt caching</a></li><li>Anthropic Extended Thinking:<a href="https://platform.claude.com/docs/en/build-with-claude/extended-thinking">Extended thinking</a></li><li>ccusage:<a href="https://ccusage.com/guide/">ccusage documentation</a></li><li>CodeBurn:<a href="https://github.com/getagentseal/codeburn">GitHub</a></li><li>TokenTracker:<a href="https://github.com/mm7894215/TokenTracker">GitHub</a></li><li>tokscale:<a href="https://github.com/junhoyeo/tokscale">GitHub</a></li><li>Mary Shaw, Writing Good Software Engineering Research Papers:<a href="https://www.cs.cmu.edu/~Compose/shaw-icse03.pdf">PDF</a></li><li>Design Science Research:<a href="https://wise.vub.ac.be/sites/default/files/thesis_info/design_science.pdf">PDF</a></li></ul>]]></content:encoded>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/">项目</category>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/Token-Studio-ROI/">Token Studio ROI</category>
<category domain="https://yanxai.com/tags/Node-js/">Node.js</category>
<category domain="https://yanxai.com/tags/Token-Studio-ROI/">Token Studio ROI</category>
<category domain="https://yanxai.com/tags/Claude-Code/">Claude Code</category>
<category domain="https://yanxai.com/tags/Codex/">Codex</category>
<category domain="https://yanxai.com/tags/Observability/">Observability</category>
<comments>https://yanxai.com/p/token-studio-roi/#disqus_thread</comments>
</item>
<item>
<title>我让 AI 改代码,它悄悄删了测试、放大了 CI 权限,所以我做了 PatchBrake</title>
<link>https://yanxai.com/p/patchbrake/</link>
<guid>https://yanxai.com/p/patchbrake/</guid>
<pubDate>Mon, 15 Jun 2026 09:40:00 GMT</pubDate>
<description>AI 编码工具让代码改动变快,但 review 压力没有变小。PatchBrake 是一个本地 CLI,用来在提交前扫描 staged diff,提醒 secret 泄露、测试删除、CI 权限放大等高风险改动。</description>
<content:encoded><![CDATA[<p>最近用 Claude Code、Codex、Cursor 这类工具写代码,我越来越明显地感觉到一件事:<strong>AI 写代码是真的快,但 review 并没有变轻松。</strong></p><p>现在很多人的 AI 编码流程大概是这样:先和 AI 讨论需求、拆实现方案,然后让 Codex、Claude Code、Cursor 或 Copilot 直接改本地代码。AI 改完后,如果项目能跑、页面看起来没问题,很多人就准备提交了。问题恰恰出在这里。</p><p>以前 review 代码,主要看逻辑对不对、命名清不清楚、有没有明显 bug。现在 AI 一次能改很多文件,而且改得很自然,很多风险不是“代码完全不能跑”,而是混在 diff 里,看起来没那么刺眼。</p><p>比如:代码能跑,但测试被删了;功能能过,但权限判断被弱化了;CI 还能跑,但 GitHub Actions 权限被放大了;配置文件看起来正常,但 secret 被写进了代码;甚至 AGENTS.md、CLAUDE.md、Cursor rules 这类文件被改了,后续 AI 的行为规则也跟着变了。</p><p>这些问题不一定马上炸,但很适合藏进一次普通提交里。所以我做了一个很小的工具:<strong>PatchBrake</strong>。</p><p>项目地址:<a href="https://github.com/RyanCoreAI/patchbrake">https://github.com/RyanCoreAI/patchbrake</a></p><p>Demo 在线体验:<a href="https://raw.githubusercontent.com/RyanCoreAI/patchbrake/main/assets/demo.gif">https://raw.githubusercontent.com/RyanCoreAI/patchbrake/main/assets/demo.gif</a></p><span id="more"></span><h2 id="1-PatchBrake-是什么?"><a href="#1-PatchBrake-是什么?" class="headerlink" title="1. PatchBrake 是什么?"></a>1. PatchBrake 是什么?</h2><p>PatchBrake 的定位很窄:<strong>AI 改完代码后,在提交前扫描这次 diff,看看有没有明显危险信号。</strong></p><p>它不是 AI Reviewer,也不调用 LLM。它不会上传你的代码,不会接 SaaS,不会做 Web Dashboard,也不会替你判断业务逻辑。它做的事情更像是提交前的一脚刹车:在代码进入 commit 之前,先把一些高风险 diff 提醒出来。</p><p>我反而是故意把它做得很“笨”:<strong>只看 diff、只跑本地规则、只报能解释清楚的风险。</strong> 因为很多 AI 编码带来的问题,并不需要另一个 AI 来推理,它们本来就是很直接的信号。</p><p>比如 staged diff 里出现:</p><figure class="highlight diff"><table><tbody><tr><td class="code"><pre><span class="line"><span class="addition">+ permissions: write-all</span></span><br></pre></td></tr></tbody></table></figure><p>或者测试被删掉:</p><figure class="highlight diff"><table><tbody><tr><td class="code"><pre><span class="line"><span class="deletion">- test("rejects anonymous users", () => {</span></span><br><span class="line"><span class="deletion">- expect(() => requireAdmin(null)).toThrow("Unauthorized");</span></span><br><span class="line"><span class="deletion">- });</span></span><br></pre></td></tr></tbody></table></figure><p>再或者配置里出现:</p><figure class="highlight diff"><table><tbody><tr><td class="code"><pre><span class="line"><span class="addition">+ OPENAI_API_KEY="sk-..."</span></span><br></pre></td></tr></tbody></table></figure><p>这些不是复杂安全研究问题,而是提交前就应该被提醒的问题。</p><h2 id="2-怎么安装和使用?"><a href="#2-怎么安装和使用?" class="headerlink" title="2. 怎么安装和使用?"></a>2. 怎么安装和使用?</h2><p>先说前提:<strong>需要已经安装 Node.js 20+</strong>。</p><p><code>npx</code> 本身是 npm 附带的工具。也就是说,如果你的电脑里没有 Node.js / npm,通常也就没有 <code>npx</code>,这时直接运行下面的命令会失败:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npx patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>所以第一次使用前,可以先在终端里检查一下:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">node --version</span><br><span class="line">npm --version</span><br><span class="line">npx --version</span><br></pre></td></tr></tbody></table></figure><p>如果这三个命令不存在,先安装 Node.js 20+。安装完成后,一般会自带 <code>node</code>、<code>npm</code> 和 <code>npx</code>。</p><p>环境没问题后,就不需要手动安装 PatchBrake 了,直接用 <code>npx</code> 运行:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npx patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>如果你想全局安装,也可以:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npm install -g patchbrake</span><br><span class="line">patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>实际使用流程很简单。AI 改完代码后,先把准备检查的改动放进 staged 区域:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">git add <changed-files></span><br></pre></td></tr></tbody></table></figure><p>如果你确认这次所有改动都属于同一个任务,也可以:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">git add .</span><br></pre></td></tr></tbody></table></figure><p>然后运行:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npx patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>这里的 <code>staged</code> 不是让你必须立刻提交。它只是告诉 PatchBrake:<strong>请检查我准备提交的这批改动。</strong></p><p>如果你用的是 Codex 桌面版,也一样。PatchBrake 不关心代码是 Codex 桌面版改的、Claude Code 改的,还是 Cursor 改的。只要最后文件落在本地 Git 仓库里,就可以在项目目录打开终端,先 <code>git add</code>,再运行:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npx patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>如果扫出 error,就先修掉,再重新扫描;没有明显风险后,再正常提交。</p><h2 id="3-实际效果"><a href="#3-实际效果" class="headerlink" title="3. 实际效果"></a>3. 实际效果</h2><p>我做了一个测试 diff:新增一个疑似 OpenAI API key、删除一个测试文件、把 GitHub Actions 权限改成 <code>write-all</code>。PatchBrake 扫出来是这样:</p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/patchbrake-real-test.png" alt="PatchBrake 本地真实测试输出"></p><p>这个输出里可以看到三类风险:</p><ul><li><strong>secret-leak</strong>:新增了疑似 API key,并且输出里已经脱敏</li><li><strong>deleted-tests</strong>:测试文件被删除</li><li><strong>workflow-permissions</strong>:GitHub Actions 权限被放大到 <code>write-all</code></li></ul><p>这就是我想要的效果:它不需要理解整个业务,只要能在提交前把这些明显风险拦一下,就已经很有价值。</p><h2 id="4-为什么只扫-diff?"><a href="#4-为什么只扫-diff?" class="headerlink" title="4. 为什么只扫 diff?"></a>4. 为什么只扫 diff?</h2><p>因为 AI 编码真正容易出问题的地方,经常不是“整个仓库历史上有没有漏洞”,而是:<strong>这一次 AI 到底改了什么。</strong></p><p>你让 AI 重构认证模块,它可能顺手删掉一个断言;你让 AI 改 release workflow,它可能把权限放大;你让 AI 补配置,它可能把 token 写进文件;你让 AI 整理项目规则,它可能改掉 agent 的约束。</p><p>这些变化如果进了主分支,后面再查就麻烦了。所以 PatchBrake 的位置很靠前:<strong>在 commit 前,多做一次本地 diff 质检。</strong></p><h2 id="5-现在能检查什么?"><a href="#5-现在能检查什么?" class="headerlink" title="5. 现在能检查什么?"></a>5. 现在能检查什么?</h2><p>目前 PatchBrake 主要覆盖这些风险:</p><ul><li><strong>secret-leak</strong>:新增 API key、token、private key、<code>.env</code> 风险</li><li><strong>deleted-tests</strong>:测试文件、测试用例或断言被删除</li><li><strong>workflow-permissions</strong>:GitHub Actions 权限被放大</li><li><strong>migration-risk</strong>:危险 migration,比如 <code>DROP</code>、<code>TRUNCATE</code>、无 <code>WHERE</code> 的 <code>DELETE</code></li><li><strong>prompt-config-drift</strong>:AGENTS.md、CLAUDE.md、<code>.cursor/rules</code>、prompt 配置变化</li><li><strong>beta 规则</strong>:auth guard 删除、危险 npm script、危险 shell 命令、依赖风险等</li></ul><p>它不追求覆盖所有漏洞。我更在意的是,先抓住 AI 改代码时最容易被忽略、但提交前最值得停一下的那几类问题。</p><h2 id="6-它和-Gitleaks-Semgrep-CodeQL-是什么关系?"><a href="#6-它和-Gitleaks-Semgrep-CodeQL-是什么关系?" class="headerlink" title="6. 它和 Gitleaks / Semgrep / CodeQL 是什么关系?"></a>6. 它和 Gitleaks / Semgrep / CodeQL 是什么关系?</h2><p>PatchBrake 不是用来替代这些工具的。</p><p>Gitleaks、TruffleHog 更专注 secret scanning;Semgrep、CodeQL 更接近静态分析和 SAST;zizmor 对 GitHub Actions 安全检查更深入。PatchBrake 的目标更小:<strong>把 AI 生成代码中常见的 diff 风险,用一个本地 CLI 在提交前统一拦一下。</strong></p><p>它更像是 AI coding workflow 前面的一道轻量 safety gate,而不是完整安全平台。</p><h2 id="7-现在还很早"><a href="#7-现在还很早" class="headerlink" title="7. 现在还很早"></a>7. 现在还很早</h2><p>PatchBrake 还是早期版本。我最想要的反馈不是泛泛夸奖,而是这三类:</p><ul><li><strong>false positive</strong>:它误报了什么?</li><li><strong>real bad diff</strong>:你真的遇到过哪些 AI 生成的危险 diff?</li><li><strong>rule request</strong>:你希望它多检查什么?</li></ul><p>如果你经常用 Codex、Claude Code、Cursor 或 Copilot 改代码,可以拿自己的项目跑一次:</p><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npx patchbrake scan --staged</span><br></pre></td></tr></tbody></table></figure><p>如果它帮你拦下过一次低级但严重的 diff 风险,欢迎给个 star,或者直接提 issue,把你的 bad diff case 留下来。</p>]]></content:encoded>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/">项目</category>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/PatchBrake/">PatchBrake</category>
<category domain="https://yanxai.com/tags/Guardrails/">Guardrails</category>
<category domain="https://yanxai.com/tags/Node-js/">Node.js</category>
<category domain="https://yanxai.com/tags/Claude-Code/">Claude Code</category>
<category domain="https://yanxai.com/tags/Codex/">Codex</category>
<category domain="https://yanxai.com/tags/PatchBrake/">PatchBrake</category>
<comments>https://yanxai.com/p/patchbrake/#disqus_thread</comments>
</item>
<item>
<title>Hexo + Matery 主题从零搭建个人博客完整教程(GitHub Pages 部署 + 美化优化全流程)</title>
<link>https://yanxai.com/2026/05/10/hexo-matery-blog-jian-zhan-jiao-cheng/</link>
<guid>https://yanxai.com/2026/05/10/hexo-matery-blog-jian-zhan-jiao-cheng/</guid>
<pubDate>Sun, 10 May 2026 04:00:00 GMT</pubDate>
<description>从零开始手把手带你搭建一个完全免费、加载飞快、可深度定制的 Hexo + Matery 个人博客,涵盖环境搭建、主题配置、评论系统、SEO 优化、配色美化等全流程。</description>
<content:encoded><</span><br></pre></td></tr></tbody></table></figure><p>示例:</p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="建议分类管理"><a href="#建议分类管理" class="headerlink" title="建议分类管理"></a>建议分类管理</h3><p>图片多了可以建子目录:</p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><h3 id="图片命名规则"><a href="#图片命名规则" class="headerlink" title="图片命名规则"></a>图片命名规则</h3><ul><li>使用英文 + 数字命名,如 <code>blog01.jpg</code></li><li>避免中文和空格</li><li>建议压缩后再上传(推荐在线工具 TinyPNG 或 squoosh)</li></ul><blockquote><p>为什么不用 <code>post_asset_folder</code>?<br>每次新建文章自动创建同名文件夹会分散图片管理。统一放在 <code>/images/</code> 下,路径固定、好找、可复用。</p></blockquote><hr><h2 id="十、写第一篇完整文章"><a href="#十、写第一篇完整文章" class="headerlink" title="十、写第一篇完整文章"></a>十、写第一篇完整文章</h2><h3 id="9-1-新建文章"><a href="#9-1-新建文章" class="headerlink" title="9.1 新建文章"></a>9.1 新建文章</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo new <span class="string">"我的第一篇博客"</span></span><br></pre></td></tr></tbody></table></figure><p>生成文件:<code>source/_posts/我的第一篇博客.md</code></p><h3 id="9-2-文章-Front-matter-完整写法"><a href="#9-2-文章-Front-matter-完整写法" class="headerlink" title="9.2 文章 Front-matter 完整写法"></a>9.2 文章 Front-matter 完整写法</h3><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line">---</span><br><span class="line">title: 我的第一篇博客</span><br><span class="line">date: 2026-05-10 12:00:00</span><br><span class="line">categories: 生活</span><br><span class="line">tags:</span><br><span class="line"><span class="bullet"> -</span> 随笔</span><br><span class="line"><span class="bullet"> -</span> 日常</span><br><span class="line">top: true # 置顶,放在首页最上面</span><br><span class="line">cover: true # 首页轮播大图展示</span><br><span class="line"><span class="section">toc: true # 显示文章目录(默认就是 true)</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></tbody></table></figure><h3 id="9-3-Matery-文章专属配置项"><a href="#9-3-Matery-文章专属配置项" class="headerlink" title="9.3 Matery 文章专属配置项"></a>9.3 Matery 文章专属配置项</h3><table><thead><tr><th>配置项</th><th>作用</th><th>可选值</th></tr></thead><tbody><tr><td><code>top: true</code></td><td>文章置顶到首页最前</td><td>true / false</td></tr><tr><td><code>hide: true</code></td><td>隐藏文章,不在首页列表显示</td><td>true / false</td></tr><tr><td><code>cover: true</code></td><td>首页轮播图展示</td><td>true / false</td></tr><tr><td><code>toc: false</code></td><td>关闭文章右侧目录</td><td>true / false</td></tr><tr><td><code>mathjax: true</code></td><td>启用数学公式渲染</td><td>true / false</td></tr><tr><td><code>img: /images/xxx.jpg</code></td><td>指定文章封面图</td><td>图片路径</td></tr><tr><td><code>summary: 摘要文字</code></td><td>自定义文章摘要</td><td>任意文字</td></tr></tbody></table><h3 id="9-4-Markdown-正文示例"><a href="#9-4-Markdown-正文示例" class="headerlink" title="9.4 Markdown 正文示例"></a>9.4 Markdown 正文示例</h3><p>以下是文章正文的常见写法,直接复制模板修改即可:</p><p><strong>二级标题和段落</strong></p><p><code>## 二级标题</code> 后用空行分隔正文。正文直接写,无需额外标记。</p><p><strong>列表</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"><span class="bullet">-</span> 列表项 1</span><br><span class="line"><span class="bullet">-</span> 列表项 2</span><br><span class="line"><span class="bullet"> -</span> 嵌套子项</span><br></pre></td></tr></tbody></table></figure><p><strong>插入图片</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"></span><br></pre></td></tr></tbody></table></figure><p><strong>文字样式</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"><span class="strong">**加粗文字**</span> 和 <span class="emphasis">*斜体文字*</span></span><br></pre></td></tr></tbody></table></figure><p><strong>引用块</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"><span class="quote">> 学而不思则罔,思而不学则殆</span></span><br></pre></td></tr></tbody></table></figure><p><strong>行内代码</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line">用 <span class="code">`hexo server`</span> 命令启动本地预览。</span><br></pre></td></tr></tbody></table></figure><p><strong>Java 代码块</strong></p><figure class="highlight markdown"><table><tbody><tr><td class="code"><pre><span class="line"><span class="code">```java</span></span><br><span class="line"><span class="code">public class Hello {</span></span><br><span class="line"><span class="code"> public static void main(String[] args) {</span></span><br><span class="line"><span class="code"> System.out.println("Hello, World!");</span></span><br><span class="line"><span class="code"> }</span></span><br><span class="line"><span class="code">}</span></span><br><span class="line"><span class="code">```</span></span><br></pre></td></tr></tbody></table></figure><blockquote><p>提示:展示 “如何在 Markdown 里写代码块” 时,外层用四个反引号 ```` `````` ` 包裹,内层的三个反引号就不会被解析为结束标记。</p></blockquote><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/hexo-matery-blog-jian-zhan-jiao-cheng/blog-article-preview.webp" alt="文章页面效果"></p><p>写好文章后,配合 Matery 的目录导航和代码高亮,阅读体验就是上面这样。</p><hr><h2 id="十一、本地预览"><a href="#十一、本地预览" class="headerlink" title="十一、本地预览"></a>十一、本地预览</h2><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo server</span><br></pre></td></tr></tbody></table></figure><p>浏览器打开 <a href="http://localhost:4000/">http://localhost:4000</a></p><p>边写边预览,修改保存后页面自动刷新。按 <code>Ctrl+C</code> 停止服务。</p><hr><h2 id="十二、发布上线"><a href="#十二、发布上线" class="headerlink" title="十二、发布上线"></a>十二、发布上线</h2><h3 id="11-1-一键部署命令"><a href="#11-1-一键部署命令" class="headerlink" title="11.1 一键部署命令"></a>11.1 一键部署命令</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo clean && hexo g && hexo d</span><br></pre></td></tr></tbody></table></figure><p>命令含义:</p><ul><li><code>hexo clean</code> —— 清空旧生成的静态文件</li><li><code>hexo g</code> (generate) —— 生成新的静态 HTML</li><li><code>hexo d</code> (deploy) —— 推送至 GitHub Pages</li></ul><h3 id="11-2-访问博客"><a href="#11-2-访问博客" class="headerlink" title="11.2 访问博客"></a>11.2 访问博客</h3><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line">https://你的用户名.github.io</span><br></pre></td></tr></tbody></table></figure><p>部署后等待 1-2 分钟,GitHub Pages 会自动刷新。</p><p><img loading="lazy" decoding="async" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0Ij48Y2lyY2xlIGN4PSI0IiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgaWQ9InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAiIGF0dHJpYnV0ZU5hbWU9InIiIGJlZ2luPSIwO3N2Z1NwaW5uZXJzM0RvdHNTY2FsZTEuZW5kLTAuMjVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjMiIGZpbGw9ImN1cnJlbnRDb2xvciI+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNnMiIGR1cj0iMC43NXMiIHZhbHVlcz0iMzsuMjszIi8+PC9jaXJjbGU+PGNpcmNsZSBjeD0iMjAiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIj48YW5pbWF0ZSBpZD0ic3ZnU3Bpbm5lcnMzRG90c1NjYWxlMSIgYXR0cmlidXRlTmFtZT0iciIgYmVnaW49InN2Z1NwaW5uZXJzM0RvdHNTY2FsZTAuZW5kLTAuNDVzIiBkdXI9IjAuNzVzIiB2YWx1ZXM9IjM7LjI7MyIvPjwvY2lyY2xlPjwvc3ZnPg==" data-original="/images/hexo-matery-blog-jian-zhan-jiao-cheng/blog-homepage.webp" alt="博客主页效果"></p><p>部署成功后,你的博客主页大概长这样。Matery 的大图轮播 + Material Design 卡片布局,颜值在线。</p><hr><h2 id="十三、部署英文作品集(进阶:单仓库多站点)"><a href="#十三、部署英文作品集(进阶:单仓库多站点)" class="headerlink" title="十三、部署英文作品集(进阶:单仓库多站点)"></a>十三、部署英文作品集(进阶:单仓库多站点)</h2><p>如果你的英文作品集(Portfolio)也需要上线,可以和博客共用一个 GitHub Pages 仓库:</p><ol><li>单独创建一个 Portfolio 项目,使用 Cactus 或其他主题</li><li>Portfolio 的 <code>_config.yml</code> 中设置 <code>root: /portfolio/</code></li><li>在博客部署前,把 Portfolio 的 <code>public/</code> 复制到博客的 <code>public/portfolio/</code> 下</li></ol><p>这样就能同时拥有:</p><ul><li>中文博客:<code>https://用户名.github.io</code></li><li>英文作品集:<code>https://用户名.github.io/portfolio/</code></li></ul><hr><h2 id="十四、主题美化:统一配色方案"><a href="#十四、主题美化:统一配色方案" class="headerlink" title="十四、主题美化:统一配色方案"></a>十四、主题美化:统一配色方案</h2><p>Matery 默认使用<strong>绿色</strong>作为主题色。以下是以紫色(<code>#8b5cf6</code>)为例的替代方案。</p><h3 id="13-1-修改的完整文件清单"><a href="#13-1-修改的完整文件清单" class="headerlink" title="13.1 修改的完整文件清单"></a>13.1 修改的完整文件清单</h3><table><thead><tr><th>文件</th><th>说明</th></tr></thead><tbody><tr><td><code>themes/matery/source/css/my.css</code></td><td>自定义样式(卡片、侧边栏、归档)</td></tr><tr><td><code>themes/matery/source/css/matery.css</code></td><td>主题核心样式(进度条、标签、分页、链接)</td></tr><tr><td><code>themes/matery/source/css/dark.css</code></td><td>暗黑模式配色</td></tr><tr><td><code>themes/matery/source/css/post.css</code></td><td>文章页 TOC 目录样式</td></tr><tr><td><code>themes/matery/source/css/gitment.css</code></td><td>Gitment 评论区样式</td></tr><tr><td><code>themes/matery/source/css/my-gitalk.css</code></td><td>Gitalk 评论区样式</td></tr><tr><td><code>themes/matery/layout/_partial/github-link.ejs</code></td><td>GitHub 角标</td></tr><tr><td><code>themes/matery/layout/_widget/recommend.ejs</code></td><td>推荐文章卡片</td></tr><tr><td><code>themes/matery/layout/contact.ejs</code></td><td>联系页按钮</td></tr><tr><td><code>themes/matery/layout/friends.ejs</code></td><td>友链卡片背景</td></tr><tr><td><code>themes/matery/layout/_widget/category-radar.ejs</code></td><td>分类雷达图</td></tr><tr><td><code>themes/matery/layout/_widget/post-charts.ejs</code></td><td>文章统计图表</td></tr></tbody></table><h3 id="13-2-颜色映射表"><a href="#13-2-颜色映射表" class="headerlink" title="13.2 颜色映射表"></a>13.2 颜色映射表</h3><table><thead><tr><th>原颜色</th><th>说明</th><th>替换为</th></tr></thead><tbody><tr><td><code>#42b983</code></td><td>绿色主题色(约 45 处)</td><td><code>#8b5cf6</code></td></tr><tr><td><code>#0f9d58</code></td><td>深绿色(GitHub 角标等)</td><td><code>#8b5cf6</code></td></tr><tr><td><code>#4cbf30</code></td><td>浅绿色(渐变起点)</td><td><code>#a78bfa</code></td></tr><tr><td><code>#38a274</code></td><td>绿色深色变体</td><td><code>#7c4dff</code></td></tr><tr><td><code>#3ecf8e</code></td><td>青绿色(图表)</td><td><code>#8b5cf6</code></td></tr><tr><td><code>#0ba360</code> / <code>#3cba92</code></td><td>联系页/友链绿色</td><td><code>#7c4dff</code> / <code>#a78bfa</code></td></tr><tr><td><code>rgba(66,185,131,0.1)</code></td><td>绿色半透明背景</td><td><code>rgba(139,92,246,0.1)</code></td></tr></tbody></table><h3 id="13-3-批量查找替换命令(Bash)"><a href="#13-3-批量查找替换命令(Bash)" class="headerlink" title="13.3 批量查找替换命令(Bash)"></a>13.3 批量查找替换命令(Bash)</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 在 themes/matery 目录下查找所有绿色残留</span></span><br><span class="line">grep -rn <span class="string">"42b983\|0f9d58\|4cbf30\|38a274\|3ecf8e\|0ba360\|3cba92"</span> \</span><br><span class="line"> --include=<span class="string">"*.css"</span> --include=<span class="string">"*.ejs"</span> .</span><br></pre></td></tr></tbody></table></figure><hr><h2 id="十五、关闭不需要的功能"><a href="#十五、关闭不需要的功能" class="headerlink" title="十五、关闭不需要的功能"></a>十五、关闭不需要的功能</h2><h3 id="14-1-统计与分析"><a href="#14-1-统计与分析" class="headerlink" title="14.1 统计与分析"></a>14.1 统计与分析</h3><p>编辑 <code>themes/matery/_config.yml</code>:</p><figure class="highlight yaml"><table><tbody><tr><td class="code"><pre><span class="line"><span class="comment"># 关闭百度统计</span></span><br><span class="line"><span class="attr">baiduAnalytics:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">id:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭 Google Analytics</span></span><br><span class="line"><span class="attr">googleAnalytics:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">id:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭百度推送</span></span><br><span class="line"><span class="attr">baiduPush:</span> <span class="literal">false</span></span><br></pre></td></tr></tbody></table></figure><h3 id="14-2-移除不用的插件"><a href="#14-2-移除不用的插件" class="headerlink" title="14.2 移除不用的插件"></a>14.2 移除不用的插件</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">npm uninstall hexo-theme-landscape hexo-renderer-pug</span><br></pre></td></tr></tbody></table></figure><p><code>hexo-theme-landscape</code> 是 Hexo 默认主题,已用 Matery 就不需要了。<code>hexo-renderer-pug</code> 是 Pug 模板引擎,Matery 用的是 EJS。</p><hr><h2 id="十六、日常写作工作流"><a href="#十六、日常写作工作流" class="headerlink" title="十六、日常写作工作流"></a>十六、日常写作工作流</h2><h3 id="新文章"><a href="#新文章" class="headerlink" title="新文章"></a>新文章</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo new <span class="string">"文章标题"</span></span><br></pre></td></tr></tbody></table></figure><h3 id="本地预览"><a href="#本地预览" class="headerlink" title="本地预览"></a>本地预览</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo server</span><br></pre></td></tr></tbody></table></figure><p>访问 <a href="http://localhost:4000/">http://localhost:4000</a> 实时查看效果。</p><h3 id="发布上线"><a href="#发布上线" class="headerlink" title="发布上线"></a>发布上线</h3><figure class="highlight bash"><table><tbody><tr><td class="code"><pre><span class="line">hexo clean && hexo g && hexo d</span><br></pre></td></tr></tbody></table></figure><h3 id="一句话总结"><a href="#一句话总结" class="headerlink" title="一句话总结"></a>一句话总结</h3><blockquote><p>写 Markdown → <code>hexo server</code> 预览 → <code>hexo clean && hexo g && hexo d</code> 发布</p></blockquote><hr><h2 id="十七、常见问题排查"><a href="#十七、常见问题排查" class="headerlink" title="十七、常见问题排查"></a>十七、常见问题排查</h2><h3 id="Q1:部署后页面空白-404?"><a href="#Q1:部署后页面空白-404?" class="headerlink" title="Q1:部署后页面空白 / 404?"></a>Q1:部署后页面空白 / 404?</h3><p>检查 <code>_config.yml</code> 中的 <code>url</code> 和 <code>root</code>:</p><ul><li>根站点:<code>root: /</code></li><li>子目录站点:<code>root: /子目录名/</code></li></ul><h3 id="Q2:图片显示不出来(裂图)?"><a href="#Q2:图片显示不出来(裂图)?" class="headerlink" title="Q2:图片显示不出来(裂图)?"></a>Q2:图片显示不出来(裂图)?</h3><ol><li>确认图片放在了 <code>source/images/</code> 下</li><li>引用路径必须以 <code>/images/</code> 开头,如 <code></code></li><li>不要用相对路径 <code>../images/</code></li></ol><h3 id="Q3:部署后网站没变化?"><a href="#Q3:部署后网站没变化?" class="headerlink" title="Q3:部署后网站没变化?"></a>Q3:部署后网站没变化?</h3><ul><li>GitHub Pages 有 1-5 分钟缓存,等一等</li><li>强制刷新浏览器:<code>Ctrl+Shift+R</code></li><li>检查 GitHub 仓库的 Actions 页面是否显示绿色对勾</li></ul><h3 id="Q4:想要自定义域名?"><a href="#Q4:想要自定义域名?" class="headerlink" title="Q4:想要自定义域名?"></a>Q4:想要自定义域名?</h3><ol><li>在 <code>source/</code> 下创建 <code>CNAME</code> 文件,写入你的域名(如 <code>blog.example.com</code>)</li><li>去域名 DNS 服务商添加 CNAME 记录指向 <code>你的用户名.github.io</code></li><li>GitHub 仓库 Settings → Pages → Custom domain 填写域名</li></ol><h3 id="Q5:如何添加评论系统?"><a href="#Q5:如何添加评论系统?" class="headerlink" title="Q5:如何添加评论系统?"></a>Q5:如何添加评论系统?</h3><p><strong>推荐 Giscus</strong>(基于 GitHub Discussions),比 Gitalk 更简单,无需申请 OAuth App:</p><ol><li>访问 <a href="https://giscus.app/">giscus.app</a>,填入仓库名</li><li>在 GitHub 仓库 Settings → Features 中开启 Discussions</li><li>把生成的配置填入 <code>themes/matery/_config.yml</code>:</li></ol><figure class="highlight yaml"><table><tbody><tr><td class="code"><pre><span class="line"><span class="attr">giscus:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">repo:</span> <span class="string">你的用户名/你的用户名.github.io</span></span><br><span class="line"> <span class="attr">repoID:</span> <span class="string">从</span> <span class="string">giscus.app</span> <span class="string">获取</span></span><br><span class="line"> <span class="attr">category:</span> <span class="string">Announcements</span></span><br><span class="line"> <span class="attr">categoryID:</span> <span class="string">从</span> <span class="string">giscus.app</span> <span class="string">获取</span></span><br><span class="line"> <span class="attr">mapping:</span> <span class="string">pathname</span></span><br><span class="line"> <span class="attr">lang:</span> <span class="string">zh-CN</span></span><br></pre></td></tr></tbody></table></figure><p>Matery 还支持 Gitalk、Valine、Waline、Twikoo 等,按需选择即可。</p><h3 id="Q6:代码块没有语法高亮?"><a href="#Q6:代码块没有语法高亮?" class="headerlink" title="Q6:代码块没有语法高亮?"></a>Q6:代码块没有语法高亮?</h3><p>确认 <code>_config.yml</code> 中启用了 Prism.js:</p><figure class="highlight yaml"><table><tbody><tr><td class="code"><pre><span class="line"><span class="attr">prismjs:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">preprocess:</span> <span class="literal">true</span></span><br></pre></td></tr></tbody></table></figure><p>Matery 主题内置了 Prism.js 的支持,开启后代码块会自动应用 Okaidia 暗色主题 + 行号。</p><h3 id="Q7:首页显示文章全文而不是摘要?"><a href="#Q7:首页显示文章全文而不是摘要?" class="headerlink" title="Q7:首页显示文章全文而不是摘要?"></a>Q7:首页显示文章全文而不是摘要?</h3><p>在文章中加入 <code><!-- more --></code> 标签,前面的内容作为摘要显示在首页卡片中,后面的内容只有点进去才能看到。</p><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这篇保姆级教程从一个空仓库开始,手把手带你完成了:</p><ol><li><strong>环境搭建</strong> → Node.js + Git + GitHub 仓库</li><li><strong>博客创建</strong> → Hexo 初始化 + 必备插件</li><li><strong>核心配置</strong> → <code>_config.yml</code> 网站配置 + Matery 主题配置</li><li><strong>内容写作</strong> → Front-matter 配置 + Markdown 规范 + 图片管理</li><li><strong>主题美化</strong> → 全站配色统一(紫/蓝/任意色)</li><li><strong>功能增强</strong> → 评论系统(Giscus)+ 搜索 + RSS + Sitemap</li><li><strong>部署发布</strong> → 一键推送到 GitHub Pages</li><li><strong>进阶扩展</strong> → 多站点部署、自定义域名、代码高亮(Prism.js 暗色主题)</li></ol><p>如果你已经跟着教程做完了所有步骤,恭喜——你现在拥有的是一个<strong>零成本、完全自定义、加载飞速、好看又好用的个人博客</strong>。</p><p>写吧。写得越多,收获越多。</p>]]></content:encoded>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/">项目</category>
<category domain="https://yanxai.com/categories/%E9%A1%B9%E7%9B%AE/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/">个人博客</category>
<category domain="https://yanxai.com/tags/Node-js/">Node.js</category>
<comments>https://yanxai.com/2026/05/10/hexo-matery-blog-jian-zhan-jiao-cheng/#disqus_thread</comments>
</item>
</channel>
</rss>