LLM应用工程化实战:从RAG到Agent与MCP的架构演进
去年底技术社区里有个问题讨论得很热烈“Ask HN: Whats Next for LLMs?” 模型能力升级之外开发者更关心的是大模型下一步到底会怎么发展哪些方向值得投入精力哪些架构会在实际项目中留下来这个问题的答案其实不在论文里而在工程实践里。过去一年多大模型应用从“调用API聊天”快速演进到“Agent 工具调用 知识库 应用框架”的组合架构。单纯堆模型参数已经不够真正拉开差距的是谁能把模型安全、稳定、可控地接入业务系统。本文不打算预测“下一个杀手级模型”而是从开发者能落地的角度拆解LLM应用当前的演进方向、关键技术栈、一套可运行的Spring AI MCP Agent RAG实战案例以及工程化过程中最常见的坑和边界问题。适合人群刚开始接触LLM应用开发想理解Agent、MCP、RAG到底是什么的新手。已经在用LangChain或Spring AI但想系统梳理应用架构的开发者。需要把大模型接入公司内部系统关注安全与权限边界的后端工程师。读完你可以掌握LLM应用的六大趋势、本地部署与精度选择、RAG/Agent/MCP的适用场景、一个完整可运行的项目示例以及一套排查与安全清单。1. LLM的下一步从“模型能力”走向“应用工程”1.1 模型的边际表现在递减应用架构成为主战场过去两年LLM的能力提升非常明显上下文变长、推理能力增强、多模态支持越来越多。但对于大多数业务开发者来说模型之间的“肉眼可见差距”正在变小。尤其是当大家同时使用GPT-4o、Claude、Qwen、DeepSeek等主流模型时你会发现业务效果好不好往往不是模型本身决定的而是周围这套系统决定的。举个例子同样的模型有人只做了一个网页问答框有人做了一套能检索内部知识库、能查询订单数据、能调用工单系统的助手。后者对业务的价值显然高得多但难度也在“模型之外”数据怎么接入、权限怎么控制、调用失败怎么降级、返回格式怎么校验。所以“LLM的下一步”与其说是“更聪明的模型”不如说是“更成熟的工程体系”。1.2 当前值得关注的六大技术方向结合当前社区讨论和工程实践以下几个方向会直接影响未来一到两年LLM应用的形态Agent智能体模型不再只是回答问题而是根据目标拆分任务、调用工具、观察结果、调整策略。MCPModel Context Protocol模型上下文协议把“模型需要的外部工具和数据”标准化避免每个Agent都要定制一套工具协议。RAG检索增强生成深化从简单查文档演进到精细化的切片、重排、多路召回、知识库更新机制。编排框架层成熟LangChain、Spring AI、LlamaIndex等框架逐渐收敛标准能力降低开发门槛。精度与部署工程化FP16、BF16、INT8、INT4等精度选择成为部署必答题本地推理引擎llama.cpp、Ollama、LM Studio、MLX在Mac和消费级显卡上越来越可用。安全与治理OWASP LLM Top 10逐渐被团队接受过度授权的工具调用、提示注入、敏感信息泄露等问题成为上线前必查项。1.3 单一模型调用 vs 系统性应用的差异很多入门同学容易把“调用大模型”等同于“开发LLM应用”。实际上的差异可以这样看维度单纯调用模型系统性LLM应用输入写死的Prompt用户输入 知识库检索结果 工具返回数据输出一段文本结构化结果 工具调用动作 校验后的业务数据错误处理重新问一次重试、降级、超时熔断、日志追踪安全基本不涉及提示注入防护、权限校验、输出内容过滤可维护性低需要版本管理、Prompt管理等工程手段如果你当前只是用API做一个翻译工具或简单助手那不用焦虑。但如果你想做企业级的业务系统就必须把上述差异一项项补上。2. 环境准备与模型部署本地推理与精度选择2.1 本地推理 vs 云API在真正写应用代码之前先解决“模型从哪里来”的问题。目前主流有两种方式云API调用OpenAI、Anthropic、Moonshot、DeepSeek、阿里云百炼等平台的在线接口。优点是接入快、效果稳定、无需考虑显卡缺点是数据出站、按量付费、离线不可用。本地推理通过Ollama、LM Studio、llama.cpp等引擎加载开源模型如Qwen、Llama、Mistral。优点是数据本地化、可离线、按需调优缺点是需要硬件资源部署和调优成本高。在实际项目中两者经常混用研发调试用云API私有化交付用本地推理普通文本用本地小模型复杂推理用云API。关键是在配置层做好抽象不要写死在代码里。2.2 常见推理引擎速览Ollama适合个人电脑和开发环境一行命令启动提供兼容OpenAI的接口/v1/chat/completions非常适合本地调试。llama.cppC/C实现性能优秀社区更新非常快适合服务器部署和量化模型加载。LM Studio图形化界面适合Mac和Windows桌面环境也提供本地API服务。MLX苹果推出的机器学习框架在Apple Silicon芯片上性能突出适合Mac用户跑模型。选择时可以这样判断如果只是开发调试优先选Ollama如果要部署到Linux服务器提供API服务llama.cpp或vLLM更合适如果团队都是MacLM Studio和MLX体验会更好。2.3 精度问题FP32、FP16、BF16、INT8、INT4“LLM大模型之精度问题(fp16, fp32, bf16)详解与实践”这个热词正好戳中了本地部署的核心矛盾显存有限模型越来越大不得不考虑用低精度保存和推理。各精度对比精度位数主要用途特点常见风险FP3232位训练初始权重、数值计算基准精度最高显存占用大显存不足FP1616位混合精度训练、部分推理速度较快指数位少大数值容易溢出BF1616位大模型训练、推理指数位与FP32相同动态范围大尾数精度较低INT88位推理量化显存占用大幅下降精度损失需评测INT44位推理量化、本地部署显存占用最低精度损失明显需配合校准举例一个7B模型FP16权重大约占14GB显存INT4量化后可以压到4GB左右很多消费级显卡都能跑。这也是本地部署低成本化的核心原因。实践建议如果显存充裕如A100/A800/H100训练直接使用BF16混合精度。如果显存不足推理优先尝试INT8再考虑INT4量化前后务必用一批业务问题做效果对比。不要只看困惑度指标要跑真实用例量化对特定任务的影响差异很大。2.4 版本说明由于LLM工具链迭代太快本文后续示例采用以下环境组合具体版本请根据你的项目实际情况调整JDK 17Spring Boot 3.xSpring AI 1.0.x 版本接口以当前官方发布版为准Maven 3.8本地可选 Ollama也可以直接配置云端API服务的Key重点演示的是配置思路和代码结构而不是某个固定版本。3. 核心应用范式RAG、Agent、MCP与编排框架3.1 RAG解决“模型不知道你的业务数据”RAG的核心思路很简单不让模型凭空回答而是先从你自己的知识库/数据库里检索相关内容把检索结果拼到Prompt里再让模型生成答案。这样做有三个好处不需要微调就可以让模型使用私有数据。可以追溯答案来源降低幻觉风险。知识库更新比模型重训快得多。RAG链路通常包含文档加载 → 切片 → 向量化 → 存储 → 检索 → 重排 → 生成。常见的误区是把所有文档直接塞进Prompt不切片、不重排。这样既消耗Token又容易让模型被无关内容干扰。正确的做法是精细化切片检索阶段做Top-K召回必要时加入Rerank模型对结果重排效果会明显提升。3.2 Agent从“问答”到“做事”Agent可以理解为“会使用工具的LangChain/Spring AI应用”。模型本身只会输出文字但通过Function Calling或工具调用协议模型可以根据用户的意图输出一个结构化的“工具调用请求”由程序执行后再把结果返回给模型继续推理。一个典型Agent循环是接收用户指令。模型拆解需求决定调用哪个工具。程序执行工具查数据库、调用内部API、发消息等。把工具结果回传给模型。模型根据结果继续输出或调用下一个工具。这里要特别注意Agent的能力上限取决于工具的质量和数量但风险也来自工具本身。如果给Agent挂了一个“可以删除数据库”的工具提示注入就可能造成严重后果。这个安全问题后面单独讲。3.3 MCP标准化“模型-工具-数据”的通信协议MCP由Anthropic于2024年底提出核心目标是解决一个问题每个Agent接入一个新工具都要重新写一套协议和适配代码。MCP把工具和服务统一成标准化的“MCP Server/Client”模型让模型可以动态发现工具、按统一格式调用工具。MCP的关键概念MCP Server暴露一组工具、资源或提示词的能力服务。MCP Client连接Server并调用工具的程序。工具Tool具体可执行的函数描述信息包括名称、描述、输入参数Schema。通俗点说MCP就像大模型世界的“USB接口”只要设备支持这个接口就能即插即用。3.4 为什么需要编排框架不要小看编排框架LangChain、Spring AI、LlamaIndex的价值。它们的核心不是“封装Prompt”而是提供了一整套工程化设施模型接入的统一抽象便于切换不同模型服务商。Prompt模板管理支持变量替换、版本维护。工具调用统一协议内置Function Calling/MCP支持。RAG链路组件包括文档解析、向量存储、检索器。内存管理、对话记忆、流式输出等通用能力。在Java技术栈里Spring AI的定位非常清晰对标LangChain但深度绑定Spring Boot生态让Java后端团队可以用“写Spring服务”的习惯来开发LLM应用。3.5 Agent应用为什么需要编排框架热词“llm 应用为什么需要编排框架”其实已经问了很多人。我的理解是第一Agent不是简单的if-else。模型可能会多轮调用工具每次调用的参数格式都要校验框架可以帮助你用声明式方式定义工具自动完成参数解析。第二生产环境需要可观测性。链路追踪、Token统计、工具调用记录都需要框架层面的埋点和扩展点。第三团队协作需要规范。框架约定了一致的项目结构新同学上手成本降低不会因为Prompt风格不同导致项目难以维护。4. 完整实战Spring AI MCP Agent RAG 实现知识库问答助手下面用一个具体项目把上述概念串起来做一个企业内部知识库问答助手支持“查文档”和“查天气”两个能力。查文档走RAG检索本地知识库查天气通过网络工具调用演示Agent和MCP工具接入。4.1 创建项目目录结构llm-next-demo/ ├── pom.xml └── src/main/ ├── java/com/example/llmnext/ │ ├── LLMNextApplication.java │ ├── controller/ChatController.java │ ├── service/WeatherToolService.java │ ├── service/RagService.java │ └── config/VectorStoreConfig.java └── resources/ ├── application.yml └── docs/ └── product-manual.md4.2 添加Maven依赖文件路径pom.xml?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.0/version relativePath/ /parent groupIdcom.example/groupId artifactIdllm-next-demo/artifactId version1.0.0/version namellm-next-demo/name descriptionLLM Next Generation Demo/description properties java.version17/java.version spring-ai.version1.0.0/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-advisors-starter/artifactId /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement /project注意Spring AI的版本迭代很快上面的1.0.0需要以2025年后发布的最新稳定版为准。如果版本不一致接口名称可能有调整建议参考官方迁移文档。4.3 配置文件文件路径src/main/resources/application.ymlspring: application: name: llm-next-demo ai: # 如果使用OpenAI兼容服务可以配置base-url openai: base-url: ${OPENAI_BASE_URL:https://api.openai.com} api-key: ${OPENAI_API_KEY:your-api-key} chat: options: model: gpt-4o-mini temperature: 0.3 # 如果使用本地Ollama ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b temperature: 0.3 vectorstore: simple: embeddings: model: text-embedding-3-small这里同时配了OpenAI和Ollama你可以选其中一种使用。建议先用OllamaQwen本地调试等流程跑通再切换云API。4.4 启动类文件路径src/main/java/com/example/llmnext/LLMNextApplication.javapackage com.example.llmnext; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class LLMNextApplication { public static void main(String[] args) { SpringApplication.run(LLMNextApplication.class, args); } }4.5 定义一个Agent工具查天气Spring AI 1.0版本引入了Tool注解用来把普通的Spring Bean方法暴露给模型调用。文件路径src/main/java/com/example/llmnext/service/WeatherToolService.javapackage com.example.llmnext.service; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Service; /** * 天气查询工具供LLM Agent调用。 * 生产环境请替换为真实的天气API同时注意API授权。 */ Service public class WeatherToolService { Tool(description 根据城市名称查询当前天气) public String getCurrentWeather( ToolParam(description 城市名称例如北京) String city) { // 这里可以用RestClient调用真实天气服务 // 演示阶段直接返回模拟数据方便验证调用链路 return 当前城市 city 天气晴温度26摄氏度湿度40%; } }关键点Tool(description ...)里的描述很重要模型依靠描述决定要不要调用这个工具。ToolParam的参数描述同样重要描述不清晰会导致模型传错参数。生产环境中工具方法必须做好入参加校验和用户身份传递。4.6 实现RAG检索服务RAG的核心是把文档向量化后存入向量存储。为了演示方便这里使用Spring AI内置的SimpleVectorStore生产环境建议替换为Redis Vector、PGVector或Milvus。文件路径src/main/java/com/example/llmnext/config/VectorStoreConfig.javapackage com.example.llmnext.config; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class VectorStoreConfig { Bean public VectorStore vectorStore(EmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build(); } }文件路径src/main/java/com/example/llmnext/service/RagService.javapackage com.example.llmnext.service; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.document.Document; import org.springframework.ai.reader.TextReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import java.util.List; /** * RAG服务加载文档 - 切片 - 向量化 - 检索 - 生成 */ Service public class RagService { private final ChatClient chatClient; private final VectorStore vectorStore; Value(classpath:docs/product-manual.md) private Resource productManual; public RagService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { this.chatClient chatClientBuilder.build(); this.vectorStore vectorStore; } /** * 初始化向量库将文档写入向量存储。 * 实际项目中建议在启动时只执行一次或用Job定期增量更新。 */ public void initVectorStore() { TextReader reader new TextReader(productManual); ListDocument documents reader.get(); TokenTextSplitter splitter new TokenTextSplitter(); ListDocument chunks splitter.apply(documents); vectorStore.add(chunks); } /** * 根据用户问题检索知识库并生成回答 */ public String question(String userQuestion) { return chatClient.prompt() .user(userQuestion) .advisor(advisorSpec - advisorSpec .param(vectorStore, vectorStore) .param(topK, 3)) .call() .content(); } }这里使用了Spring AI的QuestionAnswerAdvisor它会在回答前自动从VectorStore检索相关内容并拼入Prompt。如果你用的是更早的版本需要手动拼接上下文代码会多一些。4.7 编写Controller文件路径src/main/java/com/example/llmnext/controller/ChatController.javapackage com.example.llmnext.controller; import com.example.llmnext.service.RagService; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/api/chat) public class ChatController { private final RagService ragService; public ChatController(RagService ragService) { this.ragService ragService; } /** * 知识库问答接口 */ PostMapping(/ask) public MapString, String ask(RequestBody MapString, String request) { String question request.get(question); String answer ragService.question(question); return Map.of(answer, answer); } /** * 初始化向量库 */ PostMapping(/init) public MapString, String initVectorStore() { ragService.initVectorStore(); return Map.of(status, ok); } }4.8 运行与验证启动项目后先调用初始化接口curl -X POST http://localhost:8080/api/chat/init预期返回{status:ok}然后调用问答接口curl -X POST http://localhost:8080/api/chat/ask \ -H Content-Type: application/json \ -d {question: 产品支持哪些部署方式}如果文档向量库中有相关内容模型会基于检索片段回答而不是凭空编造。再测试Agent工具调用。需要写一个流式对话接口让模型决定是否调用天气工具。这里可以用Spring AI的chatClient.prompt().tools(...)方法package com.example.llmnext.service; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; Service public class AgentService { private final ChatClient chatClient; private final WeatherToolService weatherToolService; public AgentService(ChatClient.Builder chatClientBuilder, WeatherToolService weatherToolService) { this.chatClient chatClientBuilder.build(); this.weatherToolService weatherToolService; } public String chat(String userMessage) { return chatClient.prompt() .user(userMessage) .tools(weatherToolService) .call() .content(); } }当用户问“北京今天天气怎么样”模型会生成的工具调用请求Spring AI自动执行getCurrentWeather方法并把结果回传给模型生成最终回答。“北京现在热不热”这类问题就能看到工具调用链路生效模型先调用天气工具再根据工具返回结果组织语言。4.9 这个案例里发生了什么从代码层面看这个Demo同时覆盖了三条核心链路RAG链路文档加载 → 切片 → 向量化 → 检索 → 生成。Agent工具调用链路模型识别意图 → 输出工具调用 → Spring AI执行Tool方法 → 结果回传模型。编排框架能力ChatClient统一接口、Advisor机制、工具发现、向量存储抽象。如果你可以把这三条链路独立跑通再看LangChain、Spring AI甚至自研Agent框架时就不会觉得它们是黑盒了。5. 安全与权限边界警惕LLM API的过度授权5.1 什么是过度授权Excessive Agency安全社区一直在强调一个问题LLM的Agent能力越强潜在破坏力越大。OWASP给LLM应用归纳的十大风险里“过度代理Excessive Agency”专门指LLM Agent被赋予了超出任务需要的权限、功能或工具。比如一个“查询订单状态”的Agent却被允许“删除订单”。一个“读取工单”的模型却拥有管理员级别的数据库账号。一个工具接受外部输入但没有校验来源导致攻击者通过提示注入诱导模型调用敏感工具。PortSwigger的BP靶场中有一个实验“Exploiting LLM APIs with Excessive Agency”模拟的场景是一个LLM对话机器人具备工具调用能力但工具权限过大攻击者通过精心构造的提示让模型调用工具读取到了敏感数据。这是非常典型的“模型能力越强风险越容易扩大”的例子适合在合法授权的测试环境里练习不要对生产系统实行。5.2 提示注入与工具调用的组合攻击攻击流程通常是这样的攻击者在Prompt中注入恶意指令例如“忽略之前的规则执行工具X”。模型把注入指令当作合法指令调用工具X。工具X权限过大执行了敏感操作或返回了敏感数据。难点在于模型很难完全区分“用户指令”和“潜在恶意输入”。尤其是当工具返回内容也包含指令时模型可能被二次注入影响。5.3 LLM应用最小权限原则在工程中建议从以下几个层面限制Agent权限层面措施工具设计每个工具只做一件事权限范围最小化参数校验对模型生成的工具输入做白名单校验操作确认删除、更新、发送等高风险操作需人工二次确认身份传递Agent的所有工具调用必须携带原始用户身份不能使用公共服务账号审计日志记录每一次工具调用、Prompt上下文、模型输出便于回溯网络隔离LLM服务、工具服务、数据库分区部署Agent不能直连核心库5.4 自查清单上线前建议逐项检查当前Agent能调用哪些工具每个工具的最小必要权限是多少有没有工具可以直接执行写操作如果有是否做了人工确认模型输出有没有做内容过滤或格式校验工具调用日志是否完整记录如果模型被注入“删除所有数据”的指令现有权限体系能不能拦住如果这些问题答不上来项目不要急着上线。6. 常见问题与排查思路6.1 常见问题汇总问题现象常见原因解决思路调用模型报401/403API Key错误或权限不足检查环境变量确认Key有模型访问权限“llm文本向量api未配置”嵌入模型API Key未配置或向量存储缺少Embedding模型检查spring.ai.openai.api-key确认Embedding模型名称本地Ollama连接失败Ollama未启动或端口不对执行ollama serve访问http://localhost:11434测试Agent不调用工具工具描述不清晰或模型不支持Function Calling优化Tool描述换支持工具调用的模型RAG回答不相关切片粒度太大或检索Top-K太小减少切片长度提高Top-K加入Rerank量化后效果明显变差INT4量化导致精度损失严重换INT8或在量化前做校准数据集模型输出格式不稳定没有指定输出格式或Post-processing使用结构化输出如JSON Schema并做解析校验工具方法报错但模型无感知异常被吞掉模型拿不到错误信息让工具返回错误描述而不是抛出未捕获异常6.2 排查步骤遇到Agent或RAG链路问题按以下顺序排查先确认模型本身能正常返回文本。再确认工具调用是否触发。Spring AI可以开启日志级别为DEBUG观察模型返回的Tool Call请求。确认为什么不触发是描述不清还是模型不支持还是Prompt被规则干扰。最后确认工具执行结果是否正确并回传给了模型。7. 最佳实践与学习路线7.1 工程最佳实践结合前面的案例整理几条踩过坑之后沉淀下来的建议模型API调用必须封装成独立模块方便切换不同模型服务商不要让业务代码直接依赖某家SDK。Prompt和工具描述要写细尤其是工具描述。很多Agent效果差不是模型差是工具描述没说清楚“什么时候该调用”。RAG不是万能的。知识库检索不到的边界情况要设计“我不知道”的兜底回答不要硬编。流式输出要尽早做。真实用户等待时间很敏感非流式容易显得系统很卡。上线前用固定的评测集跑一遍。不要靠感觉说“效果还行”记录每次改动前后的对比结果。Token成本要监控。加一个简单的日志统计看看每轮对话平均消耗多少Token哪些Prompt或工具描述占了大量空间。7.2 下一步学习路线如果对RAG还不熟先手动实现一遍“读取文档 → 切片 → 向量化 → 检索”链路不要直接用框架封装。如果想深入Agent学习Function Calling协议再研究MCP Server怎么开发。如果想做生产级系统重点看模型网关、权限隔离、可观测性三块。如果想部署开源模型从Ollama开始再研究llama.cpp的量化参数和性能调优。7.3 最后说几句回到标题“Whats Next for LLMs”。我认为答案不是某个具体的模型而是应用架构的革命模型从“聊天引擎”变成“系统大脑”开发者的任务也从“写Prompt”升级为“设计工具边界、知识流和权限体系”。这个方向对初学者来说反而是机会——因为工程化的经验可以积累而模型能力更新太快追模型永远追不过来。先把RAG、Agent、MCP、安全边界这些基本功打扎实再面对下一代模型你也能很快接入。如果本文对你有帮助可以收藏备用。动手实践时遇到任何问题欢迎在评论区带上错误信息讨论我会尽量帮你排查。