资讯动态

大模型应用开发实战:从RAG到Agent的工程化落地

发布时间:2026/8/29 8:27:33 来源:尧图企业网站定制
奇点是一个容易被科幻化也容易在技术讨论中被极端化的词。当 AI 领域的头部研究者、产业领袖公开判断“Singularity Has Begun”时普通开发者看到的是职业焦虑技术团队看到的是架构迁移压力产品负责人看到的是业务改造机会。抛开预言和口号从工程实践角度看这句话真正值得关注的部分是大模型已经不再停留在聊天机器人和 demo 阶段它正在被嵌入知识库、客服、订单系统、代码生成、内容生产等真实业务链路中。这意味着 AI 应用开发的技术栈正在被重新排列本文围绕这条主线完整走一遍大模型应用开发链路环境准备、最小 API 调用、RAG 知识库、Agent 工具调用、模型部署评测、常见问题排查并给出可复用的学习路线和发布前检查清单。1. 奇点在工程语境下意味着什么AI 应用开发的技术栈正在重排1.1 奇点的信号不是模型自身而是模型进入业务流程如果只盯着模型榜单奇点是否开始很难判断。模型能力每年都在变今天的“最强”很快会被刷新。工程上更有意义的信号是模型能力是否稳定进入业务流程是否成为业务系统里不可绕过的环节。现在可以看到几个明显信号客服系统开始用大模型生成答复草稿再由人工确认而不是完全人工打字。内部知识库开始用 RAG 架构接入大模型员工用自然语言查询制度、流程、代码规范。代码开发工具开始把 AI 补全嵌入 IDE开发者的日常动作从“手写”变成“审查和修正”。Agent 类应用开始承担多步骤任务不再只是单轮问答。这些信号说明AI 正从“模型层”向“应用层”迁移。对开发者的影响不在于是不是学会了某个模型而在于能不能把模型能力和现有工程系统稳定地组合起来。奇点叙事背后真正的问题不是模型是否会产生意识而是应用开发者是否已经具备驾驭大模型不确定性的能力。1.2 大模型应用与传统软件开发的三个核心差异传统软件开发的逻辑是输入确定逻辑确定输出确定。只要代码逻辑正确相同输入基本得到相同输出。大模型应用则完全不同。第一输出不确定。同样的 prompt模型可能给出不同表述甚至不同结论。工程上必须设计输出校验、结果重试和降级策略。第二知识边界不明确。模型在训练时学会了大量知识但训练数据有截止时间内部业务数据它完全不知道。所以“知道什么”和“不知道什么”很难从模型自身得到答案只能通过检索补充来缓解。第三成本模型不同。传统接口的成本主要是服务器资源和开发时间大模型 API 的成本是 token 数量叠加。一个没有约束的 Agent 循环可能一次请求产生几十轮调用费用和延迟都会失控。这三个差异决定了 AI 应用不能按传统 CRUD 项目的思路做。架构设计、异常处理、测试策略都要重新考虑。1.3 两个必须先建立的技术判断第一个判断不要试图让大模型记住所有业务知识。业务知识应该放在知识库或数据库中模型负责理解问题和组织答案而不是背数据。正因为如此RAG 才会成为当前 AI 应用的主流架构。第二个判断不要追求一次生成完美结果。AI 应用的正确姿势通常是“生成、校验、修正、再生成”。比如 Agent 调用工具失败可以让模型根据错误信息自己修改参数重试输出 JSON 不稳定可以用结构化输出或二次校验来兜底。有了这两个判断后续的代码设计和排查思路会清晰很多。接下来的章节围绕具体工程操作展开。2. 环境准备与大模型访问先跑通一个最小 API 调用2.1 开发环境清单Python、虚拟环境、密钥管理本地做 AI 应用实验最推荐的方式是 Python 搭配虚拟环境。原因是大模型 SDK、向量库、Embedding 工具大多有 Python 实现生态完整排查问题也方便。环境要求可以按这个表格准备。依赖项学习环境建议生产环境建议Python3.10 或 3.11优先使用稳定版本由镜像或 Docker 固定版本虚拟环境venv 或 conda 均可使用 Docker 镜像不依赖本地解释器密钥管理写入.env文件不提交到 Git使用配置中心或密钥管理服务大模型访问统一兼容接口配置环境变量API 网关统一鉴权和限流密钥管理是新手最容易犯错的地方。把 API Key 硬编码在 Python 文件里一旦代码提交到公开仓库密钥就泄露了。推荐的做法是使用环境变量本地开发可以用python-dotenv加载.env文件但.env必须加入.gitignore。python -m venv .venv source .venv/bin/activate pip install requests python-dotenv2.2 用 OpenAI 兼容接口实现最小调用不同大模型厂商的接入方式有差异但 OpenAI 兼容接口已经成为事实上的标准。很多模型服务都支持这种协议包括私有化部署的模型网关。使用这种统一接口的好处是切换模型厂商时只需要改BASE_URL和API_KEY业务代码不用大改。下面是一个最简调用示例使用requests直接请求/chat/completions接口不依赖特定厂商 SDK。import os import requests from dotenv import load_dotenv load_dotenv() API_KEY os.getenv(LLM_API_KEY) BASE_URL os.getenv(LLM_BASE_URL, https://api.example.com/v1) MODEL os.getenv(LLM_MODEL, qwen-plus) def chat(messages, temperature0.7, max_tokens1024): url f{BASE_URL}/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: MODEL, messages: messages, temperature: temperature, max_tokens: max_tokens, stream: False, } resp requests.post(url, headersheaders, jsonpayload, timeout60) resp.raise_for_status() data resp.json() return data[choices][0][message][content] if __name__ __main__: result chat([{role: user, content: 用一句话解释什么是 RAG}]) print(result)这段代码的关键点有三个。第一messages是对话列表每个元素有role和content支持 system、user、assistant 三种角色后续 Agent 开发还会用到 tool 角色。第二timeout必须设置否则接口异常时请求会一直挂住。第三stream在实验阶段可以先设成False生产环境处理长回答时再切换为流式输出。2.3 核心参数速查temperature、top_p、max_tokens、stream大模型接口参数看似简单每个参数都直接影响输出质量和成本。下面用表格整理最常用的几个参数。参数含义常见值调大效果调小效果temperature采样随机性0.2 到 0.8输出更多样也可能更发散输出更保守、更稳定top_p核采样概率阈值0.7 到 0.9允许候选词范围更大输出更集中max_tokens最大生成 token 数512 到 2048支持更长回答但成本增加回答可能被截断stream是否流式返回False 或 True用户首字延迟更低等待完整结果延迟高实际项目里temperature不建议随意调到接近 0也不要直接拉到 1.5。做知识库问答时通常会把它设在 0.2 以下让模型尽量忠于检索到的资料做创意写作、营销文案时可以适当调高。max_tokens容易被忽略。模型生成内容达到上限后会直接截断可能造成 JSON 不完整、回答只有半句。如果调用带结构化输出的接口还要确认结构化输出是否会消耗额外 token。2.4 学习环境与生产环境访问模型的差异学习环境只要调通接口就能继续生产环境还要求稳定性和可控性。推荐采用这几种做法不直接让业务代码保存模型密钥而是通过网关或密钥管理服务注入环境变量。在网关层做统一的限流、熔断和审计防止误调用造成巨额费用。对模型 API 的返回做缓存尤其是重复问法和固定知识类问题可以减少 token 消耗。日志里不要打印完整请求和响应。大模型接口可能携带业务敏感信息生产日志需要脱敏。学习环境中一个 Python 脚本调用模型 API 已经足够。生产环境中通常还需要一个模型网关、一套服务编排和一组监控指标。理解这一差距后面部署章节会更容易落地。3. RAG 知识库实战让模型基于可检索的事实回答3.1 为什么纯 Prompt 回答在知识密集型场景不可靠很多人第一次做知识库问答直接在 prompt 里塞一段资料让模型回答。对于小规模静态资料这确实能用。但进入真实业务后问题很快出现资料超过上下文窗口无法全部塞进 prompt。资料频繁更新每次更新都要改 prompt。模型会把 prompt 里的资料和训练记忆混在一起难以判断回答依据。一次性输入大量无关资料会稀释注意力回答质量反而下降。纯 Prompt 方式不可靠核心原因是模型没有“查证”能力。它只能基于当前输入和训练参数生成内容。RAG 的思路是把“怎么答”和“答什么”分离模型负责组织语言知识库负责提供事实依据。3.2 RAG 架构拆解Embedding、向量库、检索、生成RAG 的全称是 Retrieval-Augmented Generation中文通常叫检索增强生成。它把知识库问答拆成四个步骤文档切片把长文本切成语义较完整的片段。向量化用 Embedding 模型把文本片段转换成向量。向量检索把用户问题转成向量在向量库中找最相似的片段。生成把检索到的片段拼入 prompt让模型基于这些资料回答。这套结构解决了一个核心矛盾模型上下文窗口有限但业务知识可能无限增长。每次请求只检索最相关的几段内容既控制了 token 成本也提高了回答准确率。3.3 最小 RAG 示例文档加载、向量化、检索、生成下面用一个最小 RAG 示例演示完整链路。示例使用sentence-transformers做向量化使用faiss做向量索引不引入重型服务端组件适合本地理解和改造。import os import numpy as np import faiss from sentence_transformers import SentenceTransformer from dotenv import load_dotenv load_dotenv() embedder SentenceTransformer(BAAI/bge-small-zh-v1.5) documents [ 订单在支付后 30 分钟内会自动同步到仓库系统。, 退款申请需要在收货后 7 天内提交。, 企业用户需要先完成实名认证才能调用开放接口。, 普通用户最多可以同时创建 5 个项目。, ] vectors embedder.encode(documents, normalize_embeddingsTrue) dim vectors.shape[1] index faiss.IndexFlatIP(dim) index.add(vectors) def search(query, top_k2): q_vec embedder.encode([query], normalize_embeddingsTrue) scores, indices index.search(q_vec, top_k) return [documents[i] for i in indices[0]], scores[0] def generate_with_context(query): context, scores search(query) prompt ( 请根据以下资料回答问题。如果资料不足以回答请直接说明不知道。\n\n 资料\n \n.join(f- {doc} for doc in context) f\n\n问题{query}\n\n回答 ) result chat( [ {role: system, content: 你是严谨的客服助手回答必须基于给定资料。}, {role: user, content: prompt}, ], temperature0.2, ) return result, context, scores if __name__ __main__: answer, context, scores generate_with_context(退款有时间限制吗) print(检索到的资料) for doc in context: print(-, doc) print(回答) print(answer)这个示例说明三个问题。第一文档向量化后存入 FAISS 索引查询时把问题转成向量用内积计算相似度。第二prompt 里明确告诉模型“资料不够就说明不知道”这会显著降低幻觉率。第三检索结果要返回给调用方方便人工判断模型回答是否真的基于资料。3.4 检索质量相关参数和常见问题检索质量直接决定 RAG 系统上限。如果检索不到相关内容模型再强也回答不好。常见问题可能原因处理建议检索结果不相关文档切片过长语义被稀释按段落或标题切片控制每片长度相同问题的检索结果不稳定未使用文本召回补充向量召回增加 BM25 关键词召回做混合检索回答依赖旧资料索引未及时更新建立文档更新任务增量刷新向量库查询里包含专有名词向量化模型对专有名词不敏感在做向量化前保留并拼接关键词这里有一个新手容易踩的坑认为向量检索是万能的。实际上用户问题中的精确 ID、订单号、人名向量检索经常表现很差。真实系统里最稳健的做法是混合检索先用关键词或结构化查询精确匹配再用向量检索扩展语义相关性最后汇总结果并排序。这个思路在下面的 Agent 示例中也会体现。4. Agent 开发从单次问答到工具调用闭环4.1 Agent 和大模型 API 调用的本质区别普通大模型 API 调用是“一问一答”用户输入模型输出流程结束。Agent 则把一次请求扩展为多轮“思考—行动—观察”的循环。模型不再只生成文本而是可以决定调用哪个工具、传什么参数、看到工具返回结果后再决定下一步。以查询订单为例。普通调用只能让模型根据训练数据猜一个答案Agent 调用会让模型先识别出需要一个订单号然后调用订单服务接口拿到真实状态后再组织回答。这才是“AI 进入业务流程”的真正含义。4.2 工具调用接口设计函数定义、参数约束、结果回注工具调用通常要求开发者在请求里声明可用函数。模型根据用户问题和函数定义生成一个结构化的工具调用请求。工程上要注意三点。第一函数定义必须清晰。函数名要能表达意图参数必须有类型和描述required字段要明确。函数描述写得越清楚模型越不容易乱调。第二参数校验必须放在工具内部。模型生成的参数不一定合法可能缺少字段、字段类型错误、甚至包含不存在的订单号。工具内部要做校验返回结构化错误信息。第三工具结果要回注给模型。模型不是真正执行代码它只是建议调用工具真正执行的是你的代码。执行后要把结果作为 tool 消息发给模型模型才能基于真实结果继续回答。4.3 一个最小 Agent 示例查询订单状态下面用一个查询订单状态的例子演示 Agent 的完整循环。这里复用第二章的chat函数但没有使用第三方 Agent 框架代码更透明更容易理解。import json import os import requests from dotenv import load_dotenv load_dotenv() API_KEY os.getenv(LLM_API_KEY) BASE_URL os.getenv(LLM_BASE_URL, https://api.example.com/v1) MODEL os.getenv(LLM_MODEL, qwen-plus) tools [ { type: function, function: { name: query_order_status, description: 根据订单号查询订单当前状态, parameters: { type: object, properties: { order_id: {type: string, description: 订单号例如 A1001} }, required: [order_id] } } } ] def query_order_status(order_id: str) - str: status_table {A1001: 已发货, A1002: 待支付} if order_id not in status_table: return json.dumps({order_id: order_id, status: 未知订单}, ensure_asciiFalse) return json.dumps({order_id: order_id, status: status_table[order_id]}, ensure_asciiFalse) def chat_with_tools(messages, tools): url f{BASE_URL}/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: MODEL, messages: messages, tools: tools, tool_choice: auto, temperature: 0.2, } resp requests.post(url, headersheaders, jsonpayload, timeout60) resp.raise_for_status() return resp.json() def run_agent(user_input: str, max_steps5): messages [{role: user, content: user_input}] for step in range(max_steps): data chat_with_tools(messages, tools) msg data[choices][0][message] messages.append(msg) tool_calls msg.get(tool_calls) if not tool_calls: return msg[content] for tool_call in tool_calls: fn tool_call[function] if fn[name] query_order_status: args json.loads(fn[arguments]) result query_order_status(args[order_id]) messages.append({ role: tool, tool_call_id: tool_call[id], content: result, }) raise RuntimeError(Agent 循环超过最大步数) if __name__ __main__: print(run_agent(请帮我查一下订单 A1001 的状态))这段代码的关键逻辑是模型返回tool_calls时系统执行函数并把结果作为 tool 消息回传直到模型不再要求调用工具。max_steps是必须加的防护防止模型陷入死循环。4.4 Agent 生产化的五个工程问题Agent 从 demo 到生产需要额外解决五个问题。第一循环控制。永远设置最大步数、最大 token 数和超时时间避免单次请求产生不可控费用。第二权限控制。Agent 能调用的工具必须遵循最小权限原则。不要让 Agent 直接获得删除数据库、转账、发公告等高风险权限至少要加人工审批或二次确认。第三结果校验。模型生成的工具参数不总是正确工具内部要有防御式校验并把错误返回给模型让模型自行修正。第四可观测性。每一轮思考、工具调用、工具结果都要记录日志否则 Agent 出错时完全无法回溯。第五回滚策略。Agent 的失误可能影响真实业务。上线前要有开关能快速禁用某个工具并保留手工处理通道。如果在 Java 服务端做 Agent可以关注 Spring AI 的 Tool Calling 抽象。它把工具定义、参数解析、结果回注包装成相对统一的接口适合已经重度使用 Spring 的技术团队。这样可以把 Agent 能力嵌入原有服务而不是另起一套 Python 服务。5. 模型部署、评测与可观测性把 AI 应用做成可靠产品5.1 模型服务方式对比云端 API、私有化部署、边缘部署模型接入和部署方式直接影响系统的稳定性、成本和安全边界。三种方式各有适用场景。部署方式优点需要关注的点适用场景云端模型 API部署简单模型迭代快数据出域token 成本限流通用问答、文本生成、内容总结私有化部署数据内网可控性高需要 GPU运维成本高金融、政务、企业知识库边缘部署低延迟离线可用模型受限升级复杂移动端、工业摄像头、低带宽环境实践中的常见折中方案是同一套业务代码里抽象模型访问层开发时用云端 API生产某些场景切到私有化部署。这样模型层的变化不会传染到业务层。5.2 用评测集做回归避免“改了一个问题坏了一片回答”大模型应用最大的测试难题是“没有标准答案”。同一个问题两个工程师可能给出不同但都合理的回答。所以必须建立一套可重复的自动化评测机制。最小可落地的评测集是 CSV 或 JSON 文件每条记录包含问题、期望回答要点、检索期望命中的文档 ID。每次变更 prompt、检索策略或模型版本后跑一遍评测集记录通过率和失败用例。[ { question: 退款有时间限制吗, expected_points: [收货后, 7天内, 可提交退款申请], expected_doc_ids: [1] }, { question: 企业用户如何调用开放接口, expected_points: [实名认证, 开放接口], expected_doc_ids: [2] } ]评测不一定要用复杂框架可以先写脚本把模型回答和期望要点做关键词匹配或人工抽检。重要的是把“这个版本好不好”从个人感觉变成可对比的数据。5.3 可观测性设计日志、跟踪、反馈闭环AI 应用的可观测性比传统应用更复杂因为一次用户请求可能包含多个模型调用、多个工具调用和多次重试。至少要记录以下指标模型调用延迟和 token 消耗。用户输入和最终输出摘要。RAG 检索命中的文档 ID 和相似度分数。Agent 的每一步动作和工具返回结果。模型返回中触发的安全过滤或格式校验失败。日志格式建议使用 JSON便于采集和分析。下面是一个简化日志样例。{ request_id: 8f3a2c1e, scene: order_status_agent, model: qwen-plus, prompt_tokens: 128, completion_tokens: 42, latency_ms: 890, tool_calls: 1, retrieved_doc_ids: [2, 5], finish_reason: stop }有了日志还不够还要有反馈闭环。最简单的做法是在回答下方放“有帮助 / 无帮助”按钮或让用户提交反馈反馈数据回流到评测集持续发现模型回答的盲区。6. 常见问题排查链路从现象倒推到根因6.1 调用超时、限流与网络问题现象请求偶尔成功偶尔超时或者直接返回 429、503。可能原因按顺序排查是否触发了模型服务限流需要查看响应头里的Retry-After或服务端返回的限流提示。请求重试逻辑是否太激进导致限流加剧。模型输入是否过长导致响应时间变长。网络链路是否稳定私有化部署场景还要检查 GPU 服务是否过载。处理建议使用指数退避重试设置合理的超时对用户请求做队列化。不要让前端请求直接穿透到模型服务。6.2 回复格式不稳定与解析失败现象要求模型返回 JSON但偶尔出现前后缀、换行、转义错误或字段缺失。这类问题优先检查是否使用了支持结构化输出的接口或响应格式约束。prompt 中是否给了字段含义和示例。是否在代码里对模型输出做了二次清理和校验。推荐做法是把“生成”和“解析”分开先让模型只输出文本再用正则或 JSON 解析器提取关键信息如果解析失败把错误信息回传给模型让它重新生成。不要相信模型第一次输出的 JSON 一定合法。6.3 检索不到、幻觉和“一本正经胡说”现象模型回答很流畅但内容和知识库资料对不上。排查顺序如下先单独验证检索。打印检索到的文档和相似度分数确认资料是否真的命中。如果检索不到检查文档切片方式和向量化模型是否适合当前语料。如果检索到了但回答错误检查 prompt 是否明确要求“只能基于资料回答”。如果资料和问题语言不一致例如中文问题配英文资料需要先做翻译或统一向量化语言。如果资料经常更新确认向量库索引是否已经刷新。6.4 推荐排查顺序表下面表格列出常见问题现象、检查重点和处理建议适合打印出来或保存在项目文档里。问题现象重点检查处理建议接口超时网络、token 长度、模型负载设置超时和重试优化 prompt 长度429 限流请求频率、重试逻辑指数退避合理并发网关限流输出 JSON 解析失败模型输出格式、是否截断使用结构化输出解析失败后重试回答不在知识库范围内检索结果、prompt 约束明确“不知道”指令加入引用溯源Agent 反复调用工具max_steps、工具参数校验限制循环让工具返回错误信息并发一高就慢模型接口并发数、缓存增加缓存限流异步化7. 当前阶段的学习路线与可复用清单7.1 一条务实的 AI 应用开发学习路径技术上不需要先从深度学习和 Transformer 原理学起。多数应用开发者更需要的是一条“够用且能扩展”的学习路径。第一阶段跑通大模型 API。掌握 messages 结构、关键参数能写一个聊天程序。学习重点是把模型当作一个不稳定的远程服务来调用。第二阶段掌握 Prompt 工程和输出约束。重点不是背模板而是学会“给模型提供上下文、约束格式、要求资料不足时承认不知道”。第三阶段做 RAG 项目。把一批文档变成可检索的知识库理解切片、Embedding、向量库、检索排序之间的关系。第四阶段做 Agent 项目。实现至少一个工具调用循环理解工具定义、参数解析、结果回注和循环控制。第五阶段进入工程化。包括模型网关、评测集、日志追踪、成本监控和持续集成。这个阶段的目标是把 AI 能力做成稳定产品。7.2 AI 应用发布前检查清单每次发布前可以按下面清单逐项确认。[ ] 模型密钥是否通过环境变量或密钥管理服务注入是否已从代码仓库中移除。[ ] 所有模型请求是否设置了超时、重试和熔断。[ ] 如果使用 Agent是否设置了最大步数和单次会话成本上限。[ ] RAG 检索结果是否有日志是否足够定位“回答是否基于资料”。[ ] 是否定义了“模型不知道”时的回答方式而不是让模型硬猜。[ ] 输出内容是否经过格式校验解析失败是否有兜底逻辑。[ ] 用户反馈渠道是否打通数据是否可以回流到评测集。[ ] 日志是否脱敏是否记录了 request_id、模型、token 数和耗时。[ ] 高风险工具调用是否有二次确认或权限审批。[ ] 是否有回滚开关能否在出现质量事故时快速切换到人工处理。7.3 进一步扩展的方向如果已经掌握上文内容下一步可以根据业务方向继续深入。多模态应用图片理解、音视频内容生成。知识库工程混合检索、重排序、知识图谱结合。Agent 框架LangGraph、Spring AI 或自研状态机。模型微调对特定场景做领域适配。AI 编程工具把 Cursor 类工具接入团队工程规范建立代码审查和提示词资产库。模型部署学习模型量化、推理加速、GPU 资源调度。无论选择哪个方向都要记住一个核心原则AI 能力的价值不在于模型能生成多华丽的文本而在于它能否被稳定地集成到业务系统能否被监控、被评测、被回滚。奇点是否开始本质上不是模型自己的事而是工程团队是否已经准备好用工程方法驾驭不确定性。对应用开发者来说最稳妥的应对方式不是追逐每一个新模型而是把大模型调用、RAG、Agent 和可观测性这套基础能力做扎实具备快速接入和替换模型的能力才能在任何技术浪潮下都保持主动权。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价