大模型应用开发实战:从提示词工程到RAG与Agent全链路 📅 发布时间:2026/9/1 5:24:52 👁 浏览次数: 从“能调 API”到“能做大模型项目”中间到底隔着什么很多同学学大模型应用路径大概是这样的先花半天把 LangChain 装好再调用一次大模型 API成功输出一句“你好我是 AI 助手”然后就开始找不到下一步了。遇到真正的业务问题时仍然不知道该先写提示词、还是先建知识库、还是先设计工具函数。甚至会把“调用大模型”和“做大模型应用”画上等号。这个差距的本质不是模型能力不够而是工程链路没有建立起来。一次真正可用的 AI 功能背后是一整条链路用户输入进入系统后要经过提示词约束、检索增强、上下文管理、工具调用、Agent 决策等多个环节最后才生成回答。任何一个环节不稳定最终结果都会变得不可用。这篇文章会带你把这条链路完整走一遍从环境搭建入手依次打通提示词工程、Embedding、向量数据库、RAG 检索增强、Agent 工具调用最终跑通一个“企业知识库问答 工具调用”的典型案例。你不仅能得到一份可以照着敲的代码还会知道每一步为什么这样做、出现问题该从哪里查起。读完你会有一个明确判断大模型项目的核心难点不在于“选哪个模型”而在于“如何把检索、上下文和工具稳定地组织在一起”。1. 这篇文章真正要解决的问题1.1 三道坎提示词失控、知识不落地、工具链路断裂把大模型接进业务系统你大概率会遇到三类问题。第一类是提示词失控。同一个问题换个句式问答案质量就明显下降加了限制条件它还是“自由发挥”。这说明你还没有把提示词当成代码来管理也没有用结构化模板把模型的行为边界约束住。第二类是知识不落地。模型没有真正“知道”你们公司的内部文档。你训练不了它也不应该训练它最合理的办法是把文档检索出来作为上下文喂给它。这个过程中文档怎么切、向量怎么算、数据库怎么存、相关片段怎么召回都会直接影响回答质量。第三类是工具链路断裂。你希望 AI 能查天气、查订单、操作内部系统而不是只能聊天。但当模型开始调用工具时你会发现它并不总能按正确参数调用也不一定能根据工具返回结果继续推进任务。稍微复杂一点的流程它就“掉链子”。这三道坎就是本文要逐步解决的问题。1.2 本文的项目场景与读者收益为了不让概念悬空我们设定一个贯穿全文的项目企业内部知识库问答助手。它的需求是员工可以像聊天一样询问制度、项目规范、产品文档系统需要基于内部文档给出有依据的回答同时助手还能调用一个查询订单状态的外部工具。这个项目同时覆盖了 RAG 和 Agent 工具调用是学习大模型应用全链路最典型的一个切入点。如果你是后端、全栈工程师或者刚接触大模型方向的学生这篇文章的价值在于把散落在各个文档里的技术点串成一条可运行的主线。你不会再纠结“LangChain 到底用来干什么”“向量数据库是不是必须的”“Tool Calling 和 Agent 是什么关系”而是直接看到一个完整答案。2. LangChain、RAG、Agent、向量数据库全景图2.1 先建立一张概念地图在写代码之前先把几个关键术语的关系理清楚。它们不是互相替代而是在一条链路里各司其职。概念通俗解释它在链路中的角色大模型 API一个“能力很强但记性差”的文本生成器最终负责理解与生成回答提示词工程给这个生成器写的“任务说明书”控制输出的格式、风格与边界Embedding把文本变成数字向量让计算机可以计算“哪两段话语义相近”向量数据库专门存储、检索向量的仓库从大量文档中快速找“最相关的片段”RAG先检索、再生成的整体方案让模型在回答前先看到限定资料工具调用让模型输出“调用哪个函数、参数是什么”把说话能力变成执行能力Agent基于大模型的自主执行框架负责拆解目标、调度工具、观察结果LangChain大模型应用的编排工具库把上面这些组件“拼装”起来LangGraph基于状态图的编排框架在复杂 Agent 流程中管理状态和节点一句话概括RAG 解决“模型基于什么回答”的问题工具调用解决“模型能做什么”的问题Agent 解决“谁来编排执行流程”的问题向量数据库和 Embedding 是让 RAG 真正可用的底层基础设施LangChain 则是把这些环节用 Python 组装起来的脚手架。2.2 LangChain 和 LangGraph 到底有什么区别这是很多初学者会问的问题。从热度看LangGraph 已经越来越被推荐用于生产级 Agent。它们的关系可以这样理解LangChain 是一套集成工具集核心价值是提供了大量现成的封装文档加载器、文本分割器、向量存储封装、Prompt 模板、模型接口封装。它适合快速拼出 RAG 链路和简单的 Agent 流程。LangGraph 是一个更底层的编排框架它把执行过程建模为“图 状态”。每个节点做一件事状态在不同节点之间流转开发者可以精细控制何时调用工具、何时返回给用户。它更适合复杂、多步骤、需要人类确认或分支判断的 Agent。初学者先用 LangChain 跑通全链路理解每个组件的作用当 Agent 流程变复杂再迁移到 LangGraph 管理状态。这是比较稳妥的学习路径。2.3 全链路数据流整个系统运行时的数据流向是用户提问 - 系统判断是检索知识库还是调用工具还是直接回答 - 如果走 RAG文档分块 Embedding - 向量库检索 - 拼装上下文 - 如果走 Tool Calling模型输出工具名和参数 - 执行工具 - 将结果回填 - 最终拼装 Prompt - 大模型生成回答 - 返回给用户后面的实战环节会照着这条链路逐段实现。3. 环境准备与前置条件3.1 运行环境建议使用 Python 3.10 或更高版本。大模型的 SDK 生态更新很快较新的 Python 版本往往对类型注解和异步支持更好。建议创建一个干净的虚拟环境mkdir ai-project cd ai-project python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate3.2 安装依赖本文使用 LangChain 生态演示核心依赖如下pip install langchain pip install langchain-openai pip install langchain-community pip install chromadb pip install python-dotenv注LangChain 的接口演进速度比较快不同小版本的 API 可能存在差异。本文代码以“理解主流程”为目标你安装时以官方最新文档为准。如果某个类的导入路径报错通常搜索报错信息就能找到新路径。3.3 模型服务的选型模型 API 的理想选择是“兼容 OpenAI 接口”的服务。这样你可以用同一套代码切换不同模型提供商。国内很多大模型厂商都提供兼容接口只要配置 Base URL 和 API Key 即可。如果你想完全本地运行也可以部署 Ollama通过本地接口接入。关键配置用.env文件管理不要硬编码在代码里# 文件路径.env OPENAI_API_KEYyour-api-key OPENAI_BASE_URLhttps://your-model-provider.com/v1 EMBEDDING_MODELtext-embedding-3-small LLM_MODELgpt-4o-mini如果使用的是国内模型服务把上面 Base URL 和模型名换成服务商提供的参数即可。还要注意API Key 是敏感信息不要把.env提交到 Git 仓库生产环境建议使用密钥管理服务。4. 提示词工程让模型输出稳定可用的第一步4.1 提示词为什么值得当作代码来写很多初学者把提示词当成“一句问话”觉得“能说清楚就行”。但实际项目中提示词的稳定性直接决定功能可用性。比如你在知识库问答里要求“只能基于给定资料回答”模型可能第一次遵守第二次就自行发挥。问题通常出在提示词没有结构化也没有用模板统一管理。好的提示词一般包含四部分角色定义让模型知道它在完成什么任务。任务规则明确能做什么、不能做什么。输入数据把检索出来的资料作为 Context 拼接进去。输出约束要求格式、长度、是否引用来源等。4.2 结构化提示词模板示例# 文件路径src/prompt_template.py from langchain_core.prompts import ChatPromptTemplate qa_system_prompt 你是一个严谨的企业知识库问答助手。 请严格遵循以下规则 1. 只能根据【参考资料】中的内容回答不得编造事实。 2. 如果参考资料不足以回答用户问题直接回复“资料库中未找到相关信息”。 3. 回答时用简洁的中文分点列出关键内容。 4. 不要输出与问题无关的背景信息。 【参考资料】 {context} qa_prompt ChatPromptTemplate.from_messages( [ (system, qa_system_prompt), (human, 用户问题{question}), ] )这段代码最重要的不是格式花哨而是把“禁止幻觉”的规则显式写进了 System Prompt。变量{context}会在运行时被检索到的文档片段填充{question}来自用户输入。用模板管理提示词你才能在不同版本间对比效果也方便上线后观测模型行为。4.3 新手最容易踩的提示词坑提示词过长但信息冗余。系统给模型的“约束”应该是精准指令而不是大段描述。把保密要求放在提示词里就以为模型“真的保密”。提示词只是行为约束不是安全机制不能依赖它处理敏感数据。不做版本管理。提示词改一次效果变一次没有记录就无法回归。建议在代码仓库里用单独目录管理。5. Embedding 与向量数据库实战5.1 Embedding 解决的是什么问题先看一个场景。员工提问“年假能休几天”文档里的原话是“员工每年可享受的带薪休假天数见下表”。关键词并不重合传统数据库按关键词搜索大概率找不到这一段。Embedding 的解法是把“年假”和“带薪休假”分别转成向量这两个向量在数学空间里的距离很近于是系统能理解它们语义相似。Embedding 本身就是一个模型它把一段文本映射为几百上千维的浮点数数组。有了这个数组我们就可以用“向量距离”来衡量语义相关度。5.2 向量数据库选型对比数据库部署形态适合场景Chroma本地嵌入式学习、原型验证、中小规模知识库FAISS本地库高性能向量检索需要自己管理存储Milvus分布式服务生产级大规模向量检索pgvectorPostgreSQL 插件与业务数据共存适合已用 PG 的团队Qdrant服务化高可用向量检索功能完善学习阶段推荐 Chroma因为它零服务器、自动持久化到本地文件几行代码就能跑起来。生产阶段按团队已有技术栈和数据量选型即可。5.3 文档加载、分块与向量化完整代码下面这段代码演示加载一个本地 Markdown 文档按章节拆分生成向量存入 Chroma并执行一次检索。# 文件路径src/build_vector_store.py from dotenv import load_dotenv from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma load_dotenv() # 1. 加载文档 loader TextLoader(docs/employee_handbook.md, encodingutf-8) documents loader.load() # 2. 文本分块 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n## , \n### , \n, 。, ], ) chunks text_splitter.split_documents(documents) print(f文档已切分为 {len(chunks)} 个片段) # 3. 初始化 Embedding 模型 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) # 4. 构建向量数据库并持久化到本地目录 vector_store Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, ) print(向量数据库构建完成已保存到 ./chroma_db) # 5. 检索验证 retriever vector_store.as_retriever(search_kwargs{k: 3}) results retriever.invoke(年假能休几天) for i, doc in enumerate(results, 1): print(f--- 第 {i} 个检索结果 ---) print(doc.page_content[:200])代码说明RecursiveCharacterTextSplitter是 LangChain 的分块工具它会优先按段落标题切分再按标点切分避免把语义完整的一句话拦腰截断。chunk_size500, chunk_overlap80表示每个片段约 500 个字符相邻片段重叠 80 个字符用重叠来避免关键信息正好落在两块边界而丢失。Chroma.from_documents会自动完成向量化并持久化persist_directory参数指定存储目录。检索时k3表示召回最相关的 3 个片段。5.4 分块策略是工程质量的关键很多项目效果差不是 Embedding 模型差而是文档切得太随意。如果块太大向量包含太多无关信息检索精度下降如果块太小语义不完整模型无法理解上下文。合理做法是根据文档结构切分例如一个 Markdown 的##标题作为一个逻辑单元遇到超长章节再继续拆。这个策略需要根据你实际文档类型反复调不要一上来就追求完美。6. RAG 检索增强生成全链路实现6.1 为什么需要 RAG而不是微调企业内部知识变化频繁如果用微调更新新制度每次都要准备数据集、训练、评测和上线时间成本极高。RAG 的思路是把知识放在外部库里回答前先检索相关内容塞进上下文让模型“带着资料回答问题”。优点是知识更新只需更新文档库不用重新训练模型也便于追溯来源。RAG 也有自身的边界它适合“知识密集型问答”不适合“要求模型学会新推理能力”的场景。常见误解是把 RAG 当成万能药遇到失败一味加更多资料。实际上检索召回质量、提示词约束、文档切分策略都会影响最终效果。6.2 把检索和生成串起来下面的代码把上一章的向量库和第四章的提示词模板组合成完整的 RAG 链路# 文件路径src/rag_chain.py from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings from langchain_core.runnables import RunnablePassthrough from prompt_template import qa_prompt load_dotenv() # 1. 加载已有向量库 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vector_store Chroma( persist_directory./chroma_db, embedding_functionembeddings, ) retriever vector_store.as_retriever(search_kwargs{k: 4}) # 2. 定义大模型 llm ChatOpenAI( modelgpt-4o-mini, temperature0.2, ) # 3. 格式化检索结果 def format_docs(docs): return \n\n.join(doc.page_content for doc in docs) # 4. 组合 RAG 链路 rag_chain ( {context: retriever | format_docs, question: RunnablePassthrough()} | qa_prompt | llm ) # 5. 提问 question 员工每年可以享受多少天带薪年假 response rag_chain.invoke(question) print(response.content)关键逻辑说明retriever | format_docs会把检索到的文档片段拼成一个大字符串作为{context}传给提示词模板。RunnablePassthrough()把用户问题直接传下去省去手动组装字典的代码。temperature0.2降低生成随机性让回答更稳定适合知识库问答这类对准确性要求高的场景。运行前确保docs/employee_handbook.md中存在相关内容。运行成功后你应该能看到模型基于检索到的片段给出回答而不是凭空编造。一个直接有效的验证方法是故意问一个文档里没有的问题正常情况下模型应回复“资料库中未找到相关信息”。如果它依然长篇大论说明 System Prompt 里的规则没有被严格执行优先检查提示词约束而不是换模型。6.3 RAG 知识库指标怎么理解热搜里经常出现“RAG 知识库指标有哪些”。这确实是上生产前绕不开的问题。业界常用 RAGAS 等框架评测核心指标包括指标面向环节含义通俗理解忠实度生成回答是否严格基于检索片段没有幻觉模型有没有“照着材料说话”答案相关性生成答案是否切题没有答非所问回答是不是用户想要的上下文相关性检索召回片段与问题的相关度系统有没有捞回有用的资料召回率检索应该召回的正确答案实际召回多少有没有漏掉关键片段精确率检索召回的片段里多少是真正有用的有没有混入大量噪声上线之前建议准备一批标准问题人工标注期望的答案片段跑一遍评测记录指标。后续每次修改文档切分策略、提示词或模型都重新评测。没有评测体系的 RAG 项目上线后基本靠运气。7. Agent 智能体与工具调用实战7.1 从“聊天”到“做事”Agent 的核心价值大模型直接接入业务形态是“你问一句它答一句”。Agent 改变了这个模式你提出目标模型负责拆解步骤、决定调用哪些工具、观察工具结果、再决定下一步。以查订单为例普通聊天模型只能回答“我无法查询订单”接入工具后模型可以调用订单查询函数拿到真实数据再组织成自然语言回答。初学者容易混淆工具调用Tool Calling和 Agent 不是一回事。工具调用是让模型在输出中声明“我要调用哪个函数、传什么参数”Agent 是用 Prompt 和编排逻辑决定“何时调用、调用后怎么处理”。工具调用是 Agent 的重要零件但 Agent 包含更多调度逻辑。7.2 定义一个可被模型调用的工具函数下面演示一个“查询订单状态”的工具函数并把它包装成模型可以理解的工具描述。# 文件路径src/tools.py from datetime import datetime from langchain_core.tools import tool tool def get_order_status(order_id: str) - str: 根据订单号查询订单状态。 参数 order_id 是用户在问题中提到的订单编号。 # 这里对接真实订单系统当前为示例逻辑 if order_id.startswith(A): return f{order_id} 已发货预计 3 天内送达 return f{order_id} 正在处理中请稍后查询这段代码的核心在两个地方函数注释写得非常详细。LangChain 会把函数名、参数说明、docstring 一并发送给模型模型根据这份“说明书”决定何时调用、如何传参。tool装饰器自动把 Python 函数转换为模型可调用的工具描述。不需要手写 JSON Schema这是 LangChain 比较方便的地方。7.3 用 LangChain 让模型自动决定调用工具接下来把工具挂到模型上构造一个最小 Agent# 文件路径src/agent_demo.py from dotenv import load_dotenv from langchain_openai import ChatOpenAI from tools import get_order_status load_dotenv() llm ChatOpenAI(modelgpt-4o-mini, temperature0) # 绑定工具 llm_with_tools llm.bind_tools([get_order_status]) messages [ (system, 你是一个订单查询助手。如果用户提供了订单号请调用工具查询订单状态。), (human, 我的订单 A10086 到哪里了), ] response llm_with_tools.invoke(messages) print(response) if response.tool_calls: for call in response.tool_calls: tool_name call[name] args call[args] print(f模型决定调用工具{tool_name}) print(f工具参数{args}) # 执行工具函数 if tool_name get_order_status: result get_order_status.invoke(args[order_id]) print(f工具返回{result})运行后模型应当输出一个tool_calls结构里面包含工具名get_order_status和参数{order_id: A10086}。注意模型此时只是“声明要调用工具”真正执行工具需要我们自己调用函数。执行完工具之后通常还需要把工具结果回传给模型让模型基于真实数据给出最终答案。这个“模型声明调用 - 程序执行 - 结果回填 - 模型继续生成”的循环就是 Agent 最底层的形态。复杂 Agent 框架只是在上面增加了更多调度策略。7.4 复杂流程用什么LangChain Agent 还是 LangGraph简单的单次工具调用用 LangChain 的bind_tools就够。当流程变成“先查订单再判断是否退款再查库存最后生成报告”每一步之间都有状态依赖时建议改用 LangGraph。LangGraph 的价值在于把流程建模成图节点可以是模型也可以是普通函数状态在节点之间显式传递支持条件分支、循环、人工介入。它的调试体验比 LangChain 原生 Agent 好很多尤其适合生产级应用。学习路径建议先用本文的bind_tools理解工具调用机制再用 LangGraph 重写同一个流程体会状态管理带来的差异。8. 常见问题与排查思路问题现象可能原因排查方式解决方案安装 chromadb 时 sqlite3 版本报错Windows 环境自带 sqlite3 版本过低Chroma 依赖高版本运行import sqlite3; print(sqlite3.sqlite_version)检查版本安装pysqlite3-binary并在导入 Chroma 前替换sqlite3模块报错提示 Embedding 维度不一致建库时用的 Embedding 模型和查询时用的模型不同查看向量库创建时的模型名和当前模型名统一使用同一个 Embedding 模型重新构建向量库检索结果与问题无关答非所问文档切块方式不合理或k值太小打印检索到的片段内容逐条检查相关性调整分块策略、增大k、尝试不同 Embedding 模型模型没有遵循“只能基于资料回答”规则提示词约束不明确或上下文占比较高时被模型忽略单独测试 System Prompt做几组对照加强指令措辞先回“未找到”再结束必要时用编程逻辑强制判断Agent 总是不调用工具或参数传错工具函数说明不够明确模型不知道何时调用检查工具函数 docstring 是否说清楚了触发条件和参数含义重写工具描述给出具体示例例如“当用户提到订单号时调用”Agent 陷入多轮循环不结束缺少停止条件或最大迭代次数限制观察调用日志看是哪一步反复执行设置最大执行轮次、增加超时、在流程中增加“结束”判断API 返回 token 超限一次放入上下文的文档片段太多计算每次请求的 token 消耗降低k、增加分块过滤规则、对大文档做摘要大模型返回内容的格式不稳定没有做输出解析完全依赖提示词检查返回结果的 JSON 结构使用 LangChain 的with_structured_output等方法做结构化输出解析9. 最佳实践与工程建议9.1 把提示词、工具、评估都纳入版本管理大模型应用和传统后端应用有一个显著区别影响行为的因素不止代码还有提示词、工具描述、文档内容。这些都应该纳入 Git 管理每次改动都走代码评审。不要把提示词偷偷改在线上调试窗口里更不要用一段无记录的 Prompt 支撑生产功能。9.2 建立日志与全链路追踪生产环境里模型回答错了你很难直接知道是检索错了还是生成错了。建议至少记录以下信息用户原始输入最终发给模型的完整 Prompt检索到的文档片段及其来源模型返回的原始输出工具调用名、参数、返回结果有了这些日志问题才能快速定位。更进一步可以接入 LangSmith 或 OpenTelemetry 这类可观测平台实现完整链路追踪。9.3 工具权限遵循最小化原则工具调用意味着模型获得了“执行能力”。如果工具可以查询数据库提示词注入就可能诱导模型执行危险操作。实际项目里给模型的工具应该是受限的只读接口不提供删除、覆盖等高危操作即使需要写操作也应该增加人工确认环节。工具名和参数要做白名单校验不要盲目信任模型输出。9.4 性能与成本平衡RAG 链路的主要性能瓶颈是向量检索和 LLM 生成。向量检索如果数据量大可以考虑加一层粗排或者用基于关键词的 BM25 和向量检索做混合召回。LLM 成本方面可以用更小的模型处理简单问题只有复杂问题才路由到更大模型同时做好结果缓存相同或相似问题直接从缓存读取。9.5 先跑通再优化先评测再上线整条链路涉及的变量非常多模型、Embedding、分块、检索、提示词、工具描述任何一项改动都可能影响效果。正确做法是先用小规模文档跑通端到端再建立评测集再持续优化。不要一上来就追求复杂架构。10. 总结与后续学习方向到这一步你已经走完了一条完整的大模型项目主线用提示词工程约束模型行为用 Embedding 和向量数据库解决知识检索用 RAG 让回答有据可依用工具调用和 Agent 让 AI 具备执行能力最后靠 LangChain 这类框架把这些环节串起来。接下来的进阶方向可以按这个顺序走先深入研究 LangGraph把 Agent 流程从简单调用升级为有状态、可分支的编排再深入学习 RAG 评测建立你自己的评估集确保每一次改动都有数据支撑然后了解混合检索、重排序、Agent 记忆等进阶手段。生产环境的挑战永远比教程复杂但核心链路搞清楚了后续遇到的问题都会变成可以定位、可以解决的工程问题。建议先把本文的代码跑通再用自己的公司文档替换 Employee Handbook最后把工具函数换成真实业务接口。动手做一遍比收藏十篇教程更有价值。