<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>chagumu&#39;s blog</title>
  
  
  <link href="https://u7u7.top/atom.xml" rel="self"/>
  
  <link href="https://u7u7.top/"/>
  <updated>2026-03-25T16:11:28.614Z</updated>
  <id>https://u7u7.top/</id>
  
  <author>
    <name>chagumu&#39;s blog</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>RAG中的Indexing全解析</title>
    <link href="https://u7u7.top/posts/b43bf9d3.html"/>
    <id>https://u7u7.top/posts/b43bf9d3.html</id>
    <published>2026-03-25T16:07:41.000Z</published>
    <updated>2026-03-25T16:11:28.614Z</updated>
    
    <content type="html"><![CDATA[<h3 id="一、Indexing-到底是什么？"><a href="#一、Indexing-到底是什么？" class="headerlink" title="一、Indexing 到底是什么？"></a>一、Indexing 到底是什么？</h3><p><strong>Indexing = 给你的知识库做 “超级目录”</strong></p><p>你有一堆文档：</p><ul><li>PDF</li><li>Word</li><li>网页</li><li>笔记</li><li>表格</li><li>书籍</li></ul><p>不能直接理解文字</p><p>不能快速查找</p><p><strong>把 “杂乱无章的文字” → 变成 “计算机能快速检索的结构化向量数据”</strong></p><p>它是 RAG 的<strong>前置基建</strong>，没有 Indexing，就没有检索，就没有准确回答。</p><h3 id="二、Indexing-的完整-5-步流程（标准-RAG-架构）"><a href="#二、Indexing-的完整-5-步流程（标准-RAG-架构）" class="headerlink" title="二、Indexing 的完整 5 步流程（标准 RAG 架构）"></a>二、Indexing 的完整 5 步流程（标准 RAG 架构）</h3><p>所有 RAG 系统的 Indexing 都遵循这 5 步，一步都不能少：</p><h4 id="1-文档加载（Load）"><a href="#1-文档加载（Load）" class="headerlink" title="1. 文档加载（Load）"></a>1. 文档加载（Load）</h4><p>作用：<strong>把非结构化文件 → 纯文本</strong></p><h4 id="2-文本清洗（Clean）"><a href="#2-文本清洗（Clean）" class="headerlink" title="2. 文本清洗（Clean）"></a>2. 文本清洗（Clean）</h4><p>去掉没用的东西：</p><ul><li>多余空格、换行</li><li>水印、页眉页脚</li><li>乱码、符号</li><li>表格乱码</li></ul><p>作用：<strong>让向量更纯净，避免噪声影响语义</strong></p><h4 id="3-文本分块（Chunk）"><a href="#3-文本分块（Chunk）" class="headerlink" title="3. 文本分块（Chunk）"></a>3. 文本分块（Chunk）</h4><ul><li>太长 → 向量无法完整表达语义</li><li>太长 → 检索不精准</li><li>太长 → 大模型读不完</li></ul><p>切分规则（最关键）：</p><ul><li>按段落</li><li>按句子</li><li>按固定长度</li><li>按语义（最推荐）</li><li>保留重叠（避免语义切断）</li></ul><blockquote><p>Chunk 质量 = 检索效果 = RAG 回答效果</p></blockquote><h4 id="4-文本向量化（Embedding）"><a href="#4-文本向量化（Embedding）" class="headerlink" title="4. 文本向量化（Embedding）"></a>4. 文本向量化（Embedding）</h4><p>就是你刚才问的：<strong>把文字变成数字向量</strong></p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">[0.23, 0.55, -0.12, ... 768维]</span></span><br></pre></td></tr></table></figure><p>作用：</p><ul><li>语义相近 → 向量距离近</li><li>语义无关 → 向量距离远</li></ul><h4 id="5-建立索引并存储（Index-amp-Store）"><a href="#5-建立索引并存储（Index-amp-Store）" class="headerlink" title="5. 建立索引并存储（Index &amp; Store）"></a>5. 建立索引并存储（Index &amp; Store）</h4><p>把所有向量存到<strong>向量数据库</strong>，并建立<strong>索引结构</strong>。</p><p>向量数据库包括：</p><ul><li>FAISS（本地轻量）</li><li>Milvus（企业级）</li><li>Pinecone（云服务）</li><li>Chroma（开发常用）</li><li>Elasticsearch（混合检索）</li></ul><p>索引结构：</p><ul><li>HNSW（最快最常用）</li><li>IVF</li><li>Flat</li></ul><p>用户提问 → 生成问题向量 → 一秒从几万、几十万文档里找出最相关的几段</p><h3 id="三、Indexing-的核心作用"><a href="#三、Indexing-的核心作用" class="headerlink" title="三、Indexing 的核心作用"></a>三、Indexing 的核心作用</h3><h4 id="1-让检索从-“几分钟”-变成-“毫秒级”"><a href="#1-让检索从-“几分钟”-变成-“毫秒级”" class="headerlink" title="1. 让检索从 “几分钟” 变成 “毫秒级”"></a>1. 让检索从 “几分钟” 变成 “毫秒级”</h4><p>没有索引：</p><ul><li>逐字遍历所有文档 → 巨慢</li></ul><p>有索引：</p><ul><li>用向量距离计算 → 瞬间匹配</li></ul><h4 id="2-让大模型不幻觉"><a href="#2-让大模型不幻觉" class="headerlink" title="2. 让大模型不幻觉"></a>2. 让大模型不幻觉</h4><p>因为检索到真实知识 → 模型只能基于事实回答</p><h4 id="3-让知识库可以无限大"><a href="#3-让知识库可以无限大" class="headerlink" title="3. 让知识库可以无限大"></a>3. 让知识库可以无限大</h4><p>几十万文档照样秒查</p><h4 id="4-让私有数据能用在大模型里"><a href="#4-让私有数据能用在大模型里" class="headerlink" title="4. 让私有数据能用在大模型里"></a>4. 让私有数据能用在大模型里</h4><p>企业数据、隐私数据、本地数据 → 全部可检索</p><h3 id="四、Indexing-最关键的-4-个技术点"><a href="#四、Indexing-最关键的-4-个技术点" class="headerlink" title="四、Indexing 最关键的 4 个技术点"></a>四、Indexing 最关键的 4 个技术点</h3><h4 id="1-分块策略（最重要）"><a href="#1-分块策略（最重要）" class="headerlink" title="1. 分块策略（最重要）"></a>1. 分块策略（最重要）</h4><ul><li>大小：300~800 字符</li><li>重叠：50~100 字符</li><li>按语义拆分 &gt; 按字数拆分</li></ul><h4 id="2-向量模型（Embedding-Model）"><a href="#2-向量模型（Embedding-Model）" class="headerlink" title="2. 向量模型（Embedding Model）"></a>2. 向量模型（Embedding Model）</h4><p>决定语义理解能力：</p><ul><li>开源：all-MiniLM-L6-v2（最快）、bge-small（最准）</li><li>商用：OpenAI Embedding、文心 Embedding</li></ul><h4 id="3-向量数据库"><a href="#3-向量数据库" class="headerlink" title="3. 向量数据库"></a>3. 向量数据库</h4><p>决定速度和规模</p><h4 id="4-索引结构"><a href="#4-索引结构" class="headerlink" title="4. 索引结构"></a>4. 索引结构</h4><p>决定检索效率</p><p>HNSW 是目前工业界标配。</p><h3 id="五、Indexing-完整流程图（文字版）"><a href="#五、Indexing-完整流程图（文字版）" class="headerlink" title="五、Indexing 完整流程图（文字版）"></a>五、Indexing 完整流程图（文字版）</h3><figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">原始文档</span><br><span class="line">   ↓</span><br><span class="line">加载（Load）→ 纯文本</span><br><span class="line">   ↓</span><br><span class="line">清洗（<span class="built_in">Clean</span>）→ 去掉噪声</span><br><span class="line">   ↓</span><br><span class="line">分块（Split）→ 一段段 Chunk</span><br><span class="line">   ↓</span><br><span class="line">向量化（Embedding）→ 向量数组</span><br><span class="line">   ↓</span><br><span class="line">建立索引（<span class="built_in">Index</span>）→ 向量数据库</span><br><span class="line">   ↓</span><br><span class="line">【完成！可用于 RAG 检索】</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;一、Indexing-到底是什么？&quot;&gt;&lt;a href=&quot;#一、Indexing-到底是什么？&quot; class=&quot;headerlink&quot; title=&quot;一、Indexing 到底是什么？&quot;&gt;&lt;/a&gt;一、Indexing 到底是什么？&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;In</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>langchain中的chain到底是什么</title>
    <link href="https://u7u7.top/posts/73f49caa.html"/>
    <id>https://u7u7.top/posts/73f49caa.html</id>
    <published>2026-03-25T15:53:21.000Z</published>
    <updated>2026-03-25T16:11:28.612Z</updated>
    
    <content type="html"><![CDATA[<h3 id="一、Chain-是什么"><a href="#一、Chain-是什么" class="headerlink" title="一、Chain 是什么"></a>一、Chain 是什么</h3><p>Chain 是 LangChain 中<strong>组件串联执行的核心机制</strong>，它将多个独立组件（提示词模板、模型、工具等）按顺序连接，前一个组件的输出自动作为下一个组件的输入，实现复杂任务的流水线化处理。</p><h3 id="二、核心特性"><a href="#二、核心特性" class="headerlink" title="二、核心特性"></a>二、核心特性</h3><ol><li><strong>链式语法</strong>：通过 <code>|</code> 符号快速拼接组件，写法简洁直观</li></ol><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">chain</span> = chat_prompt_template | model  <span class="comment"># 提示词模板 → 模型</span></span><br></pre></td></tr></table></figure><ol><li><strong>接口约束</strong>：参与成链的组件必须是 <code>Runnable</code> 接口的子类（如提示词模板、模型、嵌入模型等）</li><li><strong>对象类型</strong>：最终形成的链是 <code>RunnableSerializable</code> 对象，本身也实现了 <code>Runnable</code> 接口，可继续参与链式拼接</li><li><strong>执行触发</strong>：通过 <code>invoke()</code>（一次性执行）或 <code>stream()</code>（流式执行）触发整个链条运行</li></ol><h3 id="三、执行流程"><a href="#三、执行流程" class="headerlink" title="三、执行流程"></a>三、执行流程</h3><ol><li><strong>输入</strong>：传入字典格式参数（如 <code>&#123;&quot;history&quot;: &quot;历史对话&quot;, &quot;input&quot;: &quot;用户问题&quot;&#125;</code>）</li><li><strong>组件 1：提示词模板</strong>：将输入参数填充到模板中，生成 <code>PromptValue</code>（完整提示词文本）</li><li><strong>组件 2：模型对象</strong>：接收提示词文本，调用大模型生成回复</li><li><strong>输出</strong>：返回模型回复的 <code>AIMessage</code> 对象（或流式 chunk）</li></ol><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">输入字典 → chat_prompt_template → PromptValue → <span class="keyword">model</span> → AIMessage</span><br></pre></td></tr></table></figure><h3 id="四、可加入的组件"><a href="#四、可加入的组件" class="headerlink" title="四、可加入的组件"></a>四、可加入的组件</h3><p><img src="https://cdn.phototourl.com/member/2026-03-25-c377338d-e214-4751-95c5-8bb7c8cd7c86.png" alt="img"></p><p><img src="https://cdn.phototourl.com/member/2026-03-25-0a45d961-a49c-4820-be24-672815dd6d41.png" alt="img"></p><p>LangChain 的链（Chain）本质是 <code>Runnable</code> 接口组件的流水线，<strong>只要实现</strong> <code>**Runnable**</code> <strong>接口（或通过封装适配），任何组件 / 逻辑都能加入</strong>，以下是完整分类总结（重点补充自定义函数）：</p><h4 id="一、可加入-Chain-的核心组件"><a href="#一、可加入-Chain-的核心组件" class="headerlink" title="一、可加入 Chain 的核心组件"></a>一、可加入 Chain 的核心组件</h4><p>LangChain 的组件都遵循「输入→处理→输出」的标准化逻辑，核心可接入 Chain 的组件分为以下几类，先明确每类的核心作用和接口：</p><div class="table-container"><table><thead><tr><th><strong>组件类型</strong></th><th><strong>核心作用</strong></th><th><strong>标准输入（Input）</strong></th><th><strong>标准输出（Output）</strong></th></tr></thead><tbody><tr><td><strong>PromptTemplate</strong></td><td>格式化提示词（填充变量）</td><td>字典（如 <code>&#123;&quot;question&quot;: &quot;如何学Python？&quot;&#125;</code>）</td><td>字符串（填充后的完整提示词）</td></tr><tr><td><strong>LLM/ChatModel</strong></td><td>调用大模型（OpenAI / 智谱 / 文心等）</td><td>字符串 / 消息列表（提示词）</td><td>字符串 / BaseMessage（大模型回复）</td></tr><tr><td><strong>DocumentLoader</strong></td><td>加载外部文档（PDF / 文本 / 网页）</td><td>文件路径 / URL / 配置参数</td><td>Document 列表（含 <code>page_content</code> 字段）</td></tr><tr><td><strong>TextSplitter</strong></td><td>分割长文本（适配模型上下文窗口）</td><td>字符串 / Document 列表</td><td>Document 列表（分割后的小片段）</td></tr><tr><td><strong>VectorStore</strong></td><td>向量存储（存储文本嵌入向量）</td><td>Document 列表 + Embedding 模型</td><td>无（存储）/ 相似文档列表（检索时）</td></tr><tr><td><strong>Retriever</strong></td><td>从向量库检索相似文档</td><td>查询字符串</td><td>Document 列表（检索到的相关文档）</td></tr><tr><td><strong>OutputParser</strong></td><td>解析模型输出（转 JSON / 列表 / 自定义格式）</td><td>字符串（模型原始回复）</td><td>字典 / 列表 / 自定义对象（结构化结果）</td></tr><tr><td><strong>Tool</strong></td><td>调用外部工具（搜索 / 计算 / API）</td><td>工具入参（如 <code>&#123;&quot;query&quot;: &quot;2026年GDP&quot;&#125;</code>）</td><td>工具返回结果（字符串 / 字典）</td></tr></tbody></table></div><h4 id="二、组件的连接方式"><a href="#二、组件的连接方式" class="headerlink" title="二、组件的连接方式"></a>二、组件的连接方式</h4><p>连接组件的核心是「让前一个组件的输出，匹配后一个组件的输入」，LangChain 提供了 3 种主流方式，从简单到复杂依次是：</p><h5 id="方式-1：基础链式调用（手动拼接，适合新手）"><a href="#方式-1：基础链式调用（手动拼接，适合新手）" class="headerlink" title="方式 1：基础链式调用（手动拼接，适合新手）"></a>方式 1：基础链式调用（手动拼接，适合新手）</h5><p>直接通过变量赋值，把前一个组件的输出作为后一个的输入，逻辑最直观。</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain.prompts import PromptTemplate</span><br><span class="line"><span class="keyword">from</span> langchain_openai import ChatOpenAI</span><br><span class="line"><span class="keyword">from</span> langchain_core.output_parsers import StrOutputParser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 初始化组件</span></span><br><span class="line">prompt = PromptTemplate(</span><br><span class="line">    <span class="attribute">template</span>=<span class="string">&quot;请用简洁的语言回答：&#123;question&#125;&quot;</span>,</span><br><span class="line">    input_variables=[<span class="string">&quot;question&quot;</span>]  # 定义需要填充的变量</span><br><span class="line">)</span><br><span class="line">llm = ChatOpenAI(<span class="attribute">model</span>=<span class="string">&quot;gpt-3.5-turbo&quot;</span>, <span class="attribute">api_key</span>=<span class="string">&quot;你的API_KEY&quot;</span>)</span><br><span class="line">output_parser = StrOutputParser()  # 把模型输出转成纯字符串</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 手动连接组件（前一个的输出 → 后一个的输入）</span></span><br><span class="line">question = <span class="string">&quot;LangChain的Chain有什么作用？&quot;</span></span><br><span class="line"><span class="comment"># Step1: Prompt填充变量</span></span><br><span class="line">prompt_text = prompt.format(<span class="attribute">question</span>=question)</span><br><span class="line"><span class="comment"># Step2: 把填充后的Prompt传给LLM</span></span><br><span class="line">llm_response = llm.invoke(prompt_text)</span><br><span class="line"><span class="comment"># Step3: 解析LLM输出</span></span><br><span class="line">final_answer = output_parser.invoke(llm_response)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(final_answer)</span><br></pre></td></tr></table></figure><h5 id="方式-2：使用-管道符（LangChain-推荐，简洁）"><a href="#方式-2：使用-管道符（LangChain-推荐，简洁）" class="headerlink" title="方式 2：使用 | 管道符（LangChain 推荐，简洁）"></a>方式 2：使用 <code>|</code> 管道符（LangChain 推荐，简洁）</h5><p>LangChain 支持用 <code>|</code> 符号直接串联组件（类似 Linux 管道），自动处理输入输出的适配，是最常用的方式。</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基于上面的组件，用管道符串联成链</span></span><br><span class="line">chain = prompt | llm | output_parser</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用链（输入字典，匹配Prompt的变量）</span></span><br><span class="line"><span class="literal">result</span> = chain.invoke(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;LangChain的Chain有什么作用？&quot;</span>&#125;)</span><br><span class="line">print(<span class="literal">result</span>)</span><br></pre></td></tr></table></figure><p><strong>核心逻辑</strong>：</p><ul><li><code>prompt | llm</code>：Prompt 输出的字符串自动作为 LLM 的输入；</li><li><code>llm | output_parser</code>：LLM 输出的 BaseMessage 自动作为 Parser 的输入；</li><li>整个链的输入是 Prompt 所需的字典，输出是 Parser 处理后的字符串。</li></ul><h5 id="方式-3：自定义-Chain（适合复杂逻辑）"><a href="#方式-3：自定义-Chain（适合复杂逻辑）" class="headerlink" title="方式 3：自定义 Chain（适合复杂逻辑）"></a>方式 3：自定义 Chain（适合复杂逻辑）</h5><p>如果组件间的逻辑不是简单的「一对一」（比如需要分支、循环、多输入），可以继承 <code>BaseChain</code> 自定义。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_core.chains <span class="keyword">import</span> BaseChain</span><br><span class="line"><span class="keyword">from</span> langchain_core.promises <span class="keyword">import</span> Promise</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> Field</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CustomChain</span>(<span class="title class_ inherited__">BaseChain</span>):</span><br><span class="line">    prompt: PromptTemplate = Field(...)</span><br><span class="line">    llm: ChatOpenAI = Field(...)</span><br><span class="line">    output_parser: StrOutputParser = Field(...)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 定义链的输入变量（和Prompt一致）</span></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">input_keys</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> [<span class="string">&quot;question&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 定义链的输出变量</span></span><br><span class="line"><span class="meta">    @property</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">output_keys</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> [<span class="string">&quot;answer&quot;</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 核心：自定义组件连接逻辑</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_call</span>(<span class="params">self, inputs, run_manager=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="comment"># Step1: 处理输入（可加自定义逻辑，比如参数校验）</span></span><br><span class="line">        question = inputs[<span class="string">&quot;question&quot;</span>]</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> question:</span><br><span class="line">            <span class="keyword">return</span> &#123;<span class="string">&quot;answer&quot;</span>: <span class="string">&quot;请输入有效问题！&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Step2: 调用Prompt</span></span><br><span class="line">        prompt_text = <span class="variable language_">self</span>.prompt.<span class="built_in">format</span>(question=question)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Step3: 调用LLM</span></span><br><span class="line">        llm_resp = <span class="variable language_">self</span>.llm.invoke(prompt_text)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Step4: 解析输出</span></span><br><span class="line">        answer = <span class="variable language_">self</span>.output_parser.invoke(llm_resp)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> &#123;<span class="string">&quot;answer&quot;</span>: answer&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用自定义链</span></span><br><span class="line">custom_chain = CustomChain(prompt=prompt, llm=llm, output_parser=output_parser)</span><br><span class="line">result = custom_chain.invoke(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;LangChain的Chain有什么作用？&quot;</span>&#125;)</span><br><span class="line"><span class="built_in">print</span>(result[<span class="string">&quot;answer&quot;</span>])</span><br></pre></td></tr></table></figure><h4 id="三、复杂场景示例：检索增强生成（RAG）链（多组件串联）"><a href="#三、复杂场景示例：检索增强生成（RAG）链（多组件串联）" class="headerlink" title="三、复杂场景示例：检索增强生成（RAG）链（多组件串联）"></a>三、复杂场景示例：检索增强生成（RAG）链（多组件串联）</h4><p>实际开发中最常用的「RAG 链」就是多组件连接的典型，完整示例如下（覆盖加载→分割→检索→调用模型）：</p><figure class="highlight nim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> langchain_community.document_loaders <span class="keyword">import</span> <span class="type">TextLoader</span></span><br><span class="line"><span class="keyword">from</span> langchain_text_splitters <span class="keyword">import</span> <span class="type">CharacterTextSplitter</span></span><br><span class="line"><span class="keyword">from</span> langchain_openai <span class="keyword">import</span> <span class="type">OpenAIEmbeddings</span></span><br><span class="line"><span class="keyword">from</span> langchain_community.vectorstores <span class="keyword">import</span> <span class="type">FAISS</span></span><br><span class="line"><span class="keyword">from</span> langchain_core.runnables <span class="keyword">import</span> <span class="type">RunnablePassthrough</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 1. 加载并处理文档</span></span><br><span class="line">loader = <span class="type">TextLoader</span>(<span class="string">&quot;你的文档.txt&quot;</span>)  <span class="comment"># 加载本地文本</span></span><br><span class="line">docs = loader.load()</span><br><span class="line"><span class="comment"># 分割文本</span></span><br><span class="line">splitter = <span class="type">CharacterTextSplitter</span>(chunk_size=<span class="number">200</span>, chunk_overlap=<span class="number">20</span>)</span><br><span class="line">split_docs = splitter.split_documents(docs)</span><br><span class="line"><span class="comment"># 存入向量库</span></span><br><span class="line">embeddings = <span class="type">OpenAIEmbeddings</span>(api_key=<span class="string">&quot;你的API_KEY&quot;</span>)</span><br><span class="line">vector_db = <span class="type">FAISS</span>.from_documents(split_docs, embeddings)</span><br><span class="line"><span class="comment"># 构建检索器</span></span><br><span class="line">retriever = vector_db.as_retriever(k=<span class="number">2</span>)  <span class="comment"># 检索top2相关文档</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 定义带检索的Prompt</span></span><br><span class="line">rag_prompt = <span class="type">PromptTemplate</span>(</span><br><span class="line">    <span class="keyword">template</span>=<span class="string">&quot;基于以下文档回答问题：\n&#123;context&#125;\n问题：&#123;question&#125;&quot;</span>,</span><br><span class="line">    input_variables=[<span class="string">&quot;context&quot;</span>, <span class="string">&quot;question&quot;</span>]</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 串联RAG链（关键：用RunnablePassthrough处理多输入）</span></span><br><span class="line">rag_chain = (</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="string">&quot;context&quot;</span>: lambda x: retriever.invoke(x[<span class="string">&quot;question&quot;</span>]),  <span class="comment"># 检索文档作为context</span></span><br><span class="line">        <span class="string">&quot;question&quot;</span>: <span class="type">RunnablePassthrough</span>()  <span class="comment"># 透传用户问题</span></span><br><span class="line">    &#125;</span><br><span class="line">    | rag_prompt  <span class="comment"># 填充context和question</span></span><br><span class="line">    | llm         <span class="comment"># 调用模型</span></span><br><span class="line">    | output_parser  <span class="comment"># 解析输出</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 调用链</span></span><br><span class="line"><span class="built_in">result</span> = rag_chain.invoke(&#123;<span class="string">&quot;question&quot;</span>: <span class="string">&quot;文档里提到的Python学习方法有哪些？&quot;</span>&#125;)</span><br><span class="line">print(<span class="built_in">result</span>)</span><br></pre></td></tr></table></figure><p><strong>关键说明</strong>：</p><ul><li><code>RunnablePassthrough()</code>：透传输入变量（比如用户的问题）；</li><li><code>lambda x: retriever.invoke(x[&quot;question&quot;])</code>：从输入中提取 question，调用检索器得到 context；</li><li>最终给 Prompt 传入 <code>context</code>（检索结果）和 <code>question</code>（用户问题），实现多组件的联动。</li></ul><h4 id="四、解决组件连接的核心要点"><a href="#四、解决组件连接的核心要点" class="headerlink" title="四、解决组件连接的核心要点"></a>四、解决组件连接的核心要点</h4><ol><li><strong>输入输出格式匹配</strong>：</li></ol><ul><li><ul><li>前一个组件的输出必须是后一个的输入格式（比如 Prompt 输出字符串 → LLM 输入字符串）；</li><li>多输入时用「字典 + lambda/RunnablePassthrough」拆分 / 透传变量。</li></ul></li></ul><ol><li><strong>标准化组件</strong>：</li></ol><ul><li><ul><li>优先使用 LangChain 内置的 <code>Runnable</code> 组件（所有核心组件都实现了 <code>invoke</code> 方法）；</li><li>自定义组件时，实现 <code>invoke</code> 方法，保证输入输出标准化。</li></ul></li></ul><ol><li><strong>调试技巧</strong>：</li></ol><ul><li><ul><li>用 <code>chain.invoke()</code> 单步调用，查看每个组件的输出；</li><li>用 <code>chain.get_graph()</code> 可视化链的结构，检查连接是否正确。</li></ul></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;一、Chain-是什么&quot;&gt;&lt;a href=&quot;#一、Chain-是什么&quot; class=&quot;headerlink&quot; title=&quot;一、Chain 是什么&quot;&gt;&lt;/a&gt;一、Chain 是什么&lt;/h3&gt;&lt;p&gt;Chain 是 LangChain 中&lt;strong&gt;组件串联执行的</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Haspmap扩容原理剖析</title>
    <link href="https://u7u7.top/posts/e48005df.html"/>
    <id>https://u7u7.top/posts/e48005df.html</id>
    <published>2026-03-24T12:21:43.000Z</published>
    <updated>2026-03-24T17:08:18.881Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、为什么需要扩容？"><a href="#一、为什么需要扩容？" class="headerlink" title="一、为什么需要扩容？"></a>一、为什么需要扩容？</h1><p>HashMap 底层是一个 <strong>数组 + 链表/红黑树</strong> 的结构。数组长度是固定的，随着元素不断插入，哈希冲突概率增大，链表越来越长，查询效率从 O(1) 退化为 O(n)。</p><p>为了维持高效的查询性能，HashMap 在元素数量达到一定阈值后会自动进行扩容（resize）。</p><h2 id="二、核心参数"><a href="#二、核心参数" class="headerlink" title="二、核心参数"></a>二、核心参数</h2><p><img src="https://cdn.phototourl.com/free/2026-03-24-b0b045d4-47e7-4801-b4b6-785088904ef0.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><div class="table-container"><table><thead><tr><th>参数</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>DEFAULT_INITIAL_CAPACITY</code></td><td>16</td><td>初始容量，必须是 2 的幂</td></tr><tr><td><code>DEFAULT_LOAD_FACTOR</code></td><td>0.75f</td><td>负载因子</td></tr><tr><td><code>MAXIMUM_CAPACITY</code></td><td>2^30</td><td>最大容量</td></tr><tr><td><code>threshold</code></td><td>capacity × loadFactor</td><td>扩容阈值</td></tr></tbody></table></div><p><strong>扩容触发条件：</strong></p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">当前元素数量（size）&gt; threshold（容量 × 负载因子）</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><p>默认情况下：<code>16 × 0.75 = 12</code>，即插入第 13 个元素时触发扩容。</p><h2 id="三、扩容策略：容量翻倍"><a href="#三、扩容策略：容量翻倍" class="headerlink" title="三、扩容策略：容量翻倍"></a>三、扩容策略：容量翻倍</h2><p><img src="https://cdn.phototourl.com/free/2026-03-24-b7fc9751-9a0c-49d6-b886-88ca23e14082.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动">编辑</p><p>每次扩容，新数组容量 = <strong>旧容量 × 2</strong>。</p><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JDK 8 源码（resize 方法节选）</span></span><br><span class="line"><span class="built_in">int</span> newCap = oldCap &lt;&lt; <span class="number">1</span>; <span class="comment">// 左移1位 = ×2</span></span><br><span class="line"><span class="built_in">int</span> newThr = oldThr &lt;&lt; <span class="number">1</span>; <span class="comment">// 阈值也同步翻倍</span></span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><p>容量始终保持 2 的幂次方，这是 HashMap 设计的核心约束，与后续的位运算寻址密切相关。</p><h2 id="四、扩容全过程（JDK-8）"><a href="#四、扩容全过程（JDK-8）" class="headerlink" title="四、扩容全过程（JDK 8）"></a>四、扩容全过程（JDK 8）</h2><h3 id="4-1-resize-方法整体流程"><a href="#4-1-resize-方法整体流程" class="headerlink" title="4.1 resize() 方法整体流程"></a>4.1 resize() 方法整体流程</h3><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> Node&lt;K,V&gt;[] resize() &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] oldTab = <span class="keyword">table</span>;</span><br><span class="line">    <span class="keyword">int</span> oldCap = (oldTab == <span class="literal">null</span>) ? <span class="number">0</span> : oldTab<span class="variable">.length</span>;</span><br><span class="line">    <span class="keyword">int</span> oldThr = threshold;</span><br><span class="line">    <span class="keyword">int</span> newCap, newThr = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (oldCap &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 已达最大容量，不再扩容</span></span><br><span class="line">        <span class="keyword">if</span> (oldCap &gt;= MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            threshold = Integer<span class="variable">.MAX_VALUE</span>;</span><br><span class="line">            <span class="keyword">return</span> oldTab;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 正常扩容：容量和阈值均翻倍</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap &lt;&lt; <span class="number">1</span>) &lt; MAXIMUM_CAPACITY &amp;&amp;</span><br><span class="line">                 oldCap &gt;= DEFAULT_INITIAL_CAPACITY)</span><br><span class="line">            newThr = oldThr &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (oldThr &gt; <span class="number">0</span>)</span><br><span class="line">        newCap = oldThr; <span class="comment">// 使用指定初始容量</span></span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 使用默认值初始化</span></span><br><span class="line">        newCap = DEFAULT_INITIAL_CAPACITY;</span><br><span class="line">        newThr = (<span class="keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建新数组</span></span><br><span class="line">    Node&lt;K,V&gt;[] newTab = (Node&lt;K,V&gt;[])<span class="keyword">new</span> Node[newCap];</span><br><span class="line">    <span class="keyword">table</span> = newTab;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将旧数组元素迁移到新数组</span></span><br><span class="line">    <span class="comment">// ...（见下文）</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> newTab;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><h3 id="4-2-数据迁移（rehash）"><a href="#4-2-数据迁移（rehash）" class="headerlink" title="4.2 数据迁移（rehash）"></a>4.2 数据迁移（rehash）</h3><p><img src="https://cdn.phototourl.com/free/2026-03-24-c82a8d34-0a68-4190-a32f-2e52599043f4.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动">编辑</p><p>扩容后，所有元素需要重新计算在新数组中的位置。</p><p><strong>JDK 8 的优化：</strong> 利用容量是 2 的幂次这一特性，元素在新数组中的位置只有两种情况：</p><ul><li><strong>原位置不变</strong>：新增高位 bit 为 0</li><li><strong>原位置 + 旧容量</strong>：新增高位 bit 为 1</li></ul><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判断高位 bit</span></span><br><span class="line"><span class="keyword">if</span> ((e.<span class="built_in">hash</span> &amp; oldCap) == <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="comment">// 放到低位链表（原位置）</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 放到高位链表（原位置 + oldCap）</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><p>这个设计避免了重新计算 hash，极大提升了扩容效率。</p><p><strong>图示说明（以 oldCap=16 为例）：</strong></p><h2 id="五、链表与红黑树的处理"><a href="#五、链表与红黑树的处理" class="headerlink" title="五、链表与红黑树的处理"></a>五、链表与红黑树的处理</h2><p>JDK 8 中，链表长度 ≥ 8 时会转为红黑树。扩容时对两种结构分别处理：</p><h3 id="5-1-链表拆分"><a href="#5-1-链表拆分" class="headerlink" title="5.1 链表拆分"></a>5.1 链表拆分</h3><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将链表拆分为两条：lo（低位）和 hi（高位）</span></span><br><span class="line">Node&lt;K,V&gt; loHead = <span class="literal">null</span>, loTail = <span class="literal">null</span>;</span><br><span class="line">Node&lt;K,V&gt; hiHead = <span class="literal">null</span>, hiTail = <span class="literal">null</span>;</span><br><span class="line">Node&lt;K,V&gt; <span class="keyword">next</span>;</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">    <span class="keyword">next</span> = e.<span class="keyword">next</span>;</span><br><span class="line">    <span class="keyword">if</span> ((e.hash &amp; oldCap) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 低位链表</span></span><br><span class="line">        <span class="keyword">if</span> (loTail == <span class="literal">null</span>) loHead = e;</span><br><span class="line">        <span class="keyword">else</span> loTail.<span class="keyword">next</span> = e;</span><br><span class="line">        loTail = e;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 高位链表</span></span><br><span class="line">        <span class="keyword">if</span> (hiTail == <span class="literal">null</span>) hiHead = e;</span><br><span class="line">        <span class="keyword">else</span> hiTail.<span class="keyword">next</span> = e;</span><br><span class="line">        hiTail = e;</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">while</span> ((e = <span class="keyword">next</span>) != <span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (loTail != <span class="literal">null</span>) &#123; loTail.<span class="keyword">next</span> = <span class="literal">null</span>; newTab[j] = loHead; &#125;</span><br><span class="line"><span class="keyword">if</span> (hiTail != <span class="literal">null</span>) &#123; hiTail.<span class="keyword">next</span> = <span class="literal">null</span>; newTab[j + oldCap] = hiHead; &#125;</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><h3 id="5-2-红黑树拆分"><a href="#5-2-红黑树拆分" class="headerlink" title="5.2 红黑树拆分"></a>5.2 红黑树拆分</h3><p>红黑树同样按高低位拆分。拆分后：</p><ul><li>若子树节点数 ≤ 6，退化回链表（<code>untreeify</code>）</li><li>若节点数 &gt; 6，重新构建红黑树（<code>treeify</code>）</li></ul><h2 id="六、疑问"><a href="#六、疑问" class="headerlink" title="六、疑问"></a>六、疑问</h2><h3 id="JDK-7-与-JDK-8-扩容对比"><a href="#JDK-7-与-JDK-8-扩容对比" class="headerlink" title="JDK 7 与 JDK 8 扩容对比"></a>JDK 7 与 JDK 8 扩容对比</h3><div class="table-container"><table><thead><tr><th>对比项</th><th>JDK 7</th><th>JDK 8</th></tr></thead><tbody><tr><td>数据结构</td><td>数组 + 链表</td><td>数组 + 链表 + 红黑树</td></tr><tr><td>插入方式</td><td>头插法</td><td>尾插法</td></tr><tr><td>rehash 方式</td><td>重新计算 hash 位置</td><td>位运算优化，无需重新 hash</td></tr><tr><td>并发问题</td><td>头插法导致死循环</td><td>尾插法规避了死循环问题</td></tr></tbody></table></div><blockquote><p>⚠️ <strong>JDK 7 头插法的并发危险</strong>：多线程扩容时，头插法可能导致链表成环，引发 <code>get()</code> 死循环。JDK 8 改为尾插法解决了这一问题，但 HashMap 仍非线程安全，多线程场景请使用 <code>ConcurrentHashMap</code>。</p></blockquote><h3 id="负载因子为什么是-0-75？"><a href="#负载因子为什么是-0-75？" class="headerlink" title="负载因子为什么是 0.75？"></a>负载因子为什么是 0.75？</h3><p>这是空间与时间的权衡：</p><ul><li><strong>过小（如 0.5）</strong>：扩容频繁，空间浪费多，但冲突少</li><li><strong>过大（如 1.0）</strong>：内存利用率高，但冲突多，链表变长，查询慢</li><li><strong>0.75</strong>：在泊松分布下，桶中元素碰撞概率较低，综合性能最优</li></ul><p>官方注释中也提到，在理想随机 hash 下，0.75 的负载因子使得链表长度超过 8 的概率约为 0.00000006，极低。</p><h3 id="初始容量的选择？"><a href="#初始容量的选择？" class="headerlink" title="初始容量的选择？"></a>初始容量的选择？</h3><p>如果能预估元素数量，建议手动指定初始容量，避免多次扩容带来的性能损耗：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 预计存放 1000 个元素</span></span><br><span class="line"><span class="comment">// 初始容量 = 预计数量 / 负载因子 + 1</span></span><br><span class="line"><span class="built_in">int</span> initialCapacity = (<span class="built_in">int</span>)(<span class="number">1000</span> / <span class="number">0.75</span>) + <span class="number">1</span>; <span class="comment">// ≈ 1334，HashMap 会向上取2的幂 = 2048</span></span><br><span class="line"><span class="built_in">Map</span>&lt;<span class="built_in">String</span>, <span class="built_in">Object</span>&gt; map = <span class="keyword">new</span> HashMap&lt;&gt;(initialCapacity);</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><p>或者直接使用 Guava 的工具方法：</p><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Guava</span></span><br><span class="line"><span class="built_in">Map</span>&lt;<span class="built_in">String</span>, <span class="built_in">Object</span>&gt; map = Maps.newHashMapWithExpectedSize(<span class="number">1000</span>);</span><br></pre></td></tr></table></figure><p><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><h2 id="七、总结"><a href="#七、总结" class="headerlink" title="七、总结"></a>七、总结</h2><p><img src="https://cdn.phototourl.com/free/2026-03-24-ffc2f639-0297-4977-8a30-8e067a28145a.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><div class="table-container"><table><thead><tr><th>要点</th><th>说明</th></tr></thead><tbody><tr><td>触发条件</td><td><code>size &gt; capacity × loadFactor</code></td></tr><tr><td>扩容倍数</td><td>新容量 = 旧容量 × 2</td></tr><tr><td>迁移优化</td><td>高位 bit 判断，避免重新 hash</td></tr><tr><td>链表/树处理</td><td>拆分为高低位两组，按长度决定链表或树</td></tr><tr><td>线程安全</td><td>HashMap 非线程安全，并发请用 ConcurrentHashMap</td></tr></tbody></table></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、为什么需要扩容？&quot;&gt;&lt;a href=&quot;#一、为什么需要扩容？&quot; class=&quot;headerlink&quot; title=&quot;一、为什么需要扩容？&quot;&gt;&lt;/a&gt;一、为什么需要扩容？&lt;/h1&gt;&lt;p&gt;HashMap 底层是一个 &lt;strong&gt;数组 + 链表/红黑树&lt;/str</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>大模型学习基础概念</title>
    <link href="https://u7u7.top/posts/e48005e0.html"/>
    <id>https://u7u7.top/posts/e48005e0.html</id>
    <published>2026-03-24T12:18:33.000Z</published>
    <updated>2026-03-27T13:15:03.052Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一、核心原理"><a href="#一、核心原理" class="headerlink" title="一、核心原理"></a>一、核心原理</h2><h3 id="1-Transformer架构解析"><a href="#1-Transformer架构解析" class="headerlink" title="1. Transformer架构解析"></a>1. Transformer架构解析</h3><h4 id="为什么使用Transformer架构"><a href="#为什么使用Transformer架构" class="headerlink" title="为什么使用Transformer架构"></a>为什么使用Transformer架构</h4><ul><li><strong>并行处理能力</strong>：相比RNN/LSTM，Transformer可以并行处理整个序列，大幅提升训练效率</li><li><strong>长距离依赖捕获</strong>：通过自注意力机制，能够有效捕捉长距离的语义关系</li><li><strong>可扩展性强</strong>：架构设计使其能够轻松扩展到数十亿、数千亿参数规模</li></ul><h4 id="什么是Transformer架构"><a href="#什么是Transformer架构" class="headerlink" title="什么是Transformer架构"></a>什么是Transformer架构</h4><p>Transformer是一种基于<strong>自注意力机制</strong>（Self-Attention）的深度学习架构，由Google在2017年提出。其核心特点包括：</p><ul><li><strong>编码器-解码器结构</strong>：分别负责理解输入和生成输出</li><li><strong>多头注意力机制</strong>：从多个角度并行计算注意力权重</li><li><strong>位置编码</strong>：为序列中的每个位置添加位置信息</li><li><strong>前馈神经网络</strong>：对每个位置的表示进行非线性变换</li></ul><h3 id="2-大模型运行原理探索"><a href="#2-大模型运行原理探索" class="headerlink" title="2. 大模型运行原理探索"></a>2. 大模型运行原理探索</h3><h4 id="大模型如何理解和表示单词"><a href="#大模型如何理解和表示单词" class="headerlink" title="大模型如何理解和表示单词"></a>大模型如何理解和表示单词</h4><p><strong>大模型处理单元 — Token</strong></p><ul><li>Token是大模型处理文本的基本单位</li><li>一个Token可能是： </li></ul><ul><li><ul><li>一个完整的词（如”apple”）</li><li>一个词的一部分（如”running” → “run” + “ning”）</li><li>一个字符或标点符号</li></ul></li></ul><ul><li>通过<strong>分词器</strong>（Tokenizer）将文本切分成Token序列</li></ul><p><strong>单元的远序亲疏关系</strong></p><ul><li>通过<strong>词嵌入</strong>（Word Embedding）将Token转换为高维向量</li><li>向量空间中距离近的词语义相似</li><li>自注意力机制计算Token之间的关联强度，建立语义关系网络</li></ul><p><strong>大模型词义的载体和表现特征</strong></p><ul><li><strong>分布式表示</strong>：一个词的含义由整个向量表示，而非单一特征</li><li><strong>上下文依赖</strong>：同一个词在不同上下文中有不同的表示</li><li><strong>多义性捕获</strong>：模型能够根据上下文区分一词多义</li><li><strong>语义组合性</strong>：词向量可以通过组合表达复杂概念</li></ul><h4 id="大模型如何理解并预测输入人的内容"><a href="#大模型如何理解并预测输入人的内容" class="headerlink" title="大模型如何理解并预测输入人的内容"></a>大模型如何理解并预测输入人的内容</h4><p><strong>注意力机制</strong></p><ul><li><strong>作用</strong>：让模型”关注”输入序列中最相关的部分</li><li><strong>计算过程</strong>：</li></ul><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">Attention</span><span class="params">(Q, K, V)</span></span> = <span class="built_in">softmax</span>(QK^T / √d_k) V</span><br></pre></td></tr></table></figure><ul><li>Q（Query）：查询向量 - “我想找什么”</li><li>K（Key）：键向量 - “我是什么”</li><li>V（Value）：值向量 - “我的内容是什么”</li><li><strong>多头注意力</strong>：从多个子空间并行捕获不同类型的依赖关系</li></ul><p><strong>自注意力机制</strong></p><ul><li>Token之间相互计算注意力权重</li><li>每个Token都能”看到”序列中的所有其他Token</li><li>动态调整注意力分布，建立上下文理解</li></ul><p><strong>基于语义理解的内容生成</strong></p><ul><li><strong>自回归生成</strong>：逐个Token预测下一个最可能的Token</li><li><strong>概率分布采样</strong>： </li></ul><ul><li><ul><li>Greedy Decoding：选择概率最高的Token</li><li>Top-k采样：从概率最高的k个Token中随机选择</li><li>Top-p（核采样）：从累积概率达到p的Token集合中采样</li><li>Temperature调节：控制生成的随机性和创造性</li></ul></li></ul><h2 id="二、与传统机器学习的核心区别"><a href="#二、与传统机器学习的核心区别" class="headerlink" title="二、与传统机器学习的核心区别"></a>二、与传统机器学习的核心区别</h2><div class="table-container"><table><thead><tr><th>维度</th><th>传统机器学习</th><th>大模型</th></tr></thead><tbody><tr><td><strong>参数规模</strong></td><td>百万级</td><td>数十亿至数千亿级</td></tr><tr><td><strong>数据需求</strong></td><td>相对较小的标注数据</td><td>海量无标注数据 + 少量标注数据</td></tr><tr><td><strong>训练方式</strong></td><td>任务特定训练</td><td>预训练 + 微调</td></tr><tr><td><strong>泛化能力</strong></td><td>局限于训练任务</td><td>强大的零样本/少样本学习能力</td></tr><tr><td><strong>特征工程</strong></td><td>需要人工设计特征</td><td>自动学习特征表示</td></tr><tr><td><strong>理解深度</strong></td><td>模式识别</td><td>语义理解和推理</td></tr></tbody></table></div><hr>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;一、核心原理&quot;&gt;&lt;a href=&quot;#一、核心原理&quot; class=&quot;headerlink&quot; title=&quot;一、核心原理&quot;&gt;&lt;/a&gt;一、核心原理&lt;/h2&gt;&lt;h3 id=&quot;1-Transformer架构解析&quot;&gt;&lt;a href=&quot;#1-Transformer架构解析&quot; c</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>MCP的理解</title>
    <link href="https://u7u7.top/posts/afd39a7c.html"/>
    <id>https://u7u7.top/posts/afd39a7c.html</id>
    <published>2026-03-24T11:49:33.000Z</published>
    <updated>2026-03-24T17:04:09.708Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Model-Context-Protocol"><a href="#Model-Context-Protocol" class="headerlink" title="(Model Context Protocol)"></a>(Model Context Protocol)</h2><p><strong>简单比喻</strong>：MCP就像是给AI装了一套”插件系统”</p><p>想象一下，Claude本身就像一个很聪明的大脑，但它被”困”在一个对话框里。MCP就是让Claude能够”伸出手”去操作外部工具的协议。</p><p><strong>具体来说</strong>：</p><ul><li>Claude原本只能聊天，不能直接访问你的文件、数据库、API等</li><li>有了MCP，你可以给Claude连接各种”服务器”（MCP servers）</li><li>比如连接GitHub的MCP服务器后，Claude就能帮你查看代码、创建issue</li><li>连接Google Drive后，Claude就能读取、搜索你的文档</li><li>连接数据库后，Claude就能查询、分析数据</li></ul><p><strong>技术角度</strong>：这是Anthropic开发的一个标准化协议，让AI模型能够安全地调用外部工具和数据源。</p><p><strong>技术文档</strong>：<a href="https://juejin.cn/post/7604037348607082534?share_token=2684000a-90a2-44c4-9bdb-22930deaab35">https://juejin.cn/post/7604037348607082534?share_token=2684000a-90a2-44c4-9bdb-22930deaab35</a></p><h3 id="主流支持-MCP-的软件"><a href="#主流支持-MCP-的软件" class="headerlink" title="主流支持 MCP 的软件"></a>主流支持 MCP 的软件</h3><p> Claude Code - cursor - Vscode 的roo code插件- trae 等</p><h3 id="教程"><a href="#教程" class="headerlink" title="教程"></a>教程</h3><p><img src="https://cdn.phototourl.com/free/2026-03-24-f01f2442-a4e0-4344-bad6-1643e7d486e0.png" alt="img"></p><p>越多MCP也会有越多token消耗</p><p> <a href="https://smithery.ai/，在这里，搜索**报红的服务**，**更新**对应的配置">https://smithery.ai/，在这里，搜索**报红的服务**，**更新**对应的配置</a></p><p><img src="https://cdn.phototourl.com/free/2026-03-24-df04f18d-48bc-4a34-8bd8-d44130c29fe9.png" alt="img"></p><p><img src="https://cdn.nlark.com/yuque/0/2026/png/56763514/1772031819639-65dde0cc-dfc1-4f7b-8b24-e9c8411944ce.png" alt="img"></p><p><img src="https://cdn.phototourl.com/free/2026-03-24-187391fa-35f1-4896-94b2-42ba1ea8980e.png" alt="img"></p><h3 id="MCP聚合平台"><a href="#MCP聚合平台" class="headerlink" title="MCP聚合平台"></a>MCP聚合平台</h3><p><img src="https://cdn.phototourl.com/member/2026-03-24-5c7c6f75-c632-451f-bc17-2af3e8d72a31.png" alt="img"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Model-Context-Protocol&quot;&gt;&lt;a href=&quot;#Model-Context-Protocol&quot; class=&quot;headerlink&quot; title=&quot;(Model Context Protocol)&quot;&gt;&lt;/a&gt;(Model Context Pro</summary>
      
    
    
    
    
  </entry>
  
</feed>
