十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

从文本生成到认知处理:LLM在RAG与Agent中的角色重构与工程实践

从文本生成到认知处理:LLM在RAG与Agent中的角色重构与工程实践 最近我在复盘 LLM 工程落地时意识到自己过去的判断有明显偏差。我把 LLM 当成“一个更聪明的文本生成接口”所以早期想的所有系统都是围绕“如何把问题抛给模型、再把答案展示给用户”设计的。真正进入 RAG、Agent、MCP 这类工程体系后我才发现自己低估了 LLM 在应用架构中扮演的角色。它不只是回答问题而是作为运行时的认知处理单元参与意图识别、信息检索、工具调用和结果校验。这个角色变化直接决定了后续的技术选型、依赖配置和排错方式也回答了标题里那句判断I was wrong about the role that LLMs would come to play。下面从角色认知开始先解释为什么过去的判断会出错再通过最小工程案例把 RAG、Agent、精度和配置问题串起来。1. 我对 LLM 角色的误判模型不是产品认知步骤才是核心1.1 旧判断LLM 只是生成文本的 API早期接触 LLM 时最容易形成的认知是LLM 就是一个输入文本、输出文本的 API。这个认知在 demo 阶段没有任何问题。你给模型一段 prompt它返回一段回答调用方把回答渲染到页面上就算完成了。真正进入生产系统后这个模型会很快失效。业务需求很少是“给我一段回答”而是“基于当前用户的订单数据判断退款风险并给出处理建议”。如果只把 LLM 当成文本生成接口就需要在调用前手动整理大量数据在调用后手动解析结果再自己写规则决定下一步动作。这个过程中LLM 只扮演了一个“翻译器”并没有真正进入业务决策链路。问题在于这种用法根本没有把 LLM 的能力放到合适的位置。它把复杂业务逻辑压到了 prompt 工程和外围代码上而模型自身的判断、推理和上下文整合能力基本没有被利用。1.2 新判断LLM 是应用运行时的认知处理单元后来做 RAG 和 Agent 项目我重新理解了 LLM 的角色LLM 更像应用运行流程里的一个认知处理单元。它承担的不只是“生成答案”还包括理解用户请求中的真实意图。判断需要调用哪些工具或数据源。对检索回来的片段进行相关性判断和摘要。在多轮对话中维护状态决定下一步行为。对自身输出做基础校验和修正。这意味着LLM 不是被外围代码调用的一个黑盒而是和普通函数、数据查询、外部服务并列的一个执行步骤。它负责处理文本和逻辑中不好写死的部分其余确定性的工作仍然交给传统代码完成。这个变化最重要的影响是系统设计时不再问“这个需求能不能用 LLM 实现”而是问“这个需求里哪一步必须靠模型推理哪一步应该用普通代码或工具函数解决”。1.3 判断错误带来的三个工程后果我早期判断失误的直接后果有三个。第一上下文管理被当成无关问题。把 LLM 当 API 调用时每次请求都是独立的token 超限了就去截断。但把 LLM 当流程中的认知单元后上下文就是状态。截断会直接导致模型失去关键信息输出结果自然不符合业务预期。第二输出不稳定被当成 bug。LLM 是概率模型同样的 prompt 可能得到不同结果。这个问题不会消失只能在架构上缓解比如用结构化输出、Few-shot 示例、人工校验和重试策略。之前我总以为换更大的模型就能解决实际效果有限。第三评估只看答案不看流程。判断一个 LLM 应用好不好用不能只看最终回答是否“像样”还要看检索是否正确、工具调用是否合理、异常分支是否有兜底。只评估答案会把系统隐患掩盖掉。下表总结了角色变化带来的关注点差异。关注点旧角色文本生成 API新角色认知处理单元核心问题prompt 怎么写流程如何编排上下文请求级可截断会话级需要管理输出期望直接可用期望结构化可能需校验工具调用基本不需要关键能力需要注册与权限控制可观测性记录 prompt 和回答记录检索、调用、中间决策和错误排错点模型是否答错数据链路、工具、上下文、模型共同排查这一轮角色认知校正后后续的工程实践才有明确方向先跑通 RAG再引入 Agent最后用精度和配置问题把整个链路补完整。2. 跑通 RAG让 LLM 第一次扮演知识处理引擎2.1 RAG 为什么是角色转变的第一个例子RAGRetrieval-Augmented Generation检索增强生成是理解 LLM 角色转变最直接的场景。没有 RAG 时LLM 只能依赖训练时学到的知识生产环境里这些知识可能过时也可能根本不在训练数据里。加入检索后LLM 不再凭空回答而是基于你提供的文档片段做出判断。这个过程中LLM 的角色已经变了它成了一个“基于给定材料做推理的知识处理引擎”。系统的核心工作也从“写 prompt”变成了“把资料准备好、检索到足够相关的信息、再把信息和用户问题一起交给模型”。这也是为什么现在很多 LLM 应用开发框架都把数据接入、切分、向量化、检索作为基础设施能力。2.2 环境准备与依赖以 Python 生态为例先准备一个干净的虚拟环境。当前常见版本选择是 Python 3.10 或 3.11依赖可以先用下面这一组跑通最小案例。mkdir llm-rag-demo cd llm-rag-demo python -m venv venv source venv/bin/activate pip install langchain langchain-openai chromadb python-dotenv这里把向量数据库选择为 Chroma因为它在本地运行不需要额外起服务适合先验证链路。langchain-openai提供 OpenAI 兼容的接口封装。如果你的模型服务不是直接访问外部 API而是使用公司内部网关仍然可以通过配置 base_url 来适配。创建.env文件写入模型与向量接口配置。OPENAI_API_KEYyour-api-key OPENAI_BASE_URLhttps://your-api-endpoint/v1 EMBEDDING_MODELtext-embedding-3-small LLM_MODELgpt-4o-mini注意不同服务商的接口路径和模型名不一样。没有本地服务时可以用官方兼容接口完全本地部署时需要自己把 embedding 服务暴露成 OpenAI 兼容格式。这个字段如果漏了后面会经常遇到“文本向量 API 未配置”之类的报错。2.3 最小 RAG 代码用一个本地文本文件作为知识来源。假设docs/operation_manual.txt是一份内部操作手册。完整示例代码如下先读取文档再切分、向量化、检索最后交给 LLM 生成回答。import os from dotenv import load_dotenv from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import TextLoader load_dotenv() # 1. 读取文档 loader TextLoader(docs/operation_manual.txt, encodingutf-8) documents loader.load() # 2. 切分文档避免单次交给模型的文本过长 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , ], ) chunks splitter.split_documents(documents) # 3. 向量化并写入本地向量库 embeddings OpenAIEmbeddings(modelos.getenv(EMBEDDING_MODEL)) vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) # 4. 相似度检索 question 系统启动失败时第一步应该检查什么 retriever vectorstore.as_retriever(search_kwargs{k: 3}) docs retriever.invoke(question) # 5. 组装 prompt 并生成回答 context \n\n.join([d.page_content for d in docs]) prompt f你是一名技术支持工程师。 请只根据下面的资料回答问题不要使用资料以外的知识。 资料 {context} 问题 {question} llm ChatOpenAI(modelos.getenv(LLM_MODEL), temperature0) answer llm.invoke(prompt) print(answer.content)这段代码的关键点在于切分和检索。chunk_size500表示每个片段约 500 个字符chunk_overlap50让相邻片段保留一部分重叠避免一句话被切在边界导致语义断裂。检索时k3表示取最相关的 3 个片段。如果知识库内容很大可以调大k但也要注意拼到 prompt 后不能超过模型上下文限制。2.4 运行验证首次运行需要执行向量化会创建chroma_db目录。运行方式很简单。python rag_demo.py如果配置正确会输出一段基于operation_manual.txt内容生成的回答。为了确认 RAG 真的生效可以把资料里的关键结论改成明显不同于常识的表述。比如手册里写“启动失败先检查左侧第二块电源指示灯”如果模型回答里出现了这个细节说明检索和上下文生效了如果模型用自己的常识回答说明检索片段没有被正确利用。2.5 这一步最容易踩的坑第一个坑是向量维度不匹配。如果你先用了 A 服务的 embedding 模型创建向量库后换成了 B 服务的 embedding 模型查询时可能报维度错误。解决方法是统一模型并删除旧的本地向量库重新写入。第二个坑是“文本向量 API 未配置”。原因通常是.env文件里的 key 没有加载或者调用了不存在的 embedding 接口。检查方式是在代码前面加一行调试输出先确认环境变量是否被读到了。print(os.getenv(EMBEDDING_MODEL))第三个坑是切分策略不合适。硬按固定字符数切分很容易把一段操作步骤拆散导致检索到的片段不完整。生产环境里应该优先按标题、段落、列表结构切分再结合固定长度兜底。3. 从 RAG 到 AgentLLM 开始扮演“任务执行者”3.1 Agent 让 LLM 从回答者变成操作者RAG 场景里LLM 还只是在“回答问题”。进入 Agent 场景后LLM 的职责从“回答者”变成了“任务执行者”。它要根据用户意图决定调用哪个工具处理工具返回结果再决定下一步动作。整个循环不再是一次性的 prompt 调用而是一个不断执行的循环。这个转变意味着系统中出现了需要被模型调用的函数。这些函数可能是查询数据库、调用外部接口、读取配置文件、执行计算。模型的角色从文本生成器变成“调度中枢”。围绕 Agent 的工程实践也变复杂了需要定义工具的输入输出格式需要给模型提供足够清晰的工具描述需要限制模型的权限还需要在多次调用之间维护状态。这就是为什么现在很多项目会引入 Agent 编排框架而不是让业务代码直接硬编码调用 LLM。3.2 用 Spring AI 的 Tool 实现一个最小 Agent在 Java 后端生态中Spring AI 是一个典型的 AI 应用开发框架。它把常用的大模型调用封装成类似ChatClient的 API也支持通过注解把普通方法暴露成 Agent 可调用的工具。假设项目基于 Spring Boot 3.2核心依赖可以这样引入。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-tool-calling/artifactId /dependency接着定义一个普通业务方法用Tool注解把它描述给模型。这里以一个“根据订单号查询发货状态”的工具为例实际数据从数据库获取下面的代码只展示工具定义方式。import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; Component public class OrderTools { Tool(description 根据订单号查询发货状态返回状态码PENDING、SHIPPED、DELIVERED) public String queryShipmentStatus(String orderId) { // 生产环境应访问数据库或后端服务 if (A1001.equals(orderId)) { return SHIPPED; } return PENDING; } }然后在服务层让模型自动决定是否调用该工具。import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClient.Builder; import org.springframework.stereotype.Service; Service public class CustomerService { private final ChatClient chatClient; public CustomerService(Builder builder, OrderTools orderTools) { this.chatClient builder .defaultTools(orderTools) .build(); } public String answer(String userMessage) { return chatClient.prompt(userMessage) .call() .content(); } }当用户问“A1001 订单发货了吗”时Spring AI 会把这个问题交给模型模型根据工具描述决定调用queryShipmentStatus(A1001)再把工具返回结果和上下文一起组成最终回答。这个示例的核心不是代码量而是设计理念能用普通代码计算的状态不交给模型猜测。模型只负责判断“现在该调用哪个工具”工具执行过程仍然是确定性的。3.3 用 MCP 连接工具与模型工具越来越多后逐个在框架里注册会很繁琐。MCPModel Context Protocol模型上下文协议解决了这个问题。它把工具、数据源、文件系统等能力统一暴露成标准协议模型应用通过 MCP Client 接入 MCP Server就能动态发现和调用工具。在架构上MCP 让 LLM 不再绑定某个具体框架。同一套工具接口可以同时被 Spring AI、LangChain、其他应用复用。社区里常说的“SpringAI MCP RAG Agent Skill”组合核心思路就是把模型、知识检索和工具能力分层解耦。MCP Server 可以是一个独立服务也可以嵌入当前应用进程。协议层负责工具描述、调用参数和返回结果的传输。对于大型团队来说最直接的价值是工具团队只需要维护一套协议接口模型团队不需要反复改代码去适配新的工具方法。3.4 Agent 模式下的角色边界Agent 看起来很强大但不等于要把它做成“万能控制器”。我在项目中总结出的边界是模型做决策代码做执行数据做依据。如果一个动作规则明确比如“金额小于 100 元自动退款”应该写成普通业务代码不应该让模型判断。如果规则模糊比如“根据用户投诉内容判断退款优先级”才适合交给模型。把所有判断都塞进模型会让系统越来越不可控排错时也不知道是工具调用错了还是模型决策错了。另一个常见坑是没有约束工具权限。给 Agent 挂上数据库工具后一定要在校验阶段限制可执行的 SQL 类型不要让模型直接获得任意更新数据的能力。生产环境需要对模型可调用的工具做白名单超过白名单的调用一律拒绝。4. 精度、向量接口与模型路径角色落地时最容易翻车的三个配置4.1 fp16、fp32、bf16为什么精度会改变 LLM 的实际表现本地部署 LLM 时经常要面对 fp16、fp32、bf16 的选择。这个选择直接决定显存占用、推理速度和生成效果。格式位宽指数位尾数位常见场景主要风险fp3232 位8 位23 位训练、精度敏感性高的场景显存占用大fp1616 位5 位10 位部分推理卡、老显卡数值范围小容易溢出精度损失明显bf1616 位8 位7 位常见 AI 加速卡、大模型推理尾数少极端精度场景略逊于 fp32fp16 的问题是数值范围比 fp32 窄。模型权重里出现较大的中间结果时fp16 可能溢出导致输出乱码或质量下降。bf16 保留了和 fp32 一样的指数范围虽然尾数少了但在大多数生成任务中表现更稳定。如果你的应用只做文本生成且使用自带推理引擎的模型服务通常不需要手动选精度。真正需要手动选择的情况是自己写推理脚本、自己加载权重或者要部署到指定显存的机器上。此时建议先按“能用 bf16 优先 bf16不能用再退回 fp16本地实在没有加速支持才考虑 fp32 量化”的顺序做实测。要注意精度问题不是纯理论问题。换个精度后同一个模型输出可能发生变化。上线前务必把关键场景的回归用例跑一遍不要只验证服务能启动。4.2 “文本向量 API 未配置”到底该怎么查LLM 应用开发中embedding 接口是很容易配错的一环。常见报错形态有三种。启动时报环境变量错误OPENAI_API_KEY is not set请求时报鉴权错误401 Unauthorized运行时报模型不存在model_not_found先从环境变量开始查。很多项目里LLM 和 embedding 使用同一个 API 网关但两个接口可能需要不同的 key 或模型名。检查.env是否真的被加载以及环境变量名是否和代码一致。如果用的是本地向量服务或兼容协议网关常见错误是base_url写错。比如接口实际路径是/v1/embeddings代码里却只写了/启动时不报错第一次调用向量化才失败。最直接的排查方法是写一个短脚本单独调用一次 embedding 接口。import os from dotenv import load_dotenv from langchain_openai import OpenAIEmbeddings load_dotenv() embeddings OpenAIEmbeddings(modelos.getenv(EMBEDDING_MODEL)) vec embeddings.embed_query(测试) print(len(vec))如果这段脚本能输出向量维度说明 embedding 链路正常如果报错说明接口地址、模型名或 key 有问题。此时再去检查网关日志很快能定位。4.3 不要把 LLM 模型路径写进 ComfyUI 的 extra_model_paths.yaml很多人在本地同时使用多种 AI 工具会出现一个目录混淆问题把 LLM 模型路径写进了 ComfyUI 的extra_model_paths.yaml。ComfyUI 主要管理图像生成所需的检查点checkpoint、LoRA、VAE 等模型目录。它不会加载llama.cpp或ollama使用的 LLM 权重目录。extra_model_paths.yaml的基本结构是这样的。comfyui: base_path: /data/ai-models checkpoints: checkpoints loras: loras vae: vae如果你希望 LLM 应用读取ollama的模型目录应该修改的是 LLM 服务自身的配置而不是 ComfyUI 的配置文件。判断原则很简单哪个应用负责加载模型就配置哪个应用的模型路径。跨应用复制路径只会让两边都读不到文件。要避免这类问题建议在本地机器上维护一张“工具到模型目录”的对照表把每个工具的配置文件路径、默认模型目录、环境变量名都记录清楚。排查路径问题时先去看该应用自己的启动日志而不是凭文件名猜测。5. 用工程方法对待 LLM 的新角色排错链路与可复用清单5.1 从现象倒推根因一条覆盖模型、数据、工具、配置的排查顺序LLM 应用出错时不要第一时间怀疑模型。我通常按固定顺序排查。第一步确认输入。用户问题和传入的检索条件是否符合预期。实际项目里很多“模型答错了”的问题其实是前面的查询条件传错了。第二步确认路径和配置。.env、YAML、properties 文件是否加载模型名和接口地址是否匹配。这一层最耗时因为配置错误往往不在启动时暴露而是在第一次真实调用时才报错。第三步确认数据和检索结果。把 RAG 流程里检索到的片段单独打印出来看语义相关性和完整性。如果检索片段不对模型再强也答不对。第四步确认工具调用和权限。Agent 场景里检查模型实际调用了哪个工具、传参是什么、返回结果是什么。可以在工具方法入口打印请求参数在出口打印返回结果。第五步确认模型和精度。最后再看模型本身。是不是上下文超限是不是温度太高导致随机性过大是不是本地推理时精度选择不合适这条链路的核心是按“输入 → 配置 → 数据 → 工具 → 模型”的顺序缩小范围而不是直接跳到“换更大的模型”。5.2 高频问题与处理方案表下面这张表整理了我经常遇到的 LLM 工程问题。问题现象常见原因检查方式处理建议启动报环境变量缺失.env未加载或变量名不一致打印os.getenv结果统一变量名用dotenv或配置中心加载向量维度不匹配切换过 embedding 模型查看报错中的维度数字统一模型重建向量库上下文超限切分过大或多轮历史过长统计 token 数缩小 chunk对历史会话设置保留条数回答内容跑题检索片段相关性低打印 top-k 检索结果调整切分策略、k值或重排序工具调用参数错误工具描述不清晰查看工具入参和模型生成的参数在Tool描述中明确参数格式和示例输出格式不稳定未限制结构化输出看返回内容是否可被程序解析使用 JSON Mode、函数调用或输出校验层本地推理乱码精度选择不当对比不同精度输出优先尝试 bf16再评估 fp16用户感知的“AI 变笨”数据或上下文被截断查看最终拼给模型的 prompt检查截断策略和检索完整性排查时不要只盯着报错信息。很多 LLM 应用并不在代码层报错而是“能运行但结果不可用”。这种情况下日志里的 prompt、检索结果、工具调用记录才是关键。5.3 学习环境与生产环境的差异本地 demo 能跑通不代表生产环境可以照搬。学习环境和生产环境的关注点差别很大。维度学习环境生产环境模型访问单一 API key直连服务网关、限流、多 key 轮询数据存储本地 Chroma 或内存独立向量数据库备份与权限控制工具调用本地函数权限宽松白名单、审计、操作日志上下文单次请求验证会话状态管理token 预算控制评估手工看几个例子回归用例集自动化评估指标监控不需要延迟、错误率、token 消耗、命中率回滚重启进程模型版本、数据版本、配置版本都要可回滚生产环境里还有一个容易被忽略的点prompt 和工具描述也是需要版本管理的。它们会影响模型行为应该像代码一样被审查和保留变更记录。否则一旦线上回答质量下降很难定位是数据变了、模型变了、还是 prompt 变了。5.4 可复用落地清单以下清单可以直接用于 LLM 应用上线前的检查。确认所有模型名称、接口地址、环境变量都在配置中心或.env中有明确记录。确认 embedding 模型和向量库中的向量维度一致。确认 RAG 检索结果在典型问题上能打印出来且相关性可人工判断。确认 Agent 的工具列表已做白名单关键工具有权限校验。确认所有模型调用日志都会记录 prompt、工具调用、返回结果和 token 消耗。确认上下文有长度上限截断策略不会丢掉核心信息。确认关键场景有回归用例集精度或模型切换后能跑一遍。确认本地部署时精度选择经过实测而不只是根据显存容量猜测。确认外部模型目录路径配置在正确的应用配置文件中不要跨工具混淆。确认异常分支有兜底话术模型调用失败时用户不会看到原始报错。LLM 的角色变化不是一个纯理论问题。它直接影响你如何拆分模块、如何配置依赖、如何设计日志和如何排查故障。判断 LLM 在系统里扮演什么角色再决定把工程重心放在 prompt、数据链路还是工具编排上是当前 LLM 应用开发中最值得投入的思考方向。
返回列表