<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LLM on kenji.blog</title><link>http://kenji.blog/zh-cn/categories/llm/</link><description>Recent content in LLM on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/zh-cn/categories/llm/index.xml" rel="self" type="application/rss+xml"/><item><title>如何在本地环境中最快地微调 TinyLLaMA</title><link>http://kenji.blog/zh-cn/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/tinyllama-on-premises-fast-tuning-guide/</guid><description>&lt;img src="http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/img/eyecatch.jpg" alt="Featured image of post 如何在本地环境中最快地微调 TinyLLaMA" />&lt;h2 id="1-引言为什么现在选择-tinyllama-和本地部署">1. 引言：为什么现在选择 TinyLLaMA 和本地部署？
&lt;/h2>&lt;p>大型语言模型（LLM）的演进正以惊人的速度推进，随之而来的是模型参数量也持续膨胀至数千亿规模。虽然像 GPT-4 和 Claude 3 这样的超大型模型拥有无与伦比的性能，但推理和训练所需的计算成本，以及使用外部 API 时存在的安全和数据隐私隐患，成为了企业面临的巨大障碍。特别是在处理高机密性的内部数据或个人信息的业务中，从合规性（如 GDPR、APPI 等）的角度来看，将数据发送到云端公开的 LLM API 往往是不可接受的。&lt;/p>
&lt;p>因此，备受瞩目的是&lt;strong>小型语言模型（SLM: Small Language Models）&lt;strong>以及&lt;/strong>本地环境部署（On-Premises）&lt;/strong>。其中，“&lt;strong>TinyLLaMA&lt;/strong>”以仅 1.1B（11亿）参数的紧凑尺寸，却拥有在约 3 万亿 Token 的庞大数据集上预训练的底蕴，与同级别的模型相比，展现出惊人的性能。&lt;/p>
&lt;p>本文将提供一份完整指南，教你如何在本地环境（本地服务器或工作站）中，针对公司专属任务，以“最快且最高效”的方式对 TinyLLaMA 进行微调（Fine-Tuning）。从数学背景到最新的优化技术，再到具体的 PyTorch 实现代码，我们将进行全面的讲解。&lt;/p>
&lt;hr>
&lt;h2 id="2-tinyllama-的架构与特点">2. TinyLLaMA 的架构与特点
&lt;/h2>&lt;p>TinyLLaMA 沿用了 Meta 公司开发的 LLaMA（Large Language Model Meta AI）架构。在将参数量控制在 1.1B 的同时，使用了与 LLaMA 2 相同的技术栈，因此其生态系统兼容性极高。&lt;/p>
&lt;h3 id="主要架构组件">主要架构组件
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization):&lt;/strong>
省略了传统 LayerNorm 计算中减去均值的步骤，是一种提高了计算效率的归一化方法。它在保持训练稳定性的同时提升了吞吐量。&lt;/li>
&lt;li>&lt;strong>SwiGLU 激活函数:&lt;/strong>
在前馈神经网络 (FFN) 中，采用了 SwiGLU 替代传统的 ReLU 或 GELU。其数学表达式如下：
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
这里，$\otimes$ 表示逐元素乘积（Hadamard 积），Swish 函数为 $\text{Swish}(z) = z \cdot \sigma(\beta z)$。这大幅提升了模型的表达能力。&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding):&lt;/strong>
结合了绝对位置编码和相对位置编码优点的技术。即使在序列长度扩展时，也具有很高的泛化性能。&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA):&lt;/strong>
介于多头注意力 (MHA) 和多查询注意力 (MQA) 之间的一种方法，通过将键 (Key) 和值 (Value) 的注意力头进行分组，节省了内存带宽并显著提高了推理速度。&lt;/li>
&lt;/ol>
&lt;p>下面的 Mermaid 图展示了 TinyLLaMA 的整体数据流和 Transformer 块的结构。&lt;/p>
&lt;div class="mermaid">graph TD
A["输入文本 (Input Text)"] --> B["分词器 (Tokenizer, BPE)"]
B --> C["嵌入层 (Embedding Layer)"]
C --> D["Transformer 块 (TinyLLaMA 为 22 层)"]
D --> E["最终归一化 (RMSNorm, Final)"]
E --> F["线性投影 (Linear Projection, 词表大小)"]
F --> G["输出概率 (Output Probabilities, Softmax)"]
subgraph "Transformer 块结构 (Transformer Block Anatomy)"
D1["输入隐藏状态 (Input Hidden State)"] --> D2["RMSNorm"]
D2 --> D3["分组查询注意力 (Grouped Query Attention, GQA)"]
D3 --> D4["残差连接 (Residual Add)"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU 前馈网络 (SwiGLU FFN)"]
D6 --> D7["残差连接 (Residual Add)"]
D7 --> D8["输出到下一层 (Output to Next Layer)"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-微调的突破口lora-与-qlora">3. 微调的突破口：LoRA 与 QLoRA
&lt;/h2>&lt;p>在本地环境中进行全参数微调，即使是 1.1B 的模型，为了保存优化器状态和梯度，也会消耗数十 GB 的 VRAM（显存）。为了在有限资源下高效地进行训练，必须使用 &lt;strong>PEFT (Parameter-Efficient Fine-Tuning, 参数高效微调)&lt;/strong> 技术中的“&lt;strong>LoRA&lt;/strong>”及其量化扩展版本“&lt;strong>QLoRA&lt;/strong>”。&lt;/p>
&lt;h3 id="31-lora-low-rank-adaptation-的数学背景">3.1 LoRA (Low-Rank Adaptation) 的数学背景
&lt;/h3>&lt;p>LoRA 是一种将预训练权重矩阵固定（冻结），并将其权重的更新量（$\Delta W$）近似为两个低秩小矩阵乘积的方法。&lt;/p>
&lt;p>设预训练权重为 $W_0 \in \mathbb{R}^{d \times k}$。在全参数微调中，会直接更新 $W_0$ 得到 $W_0 + \Delta W$，而在 LoRA 中，更新矩阵 $\Delta W$ 被分解如下：&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>这里，$B \in \mathbb{R}^{d \times r}$，$A \in \mathbb{R}^{r \times k}$，而 $r$ 被称为秩 (Rank)，是一个超参数，它是一个非常小的值（通常为 8, 16, 32 等），满足 $r \ll \min(d, k)$。&lt;/p>
&lt;p>前向传播的计算如下所示：&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>在初始状态下，矩阵 $A$ 会以正态分布（高斯分布）随机初始化，而矩阵 $B$ 则被初始化为零矩阵。因此，训练开始时的 $\Delta W$ 为零，这样就可以在完全保留基础模型输出的状态下开始训练。&lt;/p>
&lt;div class="mermaid">graph LR
X["输入向量 x (Input Vector x)"] --> W0["冻结的预训练权重 (Frozen Pre-trained Weight, W_0)"]
X --> A["可训练的 LoRA 矩阵 A (Trainable LoRA Matrix A, r x k)"]
A --> B["可训练的 LoRA 矩阵 B (Trainable LoRA Matrix B, d x r)"]
W0 --> Add["向量加法 (Vector Addition)"]
B --> Add
Add --> Y["输出向量 h (Output Vector h)"]&lt;/div>
&lt;h3 id="32-qlora-quantized-lora-的创新性">3.2 QLoRA (Quantized LoRA) 的创新性
&lt;/h3>&lt;p>QLoRA 进一步推进了 LoRA 的方法，它将基础模型 $W_0$ 量化为 4-bit 精度（NormalFloat 4, NF4）加载到内存中。这极大地减少了 VRAM 的消耗。&lt;/p>
&lt;p>QLoRA 融入了 3 项关键技术：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>4-bit NormalFloat (NF4) 量化:&lt;/strong> 一种在理论上最优的数据类型，针对服从正态分布的权重进行了优化。&lt;/li>
&lt;li>&lt;strong>Double Quantization (双重量化):&lt;/strong> 对量化常数（缩放因子）本身再次进行量化，从而进一步节省内存。&lt;/li>
&lt;li>&lt;strong>Paged Optimizers:&lt;/strong> 利用 NVIDIA 的统一内存功能，在 VRAM 不足时，将优化器的状态暂时转移到 CPU 的 RAM 中。&lt;/li>
&lt;/ol>
&lt;p>通过这些技术，通常需要 16GB 到 24GB VRAM 的微调任务，现在即使在消费级显卡（如 RTX 3060 12GB 或 RTX 4070）上也能轻松运行。&lt;/p>
&lt;hr>
&lt;h2 id="4-本地环境的硬件要求与环境搭建">4. 本地环境的硬件要求与环境搭建
&lt;/h2>&lt;p>使用 QLoRA 微调 TinyLLaMA (1.1B) 时的硬件要求可以控制得非常低。&lt;/p>
&lt;h3 id="推荐硬件配置">推荐硬件配置
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU:&lt;/strong> NVIDIA RTX 3060 (12GB), RTX 3090/4090 (24GB), 或 NVIDIA A10G/A100 等。VRAM 最低 8GB 即可运行，但为了增大 Batch Size（批大小），推荐 12GB 以上。&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> 8 核以上的现代 CPU（Intel Core i7/i9, AMD Ryzen 7/9）&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> 32GB 以上（如果使用 Paged Optimizers，作为 VRAM 的退避空间非常重要）&lt;/li>
&lt;li>&lt;strong>存储:&lt;/strong> NVMe SSD（为了加速数据集的读取和模型的保存）&lt;/li>
&lt;/ul>
&lt;h3 id="软件环境搭建">软件环境搭建
&lt;/h3>&lt;p>以下是假设使用 Ubuntu 22.04 LTS 环境的搭建步骤。使用 Python 3.10 及以上版本。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建并激活虚拟环境&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 -m venv tinyllama_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> tinyllama_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 安装 PyTorch (适用于 CUDA 12.1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 安装 Transformer 相关的库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install transformers datasets peft trl accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="5-实现最快微调的优化技术">5. 实现最快微调的优化技术
&lt;/h2>&lt;p>仅仅运行脚本是不够的，为了“最快”地完成微调，需要结合以下优化方法。&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>标准的注意力机制的时间和空间复杂度相对于序列长度 $N$ 呈 $O(N^2)$ 增长。Flash Attention 2 通过优化 GPU 的 SRAM 和 HBM（高带宽内存）之间的内存访问，在不减少计算量的情况下消除了 I/O 瓶颈，将训练速度提升数倍，并大幅降低了内存消耗。&lt;/p>
&lt;h3 id="52-gradient-checkpointing-梯度检查点">5.2 Gradient Checkpointing (梯度检查点)
&lt;/h3>&lt;p>在前向传播计算出的中间激活值并不全部保存在 VRAM 中，而是仅保存一部分，在反向传播需要时再重新计算。虽然计算时间会增加约 20%，但可以大幅减少内存消耗，从而可以设置更大的 Batch Size，最终提升整体吞吐量。&lt;/p>
&lt;h3 id="53-mixed-precision-training-混合精度训练-与-bfloat16">5.3 Mixed Precision Training (混合精度训练) 与 Bfloat16
&lt;/h3>&lt;p>为了充分利用 GPU 的 Tensor Core，在训练时使用 &lt;code>bfloat16&lt;/code> (Brain Floating Point) 进行计算。与 &lt;code>float16&lt;/code> 相比，它的指数部分位数与 &lt;code>float32&lt;/code> 相同，因此发生上溢或下溢的风险极低，训练过程更加稳定。&lt;/p>
&lt;hr>
&lt;h2 id="6-实践tinyllama-的-qlora-微调代码">6. 实践：TinyLLaMA 的 QLoRA 微调代码
&lt;/h2>&lt;p>接下来，我们将讲解融合了上述所有优化技术的 PyTorch 脚本，以实现最快微调。这里使用 Hugging Face &lt;code>trl&lt;/code> (Transformer Reinforcement Learning) 库中的 &lt;code>SFTTrainer&lt;/code>。&lt;/p>
&lt;h3 id="61-准备数据集与加载模型">6.1 准备数据集与加载模型
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TrainingArguments&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">trl&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 指定模型和分词器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;TinyLlama/TinyLlama-1.1B-Chat-v1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. QLoRA 的 4-bit 量化配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">bnb_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_4bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_use_double_quant&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_quant_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;nf4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_compute_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span> &lt;span class="c1"># 使用 bfloat16 进行计算&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 加载模型 (启用 Flash Attention 2)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Loading model...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">quantization_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bnb_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_flash_attention_2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># 实现最快速度的关键&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 加载分词器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trust_remote_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pad_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">eos_token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">padding_side&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;right&amp;#34;&lt;/span> &lt;span class="c1"># 设置为 right 以避免 fp16/bf16 训练时的 bug&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-应用-lora-适配器与格式化数据集">6.2 应用 LoRA 适配器与格式化数据集
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. 准备 k-bit 训练并启用梯度检查点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gradient_checkpointing_enable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. LoRA 配置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">peft_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 秩&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_alpha&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 缩放因子&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_dropout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;none&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAUSAL_LM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;q_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;k_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;o_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;gate_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;up_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;down_proj&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># 以所有 Linear 层为目标可以提升性能&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">peft_config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">print_trainable_parameters&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 输出示例: trainable params: 14,286,848 || all params: 1,114,335,232 || trainable%: 1.282%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. 加载数据集 (此处以日语 Instruction 数据集为例)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 实际操作中可加载本地环境下的私有 JSONL 文件等&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">load_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kunishou/databricks-dolly-15k-ja&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">split&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;train&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">format_instruction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> 根据 ChatML 格式或提示词模板对字符串进行格式化
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_start|&amp;gt;user&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;instruction&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sample&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_end|&amp;gt;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;lt;|im_start|&amp;gt;assistant&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;output&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;lt;|im_end|&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format_instruction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-执行训练">6.3 执行训练
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. 设置训练参数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TrainingArguments&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-output&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">per_device_train_batch_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 如果 VRAM 充足可以调高&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradient_accumulation_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 实际 Batch Size = 8 * 2 = 16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">optim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;paged_adamw_32bit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 使用 Paged Optimizer 节省 VRAM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">learning_rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2e-4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fp16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bf16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 混合精度训练 (bfloat16)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_grad_norm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 用于测试的 500 步。正式训练时应指定 epoch 数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warmup_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group_by_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lr_scheduler_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. 使用 SFTTrainer 开始训练&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">train_dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">peft_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">peft_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_text_field&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_seq_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 根据预期的输入长度进行调整&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">training_args&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Starting training...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. 保存 LoRA 适配器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Training complete and model saved.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-性能评估与故障排除">7. 性能评估与故障排除
&lt;/h2>&lt;p>在本地环境中进行训练时，常遇到的问题及其解决方案如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>发生 OOM (Out Of Memory):&lt;/strong>
&lt;ul>
&lt;li>将 &lt;code>per_device_train_batch_size&lt;/code> 降至 &lt;code>1&lt;/code>。&lt;/li>
&lt;li>增加 &lt;code>gradient_accumulation_steps&lt;/code> 以维持实际的 Batch Size。&lt;/li>
&lt;li>将 &lt;code>max_seq_length&lt;/code> 从 &lt;code>2048&lt;/code> 缩短至 &lt;code>1024&lt;/code> 或 &lt;code>512&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Loss 降不下来或发生发散:&lt;/strong>
&lt;ul>
&lt;li>学习率 (&lt;code>learning_rate&lt;/code>) 可能设置过大。尝试将其从 &lt;code>2e-4&lt;/code> 降低到 &lt;code>5e-5&lt;/code> 左右。&lt;/li>
&lt;li>如果使用的是 Float16 而不是 Bfloat16，可能会发生梯度下溢。请确保设置了 &lt;code>bf16=True&lt;/code>。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>推理时生成乱码字符:&lt;/strong>
&lt;ul>
&lt;li>请确认 &lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> 是否设置正确。此外，还需检查数据集的格式（如 &lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code> 等特殊 Token）是否与基础模型预训练时保持一致。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-微调后的模型部署-deployment">8. 微调后的模型部署 (Deployment)
&lt;/h2>&lt;p>微调完成后，保存的并不是“整个基础模型”，而是仅有几 MB 到几十 MB 的“&lt;strong>LoRA 适配器（权重差值）&lt;/strong>”。为了能够高速地进行推理，需要将这个 LoRA 权重与原始的基础模型合并，并导出为一个单一的模型。&lt;/p>
&lt;h3 id="模型合并脚本">模型合并脚本
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 以 FP16/BF16 精度加载模型和适配器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">torch_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output_dir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 合并权重并保存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">merge_and_unload&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_serialization&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Model merged and saved successfully!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="使用-vllm-启动极速推理服务器">使用 vLLM 启动极速推理服务器
&lt;/h3>&lt;p>在本地环境中进行部署时，为了最大化推理速度（Tokens per second），强烈建议不使用 Hugging Face 标准的 &lt;code>pipeline&lt;/code>，而是使用 &lt;strong>vLLM&lt;/strong> 或 &lt;strong>TGI (Text Generation Inference)&lt;/strong>。vLLM 采用了 PagedAttention 技术，可防止 GPU 内存碎片化，从而大幅提升并发请求的处理能力。&lt;/p>
&lt;p>下面的 Mermaid 图展示了从训练到部署推理服务器的流程。&lt;/p>
&lt;div class="mermaid">graph TD
A["原始私有数据 (Raw Private Data)"] --> B["预处理与格式化 (Preprocessing &amp; Formatting, JSONL)"]
B --> C["QLoRA 微调 (QLoRA Fine-Tuning, SFTTrainer)"]
C --> D["LoRA 适配器权重 (LoRA Adapter Weights, .safetensors)"]
D --> E["与基础 TinyLLaMA 1.1B 合并 (Merge with Base TinyLLaMA 1.1B)"]
E --> F["合并后的模型 (Merged Model)"]
F --> G["通过 vLLM 服务器部署 (Deploy via vLLM Server)"]
G --> H["API 端点 / 用户界面 (API Endpoint / UI, 如聊天机器人)"]&lt;/div>
&lt;p>使用 vLLM 启动 API 服务器只需以下一条命令即可完成。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python -m vllm.entrypoints.openai.api_server &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model ./tinyllama-merged &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host 0.0.0.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --max-model-len &lt;span class="m">2048&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --dtype bfloat16
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样一来，就能够在本地环境中构建一个兼容 OpenAI API 的端点，让你能够安全且高速地利用本地 AI。&lt;/p>
&lt;hr>
&lt;h2 id="9-总结">9. 总结
&lt;/h2>&lt;p>本文以参数量仅 1.1B 但性能强悍的“TinyLLaMA”为对象，详细解说了在本地环境中最快且内存高效地进行微调的方法。&lt;/p>
&lt;ul>
&lt;li>借助 &lt;strong>LoRA / QLoRA&lt;/strong>，即使在消费级 GPU 上也能进行正式的 LLM 微调。&lt;/li>
&lt;li>充分利用 &lt;strong>Flash Attention 2&lt;/strong> 和 &lt;strong>Gradient Checkpointing&lt;/strong>，将训练时间和 VRAM 消耗优化到极致。&lt;/li>
&lt;li>通过使用 &lt;strong>vLLM&lt;/strong> 进行部署，在生产环境中也能实现高吞吐量。&lt;/li>
&lt;/ul>
&lt;p>在本地运行本地 LLM，不仅能保护数据的机密性，更是低成本构建特定领域（如法务、医疗、公司章程等）专用 AI 的最强武器。希望你能以此指南为参考，培养出专属于你公司的 TinyLLaMA。&lt;/p></description></item><item><title>使用C++开发小规模AI模型（如TinyLLaMA）的步骤</title><link>http://kenji.blog/zh-cn/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/cpp-small-ai-model-tinyllama-dev-guide/</guid><description>&lt;img src="http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/img/eyecatch.jpg" alt="Featured image of post 使用C++开发小规模AI模型（如TinyLLaMA）的步骤" />&lt;h1 id="使用c开发小规模ai模型如tinyllama的步骤">使用C++开发小规模AI模型（如TinyLLaMA）的步骤
&lt;/h1>&lt;p>近年来，在本地环境运行大型语言模型（LLM）的关注度急剧上升。特别是像TinyLLaMA（1.1B参数）这样的小规模模型，即使在资源有限的边缘设备或普通笔记本电脑（包括Windows环境）上，也能以实用的速度进行推理。虽然使用Python和PyTorch进行开发是主流，但在追求极致性能和内存节省时，C++和基于C语言的张量库“ggml”的组合成为了事实上的标准。&lt;/p>
&lt;p>本文将非常详细地讲解如何从零开始构建一个使用C++加载TinyLLaMA并进行文本生成的推理引擎（或者说，深入理解现有的llama.cpp内部结构）的开发步骤。&lt;/p>
&lt;hr>
&lt;h2 id="1-为什么选择c和ggml">1. 为什么选择C++和ggml？
&lt;/h2>&lt;p>在AI的训练阶段，具有灵活性和丰富生态系统的Python具有压倒性的优势。然而，在部署和“推理（Inference）”阶段，基于以下理由，C++成为了强大的选择。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>减少开销&lt;/strong>：可以完全消除Python的全局解释器锁（GIL）和运行时的开销。&lt;/li>
&lt;li>&lt;strong>内存效率和Arena分配&lt;/strong>：可以手动控制内存的分配和释放，从而防止垃圾回收带来的不可预测的峰值。&lt;/li>
&lt;li>&lt;strong>直接访问硬件&lt;/strong>：直接调用AVX-512、AVX2、ARM NEON等SIMD内置函数（Intrinsics），将CPU的运算能力发挥到极致。&lt;/li>
&lt;li>&lt;strong>零依赖&lt;/strong>：ggml是一个零依赖（Zero dependencies）的C/C++库，只要有编译器，即使在Windows上的MSVC环境中也很容易编译。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-架构全景图">2. 架构全景图
&lt;/h2>&lt;p>整个推理流水线的流程如以下Mermaid图表所示。这是一系列从用户输入文本开始，到最终生成下一个Token为止的过程。&lt;/p>
&lt;div class="mermaid">graph TD
A["用户输入文本"] --> B["BPE 分词器"]
B --> C["Token ID 数组"]
C --> D["嵌入层查找"]
D --> E["Transformer 块"]
E --> F["RMSNorm"]
F --> G["LM Head 层"]
G --> H["Logits 数组"]
H --> I["采样器模块"]
I --> J["下一个 Token ID"]
J --> K["去分词器"]
K --> L["输出文本块"]
J -.-> |"追加到上下文"| C&lt;/div>
&lt;p>由于它是自回归模型，输出的Token会再次被添加到上下文中，作为预测下一个Token的输入进行循环（图中虚线部分）。&lt;/p>
&lt;hr>
&lt;h2 id="3-模型格式与内存映射-mmap">3. 模型格式与内存映射 (mmap)
&lt;/h2>&lt;p>处理庞大的神经网络权重时，最大的障碍是磁盘I/O和内存消耗。在C++实现中，通过**内存映射（mmap）**来解决这个问题。&lt;/p>
&lt;h3 id="31-内存映射的原理及在windows中的实现">3.1 内存映射的原理及在Windows中的实现
&lt;/h3>&lt;p>使用mmap，可以将文件的内容直接映射到进程的虚拟内存空间中。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>零拷贝（Zero-copy）&lt;/strong>：数据直接从磁盘加载到内核的页面缓存中，不会发生向用户空间的多余拷贝。&lt;/li>
&lt;li>&lt;strong>按需加载（Page Fault）&lt;/strong>：只有当CPU实际访问该内存地址的瞬间，才会发生缺页中断（Page Fault），并且仅将需要的块（通常为4KB）加载到物理内存中。&lt;/li>
&lt;/ul>
&lt;p>在Windows环境中，不使用POSIX的 &lt;code>mmap&lt;/code>，而是使用Win32 API的 &lt;code>CreateFileMapping&lt;/code> 和 &lt;code>MapViewOfFile&lt;/code>。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Windows 操作系统"]
participant RAM["物理内存"]
participant App["C++ 应用程序"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "虚拟内存地址指针"
App->>App: "在指针处读取张量数据"
OS->>RAM: "缺页中断 / 从磁盘加载页面"
RAM-->>App: "数据已准备好进行 SIMD 计算"&lt;/div>
&lt;h3 id="32-gguf-格式的二进制结构">3.2 GGUF 格式的二进制结构
&lt;/h3>&lt;p>从Hugging Face等的 &lt;code>.safetensors&lt;/code> 格式转换而来的 &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> 是用于推理的终极格式。它具有以下严格的二进制布局：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Magic Bytes&lt;/strong>: &lt;code>0x46554747&lt;/code> (GGUF)。&lt;/li>
&lt;li>&lt;strong>Version&lt;/strong>: 格式的版本号。&lt;/li>
&lt;li>&lt;strong>Tensor Count &amp;amp; Metadata Count&lt;/strong>: 张量数量和元数据的键值对数量。&lt;/li>
&lt;li>&lt;strong>Metadata (Key-Value Pairs)&lt;/strong>: 带有字符串长度前缀的键和带有类型的值。&lt;/li>
&lt;li>&lt;strong>Tensor Info&lt;/strong>: 每个张量的名称、维度数、数据类型（如FP16，Q4_K等），以及在文件中的偏移位置。&lt;/li>
&lt;li>&lt;strong>Padding&lt;/strong>: 插入的填充物，用于将张量数据对齐到特定边界（通常为32字节或64字节）。这对使用SIMD指令（特别是AVX）进行高速内存访问至关重要。&lt;/li>
&lt;li>&lt;strong>Tensor Data&lt;/strong>: 已对齐的实际权重数据数组。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-tinyllama的数学基础与c算法">4. TinyLLaMA的数学基础与C++算法
&lt;/h2>&lt;p>TinyLLaMA为了提高效率，引入了一些高级的架构设计。为了在C++中正确实现这些，以下是它们的数学公式表示。&lt;/p>
&lt;h3 id="41-rmsnorm-root-mean-square-normalization">4.1 RMSNorm (Root Mean Square Normalization)
&lt;/h3>&lt;p>省略了LayerNorm中的均值中心化，仅进行方差的缩放，从而降低了计算成本。&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>其中 $d$ 是维度数，$\gamma$ 是训练好的缩放张量。
在C++中实现时，首先使用AVX2的 &lt;code>_mm256_fmadd_ps&lt;/code> 等快速计算数组的平方和，然后乘以平方根的倒数（如 &lt;code>_mm256_rsqrt_ps&lt;/code> 指令）来进行优化。&lt;/p>
&lt;h3 id="42-rope-rotary-position-embedding">4.2 RoPE (Rotary Position Embedding)
&lt;/h3>&lt;p>这是一种将Token的位置信息作为张量空间中的旋转（Rotate）来应用的技术。可以将其视为复平面上的旋转，对向量 $x$ 的相邻维度对 $(x_1, x_2)$ 应用以下旋转：&lt;/p>
$$ \text{RoPE}(x, m) = \begin{pmatrix} x_{1} \cos(m\theta) - x_{2} \sin(m\theta) \\ x_{1} \sin(m\theta) + x_{2} \cos(m\theta) \end{pmatrix} $$
&lt;p>在这里，$m$ 是Token的绝对位置索引，$\theta$ 是预先计算好的基频。在ggml中，只需在构建推理图时添加 &lt;code>ggml_rope&lt;/code> 算子即可并行执行。&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>在常规的多头注意力机制（MHA）中，Query、Key和Value各具有相同数量的头。然而，TinyLLaMA为了大幅减少内存带宽和KV缓存的消耗，采用了 &lt;strong>Grouped-Query Attention (GQA)&lt;/strong>。&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>在GQA中，多个Query头共享一个Key/Value头。在C++实现中，在执行矩阵乘法 &lt;code>ggml_mul_mat&lt;/code> 之前，需要根据Query的数量将KV张量进行广播（Broadcast）操作。&lt;/p>
&lt;h3 id="44-swiglu-激活函数">4.4 SwiGLU 激活函数
&lt;/h3>&lt;p>在前馈网络（FFN）层中，使用SwiGLU代替GELU。&lt;/p>
$$ \text{SwiGLU}(x) = \text{Swish}(x W_{\text{gate}}) \otimes (x W_{\text{up}}) $$
$$ \text{Swish}(z) = z \cdot \sigma(z) = z \cdot \frac{1}{1 + e^{-z}} $$
&lt;p>在计算图中，通过组合 &lt;code>ggml_silu&lt;/code> 算子和 &lt;code>ggml_mul&lt;/code> 来表示。&lt;/p>
&lt;hr>
&lt;h2 id="5-使用ggml构建计算图与内存管理">5. 使用ggml构建计算图与内存管理
&lt;/h2>&lt;p>ggml采用“Define-and-Run”的方法，先构建用于推理的静态计算图，然后再进行评估（evaluate）。&lt;/p>
&lt;h3 id="51-ggml_context-与-arena-分配器">5.1 ggml_context 与 Arena 分配器
&lt;/h3>&lt;p>ggml最独特的地方在于，推理循环内完全不进行动态内存分配（如 &lt;code>malloc&lt;/code> 或 &lt;code>new&lt;/code>）的“Arena 分配（Arena Allocation）”。
初始化时分配一大块连续的内存区域（Arena），每次调用 &lt;code>ggml_new_tensor&lt;/code> 等函数时，都会递增这个区域的指针。当完成一个推理步骤后，只需将分配指针重置到初始位置，即可立即完成下一个推理步骤的内存分配。&lt;/p>
&lt;h3 id="52-构建图的具体示例">5.2 构建图的具体示例
&lt;/h3>&lt;p>在每个推理步骤中，会在内存中组装如下的计算图。&lt;/p>
&lt;div class="mermaid">graph TD
A["Token 输入 ID"] --> B["嵌入查找"]
B --> C["ggml_rms_norm"]
C --> D["Q / K / V 投影"]
D --> E["ggml_rope 位置编码"]
E --> F["KV 缓存存储"]
E --> G["KV 缓存加载"]
G --> H["自注意力机制"]
H --> I["缩放与 Softmax"]
I --> J["注意力输出"]
J --> K["输出投影"]
K --> L["残差连接相加"]&lt;/div>
&lt;hr>
&lt;h2 id="6-量化-quantization-与-windows--simd-优化">6. 量化 (Quantization) 与 Windows / SIMD 优化
&lt;/h2>&lt;p>如果用FP16处理TinyLLaMA (1.1B)，大约需要2.2GB的内存，但通过4位量化（如Q4_K），可以将其大幅压缩至约600MB左右。&lt;/p>
&lt;h3 id="61-块量化架构">6.1 块量化架构
&lt;/h3>&lt;p>ggml并不是对整个张量进行统一量化，而是以“块（Block）”为单位进行的。
在 &lt;code>Q4_0&lt;/code> 格式中，将32个FP16值组合成一个块。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>缩悉因子（Scale factor）&lt;/strong>：1个FP16值（2字节）&lt;/li>
&lt;li>&lt;strong>量化数据&lt;/strong>：32个4位值（16字节）
通过这种方式，将局部异常值的影响降至最低。&lt;/li>
&lt;/ul>
&lt;h3 id="62-利用avx2加速点积运算">6.2 利用AVX2加速点积运算
&lt;/h3>&lt;p>当在Windows环境中为最新的x86 CPU进行编译时，可以利用 &lt;code>/arch:AVX2&lt;/code> 等编译器标志，SIMD处理流程如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>加载&lt;/strong>：将4位量化数据从内存加载到256位的AVX寄存器中。&lt;/li>
&lt;li>&lt;strong>展开与解包&lt;/strong>：通过位掩码和移位运算，将4位值展开为Int8或Int16。&lt;/li>
&lt;li>&lt;strong>反量化&lt;/strong>：乘以缩放因子，转换为浮点数。&lt;/li>
&lt;li>&lt;strong>FMA运算&lt;/strong>：使用激活值和 &lt;code>_mm256_fmadd_ps&lt;/code>（融合乘加）并行执行乘累加运算。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-kv缓存的实现细节">7. KV缓存的实现细节
&lt;/h2>&lt;p>在自回归生成中，为了省略计算过去Token的Key和Value，必须使用“KV缓存（KV Cache）”。&lt;/p>
&lt;p>在C++中实现时的要点如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>预先分配张量&lt;/strong>：为KV缓存初始化一个能够容纳最大上下文长度（例如：2048个Token）的巨大张量（推荐使用FP16）。&lt;/li>
&lt;li>&lt;strong>偏移复制&lt;/strong>：当执行针对Token位置 $N$ 的计算时，将该步骤中得到的K和V向量使用 &lt;code>ggml_cpy&lt;/code> 等方法存储到KV缓存张量的第 $N$ 行。&lt;/li>
&lt;li>&lt;strong>注意力计算时的视图（View）创建&lt;/strong>：在计算注意力时，创建一个仅指向从第0个到第 $N$ 个Token部分的“视图”，并将其传递给矩阵乘法。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-bpe-分词器与解码">8. BPE 分词器与解码
&lt;/h2>&lt;p>将输入字符串视为UTF-8的字节流，并与预先定义的词汇表进行匹配。在C++中，为了加速词汇表的搜索，通常会实现 &lt;strong>Trie树（前缀树）&lt;/strong> 或使用优先队列的算法。&lt;/p>
&lt;p>从LM Head输出的Logits，使用Temperature参数缩放概率，通过Top-K提取或Top-P（Nucleus Sampling）方法缩小候选范围，并使用随机数决定最终的下一个Token。&lt;/p>
&lt;hr>
&lt;h2 id="9-搭建c项目windows--powershell-环境">9. 搭建C++项目（Windows / PowerShell 环境）
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TinyLLaMACpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># 面向 Windows (MSVC) 的优化和 AVX2 标志的设置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/O2&lt;/span> &lt;span class="s">/arch:AVX2&lt;/span> &lt;span class="s">/fp:fast&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/STACK:8388608&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-O3&lt;/span> &lt;span class="s">-march=native&lt;/span> &lt;span class="s">-ffast-math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">OBJECT&lt;/span> &lt;span class="s">ggml/ggml.c&lt;/span> &lt;span class="s">ggml/ggml-alloc.c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">GGML_USE_AVX2&lt;/span> &lt;span class="s">GGML_USE_F16C&lt;/span> &lt;span class="s">GGML_USE_FMA&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">ggml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>PowerShell 中的构建命令示例：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-G&lt;/span> &lt;span class="s2">&amp;#34;Visual Studio 17 2022&amp;#34;&lt;/span> &lt;span class="n">-A&lt;/span> &lt;span class="n">x64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="10-总结">10. 总结
&lt;/h2>&lt;p>使用C++和ggml从零开始实现像TinyLLaMA这样的小规模AI模型的推理引擎，是揭开深度学习黑盒、学习底层硬件控制之美的绝佳机会。在充分体会利用内存映射进行零拷贝加载、SIMD优化、KV缓存构建等系统编程精髓的同时，让我们共同开拓边缘AI的未来。&lt;/p></description></item><item><title>【RAG入门指南】如何让本地AI读取你的专属文档</title><link>http://kenji.blog/zh-cn/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【RAG入门指南】如何让本地AI读取你的专属文档" />&lt;h1 id="前言">前言
&lt;/h1>&lt;p>近年来，大型语言模型（LLM）的进化引人瞩目，以ChatGPT和Claude等为首的众多AI已经渗透到我们的生活和工作中。然而，通用的LLM存在一个明显的弱点。那就是它们只知道“训练时的公开信息”。对于公司内部规定、个人笔记、未公开的项目资料等“私密文档”相关的问题，它们理所当然地无法回答。如果强行让它们回答，产生听起来很有道理但与事实不符的谎言（幻觉/Hallucination）的风险就会增加。&lt;/p>
&lt;p>因此，目前在全世界爆发性普及的是**RAG（Retrieval-Augmented Generation：检索增强生成）**这一技术架构。通过使用RAG，可以动态地从外部数据库向LLM提供专有知识，并让其基于这些知识生成准确且有根据的回答。&lt;/p>
&lt;p>此外，在处理企业领域或个人机密信息时，将数据发送到OpenAI等基于云的API在安全策略上往往是不被允许的。因此，我们需要构建结合了&lt;strong>本地AI&lt;/strong>（在自己的PC或本地服务器上完全运行的LLM）的“本地RAG”。&lt;/p>
&lt;p>本文将从RAG的基础理论开始，彻底解说使用Python构建本地RAG的具体实现方法、数学背景（向量检索的原理），以及使系统在生产环境中运行的高级技巧。&lt;/p>
&lt;hr>
&lt;h1 id="1-rag的整体架构">1. RAG的整体架构
&lt;/h1>&lt;p>RAG不是单一的AI模型，而是一个由多个组件协同工作的系统架构。它大致分为“摄取（数据导入）阶段”和“检索与生成（Retrieval &amp;amp; Generation）阶段”两部分。&lt;/p>
&lt;p>以下Mermaid图展示了RAG系统的全貌。&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "摄取阶段 (事前准备)"
Doc["专属文档 (PDF, TXT, etc.)"] --> Loader["文档加载器"]
Loader --> Splitter["文本分割 (分块/Chunking)"]
Splitter --> EmbedModel1["嵌入模型 (Embedding)"]
EmbedModel1 --> VectorDB["向量数据库"]
end
subgraph "推理阶段 (用户查询时)"
User["来自用户的提问 (查询/Query)"] --> EmbedModel2["嵌入模型 (Embedding)"]
EmbedModel2 --> QueryVector["查询向量"]
QueryVector --> Search["相似度检索 (向量检索)"]
VectorDB --> Search
Search --> Context["相关分块提取 (上下文/Context)"]
User --> PromptBuilder["构建提示词"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["本地LLM"]
LocalLLM --> Answer["生成最终回答"]
end&lt;/div>
&lt;h2 id="摄取阶段事前准备">摄取阶段（事前准备）
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>加载文档&lt;/strong>：读取PDF、Word、文本文件等非结构化数据。&lt;/li>
&lt;li>&lt;strong>分块（文本分割）&lt;/strong>：为了适应LLM的输入限制（上下文窗口），并提高检索精度，将长文章分割成有意义的块（Chunk）。&lt;/li>
&lt;li>&lt;strong>嵌入（向量化）&lt;/strong>：将分割好的块输入到嵌入模型（Embedding Model）中，并将其转换为数百到数千维的数值数组（向量）。&lt;/li>
&lt;li>&lt;strong>保存到数据库&lt;/strong>：将转换后的向量与原始文本数据关联，并保存到向量数据库（Vector DB）中。&lt;/li>
&lt;/ol>
&lt;h2 id="推理阶段运行时">推理阶段（运行时）
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>查询的向量化&lt;/strong>：使用与事前准备相同的嵌入模型，将用户的提问文本向量化。&lt;/li>
&lt;li>&lt;strong>相似度检索&lt;/strong>：在查询向量和数据库中的文档向量之间进行相似度计算，获取几条语义上最接近（相关性最高）的文本块。&lt;/li>
&lt;li>&lt;strong>构建提示词&lt;/strong>：将获取的相关文本作为“上下文（背景知识）”与用户的提问文本结合，创建输入给LLM的提示词（Prompt）。&lt;/li>
&lt;li>&lt;strong>生成回答&lt;/strong>：接收到增强提示词的LLM，基于附加的上下文信息生成回答。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-深入理解向量检索与嵌入embeddings">2. 深入理解向量检索与嵌入（Embeddings）
&lt;/h1>&lt;p>构成RAG核心的是“向量检索（语义检索）”。与传统的基于单词完全匹配或频率的关键字检索（如BM25）不同，向量检索基于“语义的相似性”。例如，“狗”和“小狗”、“PC”和“电脑”，即使是不同的单词，只要意思相近就会被检索到。&lt;/p>
&lt;h2 id="什么是嵌入模型embedding-model">什么是嵌入模型（Embedding Model）？
&lt;/h2>&lt;p>嵌入模型是一种神经网络，它接收自然语言文本作为输入，并输出固定长度的稠密向量（Dense Vector）。一般的模型（例如 &lt;code>text-embedding-3-small&lt;/code> 或开源的 &lt;code>multilingual-e5-large&lt;/code>）会将文本映射为384维或1024维的实数向量。&lt;/p>
&lt;p>在这个多维空间（潜在空间）中，模型被训练为：意思越相似的文章，在坐标空间上的距离越近。&lt;/p>
&lt;h2 id="相似度计算的数学背景余弦相似度">相似度计算的数学背景：余弦相似度
&lt;/h2>&lt;p>当向量数据库检索相关文档时，最常用的距离指标是&lt;strong>余弦相似度（Cosine Similarity）&lt;/strong>。与欧几里得距离（空间上的绝对距离）不同，余弦相似度关注“两个向量之间的夹角”。因为它不容易受文章长度（向量的范数）影响，所以非常适合计算文本的相似度。&lt;/p>
&lt;p>用数学公式表示，向量 $\mathbf{A}$ 和 $\mathbf{B}$ 的余弦相似度如下所示：&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ 表示内积（Dot Product）。&lt;/li>
&lt;li>$\|\mathbf{A}\|$ 表示向量 $\mathbf{A}$ 的L2范数（长度）。&lt;/li>
&lt;li>$n$ 是向量的维数。&lt;/li>
&lt;/ul>
&lt;p>余弦相似度的取值范围是 -1 到 1。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>接近 1&lt;/strong>：两个向量的方向几乎相同（意思非常相似）&lt;/li>
&lt;li>&lt;strong>接近 0&lt;/strong>：两个向量正交（无关）&lt;/li>
&lt;li>&lt;strong>接近 -1&lt;/strong>：两个向量方向完全相反（意思相反）&lt;/li>
&lt;/ul>
&lt;p>最近的向量数据库（Chroma, FAISS, Qdrant等）采用了被称为HNSW（Hierarchical Navigable Small World）的近似最近邻搜索（ANN）算法，经过优化，即使在数百万条向量数据中，也能在毫秒级内检索出余弦相似度高的文档。&lt;/p>
&lt;hr>
&lt;h1 id="3-构建本地rag的技术栈">3. 构建本地RAG的技术栈
&lt;/h1>&lt;p>为了构建完全不依赖云端的本地RAG，我们将利用开源生态系统。以下是推荐的技术栈。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>语言模型 (LLM)&lt;/strong>
&lt;ul>
&lt;li>工具：&lt;code>Ollama&lt;/code> 或 &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>模型：&lt;code>Llama-3-8B-Instruct&lt;/code>、&lt;code>Gemma-2-9B-It&lt;/code>、&lt;code>Qwen2-7B-Instruct&lt;/code> 等轻量且高性能的开源模型。对于日语任务，经过日语微调的 &lt;code>Llama-3-ELYZA-JP-8B&lt;/code> 等较为合适。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>嵌入模型 (Embedding)&lt;/strong>
&lt;ul>
&lt;li>模型：&lt;code>intfloat/multilingual-e5-large&lt;/code> 或 &lt;code>BAAI/bge-m3&lt;/code>。在本地运行时，通常从Hugging Face下载并使用Sentence-Transformers来执行。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>向量数据库 (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>：基于Python，设置极其简单。非常适合本地开发。&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>：Meta开发的高速向量检索库。&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>：规模更大，面向生产环境。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>编排框架&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>：用于连接（Chain）各组件的事实标准。&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>：特别专注于RAG的数据连接框架。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>这次我们将使用最容易导入的 &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong> 组合来进行实现。&lt;/p>
&lt;hr>
&lt;h1 id="4-实现教程使用python构建完整的本地rag">4. 实现教程：使用Python构建完整的本地RAG
&lt;/h1>&lt;p>接下来，我们将实际编写Python代码来构建本地RAG。请预先在PC上安装Ollama并在后台启动它。另外，在Ollama上拉取模型（例如：&lt;code>ollama run llama3&lt;/code>）。&lt;/p>
&lt;h2 id="step-1-安装必要的库">Step 1: 安装必要的库
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="step-2-实现代码全貌">Step 2: 实现代码全貌
&lt;/h2>&lt;p>以下是一个完整的Python脚本，用于读取PDF文件，将其向量化，并让本地LLM进行问答。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. 加载文档&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在加载文档...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 指定要读取的PDF的路径&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. 文本分块（Text Splitting）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 在不破坏文章含义的前提下，分割成适当的大小&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 每个分块的最大字符数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 前后分块的重叠字符数（防止上下文断裂）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;已分割成 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 个块。&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. 初始化嵌入模型 (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 使用支持多语言的强大模型&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在加载嵌入模型...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># 如果有GPU，使用 &amp;#39;cuda&amp;#39; 或 &amp;#39;mps&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. 构建向量数据库 (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在构建向量数据库...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 创建检索器（Retriever）。设置为获取排名前3的相关文档&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. 初始化本地LLM (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在连接本地LLM...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 必须事先通过 &amp;#39;ollama pull llama3&amp;#39; 等命令获取模型&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. 定义提示词模板&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;你是一位熟悉公司规定和内部信息的优秀助手。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">请仅使用以下提供的上下文（背景信息），用中文详细回答用户的问题。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">如果你在上下文中找不到答案，请不要随意猜测，而是诚实地回答“根据提供的信息无法得知”。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【上下文】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【问题】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【回答】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. 构建RAG链&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 设置是否返回信息源&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. 执行提问&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;请告诉我关于远程办公的交通费报销条件。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">问题: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【回答】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【参考的信息源】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- 第 &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;未知&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> 页: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="代码要点解说">代码要点解说
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
这是在自然语言分割中最被推荐的分割器。它会尝试按照段落(&lt;code>\n\n&lt;/code>)、行(&lt;code>\n&lt;/code>)、句号(&lt;code>。&lt;/code>)的顺序进行分割，在尽可能保持语义完整性的同时，使其适应指定的 &lt;code>chunk_size&lt;/code>。通过设置 &lt;code>chunk_overlap&lt;/code>，可以防止由于上下文边界被切断而导致的信息丢失。&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> 是一款非常强大的支持多语言的开源嵌入模型。你可以不使用云端API（如OpenAI的 &lt;code>text-embedding-ada-002&lt;/code> 等），在本地内存中离线地将文本向量化。&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
因为它在内存中或作为本地存储（基于SQLite）运行，所以不需要建立复杂的数据库服务器。通过指定 &lt;code>persist_directory&lt;/code>，可以在重新运行时跳过向量化过程，直接从磁盘读取数据库。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-高级rag技术advanced-rag-techniques">5. 高级RAG技术（Advanced RAG Techniques）
&lt;/h1>&lt;p>虽然使用上述教程构建的基础RAG系统（Naive RAG）也能运行，但在生产环境中如果要求很高的回答精度，就需要引入以下高级技术。&lt;/p>
&lt;h2 id="51-混合检索-hybrid-search">5.1 混合检索 (Hybrid Search)
&lt;/h2>&lt;p>向量检索擅长捕捉“语义”，但有时不擅长精确的关键字检索，比如“特定的专有名词”、“产品型号”、“员工ID”等。
因此，将基于向量检索的&lt;strong>语义检索&lt;/strong>和基于BM25算法等的&lt;strong>关键字检索&lt;/strong>并行执行，然后对两者的结果进行打分并合并（使用倒数排名融合；RRF等方法），可以大幅减少检索遗漏。&lt;/p>
&lt;h2 id="52-重排-re-ranking">5.2 重排 (Re-ranking)
&lt;/h2>&lt;p>向量检索速度很快，但它并不一定能准确评估上下文的语义契合度。为了提高检索精度，一般的管道流程如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>初次检索 (First-stage Retrieval)&lt;/strong>: 从向量数据库中，广泛而浅层地获取约20~30个相关分块。&lt;/li>
&lt;li>&lt;strong>重新评估 (Re-ranking)&lt;/strong>: 使用被称为Cross-Encoder的另一种参数量更大的机器学习模型（例如：&lt;code>bge-reranker&lt;/code> 等），输入用户查询和获取到的分块的配对，重新计算语义契合度分数。&lt;/li>
&lt;li>&lt;strong>筛选&lt;/strong>: 仅挑选出分数最高的3~5个作为最终的上下文，传递给LLM的提示词中。&lt;/li>
&lt;/ol>
&lt;p>通过这种方法，可以防止无关的噪声信息传递给LLM，从而大幅提高回答的精度（Precision）。&lt;/p>
&lt;div class="mermaid">graph LR
Query["查询"] --> VSearch["向量检索 (前20名)"]
VSearch --> Reranker["重排模型 (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["高精度的前3名"]
TopK --> LLM["LLM生成"]&lt;/div>
&lt;h2 id="53-语义分块与父文档检索">5.3 语义分块与父文档检索
&lt;/h2>&lt;p>有一种名为“Semantic Chunking”的方法，它不是按照固定字符数机械地分割文本，而是使用AI检测文章语义的转折点来进行分割。
此外，“Parent Document Retriever（父文档检索）”这种方法，在检索时以非常小的单位（如句子等）进行向量化以实现高精度的检索，而在传递给LLM时，则传递包含该句子的“原始大段落（父文档）”，从而为LLM提供充足的上下文（Context）。&lt;/p>
&lt;hr>
&lt;h1 id="6-本地rag运行时的挑战与对策">6. 本地RAG运行时的挑战与对策
&lt;/h1>&lt;p>在本地环境中构建和运行RAG时，存在一些特有的障碍。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>VRAM（显存）枯竭&lt;/strong>:
为了让本地LLM以实用的速度（每秒数十个Token）运行，必须将模型加载到GPU的VRAM中。以fp16（16位浮点数）运行8B级别的模型大约需要16GB的VRAM。但是，通过使用**量化（Quantization）**技术（如GGUF、AWQ格式等，将其压缩为4bit或8bit的技术），即使是8GB的VRAM（如一般的游戏PC等）也能足够高速地运行。Llama.cpp和Ollama原生支持这些量化格式。&lt;/li>
&lt;li>&lt;strong>上下文窗口的限制&lt;/strong>:
如果检索获取到的上下文数量过多，可能会超出LLM的输入上限（Token限制），或者导致模型忘记中间部分的信息（Lost in the middle现象）。调整提取的分块数量，以及通过上述重排技术进行严格筛选是不可或缺的。&lt;/li>
&lt;li>&lt;strong>数据的时效性管理&lt;/strong>:
当源文档被更新时，向量数据库中对应文档的向量也必须进行更新或删除（CRUD操作）。因为ChromaDB支持基于文档ID的更新，所以通过管理文件的哈希值，并编写仅同步差异的批处理程序是非常实用的做法。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="总结">总结
&lt;/h1>&lt;p>RAG（检索增强生成）是一种强大的范式，它将AI从通用的助手进化为“你的专属专家”或“专注于公司内部业务的专家”。&lt;/p>
&lt;p>我们了解到，即使在无法使用云服务、保密性要求极高的情况下，通过组合Ollama、LangChain、ChromaDB等开源生态系统，也能相对容易地构建出完全的“本地RAG”环境。&lt;/p>
&lt;p>基于本文解说的向量空间的数学原理，以及文本分块、重排等高级方法，请务必尝试使用您自己的数据来开发原创的AI系统。本地AI的进化速度非常惊人，今天构建的系统，明天只需替换为更聪明的轻量级模型，就能在瞬间完成性能的升级。&lt;/p>
&lt;hr>
&lt;p>&lt;em>本博客今后将继续发布关于AI技术和RAG的深度文章。如果您有任何问题或反馈，请务必在评论区留言。&lt;/em>&lt;/p></description></item><item><title>ChatGPT・Gemini・Claude的API深度对比！应该选择哪一个？</title><link>http://kenji.blog/zh-cn/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/chatgpt-gemini-claude-api-comparison/</guid><description>&lt;img src="http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/img/eyecatch.jpg" alt="Featured image of post ChatGPT・Gemini・Claude的API深度对比！应该选择哪一个？" />&lt;h1 id="chatgptgeminiclaude的api深度对比应该选择哪一个">ChatGPT・Gemini・Claude的API深度对比！应该选择哪一个？
&lt;/h1>&lt;p>AI技术的演进令人瞩目，尤其是在大型语言模型（LLM: Large Language Model）领域，OpenAI的ChatGPT（GPT系列）、Google的Gemini和Anthropic的Claude正在展开激烈的三足鼎立的霸权争夺。截至2026年，各家公司以几个月甚至几周为单位发布新模型和API功能，对于开发者和企业IT架构师来说，“应该将哪个API集成到产品中”这一问题，已成为左右项目成功与否的极其重要的决策。&lt;/p>
&lt;p>本文将从开发者的视角，对这三大AI提供商的API进行深度对比和解读。内容不仅限于简单的规格罗列，还将涵盖架构设计、详细的计费结构、延迟（Latency）的数学分析、使用Python和Node.js的具体实现示例，以及提示词缓存（Prompt Caching）等最新的成本优化方法。&lt;/p>
&lt;p>旨在为读者提供一份完整的指南，帮助您为自己的用例选择最合适的LLM API，并构建可扩展且成本高效的AI应用程序。&lt;/p>
&lt;hr>
&lt;h2 id="1-各llm-api的哲学与设计理念">1. 各LLM API的哲学与设计理念
&lt;/h2>&lt;p>在进行技术选型时，首先了解各家公司是基于何种理念来构建模型和API的，这一点非常重要。&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>OpenAI以“实现通用人工智能（AGI）”为使命，始终引领着行业的通用标准。它提供了适应不同用例的多样化模型，如GPT-4o、GPT-4o-mini，以及擅长推理的o1模型等。其生态系统最为成熟，无论官方还是非官方的库和文档都最为丰富。&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>Google秉承“AI First”的理念，以最大限度利用其自有基础设施（TPU网络）的可扩展性为武器。Gemini 1.5 Pro/Flash的最大特点是拥有高达200万Token的压倒性上下文窗口（Context Window），能够一次性处理超长文档或数小时的音视频。此外，它与Google Cloud (Vertex AI) 的深度集成对企业级用户也极具吸引力。&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropic是由前OpenAI成员创立的企业，采用了独特的“Constitutional AI（合宪AI）”安全机制。Claude 3.5 Sonnet和Opus凭借其强大的推理能力、代码生成能力，尤其是“像人类一样自然的对话”和“极少的幻觉（Hallucination）”，获得了众多开发者的狂热支持。&lt;/p>
&lt;hr>
&lt;h2 id="2-模型家族的规格深度对比">2. 模型家族的规格深度对比
&lt;/h2>&lt;p>以下是截至2026年主力模型的规格对比。&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>提供商&lt;/th>
&lt;th>主力模型&lt;/th>
&lt;th>最大上下文长度&lt;/th>
&lt;th>主要优势&lt;/th>
&lt;th>推荐用例&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>GPT-4o&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>速度、视觉识别、语音支持&lt;/td>
&lt;td>交互式应用、通用任务&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>o1-preview&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>高级逻辑推理、数学、编程&lt;/td>
&lt;td>复杂算法生成、研究用途&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Pro&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>超长文本处理、多模态（视频・音频）&lt;/td>
&lt;td>庞大代码库分析、视频摘要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Flash&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>低延迟、高吞吐量、绝对的低成本&lt;/td>
&lt;td>实时处理、海量数据批量处理&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Sonnet&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>编程能力、自然的文本生成&lt;/td>
&lt;td>软件开发辅助、高级客户支持&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Haiku&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>超极速响应、高性价比&lt;/td>
&lt;td>边缘AI、实时聊天机器人&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-架构深度剖析api请求的幕后">3. 架构深度剖析：API请求的幕后
&lt;/h2>&lt;p>调用LLM的API时，后端究竟在进行怎样的处理？为了优化性能，我们必须了解这一架构。&lt;/p>
&lt;p>以下的Mermaid图表展示了从客户端发送API请求到以流式（Streaming）返回Token的全过程。&lt;/p>
&lt;div class="mermaid">graph TD
A["客户端应用程序"] -->|HTTP/REST 或 gRPC| B["API网关"]
B --> C["负载均衡器"]
C --> D["推理集群"]
D --> E["分词器 (BPE / SentencePiece)"]
E --> F["KV缓存与注意力机制"]
F --> G["Transformer块 (前向传播)"]
G --> H["输出层 (Logits)"]
H --> I["采样器 (Temperature, Top-p, Top-k)"]
I --> J["反分词器"]
J -->|流式响应 (Chunk)| A&lt;/div>
&lt;h3 id="31-tokenization分词的算法">3.1 Tokenization（分词）的算法
&lt;/h3>&lt;p>输入到API的文本，在内部会被分割为名为“Token”的单位。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: 采用 Byte-Pair Encoding (BPE)。特别是在英语方面压缩效率极高，但在日语等非字母语言中，Token数量往往会膨胀。&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: 采用 SentencePiece（Unigram Language Model）。在多语言处理上具有优势，即使是日语文本也能以相对较少的Token数量来表达。&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: 使用定制版的BPE。强化了多语言支持，Claude 3以后，日语的Token效率也得到了大幅提升。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-延迟与性能的数学分析">4. 延迟与性能的数学分析
&lt;/h2>&lt;p>在实时应用程序中，延迟（Latency）直接影响用户体验（UX）。LLM API的延迟 $T_{total}$ 在数学上可以建立如下模型：&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>这里，各变量的含义如下：&lt;/p>
&lt;ul>
&lt;li>$T_{network}$: 网络的往返时间（RTT）。&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token): 生成第一个字符所需的时间。它很大程度上取决于注意力计算的成本，该成本与提示词长度（输入Token数）的平方成正比。&lt;/li>
&lt;li>$N$: 输出的Token总数。&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): 每个Token的生成时间。因为是自回归（Autoregressive）模型，所以需要依赖之前的输出进行串行计算。&lt;/li>
&lt;/ul>
&lt;h3 id="41-自注意力机制的计算复杂度">4.1 自注意力机制的计算复杂度
&lt;/h3>&lt;p>Transformer架构中的自注意力（Self-Attention）计算复杂度，相对于输入序列长度 $L$ 呈二次函数增长。&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>这里的 $d$ 是嵌入向量的维度数。由于这一限制，通常当提示词变长时，$T_{TTFT}$ 会急剧恶化。
然而，Google的Gemini 1.5采用了“Ring Attention”和“Block-wise Compute”等创新性的优化架构，即使输入200万Token的长文，也能成功在现实的时间内（几秒到几十秒）生成第一个Token。&lt;/p>
&lt;hr>
&lt;h2 id="5-计费体系与成本优化策略">5. 计费体系与成本优化策略
&lt;/h2>&lt;p>API的成本基本上是基于输入Token数和输出Token数来计算的。&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>但是，最新的API引入了大幅降低成本的新机制。&lt;/p>
&lt;h3 id="51-提示词缓存-prompt-caching">5.1 提示词缓存 (Prompt Caching)
&lt;/h3>&lt;p>如果每次都发送超长的系统提示词或通过RAG检索到的大量文档，将会产生巨大的成本。为了解决这个问题，各家都提供了缓存功能。&lt;/p>
&lt;p>在Anthropic (Claude) 和 Google (Gemini) 中，通过缓存特定的文本块，可以大幅削减（最高达90%）输入成本。&lt;/p>
&lt;p>使用缓存时的成本模型如下：&lt;/p>
$$ Cost_{cached} = (Tokens_{cache\_write} \times Rate_{cache\_write}) + (Tokens_{cache\_read} \times Rate_{cache\_read}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>这里的 $Rate_{cache\_read}$ 通常被设定为常规 $Rate_{in}$ 的10%〜25%左右。借此，我们可以将数万行的代码库始终作为背景知识保留，并以低廉的价格运营聊天机器人。&lt;/p>
&lt;h3 id="52-批处理api-batch-api">5.2 批处理API (Batch API)
&lt;/h3>&lt;p>对于不需要实时性的任务（如日志分析、海量数据分类等），OpenAI和Anthropic提供了批处理（Batch）API。您可以将请求打包发送，并在24小时内接收结果，作为交换，可以享受到正常API价格一半（50%折扣）的优惠。这是一种非常强大的机制。&lt;/p>
&lt;hr>
&lt;h2 id="6-开发者体验dx与sdk对比">6. 开发者体验（DX）与SDK对比
&lt;/h2>&lt;p>从开发效率的角度，我们来对比各家提供的SDK（Software Development Kit）。&lt;/p>
&lt;h3 id="61-openai-api">6.1 OpenAI API
&lt;/h3>&lt;p>使用最广泛，第三方库（LangChain, LlamaIndex等）的支持也最快。此外，通过Structured Outputs（结构化输出）功能，保证了返回的响应能够100%精准地遵循JSON Schema，这使得系统集成变得异常简单。&lt;/p>
&lt;h3 id="62-anthropic-api-claude">6.2 Anthropic API (Claude)
&lt;/h3>&lt;p>SDK接口经过精心设计，TypeScript的类型定义等备受好评，非常易于使用。特别是Message API的结构非常直观，即使是包含多张图片的多模态请求也能通过简单的代码实现。&lt;/p>
&lt;h3 id="63-google-gemini-api">6.3 Google Gemini API
&lt;/h3>&lt;p>存在通过Google Cloud Vertex AI访问和通过AI Studio访问（Google Gen AI SDK）这两种途径，初学者可能会感到有些困惑。然而，面向企业的Vertex AI SDK与GCP的IAM（身份与访问管理系统）完全集成，可以构建出高度安全的开发环境。&lt;/p>
&lt;hr>
&lt;h2 id="7-实战使用python实现多个api的集成测试">7. 实战！使用Python实现多个API的集成测试
&lt;/h2>&lt;p>在这里，我们将使用Python编写一个脚本，同时向OpenAI、Anthropic和Gemini这三个API发送异步请求，以比较它们的延迟。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">google.generativeai&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">genai&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 初始化客户端&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">openai_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">anthropic_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ANTHROPIC_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;GEMINI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;请为初学者通俗易懂地讲解量子计算机的基础，以及它对现有密码技术的影响。&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_openai&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">openai_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;OpenAI (GPT-4o)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_anthropic&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">anthropic_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;claude-3-5-sonnet-20240620&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Anthropic (Claude 3.5 Sonnet)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_gemini&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenerativeModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gemini-1.5-pro&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 使用Gemini Python SDK的异步方法&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_content_async&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Google (Gemini 1.5 Pro)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;正在向各LLM API发送请求...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 并行执行3个API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gather&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_openai&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_anthropic&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_gemini&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">latency&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;--- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Latency: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> seconds&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Response (Excerpt): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>通过运行这个脚本，可以很容易地测试出在实际网络环境中，哪个模型能够最快地（即 $T_{total}$ 最小化）做出响应。&lt;/p>
&lt;hr>
&lt;h2 id="8-使用nodejs实现工具调用tool-calling--function-calling">8. 使用Node.js实现工具调用（Tool Calling / Function Calling）
&lt;/h2>&lt;p>为了让LLM不仅是一个聊天机器人，而是作为一个能与外部系统协同工作的“AI智能体（AI Agent）”发挥作用，工具调用（Tool Calling 或 Function Calling）是必不可少的。以下是使用Node.js（TypeScript）让OpenAI的API调用天气API的示例。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">OpenAI&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;openai&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">openai&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">OpenAI&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.OPENAI_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runAgent() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get_weather&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;获取指定城市的当前天气。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parameters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;object&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">properties&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;城市名称（例如：东京，纽约）&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;location&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">openai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">completions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">model&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">role&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;今天东京的天气怎么样？需要带伞吗？&amp;#34;&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tools&lt;/span>: &lt;span class="kt">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tool_choice&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">toolCall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`LLM请求了工具调用: 函数名 = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`参数: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 在这里实现调用实际天气API（例：OpenWeatherMap）的处理
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// const weather = await fetchWeatherFromAPI(args.location);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 将获取到的结果再次传递给LLM，以生成最终的回答
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">runAgent&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Claude 3.5 Sonnet和Gemini 1.5 Pro也具备同等的Tool Calling功能，虽然在Schema的定义方法上略有不同，但基本流程是相通的。&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-vs-超长上下文窗口应该采用哪一个">9. RAG vs 超长上下文窗口：应该采用哪一个？
&lt;/h2>&lt;p>目前，企业级AI架构中最大的争论之一是：“为了引入外部知识，应该使用RAG（检索增强生成），还是应该全权交给庞大的上下文窗口（Long Context）？”&lt;/p>
&lt;h3 id="rag-retrieval-augmented-generation-的优势与挑战">RAG (Retrieval-Augmented Generation) 的优势与挑战
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>优势&lt;/strong>: 成本低（因为只将需要的块放入提示词中），回答的依据（来源）容易确定。&lt;/li>
&lt;li>&lt;strong>挑战&lt;/strong>: 依赖于语义搜索的准确度，因此对于那些上下文散落在多篇文档中的高级推理任务（例：“根据去年全年的会议记录，按时间序列分析A项目延期的根本原因”），表现并不理想。&lt;/li>
&lt;/ul>
&lt;h3 id="超长上下文-例如gemini-15-pro的200万token">超长上下文 (例如Gemini 1.5 Pro的200万Token)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>优势&lt;/strong>: 不会因为检索而遗漏信息。在“大海捞针（Needle In A Haystack: NIAH）”测试中，Gemini 1.5 Pro和Claude 3.5 Sonnet也能以99%以上的准确率提取信息。&lt;/li>
&lt;li>&lt;strong>挑战&lt;/strong>: Token消耗量巨大，导致成本增加，延迟（$T_{TTFT}$）也会增加。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>结论&lt;/strong>: 2026年的最佳实践是**“混合方案（Hybrid Approach）”**。日常的问答使用基于向量数据库的RAG，而在需要复杂分析或整体代码审查的特定任务中，则采用结合提示词缓存（Prompt Caching）的超长上下文设计，这已成为主流。&lt;/p>
&lt;hr>
&lt;h2 id="10-多模态处理能力对比">10. 多模态处理能力对比
&lt;/h2>&lt;p>下一代的AI应用程序，不仅需要理解文本，还被要求具备直接理解图像、语音和视频的能力。&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "用户"
participant Client as "前端应用"
participant API as "LLM API (多模态)"
User->>Client: 上传视频和文本提示
Client->>API: 发送视频字节/URI + 文本
Note over API: 视频分块与音频分离
Note over API: 多模态嵌入模型
API-->>Client: 返回文本摘要和时间戳
Client-->>User: 显示分析结果&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong>: 图像识别精度极高，在读取手绘草图或复杂图表方面表现优异。此外，利用Realtime API实现超低延迟（几百毫秒级别）的原生语音对话也十分强大。&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong>: &lt;strong>在视频分析领域压倒性领先。&lt;/strong> 可以直接输入1小时的视频文件（视频帧+音频），并针对“12分45秒时屏幕右侧边缘的人物手里拿的资料标题是什么？”这类精准问题给出回答。&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong>: 图像识别（Vision）能力与GPT-4o处于同一水平，非常出色。在前端开发辅助方面（例如，递交UI截图并要求“生成该页面的React组件代码”），它展现出无可匹敌的优势。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-企业级安全与合规性">11. 企业级安全与合规性
&lt;/h2>&lt;p>当企业在生产环境中使用LLM API时，最担心的往往是“我们公司的数据会不会被用来训练AI”以及“是否满足合规性要求”。&lt;/p>
&lt;p>这三家公司都明确表示，通过API发送的数据（提示词和响应）&lt;strong>不会被用于模型训练（Zero Data Retention / No Training on Customer Data）&lt;/strong>（※请注意，面向消费者的免费Web聊天界面除外）。&lt;/p>
&lt;p>如果需要更高的安全级别：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong>: 通过Azure OpenAI Service，可以利用Microsoft企业级的安全保障、SLA，以及基于Azure Private Link的内网专线连接。&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong>: 通过Google Cloud Vertex AI，可以使用VPC Service Controls实现严格的网络隔离，以及CMEK（客户管理加密密钥）进行数据保护。&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong>: 通过AWS Bedrock或Google Cloud Vertex AI使用，可以依托云服务提供商坚固的安全基础设施。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-结论按用例划分的终极选择指南">12. 结论：按用例划分的终极选择指南
&lt;/h2>&lt;p>通过多角度的对比，针对“究竟应该选哪一个？”的最终结论，其实因用例而异。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>复杂的软件开发・代码生成・高级推理&lt;/strong>:
&lt;strong>👑 胜者: Claude 3.5 Sonnet (Anthropic)&lt;/strong>
在代码的上下文理解、重构以及生成自然像人类一样的文本方面，它目前展现出最佳的性能。API的易用性以及提示词缓存带来的成本效益也同样出类拔萃。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>超长文档分析・视频/音频的批量处理&lt;/strong>:
&lt;strong>👑 胜者: Gemini 1.5 Pro (Google)&lt;/strong>
200万Token的上下文窗口是它独一无二的武器。无论是解析几百页的PDF手册，还是对长时间会议录像进行摘要，这类需要把握数据全貌的任务，没有任何模型能出其右。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>通用性・执行速度・稳定的结构化输出（JSON）&lt;/strong>:
&lt;strong>👑 胜者: GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
它能圆满完成各种任务，而且对第三方工具的支持也最为丰富。在需要使用Structured Outputs进行绝对可靠的JSON解析，或者需要使用o1模型进行超高级逻辑推理时，OpenAI生态系统是不可或缺的。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="多模型路由推荐">多模型路由推荐
&lt;/h3>&lt;p>在未来，不应仅仅依赖单一的API（避免供应商锁定），而是根据任务的难度和重要程度动态切换模型的**“LLM路由（LLM Routing）”**架构将成为趋势。
例如，面对用户的简单提问，可以用便宜且高速的 &lt;code>GPT-4o-mini&lt;/code> 或 &lt;code>Gemini 1.5 Flash&lt;/code> 来回答；只有当系统判断需要进行复杂处理时，才将任务回退给 &lt;code>Claude 3.5 Sonnet&lt;/code>。如此一来，便能在成本与性能之间取得最佳平衡。&lt;/p>
&lt;p>AI的进化永不停息。请深入了解各API的优劣势和架构特性，从而构建出灵活且可扩展的AI应用程序。&lt;/p></description></item><item><title>llama.cpp使用方法与C++定制入门</title><link>http://kenji.blog/zh-cn/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/llama-cpp-cxx-customization/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-cxx-customization/img/eyecatch.jpg" alt="Featured image of post llama.cpp使用方法与C++定制入门" />&lt;p>近年来，大型语言模型（LLM）的进化非常迅猛，其应用范围每天都在扩大。然而，要在本地环境中运行拥有数十亿、数百亿参数的模型，通常需要配备海量显存的高端GPU。打破这种“硬件壁垒”，让在普通PC、Mac甚至像Raspberry Pi这样的设备上进行LLM的实用推理成为可能的，就是 &lt;strong>llama.cpp&lt;/strong>。&lt;/p>
&lt;p>本文不仅将介绍仅仅作为命令行工具的使用方法，还将面向工程师极其详细地解析其底层技术 &lt;code>ggml&lt;/code> 的架构、Transformer与量化的数学背景，以及如何利用 C++ API 将 LLM 嵌入到自有应用程序并进行定制。&lt;/p>
&lt;hr>
&lt;h2 id="1-llamacpp-与-ggml-概述">1. llama.cpp 与 ggml 概述
&lt;/h2>&lt;p>&lt;code>llama.cpp&lt;/code> 是由 Georgi Gerganov 开发的、使用 C/C++ 编写的轻量级 LLM 推理引擎。它最初的目的是为了让 Meta 的 LLaMA 模型在 Apple Silicon (M1/M2 Mac) 上高速运行，但现在已支持各种架构和模型。&lt;/p>
&lt;p>它最大的特点在于&lt;strong>它是纯粹的 C/C++ 实现，没有外部依赖&lt;/strong>。由于不需要 Python 或 PyTorch 这种庞大的生态系统，它可以编译为单个可执行文件，因此部署极其简单。&lt;/p>
&lt;p>作为 &lt;code>llama.cpp&lt;/code> 核心的是张量运算库 &lt;strong>ggml&lt;/strong>。ggml 是从零开始设计的，旨在将机器学习中的矩阵运算在 CPU（以及部分 GPU）上优化到极致。&lt;/p>
&lt;h3 id="11-为什么-llamacpp-这么快">1.1 为什么 llama.cpp 这么快？
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>利用内存映射 (mmap)&lt;/strong>：在将模型权重加载到内存时，利用操作系统的 &lt;code>mmap&lt;/code>，可以避免全部加载到 RAM 中，实现快速启动并节省内存。&lt;/li>
&lt;li>&lt;strong>彻底的 SIMD 指令优化&lt;/strong>：利用 AVX2, AVX-512, ARM NEON, Apple AMX 等 CPU 特有的指令集，实现了矩阵乘法的超高速化。&lt;/li>
&lt;li>&lt;strong>量化 (Quantization)&lt;/strong>：将 16-bit 浮点数 (FP16) 的权重压缩为 4-bit, 5-bit, 8-bit 的整数，从而消除内存带宽的瓶颈（详情后述）。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-数学背景-transformer-与量化-quantization">2. 数学背景: Transformer 与量化 (Quantization)
&lt;/h2>&lt;p>为了深入理解 llama.cpp，有必要了解它计算的数学公式，以及它是如何对计算进行近似的。&lt;/p>
&lt;h3 id="21-transformer-的推理过程">2.1 Transformer 的推理过程
&lt;/h3>&lt;p>LLaMA 等模型采用了自回归 (Auto-regressive) 的 Transformer 解码器架构。文本生成的核心是 &lt;strong>Self-Attention&lt;/strong>（自注意力）机制。&lt;/p>
&lt;p>对于作为输入的隐藏状态矩阵 $X \in \mathbb{R}^{N \times d}$，Query $Q$、Key $K$、Value $V$ 是通过与权重矩阵相乘计算得出的。&lt;/p>
$$
Q = X W_Q, \quad K = X W_K, \quad V = X W_V
$$
&lt;p>这里，Attention 的输出定义如下：&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>在 llama.cpp 的推理循环中，成为瓶颈的是这些巨大的矩阵 $W_Q, W_K, W_V$ 以及前馈神经网络 (FFN) 的权重矩阵与向量 $X$（在生成阶段因为一次只处理一个 Token，所以 $N=1$）的乘积，也就是 &lt;strong>GEMV (General Matrix-Vector Multiplication)&lt;/strong>。&lt;/p>
&lt;h3 id="22-量化-quantization-的数学基础">2.2 量化 (Quantization) 的数学基础
&lt;/h3>&lt;p>在内存访问带宽成为瓶颈的推理过程中，使用较少位数来表示权重参数的量化是必不可少的。下面说明在 llama.cpp 中被广泛使用的块级量化（例如 &lt;code>Q4_K&lt;/code> 或 &lt;code>Q4_0&lt;/code>）的基本原理。&lt;/p>
&lt;p>例如，考虑 FP16 权重矩阵 $W$ 的一部分，长度为 $B$（通常为 32 或 64）的块 $w = [w_1, w_2, \dots, w_B]$。我们将这个块近似表示为一个 4-bit 整数 $q_i \in [-8, 7]$ 与一个单一的缩放因子 $\Delta$（FP16 或 FP32）的乘积。&lt;/p>
$$
w_i \approx \Delta \times q_i
$$
&lt;p>$\Delta$ 是根据块内的最大绝对值决定的。&lt;/p>
$$
\Delta = \frac{\max_i |w_i|}{7}
$$
&lt;p>使用量化后的权重计算点积 $y = w \cdot x$ 时，将输入向量 $x$ 也进行同样的量化 $x_i \approx \Delta_x \times q_{x, i}$，可得：&lt;/p>
$$
y = \sum_{i=1}^{B} w_i x_i \approx \Delta \Delta_x \sum_{i=1}^{B} q_i q_{x, i}
$$
&lt;p>这其中的 $\sum q_i q_{x, i}$ 部分就变成了&lt;strong>纯粹的整数运算&lt;/strong>，可以使用 SIMD 指令非常高速地进行并行计算。这就是 llama.cpp 在 CPU 上创造出惊人速度的数学奥秘。&lt;/p>
&lt;hr>
&lt;h2 id="3-架构与推理流程">3. 架构与推理流程
&lt;/h2>&lt;p>为了理解 llama.cpp 的内部运作方式，下面的 Mermaid 图表展示了整个系统的架构和数据流向。&lt;/p>
&lt;div class="mermaid">graph TD
A["用户输入 (String)"] --> B["llama.cpp Tokenizer"]
B --> C["Token IDs (int32 array)"]
C --> D["上下文缓冲区 (KV Cache)"]
D --> E["ggml 计算图"]
E --> F["Transformer 层"]
subgraph "ggml 引擎"
F --> G["Self-Attention (RoPE)"]
G --> H["前馈神经网络 (Feed Forward Network)"]
H --> F
end
F --> I["Logits (词表大小)"]
I --> J["采样器 (Temperature, Top-K, Top-P)"]
J --> K["选中的 Token ID"]
K --> L["llama.cpp Detokenizer"]
L --> M["输出字符串"]
K -. "自回归循环 (Auto-regressive loop)" .-> D&lt;/div>
&lt;p>文本生成是一个自回归的循环，每输出一个 Token，它就会作为下一个输入被添加到 KV Cache 中，并再次穿过计算图。&lt;/p>
&lt;hr>
&lt;h2 id="4-环境搭建与编译方法">4. 环境搭建与编译方法
&lt;/h2>&lt;p>在将 llama.cpp 集成到 C++ 项目中之前，让我们先尝试编译其源代码。&lt;/p>
&lt;h3 id="41-克隆仓库">4.1 克隆仓库
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> llama.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="42-使用-cmake-编译">4.2 使用 CMake 编译
&lt;/h3>&lt;p>将其作为 C++ 项目集成到其他应用中时，使用 CMake 是最标准的方式。通过启用各平台的加速器（后端），可以加速计算。&lt;/p>
&lt;p>&lt;strong>仅 CPU（基本编译）:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>使用 NVIDIA GPU (CUDA) 时:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_CUDA&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>使用 Apple Silicon (Metal) 时:&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_METAL&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>编译成功后，在 &lt;code>build/bin/&lt;/code> 目录下将生成 &lt;code>llama-cli&lt;/code> 等可执行文件，以及用于在后文所述的 C++ API 中链接的 &lt;code>llama&lt;/code> 库（以及 &lt;code>ggml&lt;/code> 库）。&lt;/p>
&lt;hr>
&lt;h2 id="5-c-定制入门-使用-llamacpp-api">5. C++ 定制入门: 使用 llama.cpp API
&lt;/h2>&lt;p>接下来，我们将讲解本文的主题，即如何通过 C++ 代码控制 llama.cpp。
如果你不仅想使用命令行工具，还想将 LLM 嵌入到你自己的应用程序（例如游戏引擎、桌面应用、嵌入式系统等）中，就需要直接调用 C++ API。&lt;/p>
&lt;p>llama.cpp 主要通过一个名为 &lt;code>llama.h&lt;/code> 的头文件提供 C 语言接口。在 C++ 中调用时，也使用此接口。&lt;/p>
&lt;h3 id="51-必要的包含与配置">5.1 必要的包含与配置
&lt;/h3>&lt;p>在自己的项目中使用 llama.cpp 时，包含以下内容：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;llama.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 错误处理宏
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define LLAMA_ASSERT(x) \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> do { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> if (!(x)) { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::cerr &amp;lt;&amp;lt; &amp;#34;Assertion failed: &amp;#34; &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; std::endl; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::terminate(); \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } while (0)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-加载模型与初始化上下文">5.2 加载模型与初始化上下文
&lt;/h3>&lt;p>首先，加载 &lt;code>.gguf&lt;/code> 格式的模型文件，并为推理分配上下文（内存空间和 KV 缓存）。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">argc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">argc&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Usage: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; &amp;lt;model.gguf&amp;gt;&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">model_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. 初始化后端（配置 CPU/GPU 等环境）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_backend_init&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. 获取模型参数的默认设置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model_params&lt;/span> &lt;span class="n">model_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_model_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_gpu_layers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">35&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 卸载到 GPU 的层数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. 加载模型
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_load_model_from_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">model_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to load model&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. 设置上下文参数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context_params&lt;/span> &lt;span class="n">ctx_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_context_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2048&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 最大上下文大小 (Token数)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_threads&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 推理使用的 CPU 线程数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. 创建上下文
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_new_context_with_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to create context&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Model and context loaded successfully!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 后续处理
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-提示词的分词-tokenization">5.3 提示词的分词 (Tokenization)
&lt;/h3>&lt;p>LLM 并不能直接理解文本，而是将其作为整数 ID（Token）的序列进行处理。需要将输入字符串转换为 Token。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Q: 日本的首都是哪里？&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">A:&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 预留余量的缓冲区大小
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 是否在开头添加特殊 Token（如 BOS: Begin of Sequence 等）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">add_special&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 将字符串转换为 Token ID 数组
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_tokenize&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add_special&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">false&lt;/span> &lt;span class="c1">// parse_special
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 缓冲区不足时需要重新分配并重试（为简化省略）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to tokenize prompt&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="54-推理循环与采样">5.4 推理循环与采样
&lt;/h3>&lt;p>将 Token 输入模型，获取下一个 Token 的概率分布 (Logits)，然后从中进行采样以决定下一个 Token，构建这样的循环。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 要生成的最大 Token 数
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 初始化用于批处理评估的结构体
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch&lt;/span> &lt;span class="n">batch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_batch_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">512&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 将提示词的 Token 添加到批次中
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 设置为仅在提示词的最后一个 Token 处输出 Logit（预测结果）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 首次评估（将提示词送入模型）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;llama_decode() failed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 当前上下文长度
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">Output: &amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 初始化采样器上下文（设置 Temperature, Top-K, Top-P 等）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">smpl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_chain_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llama_sampler_chain_default_params&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_k&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.9f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_temp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.7f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_dist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 随机种子
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_decode&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. 采样：根据当前上下文预测下一个 Token
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_token&lt;/span> &lt;span class="n">new_token_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_sample&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. 如果 Token 是 EOS (End of Sequence)，则结束循环
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token_is_eog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. 将 Token 解码为字符串（文本）并显示
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_chars&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_token_to_piece&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_chars&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_chars&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">flush&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. 将新生成的 Token 作为下一个批次准备就绪
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch_clear&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_cur&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. 模型评估（更新 KV 缓存，预测下一个 Token）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to evaluate&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 清理资源
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_backend_free&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这段代码是使用 llama.cpp 的基本 API 实现的自定义推理循环。
它使用 &lt;code>llama_batch&lt;/code> 结构体来管理 Token 群组，并通过 &lt;code>llama_decode&lt;/code> 执行神经网络的前向传播 (Forward Pass)。&lt;/p>
&lt;hr>
&lt;h2 id="6-高级定制案例-使用-c-操作-logit-与控制惩罚">6. 高级定制案例: 使用 C++ 操作 Logit 与控制惩罚
&lt;/h2>&lt;p>如果不仅仅停留在简单的文本生成，而是想强制其输出特定格式（例如仅限 JSON），或者防止其输出特定的违禁词，可以在 C++ 端直接在采样前操作 &lt;strong>Logits&lt;/strong>。&lt;/p>
&lt;p>可以获取模型输出每个 Token 之前的原始分数（在转换为概率之前的值）数组。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 推理刚刚结束，在进行采样前获取原始的 logit 数组
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">logits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_get_logits_ith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n_vocab&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_n_vocab&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 违禁 Token 的 ID 列表（例如 1234, 5678）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5678&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 将违禁 Token 的出现概率降为 0（将 Logit 设置为负无穷大）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token&lt;/span> &lt;span class="nl">bad_tok&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">bad_tok&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">INFINITY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>像这样直接操作 C++ API，就可以实现 LangChain 或 Python 难以做到或者开销极大的 &lt;strong>“在微秒级别对每个推理周期进行干预”&lt;/strong>。&lt;/p>
&lt;hr>
&lt;h2 id="7-性能调优秘籍">7. 性能调优秘籍
&lt;/h2>&lt;p>在使用 C++ 完成实现后，为了面向实际生产环境最大程度地提升速度，下面介绍几个检查点。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>批处理优化:&lt;/strong> 在同时处理来自多个用户的请求时，在 &lt;code>llama_batch&lt;/code> 中包含多个序列并一次性调用 &lt;code>llama_decode&lt;/code>（Continuous Batching，连续批处理）。这样可以合并内存访问，从而显著提高吞吐量。&lt;/li>
&lt;li>&lt;strong>启用 Flash Attention:&lt;/strong>
通过在上下文参数中设置 &lt;code>ctx_params.flash_attn = true;&lt;/code>，可以在减少内存使用量的同时加速 Attention 的计算。在处理较长上下文（数万 Token）时，这是必不可少的设置。&lt;/li>
&lt;li>&lt;strong>支持 NUMA:&lt;/strong>
在多插槽服务器环境中，在调用 &lt;code>llama_backend_init()&lt;/code> 之前正确配置 NUMA 设置，可以降低内存访问的延迟。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-结语">8. 结语
&lt;/h2>&lt;p>本文从 &lt;code>llama.cpp&lt;/code> 的数学背景开始，详细解析了其架构，并讲解了如何运用 C++ API 构建自定义推理引擎。&lt;/p>
&lt;p>虽然 Python 生态系统对于原型开发非常方便，但在要求部署到边缘设备、集成到游戏中或需要进行实时处理的生产环境中，基于 C/C++ 的 &lt;code>llama.cpp&lt;/code> 的直接控制将展现出压倒性的优势。&lt;/p>
&lt;p>希望大家也务必尝试亲手编写 C++ 代码，体验在本地环境中自由驾驭 LLM 的乐趣。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>参考链接集&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp Official Repository&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml" target="_blank" rel="noopener"
>ggml - Tensor Library&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/1706.03762" target="_blank" rel="noopener"
>Attention Is All You Need (Vaswani et al., 2017)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote></description></item><item><title>【2026年最新】在Windows环境下运行本地LLM的完全指南</title><link>http://kenji.blog/zh-cn/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/zh-cn/p/local-llm-windows-2026/</guid><description>&lt;img src="http://kenji.blog/p/local-llm-windows-2026/img/eyecatch.jpg" alt="Featured image of post 【2026年最新】在Windows环境下运行本地LLM的完全指南" />&lt;h1 id="1-引言为什么现在要在windows上运行本地llm">1. 引言：为什么现在要在Windows上运行本地LLM？
&lt;/h1>&lt;p>2026年现在，生成式AI和大型语言模型（LLM）的发展正经历着一场巨大的范式转变，从云端庞大的API服务，转向在个人PC和本地（On-premises）环境中运行的“本地LLM”。虽然OpenAI的GPT-5和Anthropic的Claude 3.5等云端AI非常强大，但企业和个人并不能将所有数据都发送到云端。出于隐私、安全、延迟以及长期和可持续成本的考虑，对本地LLM的需求正在呈现前所未有的爆发式增长。&lt;/p>
&lt;p>特别是在Windows环境中，本地LLM生态系统的演进令人瞩目。直到几年前，“AI开发和运行首选Linux”还是常识，但在2026年的今天，Windows已经转变为一个极其强大且易于使用的AI平台。&lt;/p>
&lt;p>本文将基于2026年最新的技术趋势，为您提供在Windows环境中构建、运行和优化本地LLM的完整指南。从面向初学者的使用Ollama的简单构建，到面向高级用户的利用llama.cpp的极限优化，再到VRAM计算的数学方法、架构的深入解析，以及在本地进行的微调（Fine-tuning），我们将以压倒性的丰富内容进行彻底解说。&lt;/p>
&lt;h2 id="11-2026年围绕本地llm的技术趋势">1.1 2026年围绕本地LLM的技术趋势
&lt;/h2>&lt;p>塑造当前本地LLM生态系统的主要趋势如下：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>GGUF格式的全面普及&lt;/strong>: 将元数据和张量（Tensor）整合到单一文件中的GGUF（GPT-Generated Unified Format）已经完全成为事实上的标准。这使得只需从Hugging Face下载一个文件，就可以在任何环境中运行。&lt;/li>
&lt;li>&lt;strong>MoE（Mixture of Experts）架构的民主化&lt;/strong>: 发布了许多规模较小但性能卓越的MoE模型。通过在推理时仅激活部分专家网络，可以在控制消费级PC计算负载的同时，实现媲美巨大模型的性能。&lt;/li>
&lt;li>&lt;strong>推理引擎的高度抽象化和优化&lt;/strong>: Ollama、LM Studio、AnythingLLM等工具变得更加完善，用户无需再关注安装CUDA驱动等复杂的依赖关系。此外，随着FlashAttention 3原生支持Windows，推理速度得到了急剧提升。&lt;/li>
&lt;li>&lt;strong>NPU的应用和Windows Copilot+ PC的崛起&lt;/strong>: 即使是在没有GPU的笔记本电脑上，利用内置的NPU（神经网络处理单元）以低功耗运行小型LLM（SLM: Small Language Models）的技术也已经进入实用阶段。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-硬件要求与操作系统准备">2. 硬件要求与操作系统准备
&lt;/h1>&lt;p>为了以实用的速度（每秒15到30个Token以上）运行本地LLM，硬件的选择最为关键。&lt;/p>
&lt;h2 id="21-推荐硬件配置">2.1 推荐硬件配置
&lt;/h2>&lt;p>随着AI PC的进化，所需的规格也在发生变化。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OS&lt;/strong>: Windows 11 Pro (24H2或更高版本)。这是利用WSL2的完整功能、高级内存管理以及DirectML最新API的必要条件。&lt;/li>
&lt;li>&lt;strong>CPU&lt;/strong>: Intel Core Ultra 200系列及以上，或AMD Ryzen 9000系列及以上。如果同时使用CPU进行推理，高带宽的内存通信是不可或缺的。&lt;/li>
&lt;li>&lt;strong>RAM&lt;/strong>: 最低32GB，推荐64GB或以上。主内存的带宽（MB/s）是CPU推理或卸载（Offloading）时的决定性瓶颈。DDR5-6000以上的快速内存是理想选择。&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>: NVIDIA RTX 4000/5000系列。对于本地LLM来说，最重要的不是计算性能，而是“VRAM容量”。
&lt;ul>
&lt;li>&lt;strong>入门级&lt;/strong>: RTX 4060 Ti (16GB版) - 性价比最高。非常适合8B至14B级别的模型。&lt;/li>
&lt;li>&lt;strong>中端&lt;/strong>: RTX 4070 Ti SUPER (16GB) / RTX 4080 SUPER (16GB)&lt;/li>
&lt;li>&lt;strong>高端&lt;/strong>: RTX 4090 (24GB) / RTX 5090 (32GB) - 运行30B至70B级别的量化模型所必需。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>存储&lt;/strong>: PCIe Gen4或Gen5的NVMe SSD。可以极大地缩短加载数十GB模型所需的时间。&lt;/li>
&lt;/ul>
&lt;h2 id="22-wsl2-windows-subsystem-for-linux-2-的设置">2.2 WSL2 (Windows Subsystem for Linux 2) 的设置
&lt;/h2>&lt;p>虽然许多GUI工具可以在Windows原生运行，但在使用Python进行开发、编译最新工具以及稍后将提到的LoRA微调中，WSL2非常方便。在最新的Windows 11环境中，只需在主机端安装NVIDIA驱动，即可从WSL2中透明地使用GPU（CUDA）。&lt;/p>
&lt;p>以管理员权限打开PowerShell，并执行以下命令：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 安装WSL2和最新的Ubuntu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span> &lt;span class="n">-d&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="mf">24.04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 更新内核&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-update&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>安装完成后，在WSL2终端中运行 &lt;code>nvidia-smi&lt;/code>，如果能正常识别到GPU，即表示成功。&lt;/p>
&lt;hr>
&lt;h1 id="3-本地llm的架构与推理机制">3. 本地LLM的架构与推理机制
&lt;/h1>&lt;p>了解模型在本地环境中是如何生成文本的内部结构，对于故障排除和优化非常有用。&lt;/p>
&lt;p>以下Mermaid图展示了典型的本地LLM推理流水线（Pipeline）。&lt;/p>
&lt;div class="mermaid">graph TD
User["用户输入 (Prompt)"] --> Tokenizer["分词器 (Tokenizer)"]
Tokenizer --> Embedding["嵌入层 (Embedding)"]
subgraph "Transformer Block (x Layers)"
Embedding --> Attn["自注意力机制 (Self-Attention)"]
Attn --> KVCache["KV 缓存 (保留 Key/Value)"]
Attn --> FFN["前馈神经网络 (FFN)"]
end
FFN --> Logits["Logits 计算 (Logits)"]
Logits --> Sampler["采样器 (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["输出 Token"]
OutputToken --> |"自回归生成"| Tokenizer
OutputToken --> Decoder["反分词器 (Detokenizer)"]
Decoder --> FinalOutput["最终输出文本"]&lt;/div>
&lt;h2 id="31-两个阶段prefill-与-decode">3.1 两个阶段：Prefill 与 Decode
&lt;/h2>&lt;p>LLM的文本生成分为计算特性不同的两个阶段。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Prefill（提示词处理）阶段&lt;/strong>: 这是一个一次性处理和理解输入的完整提示词（Prompt）的阶段。由于可以进行并行计算，GPU的计算能力（FLOPS）直接影响速度。如果提示词很长，此阶段可能需要几秒钟的时间。&lt;/li>
&lt;li>&lt;strong>Decode（Token生成）阶段&lt;/strong>: 这是一个逐个预测Token，并将其作为下一次输入的（自回归）阶段。在这个阶段并行计算受到限制，因此GPU的VRAM带宽（Memory Bandwidth）成为了决定性的瓶颈。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="4-vram消耗量的计算与模型大小的数学理解">4. VRAM消耗量的计算与模型大小的数学理解
&lt;/h1>&lt;p>为了准确判断“我的PC能运行哪个模型？”，必须理解VRAM的计算公式。如果由于VRAM不足而导致回退（Fallback）到系统内存（RAM），推理速度将会变慢10倍到100倍。&lt;/p>
&lt;h2 id="41-基于参数大小的基础vram">4.1 基于参数大小的基础VRAM
&lt;/h2>&lt;p>这是将模型的权重（Weights）加载到VRAM中所需的内存量。
使用模型大小 $P$ （参数数量，单位：10亿 = 1B）和每个参数的字节数 $B$ 来计算。&lt;/p>
$$
V_{base} = P \times B \quad \text{(GB)}
$$
&lt;p>例如，要将8B（80亿）参数的模型以FP16（半精度浮点数，16位=2字节）加载时：&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ GB}
$$
&lt;p>也就是说，即使拥有16GB的VRAM的GPU，仅加载模型就几乎达到了极限。&lt;/p>
&lt;h2 id="42-量化quantization的魔法">4.2 量化（Quantization）的魔法
&lt;/h2>&lt;p>于是“量化”便登场了。通过降低参数的精度，能够显著缩小模型大小。在最常见的4bit量化（例如：Q4_K_M）的情况下，每个参数平均约占用0.55字节。&lt;/p>
$$
V_{base\_4bit} = 8 \times 0.55 = 4.4 \text{ GB}
$$
&lt;p>因此，如果有16GB的VRAM，就可以非常游刃有余地运行8B模型。&lt;/p>
&lt;h2 id="43-kv缓存的计算支持gqa版">4.3 KV缓存的计算（支持GQA版）
&lt;/h2>&lt;p>在推理时，用于保存过去上下文的“KV缓存”会消耗VRAM。在Llama 3等最新模型中，为了节省内存，采用了GQA（Grouped Query Attention）。&lt;/p>
&lt;p>KV缓存的消耗量 $V_{kv}$ （千兆字节，GB）可以通过以下公式表示：&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times \left( \frac{h_{kv}}{h_q} \right) \times h_q \times d \times B_{kv} \div 10^9
$$
&lt;p>整理后，可以使用Key和Value的注意力头数 $h_{kv}$ 简单地计算：&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times h_{kv} \times d \times B_{kv} \div 10^9
$$
&lt;p>其中：&lt;/p>
&lt;ul>
&lt;li>$b$: 批处理大小（个人本地使用通常为1）&lt;/li>
&lt;li>$s$: 序列长度（上下文长度，例如：8192）&lt;/li>
&lt;li>$l$: 层数（例如：32）&lt;/li>
&lt;li>$h_{kv}$: KV注意力头数（例如：8）&lt;/li>
&lt;li>$d$: 每个头的维度数（例如：128）&lt;/li>
&lt;li>$B_{kv}$: KV缓存的字节数（如果为FP16则为2）&lt;/li>
&lt;/ul>
&lt;p>计算示例（Llama 3 8B, 上下文8192, FP16缓存）：
$V_{kv} = 2 \times 1 \times 8192 \times 32 \times 8 \times 128 \times 2 \div 10^9 \approx 1.07 \text{ GB}$&lt;/p>
&lt;p>请注意，上下文长度 $s$ 越长，所需的VRAM就会呈线性增加。&lt;/p>
&lt;hr>
&lt;h1 id="5-实践1使用ollama进行最快最短的设置">5. 实践1：使用Ollama进行最快、最短的设置
&lt;/h1>&lt;p>了解了理论之后，让我们实际在Windows环境中运行LLM。
在2026年，对用户最友好的工具是“Ollama”。它提供了一个类似于Docker的直观命令行界面（CLI）。&lt;/p>
&lt;h2 id="51-安装与运行">5.1 安装与运行
&lt;/h2>&lt;ol>
&lt;li>从 &lt;a class="link" href="https://ollama.com/" target="_blank" rel="noopener"
>Ollama官网&lt;/a> 下载Windows版安装程序并运行。&lt;/li>
&lt;li>打开PowerShell并输入以下命令。在这里，我们将使用支持日语的 &lt;code>llama3:8b&lt;/code>。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">llama3&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="n">8b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>首次运行时会下载模型。下载完成后，就可以直接在终端上进行对话了。&lt;/p>
&lt;h2 id="52-使用modelfile创建自定义ai">5.2 使用Modelfile创建自定义AI
&lt;/h2>&lt;p>可以轻松创建具有特定人格（Persona）的AI。在任意位置创建一个 &lt;code>Modelfile&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">FROM llama3:8b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SYSTEM &amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">你是一名极其优秀的资深软件工程师。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">对于用户的提问，请务必结合代码示例，有逻辑且简明扼要地进行回答。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER temperature 0.3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER num_ctx 8192
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>使用以下命令构建并运行自定义模型：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">create&lt;/span> &lt;span class="n">SeniorDev&lt;/span> &lt;span class="o">-f&lt;/span> &lt;span class="p">./&lt;/span>&lt;span class="n">Modelfile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">SeniorDev&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="53-从外部应用ai编辑器中使用">5.3 从外部应用（AI编辑器）中使用
&lt;/h2>&lt;p>Ollama会在 &lt;code>http://localhost:11434&lt;/code> 公开一个兼容OpenAI的API端点。
在Cursor或Continue.dev等VS Code扩展插件的后端设置中，将URL指定为上述地址，并将模型名称指定为 &lt;code>SeniorDev&lt;/code> 等，只需这样就能实现一个免费且强大的本地编程助手。&lt;/p>
&lt;hr>
&lt;h1 id="6-实践2使用llamacpp进行极限性能调优">6. 实践2：使用llama.cpp进行极限性能调优
&lt;/h1>&lt;p>如果您想要进行细致的内存管理，或者想尽早尝试最新的格式（如EXL2或IQ量化等），可以直接操作核心引擎 &lt;code>llama.cpp&lt;/code>。&lt;/p>
&lt;h2 id="61-llamacpp-的构建步骤">6.1 llama.cpp 的构建步骤
&lt;/h2>&lt;p>在Windows环境中，最好的方式是使用CUDA Toolkit和CMake从源码进行构建。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">git&lt;/span> &lt;span class="n">clone&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">//&lt;/span>&lt;span class="n">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ggerganov&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">&lt;/span>&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 配置为支持CUDA并进行编译&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-DLLAMA_CUBLAS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">ON&lt;/span> &lt;span class="n">-DBUILD_SHARED_LIBS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">OFF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span> &lt;span class="n">-j&lt;/span> &lt;span class="mf">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="62-在服务器模式下进行高级启动">6.2 在服务器模式下进行高级启动
&lt;/h2>&lt;p>使用构建好的 &lt;code>llama-server.exe&lt;/code> 来托管模型。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.\&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="n">Release&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="nb">llama-server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">exe&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-model&lt;/span> &lt;span class="s2">&amp;#34;C:\models\Llama-3-8B-Instruct.Q4_K_M.gguf&amp;#34;&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-ctx-size&lt;/span> &lt;span class="mf">8192&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-n-gpu-layers&lt;/span> &lt;span class="mf">99&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-threads&lt;/span> &lt;span class="mf">8&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-flash-attn&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-port&lt;/span> &lt;span class="mf">8080&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>--n-gpu-layers 99&lt;/code>: 将尽可能多的层卸载到GPU VRAM中。&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code>: 启用FlashAttention 3，以提升推理速度并减少KV缓存的VRAM消耗。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="7-gui前端lm-studio与本地rag的构建">7. GUI前端：LM Studio与本地RAG的构建
&lt;/h1>&lt;p>如果您对命令行有抵触情绪，或者想要直观地进行RAG（检索增强生成），可以使用GUI。&lt;/p>
&lt;h2 id="71-lm-studio">7.1 LM Studio
&lt;/h2>&lt;p>LM Studio是一个将模型搜索、下载、系统要求预检查以及聊天UI等功能集于一身的优秀应用程序。只需点击应用内的“Local Server”按钮，就能启动兼容OpenAI的API。&lt;/p>
&lt;h2 id="72-使用anythingllm的rag架构">7.2 使用AnythingLLM的RAG架构
&lt;/h2>&lt;p>这是一个让系统读取公司内部文档或个人笔记的RAG环境架构图。&lt;/p>
&lt;div class="mermaid">graph LR
Document["文档 (PDF, MD)"] --> Chunking["数据分块 (Chunking)"]
Chunking --> EmbedModel["嵌入模型 (Embedding Model)"]
EmbedModel --> VectorDB["向量数据库 (Vector DB)"]
UserQuery["用户查询"] --> EmbedQuery["查询嵌入"]
EmbedQuery --> VectorDB
VectorDB --> |"相似度检索"| RetrievedDocs["提取相关文档"]
UserQuery --> PromptBuilder["生成提示词"]
RetrievedDocs --> PromptBuilder
PromptBuilder --> LocalLLM["本地 LLM"]
LocalLLM --> Answer["最终回答"]&lt;/div>
&lt;p>使用AnythingLLM桌面版（Windows），只需在设置界面中指定Ollama（用于LLM和Embedding），并设置为使用本地VectorDB（LanceDB），几分钟内就能完成这个架构的搭建。一个完全不向外部发送任何数据的私有AI就此诞生。&lt;/p>
&lt;hr>
&lt;h1 id="8-在windows-wsl2上进行微调-lora">8. 在Windows WSL2上进行微调 (LoRA)
&lt;/h1>&lt;p>除了在本地运行，如果您还想用自己的数据让模型变得更聪明，可以使用LoRA（低秩自适应）进行微调。在2026年，通过使用名为“Unsloth”的库，在Windows的WSL2环境中，即使只有16GB的VRAM，也能在几个小时内完成8B模型的训练。&lt;/p>
&lt;p>在WSL2的Ubuntu中执行以下命令来构建环境：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda create --name unsloth_env &lt;span class="nv">python&lt;/span>&lt;span class="o">=&lt;/span>3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conda activate unsloth_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --no-deps trl peft accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Unsloth将CUDA内核优化到了极致，与标准的Hugging Face库相比，训练速度约为原来的2倍，而VRAM消耗量减少了一半左右。启动Jupyter Notebook并加载数据集（JSONL格式）后，即使是使用VRAM为12GB至16GB的RTX 4060 Ti等显卡，也可以完成几个Epoch的训练。&lt;/p>
&lt;hr>
&lt;h1 id="9-性能与故障排除">9. 性能与故障排除
&lt;/h1>&lt;p>以下是经常遇到的问题及其解决方案。&lt;/p>
&lt;h3 id="1-推理速度极慢1至2-tokenss">1. 推理速度极慢（1至2 tokens/s）
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: 模型无法完全装入VRAM，被卸载到了系统内存（RAM）中。
&lt;strong>对策&lt;/strong>: 请在任务管理器中检查“专用GPU内存”。如果已达到极限，请减小上下文大小（&lt;code>-c&lt;/code>），或者使用位数更低的量化模型（如Q4_K_M等）。&lt;/p>
&lt;h3 id="2-cuda-out-of-memory-错误">2. “CUDA out of memory” 错误
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: VRAM完全枯竭。特别是当对话时间过长导致KV缓存不断膨胀时会发生这种情况。
&lt;strong>对策&lt;/strong>: 在Ollama中，有意地减小 &lt;code>num_ctx&lt;/code> 的值；在llama.cpp中，减小 &lt;code>-c&lt;/code> 的值以进行限制。&lt;/p>
&lt;h3 id="3-生成的日语不正常">3. 生成的日语不正常
&lt;/h3>&lt;p>&lt;strong>原因&lt;/strong>: 提示词模板不匹配，或者是使用了不支持的模型。
&lt;strong>对策&lt;/strong>: 请使用模型名称中包含 &lt;code>Instruct&lt;/code> 的模型，并确认工具端是否选择了模型作者指定的正确模板，例如ChatML或Llama3格式。&lt;/p>
&lt;hr>
&lt;h1 id="10-总结与未来展望">10. 总结与未来展望
&lt;/h1>&lt;p>在2026年，在Windows环境中构建本地LLM已经不再是少数工程师的特权。随着GGUF格式成为事实上的标准、Ollama和LM Studio等完善生态系统的出现，以及以FlashAttention为首的硬件优化，任何人都可以轻松获得企业级的AI环境。&lt;/p>
&lt;p>请务必活用本文中解说的以下几点：&lt;/p>
&lt;ol>
&lt;li>使用&lt;strong>VRAM的数学计算&lt;/strong>，逻辑性地选择最适合您PC规格的模型大小和量化级别。&lt;/li>
&lt;li>使用&lt;strong>Ollama&lt;/strong>以最快的速度搭建环境，并与AI编辑器联动，从而急剧提高生产力。&lt;/li>
&lt;li>通过&lt;strong>llama.cpp&lt;/strong>的高级参数控制，榨干硬件的极限性能。&lt;/li>
&lt;li>使用&lt;strong>AnythingLLM&lt;/strong>构建能够处理机密数据的安全的本地RAG系统。&lt;/li>
&lt;li>活用&lt;strong>Unsloth (WSL2)&lt;/strong>，培育出拥有专属于您专业知识的自定义AI。&lt;/li>
&lt;/ol>
&lt;p>AI的“民主化”已经不再是一个流行语，而是一个运行在您的Windows桌面上的真实系统。摆脱云端API的使用成本和信息泄露风险，现在就请迈入这个自由且强大的私有AI世界吧。&lt;/p></description></item></channel></rss>