ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LLM应用开发实战:从RAG到Agent的完整指南

LLM应用开发实战:从RAG到Agent的完整指南 1. 从一份收藏夹说起awesome-llm-apps 到底在解决什么问题过去一年多我几乎每周都会收到类似这样的私信“刚接触大模型想知道现在 LLM 到底能做什么、业界都有哪些成熟玩法有没有一个地方能一次性看全”说实话这个问题在 2023 年初还很好回答因为那时候能跑的 LLM 应用掰着手指头都能数过来。但到了现在GitHub 上跟 LLM 相关的仓库已经多到搜都搜不完光“LangChain”一个关键词就能翻出上千个结果更别提各种 agent、RAG、微调、评估框架了。我最初关注 awesome-llm-apps 这个仓库也是因为同样的困惑。它是一个典型的“精选清单”类项目帮你把 LLM 生态里值得参考的应用、框架、工具和论文按类别整理好。这类仓库的好处在于你不用自己从零去趟一遍整个生态只需要跟着维护者已经筛选过的路线走就能快速知道当前的主流方案、热门方向以及哪些东西已经被验证过是靠谱的。说句实话awesome 系列仓库在技术圈里一直存在争议。有人觉得它就是个“收藏夹”收藏了从来不看的占大多数也有人觉得这类仓库信息密度太低不如直接读源码或官方文档。但我个人观点是对于 LLM 应用开发这件事awesome-llm-apps 的价值并不在于“资源列表”本身而在于它提供了一张生态地图。你不需要把它从头到尾读完只需要在决定技术方向、选型框架、或者想看看别人怎么做某个功能时把它当做一个起点索引。这篇文章我会基于这个仓库的内容脉络加上我自己实际开发 LLM 应用的经验把当前大模型应用开发的几个核心方向做一次系统梳理。从基础工具链、到 RAG 应用、再到 autonomous agent每一块我都会给出选型思路、落地细节和常见坑点希望给正在规划 LLM 应用开发的团队或个人开发者一份可以直接参考的实战地图。2. LLM 应用生态全景从工具链到上层应用2.1 基础工具层模型推理与服务化是地基很多人开始接触 LLM 时第一反应就是“调用 OpenAI API”好像 LLM 应用就等于“拿着别人的模型接口写代码”。但实际上进入 2024 年之后LLM 应用的基石已经远远不止“调用 API”这么简单了。如果你打开 awesome-llm-apps 的仓库会发现其中很大一部分篇幅在收录模型推理、服务化部署、量化加速等相关项目。我们先把“LLM”这个词拆开看。现在的 LLMLarge Language Model大语言模型应用开发实际上分成了模型端和推理端两个层面。模型端关注的是用什么基座模型、是闭源调用还是开源部署、需不需要微调、怎么评估效果。推理端关注的是部署在什么硬件上、用哪种推理框架、怎么优化吞吐和延迟、怎么控制成本。在模型调用层面目前业内已经形成了比较清晰的选型逻辑。闭源模型比如 OpenAI 的 GPT 系列、Anthropic 的 Claude 系列在通用能力和指令遵循上依然有优势适合追求效果、不想投入太多工程资源的团队开源模型比如 Llama 系列、Qwen 系列、Mistral 系列则胜在数据可控、可私有化部署、长期成本更低特别适合有数据合规要求或需要深度定制的场景。我在实际做项目时有个比较深的体会如果你做的是 To B 项目尤其是金融、医疗、政务这类对数据安全敏感的领域尽早把开源模型跑通比什么都重要。因为客户的第一句话往往是“数据不能出内网”这就意味着你从一开始就要考虑私有化部署。而开源模型 一套成熟的推理优化方案比如 vLLM、TensorRT-LLM、SGLang是目前最稳妥的组合。2.2 应用框架层为什么大家都在聊“LLM 框架”聊完底层我们来看应用开发层。这个层面是 awesome-llm-apps 收录最丰富的部分也是很多新人最容易迷失的地方。所谓“LLM 框架”简单说就是帮你把“调用模型”这件事封装成更高阶能力的工具集。比如 LangChain、LlamaIndex、Semantic Kernel 这些框架本质上都在解决同一个问题让开发者不用每次都从零写 prompt 拼接、上下文管理、工具调用、记忆存储这些繁琐的中间逻辑而是用一套抽象好的 API 快速搭出应用骨干。我用一个生活化的类比来解释框架存在的意义没有框架的时代搭一个 LLM 应用就像自己从砌砖开始盖房子——地基要自己打、墙要自己垒、水电要自己铺虽然能盖出来但周期长、返工多有了框架之后你拿到的是一套“预制构件 施工图”大部分标准结构已经有了你只需要根据自己项目的特殊需求去定制关键部分。不过框架不是越多越好。我在社区里看到很多团队犯了“框架迷信”的问题——项目刚启动就上了全套 LangChain 向量数据库 Agent 工具链结果做到一半发现框架的抽象泄漏太严重遇到问题根本不知道是框架的 bug 还是自己代码的 bug最后被迫重写。我的建议是小项目直接裸写用 prompt 工程 最少的依赖就能跑通中大型项目再引入框架而且要挑选一个“活得久、社区活跃、抽象层级合理”的框架深挖。比如就做 RAG 场景的话LlamaIndex 比 LangChain 更聚焦、上手成本更低如果要做多步骤 agent 工作流LangChain 的工具生态更丰富。选框架就像选工作搭档——不是越强越好而是匹配度越高越好。2.3 应用层爆发从聊天机器人到垂直场景落地框架之上就是真正的应用层了。打开 awesome-llm-apps 仓库你会看到 LLM 的应用方向已经覆盖了智能客服、代码生成、文档问答、SQL 转换、文本摘要、翻译润色、教育辅导、法律咨询、医疗问诊、数据分析、智能家居控制等几乎所有行业。这里有个值得注意的趋势通用聊天机器人已经不再是 LLM 应用的主流了。早期大家觉得“能对话”就很厉害但现在客户要的都是“能干活”。什么叫能干活你要做智能客服就得能对接工单系统、能查询订单状态、能在答不出来时优雅转人工你要做文档问答就得能处理 PDF/Word/扫描件、能引经据典给出原文出处、能区分“不知道”和“不想回答”。从技术实现角度看当前 LLM 应用已经形成了几条相对成熟的技术路线第一类是增强检索问答RAG核心逻辑是把私有知识库切分成向量、构建索引用户提问时先做向量检索召回相关文档片段再拼接到 prompt 里交给 LLM 生成答案。这类方案最适合“基于私有知识做问答”的场景比如企业知识库、合同审查、政策解读、学术文献综述等。第二类是自主 Agent 工作流核心逻辑是LLM 不只是“回答问题”而是作为一个“大脑”去编排一系列操作——拆解任务、调用外部工具搜索、代码执行器、数据库、API、观察结果、调整策略、最终交付结果。这类方案适合流程复杂、需要多步推理的任务比如竞品分析报告生成、批量数据处理、自动化测试等。第三类是垂类微调模型核心逻辑是在开源基座模型基础上用行业数据做继续预训练或指令微调让模型本身具备特定领域的知识和表达习惯。这类方案适合对输出质量和领域专业性要求极高的场景比如医疗诊断辅助、法律文书生成、金融研报分析等。三类路线不是互斥的实际上很多成熟产品是混合使用的。比如你先用 RAG 做知识召回再用 agent 做任务编排最后用微调模型保证输出风格和领域准确性——这种组合方式在 2024 年已经是非常常见的架构了。3. 从 LLM 原理到核心组件把“黑盒”拆开看3.1 LLM 到底是如何工作的从预训练到推理很多做应用开发的人对 LLM 的使用很熟练但一遇到“模型效果不好”或者“为什么这么设计”的问题就开始抓瞎。我觉得应用开发者虽然不需要成为算法专家但至少要理解 LLM 的几个核心原理否则你连 prompt 都写不好、连错误都排查不了。先说预训练。LLM 的预训练本质上是一个“大型完形填空”任务模型在大规模文本数据上学习——给它一段文本的前半部分让它预测下一个词是什么。通过海量数据的反复训练模型学会了语言的语法、事实知识、推理模式、代码逻辑等一系列能力。这也是为什么 LLM 的参数量和数据量如此重要的原因——能力很大程度上是“喂”出来的。这里要补充一个重要的知识点损失函数。在预训练阶段模型的核心优化目标是交叉熵损失Cross-Entropy Loss简单理解就是模型对每个位置的下一个词预测要尽量让正确词的概率最大。你可能会问为什么预训练不直接优化“回答正确率”因为“下一个词预测”这个任务足够通用且自监督——不需要人工标注只要有文本就能训练而且优化这个目标的过程会让模型学到大量隐含的世界知识。再说推理。推理阶段模型做的事情其实和预训练是一样的——逐个生成 token每次生成时根据当前的上下文预测下一个词的概率分布。但这就引出了应用开发中最关键的参数问题temperature温度和top_p核采样。temperature 控制的是生成文本的随机性。temperature 越高概率分布越平滑模型越倾向生成多样化、有创意的内容temperature 越低概率分布越尖锐模型越倾向生成保守、确定性的内容。我一般做代码生成、SQL 转换、数据提取这类任务时会把 temperature 调到 0 或接近 0因为这类任务“答错”的代价很高确定性优先做文案创作、头脑风暴、角色扮演时会把 temperature 调到 0.7 甚至更高让输出更有灵气。top_p 是另一种采样策略它控制的是“从概率累积到多少的词表范围内采样”。top_p0.9 意味着每次生成时只从前 90% 概率质量的词里采样丢弃掉长尾的低概率词。这样做的目的是平衡多样性和质量——既不会像纯贪心解码那样单调又不会采样到完全离谱的词。理解这些原理之后你就明白为什么“prompt 怎么写都一样”这种说法是错的。LLM 的推理过程本质上是“在概率空间里做搜索”你的 prompt 就是在限定搜索的范围和方向写得好不好直接影响搜索效率和结果质量。3.2 RAG 应用实战从文档切分到检索增强RAGRetrieval-Augmented Generation是当前 LLM 应用开发中落地最多、见效最快、坑也最多的方向。几乎每个想用 LLM 处理私有知识的团队第一站都会走到 RAG。我们来拆解一下 RAG 的完整流程和每个环节的关键要点。整个 RAG 流程可以概括为四个环节文档加载 → 文本切分 → 向量化与索引 → 检索与生成。文档加载环节的难点不在于“读取文件”而在于“解析格式”。PDF 是最常见的坑有的 PDF 是文本型的可以直接提取文字有的是扫描件需要 OCR 识别还有的是复杂的多栏排版、包含表格和图片直接提取会导致文本顺序错乱。我处理 PDF 的经验是优先用 PyMuPDFfitz做文本提取遇到扫描件再用 PaddleOCR 或 Tesseract对于多栏文档需要在提取后做版面分析否则切出来的 chunk 是“跨栏拼接”的语义完全断裂。文本切分是 RAG 中最容易被低估的环节也是最直接影响检索效果的因素之一。切分粒度太粗一个 chunk 包含太多不相关信息检索召回的噪音就大切分粒度太细单个 chunk 可能信息不完整模型生成时缺少上下文。我常用的切分策略是按标题层级优先切分Markdown/HTML 文档天然有结构没有明确结构时按段落切分段落过长时再按句子边界二次切分每个 chunk 控制在 300-500 字左右。同时要设置相邻 chunk 之间的 overlap重叠避免切分点恰好落在语义中间导致信息丢失。向量化环节的核心是选择合适的 embedding 模型。现在中文场景下比较可靠的选择包括BGE 系列BAAI/bge-large-zh、M3E、OpenAI 的 text-embedding-3 系列、智源的 bge-m3 等。选择 embedding 模型时要注意一个关键指标检索效果是否匹配你的领域文本。通用 embedding 模型在正式文档、新闻语料上表现不错但遇到专业术语密集的垂直领域比如医疗、法律、工程可能需要用领域数据做 fine-tune或者选用已经在类似语料上预训练过的模型。检索与生成环节有几个容易被忽略的细节。第一纯向量检索不一定是最优解业界现在普遍采用“混合检索”策略向量检索负责语义召回BM25 关键词检索负责精确匹配最后把两者的结果做融合排序。第二检索到的文档片段不能全塞进 prompt要根据相关性做重排rerank。推荐用专门的 rerank 模型比如 BGE-reranker它比 embedding 模型更精准地判断“哪段文本真正回答了用户的问题”。第三生成阶段的 prompt 设计要明确告诉模型只依据提供的资料回答不要编造如果资料中没有答案要明确说“不知道”。3.3 意图识别方案对比从 TextCNN 到 BERT 再到 LLM聊到 LLM 应用就绕不开意图识别Intent Recognition这个经典任务。在很多实际产品中意图识别是整个对话系统的第一环——用户说了一句话系统得先判断“他想干什么”然后才能路由到对应的处理流程。传统的意图识别方案有两条路线。基于规则/关键词的方案最轻量通过正则匹配、关键词词库就能实现适合意图种类少、表述固定的场景但它几乎无法处理用户换一种说法的情况。基于机器学习/深度学习的方案是更常见的选择早期用 TextCNN——把句子用词向量表示后通过卷积操作提取局部特征做分类模型轻量、训练快适合文本较短、特征明显的任务后来用 BERT 及其变体——通过预训练语言模型把整个句子的语义编码成向量再在顶部加分类层效果明显超过 TextCNN尤其擅长处理语义相似但字面差异大的表述。那么 LLM 出现之后意图识别应该怎么做这里要说一个很多入门者容易踩的坑不要一上来就用 LLM 做意图识别。用 LLM 做意图识别的优点是零样本不需要标注数据、灵活意图可以随时改、能处理复杂语义比如“帮我查一下昨天订单为什么还没发货顺便看看能不能退款”这种包含多个意图的复合句。但缺点是延迟高一次推理可能要几百毫秒到几秒、成本高每次调用都在消耗 token、可控性差模型可能输出不在预设意图集里的结果。我的实际经验是对于意图体系固定、量级大、对延迟敏感的场景用 BERT 类模型做分类是性价比最高的方案对于意图体系经常变化、长尾意图多、需要理解复杂语义的场景才考虑用 LLM 做意图解析而且最好配合结构化输出比如用 JSON 格式返回意图列表和置信度。还有个常见的做法是“级联架构”先用轻量级的 TextCNN 或 BERT 模型做第一层粗粒度意图分类当置信度低于阈值时再调用 LLM 做二次判断。这样 90% 的请求走快速通道只有模棱两可的请求才消耗 LLM 的算力和成本。我在一个智能客服项目中实践过这种方案整体响应时间降低了 60% 以上而且意图识别的准确率比单用 BERT 提升了 12 个百分点。3.4 Agent 如何打破 LLM 的能力边界如果说 RAG 解决的是“LLM 知识不够”的问题那么 Agent 解决的就是“LLM 只会说不会做”的问题。这也是当前 LLM 应用开发中最热门、最有想象空间、也是落地最难的方向之一。Agent 的核心结构可以用三个词概括规划Planning、工具Tools、反思Reflection。规划是指 LLM 将一个大任务拆解成多个子任务并决定执行的先后顺序。这个能力来源于 LLM 的推理能力——你给模型一个目标它能在思维链的引导下设计出执行路径。但规划不是一次性的好的 Agent 会“边做边规划”每完成一步就根据当前状态调整后续计划。这就像你做饭的时候本来计划做四菜一汤结果发现没买豆腐你会调整菜单而不是傻等豆腐出现。工具是 Agent 的“手脚”。LLM 本身只能输出文字但通过让模型学会调用外部 API 和函数它就能完成“读取文件、执行代码、查询数据库、调用搜索引擎、发送 HTTP 请求”这些实际动作。实现机制上现在主流做法是“函数调用Function Calling”你把工具的描述和参数结构告诉模型模型在生成回复时输出一个结构化的“调用请求”你的代码解析这个请求、执行对应函数、把结果返回给模型模型再基于结果继续推理。反思是 Agent 和普通“流程脚本”最本质的区别。简单说就是Agent 在执行完一个动作后会观察结果是否符合预期如果不符合它会自我纠正、换一种方式再来。比如写代码的 Agent它会写完一段代码后尝试运行如果报错了它会读报错信息、修改代码、重新运行直到成功或达到重试上限。这种“循环 反馈 修正”的机制让 Agent 能够处理不确定性很高的任务。在我评测过的各种 agent 框架和项目中效果较好的一般都遵循一个模式把任务拆解逻辑写得足够清晰、给模型足够的上下文信息、工具函数的输入输出设计得足够“陡峭”即信息密度高、噪声少。反之很多失败案例的共同特征是让模型在信息不足的情况下做决策、工具函数输入输出含糊导致模型困惑、没有设置步骤上限导致死循环。4. 实操篇5 小时搭一个可用的私有知识库问答系统4.1 方案选型与仓库结构设计前面讲了不少原理和趋势这一节我们落到具体实操上。我以“私有知识库问答系统”为例完整演示一个 LLM 应用从零到可用的搭建过程。选这个方向是因为一是它覆盖了 RAG 的完整链路二是它几乎是当前企业场景里需求最刚性、最容易落地见效的应用类型。先说方案选型。我的设计原则是“够用就好逐步演进”。技术栈选择如下模型基座用 Qwen2.5-7B-Instruct中文能力强、MIT 协议可商用、7B 参数在单卡上就能跑部署用 vLLM 做推理加速。如果你没有 GPU 资源也可以用 OpenAI-compatible 的 API 接口替代代码不用改太多。向量库用 Milvus Lite 或 Chroma单机场景下足够不需要上来就上分布式向量数据库那会引入大量运维复杂度。Embedding用 BAAI/bge-large-zh-v1.5中文效果在开源模型里是头部水平而且支持 512 token 的输入长度配合我们的切分策略够用。后端框架用 FastAPI 提供 API 服务配合 LangChain 的 LCEL 语法做 RAG 流程编排或者直接用 LlamaIndex 的 QueryPipeline。我下面演示用 LangChain 的版本因为它的生态更通用。项目的目录结构建议这样设计llm-qa-system/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── config.py # 配置管理 │ ├── rag/ │ │ ├── loader.py # 文档加载 │ │ ├── splitter.py # 文本切分 │ │ ├── embedder.py # 向量化服务 │ │ ├── retriever.py # 检索器 │ │ └── generator.py # 生成器 │ └── api/ │ ├── routes.py # API 路由 │ └── schemas.py # 数据模型 ├── data/ │ ├── raw/ # 原始文档 │ └── vector_store/ # 向量库持久化 ├── scripts/ │ ├── ingest.py # 数据入库脚本 │ └── evaluate.py # 效果评估脚本 └── requirements.txt这样分层的目的是每层职责单一、替换成本低。比如你以后想把 LangChain 换成 LlamaIndex只需要改 rag 目录下的实现API 层不用动想换向量库只需要改 retriever 里的连接配置。4.2 数据入库全流程加载、切分、向量化数据入库是整个 RAG 系统中“做得越细致、后期效果越好”的环节。很多团队在这步图省事结果上线后检索效果惨不忍睹最后又回头补课。第一步是把文档加载进来。我写的 loader 会优先按文档类型分发# app/rag/loader.py from langchain_community.document_loaders import PyMuPDFLoader, TextLoader, Docx2txtLoader def load_document(file_path: str): if file_path.endswith(.pdf): loader PyMuPDFLoader(file_path) elif file_path.endswith(.docx): loader Docx2txtLoader(file_path) elif file_path.endswith(.txt) or file_path.endswith(.md): loader TextLoader(file_path, encodingutf-8) else: raise ValueError(f不支持的文件格式: {file_path}) documents loader.load() # 清理一下文档内容去掉多余空行和特殊字符 for doc in documents: doc.page_content re.sub(r\n{3,}, \n\n, doc.page_content) doc.page_content re.sub(r[ \t]{2,}, , doc.page_content) return documents这里有个细节要注意PyMuPDFLoader对文本型 PDF 效果好但对扫描版 PDF 无能为力。如果你在企业场景中经常遇到扫描件建议在 loader 里加一个 OCR 分支用 PaddleOCR 或 RapidOCR 兜底。第二步是文本切分。这个步骤我强烈建议你用“按结构切分 按长度二次切分”的组合策略# app/rag/splitter.py from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter def split_documents(documents): # 先按 Markdown 标题结构切分 headers_to_split_on [ (#, H1), (##, H2), (###, H3), ] markdown_splitter MarkdownHeaderTextSplitter(headers_to_split_on) # 对结构切分后的结果再做长度控制 text_splitter RecursiveCharacterTextSplitter( chunk_size400, chunk_overlap80, separators[\n\n, \n, 。, , , ., , ], length_functionlen, ) all_chunks [] for doc in documents: if doc.metadata.get(source, ).endswith(.md): sections markdown_splitter.split_text(doc.page_content) for section in sections: sub_chunks text_splitter.split_text(section.page_content) for i, sub_chunk in enumerate(sub_chunks): all_chunks.append({ content: sub_chunk, metadata: { source: doc.metadata[source], section: section.metadata.get(H2, ), chunk_index: i, } }) else: sub_chunks text_splitter.split_text(doc.page_content) for i, sub_chunk in enumerate(sub_chunks): all_chunks.append({ content: sub_chunk, metadata: {source: doc.metadata[source], chunk_index: i} }) return all_chunks为什么要用这种组合策略因为标题结构切分能保证“同一个 chunk 内的话题一致”而长度控制能保证“chunk 大小适合 embedding 模型和 LLM 输入”。我在实际测试中发现直接按固定长度硬切的长文本经常出现“一个 chunk 里包含两个完全无关的话题”的情况检索召回时容易答非所问。用结构切分能显著减少这种问题。第三步是向量化和入库。这里有一个容易踩的坑embedding 模型选择后不要频繁更换。因为更换 embedding 模型意味着所有已入库的向量都要重新计算而且新旧向量之间的余弦相似度没有可比性Mix 使用会导致检索结果混乱。# scripts/ingest.py from langchain_community.embeddings import HuggingFaceBgeEmbeddings from langchain_community.vectorstores import Milvus # 初始化 embedding 模型 embedding_model HuggingFaceBgeEmbeddings( model_nameBAAI/bge-large-zh-v1.5, encode_kwargs{normalize_embeddings: True}, # bge 模型建议开启归一化 ) # 连接向量库Milvus Lite vector_store Milvus( embedding_functionembedding_model, collection_nameknowledge_base, connection_args{uri: ./data/vector_store/milvus.db}, ) # 批量入库 chunks split_documents(loaded_documents) texts [c[content] for c in chunks] metadatas [c[metadata] for c in chunks] vector_store.add_texts(textstexts, metadatasmetadatas)入库完成之后建议做一个自查动作随机抽几个问题手动做向量检索看看召回的前 5 个 chunk 是否真的和问题相关。这一步可以提前发现切分策略或 embedding 模型的问题比全部做完再评估效率高得多。4.3 检索与生成配置混合检索 重排 防幻觉 Prompt知识入库之后接下来就是搭建“检索 → 增强 → 生成”的完整链路。检索环节我推荐实现一个轻量的混合检索。具体做法是向量召回 BM25 召回 结果融合。LangChain 支持通过EnsembleRetriever实现# app/rag/retriever.py from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever from langchain_community.vectorstores import Milvus def build_retriever(vector_store, documents_for_bm25, top_k8): # 向量检索器 vector_retriever vector_store.as_retriever( search_typesimilarity, search_kwargs{k: top_k} ) # BM25 关键词检索器 bm25_retriever BM25Retriever.from_documents(documents_for_bm25) bm25_retriever.k top_k # 融合检索器权重可以自己调 ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.3, 0.7] ) return ensemble_retriever为什么需要混合检索因为向量检索擅长语义相似匹配但在精确关键词匹配上往往不如 BM25。比如用户问“2024 年 Q3 的毛利率是多少”如果知识库里相关文档中明确出现了“毛利率”这个词但语义上和用户的问法不完全匹配比如文档里写的是“净利率”但含义相近向量检索可能召回到语义最相关但并非用户想要的段落而 BM25 能确保“毛利率”这个精确词一定被召回。召回之后不要直接把 top 8 的结果全部塞给 LLM。这一步我建议加上**重排rerank**环节。做法是先用混合检索召回 10-20 个候选片段再用 reranker 模型精排选前 4-6 个这样做能显著提升生成质量同时控制 prompt 长度、降低 token 成本。# app/rag/retriever.py (续) from BCEmbedding import RerankerModel reranker RerankerModel(model_name_or_pathmaidalun1020/bce-reranker-base_v1) def rerank_documents(query, documents, top_n5): contents [doc.page_content for doc in documents] scores reranker.compute_score([[query, content] for content in contents], normalizeTrue) scored_docs sorted(zip(documents, scores), keylambda x: x[1], reverseTrue) return [doc for doc, score in scored_docs[:top_n]]生成环节的核心是 prompt 设计。这里我给出一个经过多轮优化、在多个项目上验证有效的基础模板# app/rag/generator.py from langchain_openai import ChatOpenAI rag_prompt_template 你是一个企业内部知识库助手。请根据以下资料片段回答用户问题。 规则 1. 只依据提供的资料回答问题不得编造不在资料中的信息。 2. 如果资料中没有足够的信息回答用户问题请直接回答“根据现有资料无法回答该问题”。 3. 回答时先给出结论再用不超过 3 点的要点补充依据。 4. 如果资料之间有矛盾请明确指出矛盾情况。 资料片段 {context} 用户问题 {question} 请回答 def generate_answer(question, contexts): llm ChatOpenAI( modelgpt-4o-mini, # 或指定你的本地模型地址兼容 OpenAI 协议即可 temperature0.2, max_tokens1024, ) context_text \n\n---\n\n.join([ctx.page_content for ctx in contexts]) prompt rag_prompt_template.format(contextcontext_text, questionquestion) response llm.invoke(prompt) # 最好同时把引用来源返回给前端增强可信度 sources [{content: ctx.page_content[:80], source: ctx.metadata.get(source, ), section: ctx.metadata.get(section, )} for ctx in contexts] return response.content, sources这里要特别强调 prompt 里的“3 条规则”不是随便写的。第 1 条防幻觉是最关键的一条第 2 条防“硬答”避免模型在信息不足时强行生成一个看起来合理但错误答案第 3 条控制输出结构让答案看一眼就能抓住重点。这几条规则每一条都是我在项目测试中遇到真实错误后才加上的。最后把这些串成一个 FastAPI 接口一个可用的私有知识库问答系统就算跑通了。我实测下来在普通配置的服务器上一次完整问答的响应时间在 2-5 秒之间取决于检索的文档数量和生成的长度这个表现对于企业内部知识库场景是完全可以接受的。4.4 效果评估不要只看“答得爽”要看“答得准”很多团队做 RAG 应用时最容易犯的错误就是“自测几个问题觉得不错就上线了”。这种凭感觉的评估方式在 Demo 阶段没问题但一旦面临真实用户的复杂提问效果往往立刻现出原形。正规的做法是把 RAG 评估拆成两个独立的环节检索评估和生成评估。检索评估关注的是“正确的文档片段有没有被召回”。常用的指标是召回率RecallK和命中率Hit Rate。具体做法是准备一批问题 正确答案所在的文档片段对测试时用检索器去召回看正确答案对应的片段是否出现在前 K 个结果里。这一步可以用开源的评估工具比如 LlamaIndex 的RetrieverEvaluator半自动化完成。生成评估关注的是“最终答案是否正确、完整、忠实于资料”。忠实度Faithfulness是最需要关注的维度——它衡量的是生成答案中的信息是否都能在检索到的资料中找到依据而不是模型自己编造的。除了人工评估也可以用 RAGAS 这类开源框架做自动化评估用 LLM 作为裁判来打分。我在多个项目上的经验是先优化检索再优化生成。如果检索环节的命中率不高再怎么调生成 prompt 都是白搭——因为模型根本看不到正确的资料。如果你的检索命中率已经能做到 90% 以上再花精力去调生成的 prompt 和温度参数回报率会更高。5. 深入 autonomous agents 方向从概念到真实项目5.1 现有框架选型LangChain、AutoGPT、MetaGPT 该怎么选聊完了 RAG 这个“LLM 应用的确定性场景”我们来聊聊 autonomous agents 这个明显更前沿也更有挑战的方向。“LLM-powered autonomous agents”这个概念在过去一年里被讨论得非常多——让 LLM 不只是回答问题而是作为一个自主系统理解目标、拆解任务、调用工具、自我反思、循环执行直到完成任务。但在开始做之前你一定绕不开框架选型的问题。目前市面上主流的 Agent 框架大致可以分成三个流派。第一个流派是 LangChain / LlamaIndex 为代表的“开发框架派”。它们提供的是底层的编排能力——你可以自定义 agent 的每个环节模型怎么选、工具怎么定义、记忆怎么存储、规划怎么执行。这个流派的优点是灵活、可控制、和现有代码架构融合度高缺点是开发量大、需要你理解 agent 的底层机制而不是“开箱即用”。第二个流派是 AutoGPT / BabyAGI 为代表的“自主 agent 派”。它们强调“给定目标全自动执行”agent 自己规划每一步动作直到完成目标。优点是演示效果极其震撼——你输入一个目标它自己拆出十几个步骤并执行缺点是在真实业务场景中可控性太差跑着跑着就跑偏或者陷入无尽的循环之中。我见过不少团队用 AutoGPT 做原型但几乎没有看到用它上生产的案例。第三个流派是 MetaGPT / ChatDev 为代表的“多 agent 协作派”。它们模拟了一个团队Product Manager、Architect、Engineer、QA 各自是一个 agent通过预设的协作流程完成复杂任务。这个流派在“软件自动开发”等特定场景下表现亮眼——因为软件开发本身有清晰的流程和交付物多个 agent 各司其职就像真实团队。缺点是系统复杂度高、token 消耗极大、针对非软件开发任务时表现一般。我的建议是如果你有工程化能力优先选 LangChain 或 LlamaIndex 这类底层框架自己搭 agent如果你想快速看效果、理解 agent 的运行机制可以用 AutoGPT 跑几个 demo 感受一下如果你要做的任务恰好是软件开发/代码生成这一类结构化的任务MetaGPT 值得一试。5.2 Agent 的核心机制拆解Plan、ReAct、Reflect不管用哪种框架要做出一个“不智障”的 agent你必须理解它背后的几个核心机制。我把它们归纳为 Plan、ReAct、Reflect 三个词。Plan规划Agent 的第一步是理解目标并制定计划。最简单的实现方式是“先让 LLM 输出一个步骤列表然后逐步执行”。比如目标是“调研一下开源 LLM 推理框架的现状”agent 的计划可能是1搜索相关资料2总结每个框架的特点3对比分析4生成报告。这个计划不是一次性的在执行过程中很可能需要调整。好的 agent 设计应该让 LLM 在每完成一步后重新评估计划而不是机械地执行完预设的所有步骤。ReAct推理与行动ReAct 是目前最主流的 agent 工作范式核心思想是交替进行“推理Reasoning”和“行动Action”。每一步中agent 先分析当前状态Thought然后决定调用哪个工具去获取信息或执行操作Action工具返回结果后agent 再基于新信息继续推理Observation。这个过程循环往复直到得到最终答案。ReAct 的本质是让 LLM 的推理过程与外部环境交互而不是闭门造车。举个生活化的例子你开车去一个陌生的地方你不会只凭出发前规划好的路线走到底而是每到一个路口看路标观察、判断方向推理、然后做出转弯操作行动走错了再调整。Reflect反思反思机制是 agent 进化的关键也是多数失败案例中最缺失的一环。反思的意思是当行动结果不符合预期时agent 能够自我审视“哪里出了问题”然后调整策略。比如一个写代码的 agent执行完脚本后报错了它能做的就是读报错信息 → 猜测可能的错误原因 → 修改代码 → 重跑。没有反思机制的 agent就像一个不会吸取教训的实习生——同一个错误犯十遍。实现反思并不复杂常见做法是当工具调用失败时把错误信息反馈给 LLM并明确要求它分析错误原因并给出修正方案这个“错误信息”本身就是最直接的反省素材。5.3 实战案例基于 LLM 的销售线索自动调研 Agent理论讲完了我们来看一个我实际做过的 agent 案例——销售线索自动调研 Agent。背景是这样一个做 To B 软件销售的团队每天要花大量时间在“调研潜在客户”这件事上——打开客户官网看业务介绍、查他们最近有没有融资/招聘/产品发布动态、看看有哪些高层变动然后整理成一份简报。这个流程重复、耗时非常适合自动化。这个 agent 的架构设计如下任务输入客户公司名称比如“XX云计算有限公司”任务拆解用户输入公司名后agent 自动规划出子任务1获取公司基本工商信息2搜索公司简介和主要产品3搜索最近一个月新闻/动态4搜索关键人员信息5汇总输出结构化简报。工具集这里定义了四个工具——工商信息查询 API、搜索引擎、网页抓取器把搜索到的网页正文抓下来给 LLM 读、以及一个“生成 Markdown 报告”的输出工具。执行逻辑agent 用 ReAct 循环每完成一个子任务就把信息写入上下文直到所有子任务完成最后调用“生成报告”工具。一个值得注意的设计细节是我特意把“搜索”工具设计成能接收“搜索关键词”和“时间范围”两个参数这样 agent 可以自己构造更精确的搜索词比如”XX云计算有限公司 2024 融资“而不是把所有信息塞进一次搜索里。把工具设计成“输入输出明确、参数语义清晰”是 agent 能否有效工作的关键。这类 agent 落地后的效果原来销售人员做一份客户简报需要 30-60 分钟用 agent 之后压缩到 3-5 分钟而且格式标准化程度更高。虽然报告中部分信息需要二次校验但作为初稿实际价值非常大。5.4 Agent 落地中的六个高频坑Agent 方向看起来前途无量但落地过程可以说处处是坑。我总结了自己和社区里高频遇到的六个问题每一个都值得开发者重视。坑一Agent 陷入无限循环。这是最常见的问题——agent 不停地调用同一个工具、得到同样的结果、然后再次调用陷入死循环。解决办法有两个一是全局设置最大迭代步数比如 15 步超过后强制停止并输出当前进度二是让 LLM 在每一步推理时明确输出“下一步动作”并设置规则——如果当前动作和上一步动作完全一样且没有新信息则视为重复自动跳过。坑二上下文窗口被撑爆。Agent 每执行一步工具返回的结果都会追加到上下文里。多轮循环后大量信息堆积很容易超过上下文窗口限制。解决办法是对工具返回的长文本做摘要/截断再放入上下文定期对历史信息做压缩总结对已经完成且不再需要的中间信息做清理。坑三工具调用格式错误。这个问题在接非标准工具时特别常见——LLM 生成的 JSON 参数不合法、字符串少了个引号、参数类型对不上都可能导致工具调用失败。解决办法是在调用工具前加一层 schema 校验和自动修复对简单的格式错误比如多余逗号、单双引号不匹配可以先用正则修复再交给 json.loads 解析。坑四Agent 在关键决策点“自作主张”。LLM 的决策本质上是概率性的你无法保证它在每一次推理时都选择你期望的路径。对策是在 prompt 中明确限定工具调用的边界对高风险操作比如删除文件、发送消息、扣费操作加一层人工确认机制用确定性代码做“干路由”让 LLM 只负责“生成内容”而不是“做决策”。坑五成本失控。Agent 的 token 消耗量往往远超预期尤其是多轮循环 长上下文的场景一次完整任务的 token 消耗可能是普通问答的几十倍。对策是给每次任务设置 token 预算上限优先使用便宜的模型做中间步骤推理比如用 7B 模型做子任务只有最终汇总时才调用更强的模型对工具返回的原始数据进行压缩后再入上下文。坑六评估和调试困难。Agent 的执行过程存在大量不确定性——同一个输入两次运行的结果可能完全不同这让“修 bug”变得非常困难。推荐的实践是在 agent 的每个关键步骤都打日志包括输入、输出、工具调用参数和结果支持“手动重放”——把某次失败的完整 trace 喂给另一个 LLM让它分析失败原因对于生产环境的高频任务尽量把流程“半固化”——固定任务拆解模板让 LLM 只在局部做微调而不是每次完全自由发挥。6. 垂域 LLM 的数据准备知识库质量决定应用上限6.1 通用模型和垂域模型的分野在 awesome-llm-apps 收录的项目里有一类方向正在变得越来越多垂直领域的大模型应用英文叫 Vertical Domain LLM。这背后有一个非常现实的问题——通用大模型虽然知识面广但在特定行业里的表现往往不够“专业”。举个例子你让 GPT-4 写一段骨科疾病的诊断建议它可能能写出像模像样的内容但如果你让一位三甲医院的骨科医生来看会发现里面的诊断标准过时、用药方案不够精准、甚至在某些罕见病例上出现明显的“一本正经胡说八道”。这就是通用模型和垂域模型之间的差距。垂域 LLM 的做法通常是在通用基础模型之上用行业专属数据进行继续训练或微调让模型学会特定行业的“行话”和“规则”。但我要先泼一盆冷水垂域模型的效果上限很大程度上由数据质量决定而不是由训练技巧决定。我见过很多团队花大价钱买 GPU、花大量时间调训练参数但数据准备环节只是简单爬取了一批行业文章就开练。结果训练出来的模型要么学到一堆噪声知识要么干脆“灾难性遗忘”——把通用能力丢掉了。数据准备这件事真的值得你花整个项目至少一半的时间去做。6.2 垂域数据准备的四步法基于我在垂域 LLM 项目上的经验这里分享一套比较实用的垂域数据准备流程一共四步采集、清洗、结构化、质检。第一步是数据采集。首先要明确数据源的范围和边界例如做医疗垂域模型需要哪些类型的语料——医学教材、临床指南、药品说明书、病历脱敏数据、医学论文、患者科普文章每种数据源的特点和质量差异都很大。采集时要注意数据的版权合规性尤其是商业化的垂域模型使用盗版书籍或未经授权的论文做训练数据会带来巨大法律风险。第二步是数据清洗。这一步的目标是去掉“对训练有害”的数据。需要处理的包括重复数据用 MinHash 或 SimHash 做去重是标准操作、低质量数据检测并过滤乱码、广告、无意义文本、有害数据政治敏感、暴力、色情内容必须过滤、个人隐私数据姓名、手机号、身份证号等需要脱敏。清洗这一步没有太多花哨技巧核心就是“宁可错杀不可放过”——训练数据里混入一条有害内容带来的风险会远远大于它带来的信息量。第三步是数据结构化。对训练 LLM 来说原始文本并不足够你通常需要把数据处理成“指令-回答”对的形式也就是常说的 SFT 数据格式。比如你要训练一个法律垂域模型需要把原始法律条文和案例解析成这样的结构指令是“什么是留置权”回答是“留置权是指……”指令是“张三借了李四十万不还李四能扣押张三的车吗”回答是“需要看车辆是否与债权相关……”等等。这一步需要大量的人工标注。有人会建议你用 LLM 自动生成训练数据这确实可行但一定要做人工抽检和修正否则模型学到的知识会带着老师的“幻觉”基因。第四步是数据质检。训练之前一定要用独立于训练集之外的验证集来评估数据质量。一个可行的做法是从准备的数据里随机抽取 200-500 条样本让领域专家逐条评估“指令是否清晰、回答是否准确、格式是否统一”。如果通过率低于 95%就说明数据质量还有问题不建议直接进入训练环节。6.3 处理数据不足的另一条路RAG 通用模型最后我要说一个反直觉的结论大多数场景下你根本不需要微调垂域模型用 RAG 通用模型就够了。为什么这么说首先是成本问题训练一个垂域模型需要的是 GPU 集群、标注团队、训练工程师整个周期按月计算、成本按百万计而做一个 RAG 应用只需要把领域知识切片入库成本可能只要前者的一小部分周期按天计算。其次是维护问题垂域模型的知识是“固化”在参数里的行业知识一旦更新你需要重新训练而 RAG 应用只需要更新向量库里的文档实时生效。那什么时候才需要垂域微调我总结了几条判断标准1你对输出的“领域风格”有极高要求比如需要符合某种特定的文书格式用 prompt 难以稳定约束2你使用的基座模型完全不具备相关领域能力比如用英文为主的模型做深度的中文古文处理3你有大量高质量的行为数据比如海量的历史工单和对应的最优回答这些数据的模式难以用文本检索覆盖。把 RAG 和微调结合起来是更高级的玩法先用垂域数据微调一个“自带领域知识”的基座模型然后在这个模型之上再加 RAG 做时效性知识的补充。这种“微调保底 RAG 补新”的架构在严谨性和实时性两方面都能兼顾是目前垂域 LLM 落地效果最好的方案之一。7. LLM 学习路线与实战建议7.1 给新手的三个学习阶段建议每次聊完技术细节总有人问“我想入门 LLM 应用开发该从哪开始”这里我给出我的学习路线建议不一定适用于所有人但希望对阶段感不清晰的朋友有参考价值。第一阶段是学会和模型打交道1-2 周。目标是把 LLM 当成一个“能力很强的实习生”来使用。你需要掌握调用 API 的基本方法、prompt 工程的核心技巧角色设定、少样本示例、思维链、输出格式控制、temperature 和 top_p 等参数的含义与调法、处理结构化输出JSON 模式的方法。这一阶段不要急着上框架直接用基础 API 写几个小工具比如“关键词提取器”“文本重写器”“会议纪要生成器”先把模型的手感找到。第二阶段是理解应用的典型范式3-6 周。目标是把 RAG、agent、function calling 这几个核心范式都亲手实现一遍。建议做一个相对完整的项目比如前面讲的“私有知识库问答系统”走通入库、检索、生成、评估的全流程。过程中你会遇到很多实际工程问题——向量库选型、chunk 大小、多文档冲突、检索精度优化等这些问题不亲手做一遍是不会有深刻体会的。第三阶段是深度优化和业务落地持续。到这一阶段你已经能搭建可用的 LLM 应用了接下来要解决的不是“能不能跑”而是“跑得好不好、稳不稳、省不省”。你需要关注延迟优化推理加速、缓存、并行处理、成本控制token 压缩、降级策略、模型分级、效果评估离线评估、线上指标、回归测试、安全合规prompt 注入防护、内容审核、隐私保护。7.2 推荐学习的开源项目和资源除了 awesome-llm-apps我相对推荐以下几个开源项目适合不同阶段的学习LangChain / LlamaIndex 官方文档和教程框架的官方文档是学习 RAG 和 agent 的最佳教材。LangChain 的“Use Cases”板块覆盖了问答、agent、摘要、SQL 等几乎所有常见场景每个案例都有完整代码。OpenAI Cookbook虽然是针对 OpenAI 模型的但其中的很多模式是通用的尤其是 function calling 和 prompt 工程的示例。dify / FastGPT 这类低代码平台如果你想快速看效果不纠结底层实现可以先用这类工具搭一个可用的 LLM 应用理解流程之后再看它们是怎么实现的。真实项目的 README在 awesome-llm-apps 里挑几个 star 数高、且和你想做的方向一致的项目直接把源码 clone 下来读关注它们的项目结构、prompt 设计、工具定义方式这些比空谈理论有用得多。7.3 关于“哪个方向值得投入”的个人判断最后简单聊聊方向判断。我在这个领域投入的时间越多越觉得 LLM 应用开发的核心竞争力不在于“会用最新的框架”而在于“能结合具体业务场景做判断”。RAG 是最值得投入的方向因为它在需求最刚性的内部知识管理、客服、辅助决策场景中能直接产生业务价值而且技术已经相对成熟、落地路径清晰。Agent 是有巨大想象空间但落地上仍有不少挑战的方向适合有一定工程能力的团队小步尝试不建议一开始就搞复杂多 agent 架构。垂域微调适合资源充足、对效果要求极高、且已有高质量数据的团队不是普通开发者的首选。我不建议盲目追求“最前沿的技术”而是建议你从业务需求反推技术选型。LLM 生态更新迭代极快今天的新框架下个月可能就无人维护了但底层的能力——理解模型行为、设计高质量 prompt、搭建稳定的数据链路——是始终通用的。把这些基本功练好不管生态怎么变你都能迅速切换到新的工具上面。我自己在这条路上踩过不少坑最开始也是什么都想尝试结果项目半途而废了几次。后来逐渐形成了上面这套“从业务出发、小步验证、逐步扩张”的打法项目交付成功率高了很多。希望这篇文章能给你一些参考少走一些弯路。
返回列表