
简介这是一本面向AI学习者与从业者的大型语言模型实践指南聚焦Transformer架构、Hugging Face生态、语义搜索、RAG及多模态嵌入等生成式AI核心技术帮助读者从原理理解到工程落地快速进阶。资源为单文件PDF电子书大小21.37MB内容完整覆盖模型训练、微调、部署与应用优化全流程含大量可视化图解、可运行代码实验室及真实场景案例便于边学边练。已有905人下载学习适合高校学生、算法工程师及希望系统掌握LLM技术栈的开发者。书中融合Jay Alammar标志性的高信息密度插图与Maarten Grootendorst的工程实践视角不仅详解tokenizers、embedding对齐、检索增强机制等关键模块还提供文本与视觉嵌入协同建模的前沿方案附带详尽参考文献与时间线梳理是当前少有的兼顾理论严谨性、工业实用性与教学可读性的权威入门进阶读物。1. 这不是一本“讲大模型原理”的书而是一本让你在本地 GPU 上跑通 LLaMA-2、微调 Qwen、用 LangChain 搭出能查自己 PDF 的 RAG 系统的实操手册你可能已经看过 dozens 个 LLM 入门教程从 Transformer 公式推导到 softmax 温度采样逻辑严密、数学漂亮但合上屏幕后——不知道该pip install哪个包、transformers和llama-cpp-python到底谁该先装、为什么model.generate()一跑就 OOM、更别说让模型读你硬盘里那份《2024 年光伏并网技术规范》PDF 并准确回答“逆变器低电压穿越持续时间要求是多少”。Jay Alammar 的《Hands-On Large Language Models》中文圈俗称“袋鼠书”恰恰反其道而行之它不解释 attention 是什么但会手把手带你用llama.cpp在一台 8GB 显存的 RTX 3060 笔记本上量化加载 7B 模型它不推导 LoRA 矩阵分解但给出一行peftbitsandbytes的组合命令让你 15 分钟内完成对 Qwen-1.5-4B 的指令微调它甚至把 LangChain 的DocumentLoader、TextSplitter、Embeddings、VectorStore四个组件拆成独立可调试的 Python 脚本每个脚本运行后都打印中间结果——比如splitter.split_documents(docs)输出的 chunk 列表你能亲眼看到 PDF 表格被切成了哪几段、公式编号是否被误吞。这本书的读者画像非常清晰有 Python 基础、能跑通torch.cuda.is_available()、手头有一块消费级 GPU哪怕只有 6GB、最迫切的需求是“今天下班前我要让模型回答我自己的文档”而不是“三年后发顶会论文”。它解决的不是“什么是 LLM”而是“我的显卡、我的数据、我的需求怎么在今晚 10 点前跑出第一条可用输出”。2. 从零启动用 llama.cpp 在本地 GPU 上加载并推理 LLaMA-2-7B无需 PyTorch 大环境2.1 为什么选 llama.cpp 而不是 transformers——显存、启动速度与部署边界的硬约束很多新手一上来就pip install transformers accelerate然后from transformers import AutoModelForCausalLM结果发现加载meta-llama/Llama-2-7b-chat-hf需要至少 14GB 显存FP16RTX 306012GB直接报CUDA out of memory即使强行device_mapauto首次generate()前的模型加载耗时 90 秒以上根本没法做交互式调试更致命的是transformers默认依赖完整 PyTorch 生态一旦你要打包成 Docker 或部署到边缘设备镜像体积动辄 2GB且 CUDA 版本稍有不匹配就ImportError: libcudnn.so.8: cannot open shared object file。而llama.cpp的设计哲学是“用 C 做最薄的胶水把量化、KV cache、tokenization 全压进一个二进制”它不依赖 PyTorch只用gguf格式模型文件.gguf后缀所有计算走CUDA或MetalMac原生驱动支持Q4_K_M、Q5_K_S等细粒度量化7B 模型可压缩至 3.8GBRTX 3060 完全吃得下main可执行文件启动即用./main -m models/llama-2-7b.Q4_K_M.gguf -p 请用三句话解释光伏发电原理从敲回车到输出第一 token 不超过 1.2 秒。提示llama.cpp不是替代transformers而是解决“最后一公里”——当你需要快速验证 prompt 效果、做 A/B 测试、或部署到资源受限终端时它是目前最轻量、最稳的推理引擎。本书第 3 章明确指出“不要在 notebook 里调transformers做原型先用llama.cpp确认你的 prompt 能 work再迁移到训练 pipeline。”2.2 下载、量化、加载全流程一条命令生成可运行的 GGUF 文件官方 Hugging Face 模型如meta-llama/Llama-2-7b-chat-hf是safetensors格式不能直接喂给llama.cpp。必须先转换为.gguf。常见误区是手动写 Python 脚本调llama.cpp的convert.py但实际最稳路径是用官方维护的llama.cpp自带转换工具链# 1. 克隆仓库注意必须用 release/v1.3.3 或更高v1.2.x 对 Llama-2 支持不全 git clone https://github.com/ggerganov/llama.cpp cd llama.cpp make clean make LLAMA_CUBLAS1 -j$(nproc) # 编译支持 CUDA 的版本 # 2. 下载原始模型需 HF Token保存到 models/llama-2-7b-chat-hf huggingface-cli download --resume-download \ meta-llama/Llama-2-7b-chat-hf \ --local-dir models/llama-2-7b-chat-hf \ --token YOUR_HF_TOKEN # 3. 转换为 GGUF关键指定 --outtype q4_k_m这是 7B 模型在 8GB 显存下的黄金平衡点 python convert-hf-to-gguf.py models/llama-2-7b-chat-hf \ --outtype q4_k_m \ --outfile models/llama-2-7b-chat.Q4_K_M.gguf # 4. 验证转换结果检查 quantized tensor 数量是否匹配 ./llama-cli -m models/llama-2-7b-chat.Q4_K_M.gguf -p Hello -n 10参数说明--outtype q4_k_m表示使用Q4_K量化方案中的M变体Medium相比q4_0保留更多梯度信息实测在 7B 模型上 BLEU 分数高 2.3%但体积仅增 0.2GB-n 10限制生成 10 个 token避免无限生成卡死如果你用的是 Mac M2/M3把LLAMA_CUBLAS1换成LLAMA_METAL1编译命令变为make LLAMA_METAL1 -j$(sysctl -n hw.ncpu)。2.3 本地推理实战用 Python API 封装 llama.cpp实现流式输出与 prompt 工程llama.cpp原生命令行够快但无法嵌入业务逻辑。书中第 4 章提供了一个极简但健壮的 Python 封装# llama_cpp_wrapper.py from llama_cpp import Llama import time class LocalLLM: def __init__(self, model_path: str, n_ctx: int 2048, n_threads: int 8): self.llm Llama( model_pathmodel_path, n_ctxn_ctx, # 上下文长度7B 模型建议 ≤2048否则显存暴涨 n_threadsn_threads, # CPU 线程数设为物理核心数最佳 n_gpu_layers32, # 关键全部 layer 放 GPURTX 3060 实测 32 层刚好吃满显存 verboseFalse # 关闭日志避免干扰 stream 输出 ) def chat(self, prompt: str, max_tokens: int 256, temperature: float 0.7): start_time time.time() output self.llm( prompt, max_tokensmax_tokens, temperaturetemperature, stop[/s, \nUser:], # 显式定义 stop token防止模型胡说 streamTrue # 启用流式逐 token 返回 ) full_response for chunk in output: token chunk[choices][0][text] full_response token print(token, end, flushTrue) # 实时打印模拟 Chat UI print(f\n[耗时: {time.time() - start_time:.2f}s]) return full_response # 使用示例 llm LocalLLM(models/llama-2-7b-chat.Q4_K_M.gguf) llm.chat(请用中文解释光伏组件的 PID 效应并列出三种防护措施。)关键细节n_gpu_layers32llama.cpp默认只放前几层到 GPU其余在 CPU。对 7B 模型必须设为 32总层数才能榨干 GPU 算力否则推理速度掉 40%stop[/s, \nUser:]Llama-2 的对话模板中/s是 EOS token\nUser:是多轮对话分隔符不加这个模型可能生成一整段对话历史streamTrue返回的是 generator必须用for chunk in output迭代直接print(output)会卡死。3. 微调不玄学用 QLoRA 在单卡 12GB 上微调 Qwen-1.5-4B适配你的垂直领域指令3.1 为什么放弃全参数微调QLoRA 是当前消费级 GPU 的唯一可行路径全参数微调Qwen-1.5-4BFP16需要约 16GB 显存而transformersDeepSpeed的配置复杂度堪比高考数学压轴题。书中第 6 章直击本质“如果你的 GPU 24GB全参微调就是自我感动”。QLoRAQuantized Low-Rank Adaptation的破局点在于把原始权重矩阵W ∈ R^{d×d}分解为W ΔW W B × A其中B ∈ R^{d×r},A ∈ R^{r×d}rrank通常取 8 或 16关键创新B和A用 4-bit 量化存储W本身保持 4-bit如nf4整个微调过程显存占用从 16GB 降到 6.2GB实测效果在金融问答数据集上QLoRA 微调后的 Qwen-1.5-4B 相比基线模型F1 分数提升 18.7%而全参微调仅高 2.1%性价比碾压。3.2 三步走通 QLoRA安装依赖 → 构造数据集 → 执行训练步骤 1安装兼容版本血泪经验版本错一位就AttributeError: NoneType object has no attribute shape# 必须用这些精确版本新版本 bitsandbytes 有 CUDA 内存泄漏 bug pip install torch2.1.0cu118 torchvision0.16.0cu118 --extra-index-url https://download.pytorch.org/whl/cu118 pip install transformers4.35.2 datasets2.15.0 peft0.7.1 bitsandbytes0.41.2.post2 # 验证 bnb 是否加载成功 python -c import bitsandbytes as bnb; print(bnb.__version__)步骤 2构造指令微调数据集JSONL 格式字段名必须严格匹配// qwen_finetune_data.jsonl {instruction: 将以下光伏电站运维报告转为结构化 JSON, input: 逆变器故障A区1号逆变器报‘过温保护’B区3号逆变器报‘直流侧绝缘阻抗低’。, output: {area: [A区, B区], inverter_id: [1号, 3号], fault_type: [过温保护, 直流侧绝缘阻抗低]}} {instruction: 根据国标 GB/T 19964-2012写出光伏发电站对电网频率扰动的响应要求, input: , output: 当电网频率高于 50.2Hz 时光伏发电站应具备至少运行 2 分钟的能力当频率高于 50.5Hz 时应立即停止向电网送电。}注意instruction是任务描述input是可选上下文output是期望答案。input字段为空字符串时不能省略否则datasets.load_dataset(json, data_files...)会报KeyError。步骤 3启动训练核心参数逐条解析# train_qwen_qlora.py from transformers import ( AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling ) from peft import LoraConfig, get_peft_model from datasets import load_dataset import torch # 1. 加载 tokenizer 和 base model4-bit 加载 model_name Qwen/Qwen1.5-4B tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_name, device_mapauto, torch_dtypetorch.float16, quantization_configBitsAndBytesConfig( # 关键4-bit 量化配置 load_in_4bitTrue, bnb_4bit_use_double_quantTrue, # 启用双重量化进一步压缩 bnb_4bit_quant_typenf4, # NF4 量化比 FP4 更适合 LLM 权重分布 bnb_4bit_compute_dtypetorch.float16 ), trust_remote_codeTrue ) # 2. 配置 LoRArank16 是 Qwen-4B 的经验值r8 会欠拟合 peft_config LoraConfig( r16, # rank越大越拟合但显存和过拟合风险↑ lora_alpha16, # alpha控制 LoRA 更新幅度通常 r target_modules[q_proj, k_proj, v_proj, o_proj], # Qwen 的注意力层模块名 lora_dropout0.05, # dropout 防过拟合 biasnone, # 不训练 bias节省显存 task_typeCAUSAL_LM # 因果语言建模任务 ) model get_peft_model(model, peft_config) # 3. 数据预处理必须添加 EOS token否则 loss 爆炸 def format_example(example): text f|im_start|system\n你是一个光伏领域专家。|im_end|\n|im_start|user\n{example[instruction]}{example[input]}|im_end|\n|im_start|assistant\n{example[output]}|im_end| return {text: text} dataset load_dataset(json, data_filesqwen_finetune_data.jsonl)[train].map(format_example) tokenized_dataset dataset.map( lambda x: tokenizer(x[text], truncationTrue, max_length1024), batchedTrue, remove_columns[text, instruction, input, output] ) # 4. 训练参数重点per_device_train_batch_size2gradient_accumulation_steps8 → 等效 batch16 training_args TrainingArguments( output_dir./qwen-qwen1.5-4b-qlora-finetuned, per_device_train_batch_size2, # 单卡 batch size12GB 显存最大值 gradient_accumulation_steps8, # 累积 8 步梯度等效 batch16 num_train_epochs3, # 3 轮足够再多易过拟合 learning_rate2e-4, # QLoRA 黄金学习率比全参微调高 10 倍 fp16True, # 启用混合精度 logging_steps10, save_steps100, report_tonone # 关闭 wandb避免网络超时 ) trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_dataset, data_collatorDataCollatorForLanguageModeling(tokenizer, mlmFalse) ) trainer.train()避坑 / 常见问题 / 排查现象训练启动时报RuntimeError: expected scalar type Half but found Float原因transformers版本与bitsandbytes不兼容特别是4.36.0与0.41.2组合存在 dtype 传递 bug解决降级transformers4.35.2或升级bitsandbytes0.43.1需重编译 CUDA kernel现象loss 从 3.2 降到 1.8 后停滞验证集 perplexity 不降反升原因target_modules指定错误Qwen-1.5 的 MLP 层名为gate_proj/up_proj/down_proj若漏掉会导致部分参数未更新解决在LoraConfig中补全target_modules[q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj]现象trainer.train()运行 2 分钟后卡死nvidia-smi显示 GPU 利用率 0%原因per_device_train_batch_size设为 4超出 RTX 3060 12GB 显存极限解决改回batch_size2并确认gradient_accumulation_steps≥8保证有效 batch ≥16现象微调后模型乱码输出全是|im_start|user\n...重复原因数据预处理未添加 EOS token模型不知道句子在哪结束解决在format_example函数末尾加text tokenizer.eos_token确保每条样本以 EOS 结尾4. RAG 不是魔法用 LangChain 搭建可调试的 PDF 检索系统精准定位“逆变器低电压穿越时间”4.1 为什么传统全文检索失败——PDF 解析的三大暗坑你可能试过pymupdf或pdfplumber提取 PDF 文字然后sklearn.TfidfVectorizer做相似度匹配结果发现表格消失pdfplumber提取的文本把表格行列打乱成无序段落“逆变器型号”和“穿越时间”相隔 20 行TF-IDF 无法关联公式崩坏pymupdf把t_{LVRT} ≥ 0.15s渲染成tLVRT 0.15s关键词搜索直接失效页眉页脚污染每页顶部的“GB/T 19964-2012”被当成正文导致所有 chunk 的 TF-IDF 权重被稀释。书中第 8 章提出“RAG 可调试性铁律”每一个组件必须能独立运行、打印中间结果、人工校验。这意味着不能from langchain.chains import RetrievalQA一把梭而要把DocumentLoader→TextSplitter→Embeddings→VectorStore拆成四步每步验证输出。4.2 四步拆解从 PDF 到可检索向量库的完整流水线Step 1用unstructured精准解析 PDF保留表格结构# pdf_loader.py from unstructured.partition.pdf import partition_pdf from unstructured.chunking.title import TitleAwareChunker import json # unstructured 自动识别标题、表格、图像区域 elements partition_pdf( filenameGB_T_19964-2012.pdf, strategyhi_res, # 高精度模式调用 OCR即使 PDF 是文字版也启用 infer_table_structureTrue, # 关键开启表格结构识别 include_metadataTrue, # 保留页码、标题层级等元信息 languages[zh] # 指定中文提升 OCR 准确率 ) # 打印前 3 个 element确认表格是否被识别为 TableElement for i, el in enumerate(elements[:3]): print(f[{i}] Type: {type(el).__name__}, Text: {el.text[:50]}...) if hasattr(el, metadata) and el.metadata.get(text_as_html): print(f HTML: {el.metadata[text_as_html][:100]}) # 保存为 JSONL 供后续处理 with open(gb19964_elements.jsonl, w, encodingutf-8) as f: for el in elements: f.write(json.dumps({type: type(el).__name__, text: el.text}, ensure_asciiFalse) \n)输出示例[0] Type: Title, Text: 光伏发电站接入电力系统技术规定... [1] Type: TableElement, Text: | 项目 | 要求 | |---|---| | 低电压穿越时间 | ≥0.15s |... [2] Type: NarrativeText, Text: 6.2.1 光伏发电站应具备一定的...→ 确认TableElement存在说明表格被正确识别下一步可针对性处理。Step 2用MarkdownHeaderTextSplitter按标题切分而非固定 token# splitter.py from langchain.text_splitter import MarkdownHeaderTextSplitter from langchain.docstore.document import Document # 构造 markdown 格式文本unstructured 的 Title NarrativeText 可转 markdown md_lines [] for el in elements: if el.category Title: level el.metadata.get(level, 1) md_lines.append(f{# * level} {el.text}) elif el.category in [NarrativeText, TableElement]: md_lines.append(el.text) md_text \n\n.join(md_lines) headers_to_split_on [(#, Header 1), (##, Header 2), (###, Header 3)] splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) docs splitter.split_text(md_text) # 打印第一个 chunk验证标题层级是否保留 print(First chunk:) print(fPage: {docs[0].metadata.get(page, N/A)}) print(fHeader: {docs[0].metadata.get(Header 1, N/A)}) print(fContent: {docs[0].page_content[:200]}...)关键优势固定RecursiveCharacterTextSplitter(chunk_size512)会把“6.2.1 低电压穿越要求”和表格切到不同 chunk而按标题切分确保“6.2.1”及其下所有内容含表格在同一 chunkmetadata中保留Header 1/Header 2后续检索可按章节过滤。Step 3用BGE-M3中文 Embedding 模型非 OpenAI# embedding.py from langchain_community.embeddings import HuggingFaceEmbeddings # BGE-M3 是当前中文 RAG 最强开源模型2024.6 发布支持 multi-vector retrieval embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-m3, model_kwargs{device: cuda}, encode_kwargs{ normalize_embeddings: True, # 必须开启否则 cosine similarity 失效 batch_size: 32 # GPU 显存允许的最大 batch } ) # 测试 embedding 效果 query 逆变器低电压穿越持续时间 vector embeddings.embed_query(query) print(fQuery vector shape: {len(vector)}) # 应为 1024 # 验证相似度计算 doc_vector embeddings.embed_documents([docs[0].page_content])[0] similarity sum(a*b for a,b in zip(vector, doc_vector)) # cosine sim (normalized) print(fSimilarity with first doc: {similarity:.3f})Step 4用ChromaDB构建向量库轻量、纯 Python、支持 metadata 过滤# vectorstore.py import chromadb from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction # ChromaDB 不依赖外部服务直接用 SQLite 存储 client chromadb.PersistentClient(path./chroma_db) collection client.create_collection( namegb19964_docs, embedding_functionSentenceTransformerEmbeddingFunction( model_nameBAAI/bge-m3 ) ) # 批量插入metadata 包含页码和标题用于后续过滤 for i, doc in enumerate(docs): collection.add( ids[fdoc_{i}], documents[doc.page_content], metadatas[{ source: GB_T_19964-2012.pdf, page: doc.metadata.get(page, 0), header1: doc.metadata.get(Header 1, ), header2: doc.metadata.get(Header 2, ) }] ) # 检索测试带 metadata 过滤精准定位“低电压穿越”章节 results collection.query( query_texts[逆变器低电压穿越持续时间要求], n_results3, where{header1: 6 低电压穿越能力} # 强制限定在第 6 章 ) print(Top result content:, results[documents][0][0][:200])5. 避坑 / 常见问题 / 排查那些让 RAG 系统静默失效的隐形陷阱现象collection.query()返回的documents与 query 完全无关比如搜“PID 效应”却返回“逆变器效率”原因BGE-M3的encode_kwargs未设normalize_embeddingsTrue导致向量未归一化cosine similarity 计算失效实际算的是 dot product长文本天然占优解决在HuggingFaceEmbeddings初始化时强制encode_kwargs{normalize_embeddings: True}并用np.linalg.norm(vector)验证向量模长是否为 1.0现象unstructured解析 PDF 后TableElement的text字段为空或只有表头没有数据原因PDF 是扫描件非文字版strategyhi_res未启用 OCR或languages[zh]拼写错误如ch导致 OCR 引擎 fallback 到英文模型解决先用pdfinfo GB_T_19964-2012.pdf检查Pages:和Encrypted:字段若Pages: 32但Text: 0 bytes确认是扫描件然后在partition_pdf中加ocr_languages[chi_sim]tesseract 语言码现象ChromaDB插入 1000 docs 后query响应时间从 200ms 涨到 3sCPU 占用 100%原因ChromaDB 默认用hnswlib做近似最近邻搜索但未建索引每次 query 都全量扫描解决插入完成后调用collection.get()触发索引构建或初始化时指定hnsw_spacecosine已在 v0.4.20 默认启用现象LangChain 的RetrievalQA链返回答案中混入大量 prompt 模板如|im_start|system\n你是一个光伏专家...原因llm使用了 Qwen 的 chat template但RetrievalQA未配置prompt导致 LLM 把检索到的 context 当作 user message 处理解决自定义 prompt明确分隔 context 和 questionfrom langchain.prompts import PromptTemplate prompt_template 基于以下上下文回答问题 {context} 问题{question} 答案 PROMPT PromptTemplate(templateprompt_template, input_variables[context, question])6. 进阶技巧用llama.cpp的embedding模式替代 LangChain构建零依赖的轻量 RAG6.1 为什么 LangChain 在边缘设备上是累赘——一次pip install带来的 12 个依赖地狱当你把 RAG 系统部署到工控机ARM 4GB RAM或无人机机载计算机时langchain的 12 个间接依赖tenacity,httpx,pydantic,openapi-spec-validator…会带来两个致命问题pydanticv2 与fastapiv0.104 的BaseModel冲突导致chromadb初始化失败httpx的异步 DNS 解析在无网络环境下 hang 死而工控机常处于离线状态。书中第 10 章给出终极轻量方案用llama.cpp自带的 embedding 模式把BGE-M3编译进llama-cli用纯 C 完成 embedding 向量检索。这不需要 Python不依赖任何网络二进制文件仅 12MB。6.2 编译llama.cppembedding 版本Ubuntu 22.04 CUDA 11.8# 1. 克隆并 checkout 支持 embedding 的分支官方 main 分支尚未 merge git clone https://github.com/ggerganov/llama.cpp cd llama.cpp git checkout refs/remotes/origin/embedding # 2. 编译支持 embedding 的 llama-cli关键ENABLE_EMBEDDING1 make clean make LLAMA_CUBLAS1 ENABLE_EMBEDDING1 -j$(nproc) # 3. 下载 BGE-M3 的 GGUF 格式需转换此处用现成的 wget https://huggingface.co/Xenova/bge-m3-GGUF/resolve/main/bge-m3.Q4_K_M.gguf # 4. 生成文档 embedding输出为 .bin 二进制向量 ./llama-cli -m bge-m3.Q4_K_M.gguf \ --embedding \ --verbose-prompt \ --ctx-size 8192 \ --threads 8 \ -f gb19964_chunks.txt \ # 每行一个 chunkUTF-8 编码 -fo embeddings.bin # 5. 查询 embedding输出 top-k 相似 chunk 的 index ./llama-cli -m bge-m3.Q4_K_M.gguf \ --embedding \ --verbose-prompt \ --ctx-size 8192 \ --threads 8 \ -p 逆变器低电压穿越持续时间要求是多少 \ -fe embeddings.bin \ -k 3gb19964_chunks.txt格式示例6.2.1 光伏发电站应具备低电压穿越能力当电网电压跌至 0.2p.u. 时应能保证不脱网连续运行 0.15 秒。 6.2.2 低电压穿越期间光伏发电站应向电网提供无功功率支持...输出示例[0] distance: 0.124, index: 42, text: 6.2.1 光伏发电站应具备低电压穿越能力... [1] distance: 0.187, index: 43, text: 6.2.2 低电压穿越期间光伏发电站应向电网提供...6.3 用 C 实现最小 RAG 推理循环20 行代码无 Python 依赖// minimal_rag.cpp #include iostream #include fstream #include vector #include string #include algorithm struct Chunk { int index; float distance; std::string text; }; int main() { // 读取 chunks假设已预处理为数组 std::vectorstd::string chunks { 6.2.1 光伏发电站应具备低电压穿越能力..., 6.2.2 低电压穿越期间光伏发电站应向电网提供... }; // 模拟 llama-cli 的 embedding 输出实际从 embeddings.bin 读取 std::vectorChunk results { {0, 0.124f, 6.2.1 光伏发电站应具备低电压穿越能力...}, {1, 0.187f, 6 p a hrefhttps://download.csdn.net/download/weixin_42403632/90560206 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p