LLM应用开发实战:从RAG到Agent的完整指南
2026/9/15 5:17:25 网站建设 项目流程

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”这个词拆开看。现在的 LLM(Large 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_p=0.9 意味着每次生成时只从前 90% 概率质量的词里采样,丢弃掉长尾的低概率词。这样做的目的是平衡多样性和质量——既不会像纯贪心解码那样单调,又不会采样到完全离谱的词。

理解这些原理之后,你就明白为什么“prompt 怎么写都一样”这种说法是错的。LLM 的推理过程本质上是“在概率空间里做搜索”,你的 prompt 就是在限定搜索的范围和方向,写得好不好,直接影响搜索效率和结果质量。

3.2 RAG 应用实战:从文档切分到检索增强

RAG(Retrieval-Augmented Generation)是当前 LLM 应用开发中落地最多、见效最快、坑也最多的方向。几乎每个想用 LLM 处理私有知识的团队,第一站都会走到 RAG。我们来拆解一下 RAG 的完整流程和每个环节的关键要点。

整个 RAG 流程可以概括为四个环节:文档加载 → 文本切分 → 向量化与索引 → 检索与生成

文档加载环节的难点不在于“读取文件”,而在于“解析格式”。PDF 是最常见的坑:有的 PDF 是文本型的,可以直接提取文字;有的是扫描件,需要 OCR 识别;还有的是复杂的多栏排版、包含表格和图片,直接提取会导致文本顺序错乱。我处理 PDF 的经验是:优先用 PyMuPDF(fitz)做文本提取,遇到扫描件再用 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, encoding="utf-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_size=400, chunk_overlap=80, separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""], length_function=len, ) 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_name="BAAI/bge-large-zh-v1.5", encode_kwargs={"normalize_embeddings": True}, # bge 模型建议开启归一化 ) # 连接向量库(Milvus Lite) vector_store = Milvus( embedding_function=embedding_model, collection_name="knowledge_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(texts=texts, metadatas=metadatas)

入库完成之后,建议做一个自查动作:随机抽几个问题,手动做向量检索,看看召回的前 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_k=8): # 向量检索器 vector_retriever = vector_store.as_retriever( search_type="similarity", 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_path="maidalun1020/bce-reranker-base_v1") def rerank_documents(query, documents, top_n=5): contents = [doc.page_content for doc in documents] scores = reranker.compute_score([[query, content] for content in contents], normalize=True) scored_docs = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True) 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( model="gpt-4o-mini", # 或指定你的本地模型地址,兼容 OpenAI 协议即可 temperature=0.2, max_tokens=1024, ) context_text = "\n\n---\n\n".join([ctx.page_content for ctx in contexts]) prompt = rag_prompt_template.format(context=context_text, question=question) 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 评估拆成两个独立的环节:检索评估生成评估

检索评估关注的是“正确的文档片段有没有被召回”。常用的指标是召回率(Recall@K)和命中率(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、搭建稳定的数据链路——是始终通用的。把这些基本功练好,不管生态怎么变,你都能迅速切换到新的工具上面。

我自己在这条路上踩过不少坑,最开始也是什么都想尝试,结果项目半途而废了几次。后来逐渐形成了上面这套“从业务出发、小步验证、逐步扩张”的打法,项目交付成功率高了很多。希望这篇文章能给你一些参考,少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询