在接触大模型应用开发时,不少同学容易陷入一个误区:今天刷到一个视频讲 LangChain 调用模型,明天看到一篇帖子聊 RAG 检索流程,后天又刷到 Agent 工具调用的演示片段。知识点零零散散学了不少,真到自己动手做一个完整应用时,却不知道从哪下手:环境怎么搭、配置怎么填、代码结构怎么组织、上线之后怎么排查问题,每一步都可能卡住。
这篇文章想解决的正是这个问题。我会围绕大模型应用开发的核心路径,把 AI 应用基础、RAG 知识库搭建、Agent 开发、工程化落地这几个关键环节串成一条完整的学习和实战线索。文章里会给出可以直接复制的代码、配置、命令,也会解释每一步背后的设计思路。无论你是刚接触大模型开发的新手,还是已经写过一些 Demo 但想往工程化方向走的开发者,都可以按照这套路径逐步上手。
1. 大模型应用开发到底在开发什么
1.1 从模型调用到业务系统的距离
很多初学者对大模型应用开发的理解,停留在“用 API 把模型结果取回来”这一步。如果你只是写一段代码,把用户输入发给 GPT、文心一言、通义千问或者其他模型,再把生成结果打印出来,这在严格意义上还不算“应用开发”,它更像是一次接口调试。
真正的大模型应用开发,是在模型能力之上叠加工程能力,让模型在一个明确的业务场景中稳定可靠地工作。这里面至少包含几层内容:
- 第一层是模型接入层,负责统一管理不同模型的 API 调用,处理认证、超时、重试、流式输出、Token 统计等基础问题。
- 第二层是提示词与上下文管理层,负责把用户问题组织成模型能理解的输入,并在多轮对话中维护上下文。
- 第三层是增强检索层,也就是 RAG,负责把外部知识库接入模型,让模型回答问题时能引用真实资料。
- 第四层是 Agent 执行层,负责让模型在复杂任务中自行规划步骤、调用工具、读取结果、决定下一步动作。
- 第五层是应用集成层,负责把上面这些能力封装成 API 服务、Web 应用或者自动化任务,并接入业务系统。
所以,学习大模型应用开发,表面上学的是模型 API,实际上学的是如何用工程手段把模型的“通用能力”变成业务的“可用能力”。
1.2 为什么 RAG 和 Agent 是核心
在当前的落地实践中,RAG 和 Agent 是出现频率最高的两个方向。
RAG 解决的是知识实时性和可信度的问题。大模型训练数据存在截止时间,无法自动知道企业内部文档、产品手册、最新的政策条款。RAG 通过把用户问题先用向量检索去匹配相关知识片段,然后把检索到的知识片段和原始问题一起交给模型生成回答,让模型的输出有据可依。这也是目前大多数企业知识库问答系统的底层方案。
Agent 解决的是多步骤任务自动化的问题。模型本身只能“生成文本”,无法直接查询数据库、调用接口、操作文件。Agent 框架允许模型将任务分解成多个步骤,每一步选择一个合适的工具去执行,再根据工具返回的结果决定后续动作。例如“帮我查一下上周销售额,再生成一份分析报告发送到群里”,这类任务靠单次模型调用无法完成,但 Agent 可以把查数、分析、生成报告、发送消息拆成多个可执行节点。
掌握 RAG 和 Agent,相当于掌握了大模型应用开发中最具通用性的两个分支。后面所有复杂的业务系统,基本都会以这两种能力为基础进行扩展。
2. 环境准备与项目结构
2.1 Python 环境与依赖管理
大模型应用开发目前的主流语言是 Python,主要原因是相关生态工具基本都以 Python 优先发布。如果你对 Java 更熟悉,也可以找到对应 SDK,但学习资料和社区方案往往还是以 Python 为主。
建议本机环境按下面的方式准备:
- Python 版本:3.10 或 3.11 比较稳妥,太老的版本对部分新库支持不友好,太新的版本偶尔会遇到依赖编译问题。
- 包管理工具:建议使用
pip配合venv,或者使用conda管理独立环境。不要直接往系统 Python 里装一堆依赖,项目多了以后会很乱。 - 代码编辑器:VS Code 或 PyCharm 都可以,VS Code 搭配 Python 插件足够用。
创建独立环境的命令如下:
python3 -m venv llm_env source llm_env/bin/activate在虚拟环境中安装基础依赖:
pip install openai python-dotenv langchain langchain-community chromadb fastapi uvicorn这里说明一下这些库的作用:
openai:OpenAI SDK,目前也是很多兼容 OpenAI 接口规范的模型平台通用的客户端。python-dotenv:用于读取.env文件中的环境变量,避免把 API Key 硬编码在代码里。langchain和langchain-community:集成各种模型、向量库、工具的框架生态。chromadb:轻量级向量数据库,适合本地学习和中小型项目。fastapi和uvicorn:用来快速将应用封装成 HTTP 服务。
需要注意,大模型生态的库更新速度非常快,不同版本之间 API 可能有差异。这里的版本组合只是提供一种稳定可行的示例,你在实际操作时要根据当前库版本调整细节。
2.2 模型 API 的两种接入方式
在开始写代码前,先要明确模型从哪来。目前国内开发者主要有两条路径:
第一种是使用国内大模型平台的 API,例如百度千帆、阿里百炼、智谱 AI、DeepSeek 开放平台、腾讯混元等。这类平台一般会提供 OpenAI 兼容接口,也就是说你可以直接使用openai库,只需要修改base_url和 API Key。
第二种是使用本地模型,例如通过 Ollama 部署 Qwen、Llama 等开源模型。这种方式适合数据敏感、需要私有化部署的场景,也是一个验证模型能力的低成本方案。
不管用哪种方式,建议你在项目根目录创建一个.env文件,统一管理配置,例如:
MODEL_API_KEY=你的密钥 MODEL_BASE_URL=https://你的模型服务地址 MODEL_NAME=你的模型名称然后在代码中这样加载:
import os from dotenv import load_dotenv load_dotenv() api_key = os.getenv("MODEL_API_KEY") base_url = os.getenv("MODEL_BASE_URL") model_name = os.getenv("MODEL_NAME")这样做的好处是代码里不出现真实密钥,后续切换到其他模型平台时只需要修改环境变量。
2.3 示例项目结构
为了让后面的实战代码更清晰,我们先约定一个统一的工程结构:
llm-app-demo/ ├── .env ├── requirements.txt ├── data/ │ └── knowledge.txt ├── src/ │ ├── llm_client.py │ ├── rag_service.py │ ├── agent_service.py │ └── main.py └── README.mddata/放知识库原始文档。src/llm_client.py封装统一的模型调用逻辑。src/rag_service.py实现 RAG 检索增强生成。src/agent_service.py实现 Agent 工具调用。src/main.py提供 FastAPI 服务入口。
后面每个实战环节都会在指定文件里给出完整代码。
3. 大模型应用核心基础:提示词与模型调用
3.1 模型客户端封装
我们先把模型调用能力封装成一个统一的客户端模块,后面所有环节都复用它。这样做的好处是,如果以后要更换模型,只需要改一个文件。
文件路径:src/llm_client.py
from openai import OpenAI import os class LLMClient: def __init__(self): self.client = OpenAI( api_key=os.getenv("MODEL_API_KEY"), base_url=os.getenv("MODEL_BASE_URL") ) self.model_name = os.getenv("MODEL_NAME") def chat(self, messages, temperature=0.7): response = self.client.chat.completions.create( model=self.model_name, messages=messages, temperature=temperature ) return response.choices[0].message.content这个chat方法接收的消息格式是 OpenAI 标准的messages数组,每一条消息包含role和content。role有三种取值:
system:给模型设定整体角色和行为规则。user:用户输入。assistant:模型的回复,在多轮对话中需要把历史回复传回去。
3.2 提示词设计的基本规则
模型输出质量很大程度上取决于提示词。提示词不是简单地把问题扔给模型,而是要明确地交代任务背景、输入内容、输出格式和约束条件。
这里给出一个示例,模拟“客服助手”场景:
messages = [ {"role": "system", "content": "你是一个数码产品客服助手。" "回答要简洁专业,不要编造产品参数。" "如果用户询问的问题不在给定资料中," "请直接说明'当前资料暂未覆盖该问题'。"}, {"role": "user", "content": "这款手机的电池容量是多少?"} ] result = LLMClient().chat(messages) print(result)这个提示词里包含了几层信息:角色定义、回答风格、知识边界约束、无法回答时的处置方式。实际生产环境中的提示词往往还要加入更严格的结果格式要求,例如要求模型只输出 JSON、不要输出多余解释等。
3.3 输出结构化
在业务系统中,我们通常不希望模型返回自由格式的文本,而是希望它返回结构化数据,例如 JSON。
messages = [ {"role": "system", "content": "请从用户反馈中提取关键信息," "以JSON格式返回,包含字段:" "issue_type、issue_description、urgency_level。"}, {"role": "user", "content": "我们公司的网站昨天下午开始访问非常慢," "用户投诉很多,希望尽快处理。"} ]模型输出如果不够稳定,还可以在代码中做一次格式校验,避免解析异常。这一步属于工程兜底,不能指望模型百分之百遵守格式约定。
4. RAG 知识库搭建实战
4.1 RAG 的整体流程
RAG 的核心流程可以理解为四个步骤:加载文档、切分文本、向量化存储、检索增强生成。
为什么不能直接把整篇文档丢给模型?原因有两个。一是模型上下文窗口有限,长文档可能超出容量;二是无关内容混入会导致模型注意力分散,回答质量下降。因此需要把文档切分成合理的片段,检索时只挑最相关的片段交给模型。
下面的流程图展示了 RAG 系统的主要环节:
用户输入 -> 向量化用户问题 -> 在向量库中检索相似片段 -> 拼接提示词 -> 模型生成回答4.2 知识库文档准备
在data/knowledge.txt中准备一些简单的企业知识内容,例如:
公司于2015年成立,总部位于杭州,主要业务是企业级SaaS产品研发。 公司目前员工规模超过500人,研发团队占比60%。 企业版产品支持私有化部署,支持MySQL、PostgreSQL数据库。 标准版产品按月订阅,支持支付宝、微信支付、对公转账三种付款方式。 客服热线服务时间是工作日早上9点到下午6点。 售后技术支持响应时效为:标准版24小时内,企业版2小时内。这只是演示数据。真实场景中,知识库内容往往来自产品手册、运维文档、FAQ、合同条款等,格式可能是 PDF、Word、Markdown,需要先用解析工具转成纯文本。
4.3 文档切分
文档切分直接影响检索效果。切得太粗,一个片段包含太多话题,检索匹配不精准;切得太细,一个片段语义不完整,模型缺少上下文。
这里用固定字符长度切分,并加入了重叠机制,避免关键句子正好被截断:
文件路径:src/rag_service.py
from langchain.text_splitter import RecursiveCharacterTextSplitter def load_and_split_document(file_path): with open(file_path, "r", encoding="utf-8") as f: text = f.read() splitter = RecursiveCharacterTextSplitter( chunk_size=200, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ","] ) chunks = splitter.split_text(text) return chunkschunk_size=200表示每个片段大约 200 个字符,chunk_overlap=50表示相邻片段有 50 个字符重叠。实际项目中这两个参数需要根据文档类型反复调试,并没有唯一的“正确值”。
4.4 向量化与检索
接下来实现向量化存储和检索。为了减少复杂度,示例使用 Chroma 作为向量数据库。
from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma def build_vector_store(chunks): embeddings = OpenAIEmbeddings( openai_api_base=os.getenv("MODEL_BASE_URL"), openai_api_key=os.getenv("MODEL_API_KEY") ) vector_store = Chroma.from_texts( texts=chunks, embedding=embeddings, persist_directory="./chroma_db" ) return vector_store def search_knowledge(vector_store, query, top_k=3): docs = vector_store.similarity_search(query, k=top_k) return [doc.page_content for doc in docs]similarity_search的作用是计算用户问题与每个文本片段在向量空间中的余弦相似度,返回最接近的top_k个片段。
这里有一个初学者常踩的坑:向量化使用的模型必须保持一致。如果存文档时用模型 A,检索时却换成了模型 B,向量空间不一致,检索结果会非常差。
4.5 组装增强提示词
检索到相关片段后,需要把这些片段拼到提示词中,再交给模型生成回答。
def generate_answer_with_rag(user_query): vector_store = build_vector_store(load_and_split_document("data/knowledge.txt")) relevant_chunks = search_knowledge(vector_store, user_query) context = "\n\n".join(relevant_chunks) messages = [ {"role": "system", "content": "你是企业知识库问答助手。" "请严格基于提供的知识片段回答用户问题。" "如果知识片段中不存在相关信息," "明确告知用户'知识库中未找到相关内容'。"}, {"role": "user", "content": f"知识片段:\n{context}\n\n" f"用户问题:{user_query}"} ] return LLMClient().chat(messages)检索文档的时机放在每次问答时,这在演示代码里是为了直观,但生产环境中不应该每次重建向量库,而应该把向量库持久化,启动时加载。
4.6 RAG 效果验证
运行下面的主程序验证效果:
if __name__ == "__main__": answer = generate_answer_with_rag("公司支持哪些数据库?") print(answer)预期回答应该包含“MySQL、PostgreSQL”这类知识库中的信息。如果回答里出现知识库以外的编造内容,说明提示词约束或检索相关度还需要调整。
4.7 RAG 的常见失败模式
RAG 系统在真实项目中表现不好,通常不是模型的问题,而是检索链路的问题。常见的失败模式有:
- 片段切分位置不当,导致一句话被切断、语义丢失。
- 检索到不相关片段,模型只能基于无关信息作答,结果答非所问。
- 知识库覆盖不足,用户问题在资料中根本没有对应答案。
- 重排序缺失,只依赖向量相似度,关键词匹配被忽略。
在工程上,除了向量检索之外,通常还会加入关键词检索,再把两种检索结果做融合排序。这个方案在社区中常被称为“混合检索”,它能在很多场景下显著提升回答准确率。
5. Agent 开发实战
5.1 Agent 的工作原理
Agent 和普通对话的最大区别在于:普通对话是“一问一答”,Agent 则是“模型规划+工具执行+结果反馈”的循环过程。
以“查询订单状态”为例,模型本身并不知道订单系统的数据在哪里,它需要一次工具调用,由一个函数去数据库查出订单状态,再把查询结果作为消息反馈给模型,让模型组织最终回答。
一个典型的 Agent 循环包含以下步骤:
- 接收用户任务。
- 模型判断当前任务是否可以完成,如果可以,直接产出回答;如果不行,决定调用哪个工具。
- 系统执行对应工具,拿到结果。
- 结果反馈给模型,模型判断是否继续调用工具或产出最终答案。
- 循环直至任务完成或达到最大步数。
5.2 定义工具函数
先把两个简单的工具函数写好,一个用于模拟天气查询,一个用于模拟订单查询:
文件路径:src/agent_service.py
def get_weather(city: str) -> str: weather_data = { "北京": "晴,气温 20℃", "上海": "小雨,气温 24℃", "深圳": "多云,气温 28℃" } return weather_data.get(city, f"{city}暂无天气数据") def get_order_status(order_id: str) -> str: order_data = { "1001": "已发货,预计后天到达", "1002": "正在拣货", "1003": "已签收" } return order_data.get(order_id, f"订单{order_id}不存在")真实项目中,这些工具函数应该去查外部 API、数据库或内部服务,而不是返回写死的字典。这里只是演示 Agent 的工具调用机制。
5.3 用 OpenAI 函数调用实现 Agent
OpenAI 兼容接口普遍提供了工具调用能力。我们可以在请求中声明一组可用工具,模型在需要时会返回一个工具调用指令,而不是直接生成最终答案。
from llm_client import LLMClient import json class SimpleAgent: def __init__(self): self.client = LLMClient().client self.model_name = LLMClient().model_name def run(self, user_input): messages = [ {"role": "system", "content": "你是一个智能助手," "可以调用工具获取实时信息," "根据工具结果回答用户问题。"}, {"role": "user", "content": user_input} ] tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询城市天气情况", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } }, { "type": "function", "function": { "name": "get_order_status", "description": "查询订单物流状态", "parameters": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单编号"} }, "required": ["order_id"] } } } ] for step in range(5): response = self.client.chat.completions.create( model=self.model_name, messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message if not message.tool_calls: return message.content messages.append(message) for tool_call in message.tool_calls: function_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) if function_name == "get_weather": result = get_weather(arguments["city"]) elif function_name == "get_order_status": result = get_order_status(arguments["order_id"]) else: result = "未知工具" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) return "任务执行达到最大步数,请尝试简化问题。"这个run方法里最关键的逻辑是:判断返回结果中是否包含tool_calls。如果包含,说明模型决定调用工具,我们需要执行对应函数,并把结果以role=tool的消息回传给模型,让模型继续决策。
5.4 Agent 运行验证
if __name__ == "__main__": agent = SimpleAgent() print(agent.run("北京今天天气怎么样?")) print(agent.run("帮我查一下订单1002的状态"))第一次运行可能需要检查你的模型服务是否支持工具调用。部分模型或兼容平台对工具调用的支持并不完整,如果出现报错,可以先确认接口文档中对tools参数的说明。
5.5 Agent 的边界与风险
Agent 的能力看起来很强大,但落到生产环境时需要注意几个问题:
- 工具权限控制:Agent 能调用什么工具,必须在系统层面做严格限制,不能把数据库删除操作、资金操作直接暴露给模型。
- 参数校验与确认:模型生成的工具参数可能是不合法、不完整的,执行工具前必须做参数校验,涉及重要操作时应该加入人工确认环节。
- 执行步数限制:一定要设置最大循环次数,否则模型可能在复杂任务中陷入无限调用。
- 日志审计:每次工具调用记录需要完整保存,方便出问题时回溯。
6. 将应用封装成 API 服务
6.1 使用 FastAPI 提供服务
为了让前面写的功能可以被前端、测试工具或者外部系统调用,我们用 FastAPI 将其封装成接口。
文件路径:src/main.py
from fastapi import FastAPI from pydantic import BaseModel from rag_service import generate_answer_with_rag from agent_service import SimpleAgent app = FastAPI(title="大模型应用实战服务") class ChatRequest(BaseModel): query: str @app.post("/api/rag") def rag_api(request: ChatRequest): answer = generate_answer_with_rag(request.query) return {"answer": answer} @app.post("/api/agent") def agent_api(request: ChatRequest): agent = SimpleAgent() answer = agent.run(request.query) return {"answer": answer}启动服务:
uvicorn src.main:app --reload --port 8000启动后在浏览器访问http://localhost:8000/docs,可以看到 FastAPI 自动生成的接口文档页面,可以直接在页面上调试两个接口。
6.2 接口返回格式设计
真实项目里,接口返回值不建议只是一个字符串。更合理的返回结构通常包括:
{ "code": 0, "message": "success", "data": { "answer": "具体回答内容", "sources": ["来源文档1", "来源文档2"], "latency_ms": 123 } }其中sources是 RAG 检索到的来源信息,这对用户核验答案非常关键。latency_ms用于监控性能。实际开发时可以根据业务需要扩展。
7. 常见问题与排查思路
大模型应用开发涉及的组件较多,出现问题时的排查思路也很重要。下面整理了一些高频问题:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| API 调用报 401 | API Key 错误或没有权限 | 检查环境变量,确认密钥有效 |
| 连接超时或限流 | 网络问题或超出模型速率限制 | 检查网络,增加重试,降级并发 |
| 模型返回内容为空 | 输入触发了安全过滤 | 调整提示词措辞,规避敏感内容 |
| RAG 检索结果不相关 | 切分粒度不合适 | 调整 chunk_size 和 overlap |
| RAG 回答编造内容 | 提示词约束不足 | 增加“仅基于资料回答”的系统提示 |
| Agent 不调用工具 | 模型或接口不支持工具调用 | 检查模型文档,更换支持工具调用的模型 |
| Agent 循环过多 | 任务拆解不清晰或工具返回异常 | 设置最大步数,优化工具描述 |
| 依赖安装失败 | Python 版本或包版本冲突 | 升级 pip,创建独立虚拟环境 |
在排查问题时,一个比较推荐的思路是“逐层隔离”。先单独测试模型调用,确认模型本身工作正常;再测试检索链路,确认返回的片段确实相关;最后才测试整体拼接和生成效果。这样可以避免把模型问题、检索问题和代码问题混在一起。
8. 大模型应用开发的工程化建议
8.1 重视提示词版本管理
提示词不是一次性写好的,而是需要反复迭代的。建议在两个层面管理:
- 将提示词模板保存在单独的目录或配置中心,而不是散落在代码里。
- 每次修改提示词都要记录改动内容和测试结果,方便对比回滚。
在团队协作中,简单做法是用一个prompts/目录存放不同场景的提示词模板:
prompts/ ├── rag_system_prompt.txt ├── agent_system_prompt.txt ├── summarization_prompt.txt8.2 引入评估机制
大模型应用的输出是概率性的,同一个输入在不同时间可能得到不同回答。因此,项目上线前必须建立评估集。评估集至少要包含几十个有标准答案的测试问题,每次修改提示词、检索逻辑或模型参数后,都要跑一遍评估集,统计回答正确率。
评估方式可以由人工打标,也可以用另一个模型对回答打分。两者各有优劣,人工打标准确但耗时,模型评估效率高但可能误判。
8.3 安全与隐私边界
涉及真实业务数据时,安全是不可绕过的话题。开发过程中要遵守最小权限原则:
- API Key 不写进代码仓库,使用环境变量或密钥管理服务。
- 用户上传的文档在存储和向量化时考虑脱敏。
- 涉及删除、修改、支付等敏感操作的工具调用,Agent 必须设置人工审批。
8.4 日志与可观测性
生产环境的大模型应用,日志中至少要记录:
- 用户输入原文。
- 检索到的文档片段及来源。
- 发送给模型的完整提示词。
- 模型返回的完整内容。
- 每次工具调用的名称、参数和返回结果。
- 耗时和 Token 消耗。
这些日志既是排查问题的依据,也是后续优化评估的数据基础。你不能只记录最终回答,否则一旦回答有误,完全无法回溯是哪一层出了问题。
9. 总结与后续学习路线
走到这里,你已经具备了大模型应用开发的一条完整主线:模型调用、RAG 知识库、Agent 工具调用、API 服务封装,以及对工程化问题的基本认知。
如果继续深入,下一步可以往几个方向扩展:
- 深入了解向量数据库的选型与调优,例如 Milvus、Weaviate、Elasticsearch 向量检索能力。
- 学习重排序模型在 RAG 链路中的应用,掌握更精准的检索增强方案。
- 探索更完善的 Agent 编排框架,例如 LangGraph 这类支持状态管理和循环控制的工具。
- 研究 Function Calling 之外的模型能力,例如多模态输入、结构化输出约束。
- 在实际业务中选择一个场景,从需求分析开始,完成一个完整的大模型应用并部署上线。
最后给一个学习建议:不要沉迷于“收集课程”和“囤积资料”,也不要只看不练。大模型应用开发是一项动手技能,每一步都值得亲手敲一遍代码、观察一次输出、排查一遍报错。建议从今天开始,把本篇文章里的 RAG 示例跑通,再尝试替换为你自己的一段文档内容,体会检索结果随切分参数变化的差异。这个过程比看任何视频教程都更有价值。