当“AI 洪流”已经不再是预言,而是每一天都在发生的现实,开发者真正需要的不是焦虑,而是一套能落地的 AI 应用开发方法。本文从大模型应用开发的基础概念讲起,逐步拆解环境搭建、RAG、Agent、模型部署等关键环节,并提供一个完整的 Python 实战项目,帮助你从“调用 API”进阶到“工程化落地”。
1. AI 浪潮下,开发者正在面对什么
1.1 从“AI 概念”到“AI 洪流”
过去两年,AI 领域的变化速度几乎超出了所有人的预期。大语言模型从只能聊天对话,发展到可以写代码、处理文档、调用工具、自动完成多步任务;各类 AI 编程助手、AI Agent、AI 视频生成工具层出不穷。The Impending, Inescapable Deluge of A.I这个标题想表达的,正是这种“即将到来且无法回避”的技术洪流。
对于开发者来说,这种洪流带来的不是简单的工具替换,而是整个应用架构和研发方式的改变。以前我们写一个问答系统,需要分词、意图识别、对话管理、知识库检索等多个模块;现在,一个大模型配合合适的提示词就能完成大部分工作。但与此同时,模型输出不稳定、Token 成本不可控、数据隐私难以保障、幻觉问题难以消除等新挑战也随之而来。
1.2 AI 应用开发的核心分层
要把 AI 落到真实业务中,不能只停留在“调用一下接口”的层面。站在工程视角,我们可以把 AI 应用开发拆成几个层次:
- 模型层:包括使用云端大模型 API,或者本地部署开源模型。这一层解决的是“模型从哪里来”。
- 应用层:包括提示词工程、上下文管理、RAG(检索增强生成)、Agent 工作流。这一层解决的是“如何让模型输出符合业务要求”。
- 平台层:包括模型网关、缓存、限流、日志、评估、监控。这一层解决的是“生产环境能不能稳定运行”。
很多初学者容易犯的错误是只关注模型层,拿着 API Key 调通一个 demo 就认为完成了 AI 应用。实际上,真正能上线的 AI 系统,70% 以上的工作量都在应用层和平台层。
1.3 开发者需要掌握哪些能力
你现在打开招聘网站,会看到大量与 AI 相关的岗位:AI 应用开发、AI Agent 开发、AI 产品经理、大模型部署工程师。这些岗位的共同点是:都需要开发者理解模型的基本原理,具备工程化能力,能够把模型能力嵌入到具体业务场景中。
具体来说,一个合格的 AI 应用开发者需要掌握以下能力:
- 熟悉大模型 API 的调用方式和参数含义。
- 掌握提示词工程,能够写出稳定、可复用的提示词模板。
- 理解 RAG 的完整链路,包括文档加载、切片、向量化、检索、重排。
- 了解 Agent 的工作原理,能够让模型调用外部工具完成复杂任务。
- 具备模型部署经验,知道如何在成本和性能之间做取舍。
- 建立 AI 应用的评估和监控体系,而不是只凭感觉调 Prompt。
这些能力并不是一两天能掌握的,但通过一个完整的项目实践,可以帮你把零散的知识串联起来。
2. AI 应用开发环境准备
2.1 操作系统与硬件
AI 应用开发对操作系统没有严格限制,Windows、macOS、Linux 都可以。如果你是学生或者个人开发者,使用 Windows 或 macOS 完全够用;如果是团队协作或者生产部署,推荐使用 Linux 服务器。
硬件方面,如果你的主要工作是通过 API 调用云端大模型,那么普通开发机就可以,不需要独立显卡。只有当你要本地部署开源模型(例如通过 Ollama 运行 Qwen、Llama 等),才需要关注显存大小。一般来说:
- 7B 级别的量化模型,建议至少 8GB 显存。
- 13B 级别模型,建议 16GB 以上显存。
- 70B 级别模型,需要多卡或者纯 CPU 推理(速度较慢)。
本文的实战案例以调用云端 API 为主,同时兼容本地 Ollama 部署方式。
2.2 Python 环境与依赖
Python 是 AI 应用开发最主流的语言,生态最完善。建议使用 Python 3.10 及以上版本。为了管理项目依赖,推荐使用venv创建独立虚拟环境。
python -m venv ai-env source ai-env/bin/activate # Linux / macOS ai-env\Scripts\activate # Windows python -m pip install --upgrade pip我们实战中会用到的主要依赖有:
pip install requests openai python-dotenv flask这里简单说明一下:
openai:官方 Python SDK,但很多兼容 OpenAI 接口的模型服务也可以用它。requests:用于发起 HTTP 请求,当我们不使用 SDK 时可以直接调用 API。python-dotenv:用于读取.env配置文件中的密钥。flask:用最简单的 Web 框架把 AI 服务暴露成 HTTP 接口。
版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。
2.3 模型服务选择:云端 API 与本地部署
在开始编码之前,需要先确定模型服务的来源。目前主流方式有两种。
第一种:云端大模型 API。调用 OpenAI 兼容接口,优点是模型能力强、无需关注硬件、接入简单。缺点是数据会发送到第三方服务,敏感业务需要谨慎;同时按 Token 计费,高并发下成本不低。
第二种:本地部署开源模型。例如通过 Ollama 运行 Qwen、Llama 等开源模型。优点是数据不出内网,适合隐私要求高的场景,长期使用成本更低。缺点是模型能力相对云端旗舰模型有差距,并且需要硬件投入。
实践中很多团队会采用“混合策略”:一般对话场景用本地小模型降低成本,复杂推理场景用云端大模型保证效果。下面的代码会同时考虑这两种接入方式。
2.4 项目结构规划
一个规范的 AI 应用项目,建议这样组织目录:
ai_assistant/ ├── .env ├── requirements.txt ├── config.py ├── llm_client.py ├── memory.py ├── tools.py ├── agent.py ├── app.py └── README.mdconfig.py统一管理配置,llm_client.py封装模型调用,memory.py管理对话记忆,tools.py定义 Agent 可用的工具,agent.py编排 Agent 工作流,app.py提供 HTTP 服务。这种分层的结构在项目变大后优势非常明显。
3. 大模型应用开发核心概念拆解
3.1 提示词工程:与大模型沟通的语法
提示词工程(Prompt Engineering)是 AI 应用开发中最基础也最重要的技能。同一个问题,用不同的提示词表达,模型输出的质量可能天差地别。
一个高质量的提示词通常包含以下几个要素:
- 角色设定:告诉模型它是什么角色,例如“你是一名资深 Java 工程师”。
- 任务描述:清晰说明需要完成的任务,避免模糊表达。
- 约束条件:明确输出的格式、长度、语气等限制。
- 示例:给出一两个示例可以帮助模型理解你的预期。
一个简单的示例:
system_prompt = """ 你是一名专业的 Python 代码审查专家。 请审查用户提交的代码,从以下维度给出反馈: 1. 代码可读性 2. 潜在 Bug 3. 性能问题 4. 改进建议 要求:反馈使用中文,每个问题给出具体行号和修改建议。 """这里需要注意的是,提示词不是一次写好的,而是需要根据模型反馈反复迭代。在实际项目中,提示词最好配置在独立文件中管理,而不是散落在业务代码里。
3.2 上下文管理与 Token 预算
大模型的输入输出都受 Token 数量限制。Token 可以简单理解为“模型处理文本的最小单位”,英文中一个单词通常对应 1 到 2 个 Token,中文一个汉字大约对应 1 到 2 个 Token。
在对话系统中,每次请求需要把历史消息全部发送给模型,模型才能记住之前的对话内容。如果历史消息太长,会带来两个问题:
- 超出模型的上下文窗口限制,请求直接报错。
- Token 消耗增加,成本上升,响应变慢。
因此,上下文管理是 AI 应用工程化的核心问题。常见策略有:
- 滑动窗口:只保留最近 N 轮对话。
- 摘要压缩:当历史对话超过阈值时,先让模型把旧对话总结成摘要,再和新对话一起发送。
- 关键信息抽取:从历史对话中提取用户偏好、关键事实等结构化信息,替代完整的历史消息。
3.3 RAG:让模型拥有你的私有知识
大模型的知识来源于训练数据,对于企业内部文档、最新资讯、个人笔记等内容,模型是无法准确回答的。RAG(Retrieval-Augmented Generation,检索增强生成)是解决这个问题的常用方案。
RAG 的基本流程是:
- 将知识库文档切分成小块。
- 对每个文本块做向量化,存入向量数据库。
- 用户提问时,将问题向量化,在向量库中检索最相关的文本块。
- 将检索到的文本块与用户问题一起发送给大模型。
- 模型根据检索到的知识生成回答。
RAG 的最大价值在于:不需要微调模型,就能让模型掌握新的知识;知识更新只需重新处理文档,成本低、速度快。因此它成为目前企业知识库问答、智能客服等场景的首选方案。
3.4 AI Agent:从问答到任务执行
如果说 RAG 让模型“知道更多”,那么 Agent 就是让模型“能做更多”。
Agent(智能体)的核心思想是:模型不再只是生成文本,而是能够决定调用哪些工具、按什么顺序调用、如何处理工具返回的结果。例如,用户问“帮我查一下明天的天气”,模型先调用天气查询工具获取数据,然后根据数据生成回答。
一个最小化的 Agent 工作流通常包含:
- 将用户问题发送给模型,同时提供可用工具的说明。
- 模型返回一个决策:直接回答,或者调用某个工具。
- 如果模型决定调用工具,程序执行对应函数,把结果返回给模型。
- 模型结合工具结果,生成最终回答。
这个“模型决策-执行工具-返回结果-再生成”的循环,就是 Agent 的基本雏形。更复杂的 Agent 还会包含规划、记忆、反思等机制。
3.5 模型部署与推理优化
当应用从 Demo 走向生产,模型部署就成了绕不开的问题。这里需要区分两种场景:
场景一:调用云端模型 API。需要考虑限流、重试、缓存、成本控制。例如在 API 客户端中增加指数退避重试机制,对相同的问题做缓存,对模型输出做合规检查。
场景二:自建模型推理服务。需要考虑显存管理、并发控制、推理加速。常用工具有 vLLM、TensorRT-LLM 等。部署时还需要考虑模型量化,用精度换速度。
在工程实践中,模型部署的目标是“稳定、可控、可观测”。不要把部署简单地理解成启动一个服务,还需要考虑模型版本管理、灰度发布、监控告警等能力。
4. 完整实战:构建一个轻量级 AI 问答与 Agent 服务
接下来我们用 Python 从零构建一个轻量级 AI 问答服务。这个项目会展示模型调用、对话记忆、工具调用等核心能力,麻雀虽小但五脏俱全。
4.1 创建项目结构
首先创建项目目录和文件:
mkdir ai_assistant cd ai_assistant python -m venv ai-env source ai-env/bin/activate # Windows 使用 ai-env\Scripts\activate pip install openai python-dotenv flask创建项目文件:
ai_assistant/ ├── .env ├── config.py ├── llm_client.py ├── memory.py ├── tools.py ├── agent.py ├── app.py └── requirements.txt4.2 配置文件与模型接入
在实际项目中,API Key、模型名称、基础地址等敏感信息不应该硬编码在代码中,而是放到环境变量里。我们使用.env文件管理。
# 文件路径:ai_assistant/.env # 模型服务类型:openai 或 ollama LLM_PROVIDER=openai # OpenAI 兼容接口的配置 OPENAI_API_KEY=your-api-key-here OPENAI_BASE_URL=https://api.openai.com/v1 OPENAI_MODEL_NAME=gpt-4o-mini # 如果使用 Ollama 本地模型,取消下面的注释 # LLM_PROVIDER=ollama # OPENAI_BASE_URL=http://localhost:11434/v1 # OPENAI_MODEL_NAME=qwen2.5:7b注意:your-api-key-here需要替换成你自己的 API Key。如果你有自定义的模型服务地址,只需要修改OPENAI_BASE_URL,因为大多数模型服务都兼容 OpenAI 的接口协议。
然后编写配置模块:
# 文件路径:ai_assistant/config.py import os from dotenv import load_dotenv load_dotenv() class Config: # 模型服务提供商:openai 或 ollama LLM_PROVIDER = os.getenv("LLM_PROVIDER", "openai") # OpenAI 兼容接口配置 OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1") OPENAI_MODEL_NAME = os.getenv("OPENAI_MODEL_NAME", "gpt-4o-mini") # 系统提示词 SYSTEM_PROMPT = os.getenv("SYSTEM_PROMPT", "你是一个乐于助人的 AI 助手。")4.3 封装模型调用客户端
接下来封装一个LLMClient类,统一处理模型调用逻辑。这里使用 OpenAI 的 Python SDK,并通过base_url兼容不同的模型服务。
# 文件路径:ai_assistant/llm_client.py from openai import OpenAI from config import Config class LLMClient: def __init__(self): self.client = OpenAI( api_key=Config.OPENAI_API_KEY, base_url=Config.OPENAI_BASE_URL, ) self.model = Config.OPENAI_MODEL_NAME def chat(self, messages, temperature=0.7, max_tokens=1024): """ 发送对话消息给模型。 messages: 消息列表,格式为 [{"role": "system", "content": "..."}, {"role": "user", "content": "..."}] """ try: response = self.client.chat.completions.create( model=self.model, messages=messages, temperature=temperature, max_tokens=max_tokens, ) return response.choices[0].message.content except Exception as e: # 实际项目中这里需要记录日志并做更细粒度的异常分类 raise RuntimeError(f"模型调用失败: {e}") def chat_with_tools(self, messages, tools, tool_choice="auto"): """ 带工具调用的模型请求。 返回完整的响应对象,由调用方决定是继续执行工具还是直接返回。 """ try: response = self.client.chat.completions.create( model=self.model, messages=messages, temperature=0.7, tools=tools, tool_choice=tool_choice, ) return response except Exception as e: raise RuntimeError(f"模型工具调用失败: {e}")这里要注意的是:
temperature控制模型输出的随机性,取值 0 到 2 之间。需要稳定输出的场景(如信息抽取)建议调低到 0.2 左右,需要创造性输出的场景可以调高。max_tokens限制模型单次生成的最大 Token 数。设置太小可能输出被截断,设置太大会增加成本和延迟。- 当使用 Ollama 等本地模型时,可能不完全支持工具调用功能,需要根据实际模型能力调整。
4.4 对话记忆管理
先实现一个简单的对话记忆类,用列表保存消息历史。为了避免 Token 无限增长,我们实现一个滑动窗口机制,只保留最近 N 轮消息。
# 文件路径:ai_assistant/memory.py class ConversationMemory: def __init__(self, max_rounds=10, system_prompt=""): """ max_rounds: 最多保留多少轮对话(一轮包含 user 和 assistant 各一条) system_prompt: 系统提示词 """ self.max_rounds = max_rounds self.messages = [] if system_prompt: self.messages.append({"role": "system", "content": system_prompt}) def add_user_message(self, content): self.messages.append({"role": "user", "content": content}) self._trim() def add_assistant_message(self, content): self.messages.append({"role": "assistant", "content": content}) self._trim() def get_messages(self): return self.messages def clear(self): """清空历史,但保留系统提示词""" system_messages = [m for m in self.messages if m["role"] == "system"] self.messages = system_messages def _trim(self): """ 如果消息数量超过最大轮数限制,删除最早的非 system 消息。 这里简单地按总消息数裁剪,实际项目可以做更精细的处理。 """ # 计算 system 消息数量 system_count = sum(1 for m in self.messages if m["role"] == "system") # 总共允许的最大消息数 = 系统消息数 + 轮数 * 2 max_messages = system_count + self.max_rounds * 2 if len(self.messages) > max_messages: # 保留最早的 system 消息,删除最早的非 system 消息 non_system_messages = [m for m in self.messages if m["role"] != "system"] to_remove = len(non_system_messages) - self.max_rounds * 2 if to_remove > 0: removed = 0 new_messages = [] for m in self.messages: if m["role"] == "system": new_messages.append(m) elif removed < to_remove: removed += 1 else: new_messages.append(m) self.messages = new_messages滑动窗口是一个朴素但有效的记忆管理方案。它的优点是实现简单、行为可预期,缺点是无法保留早期关键信息。在实际产品中,你可以把滑动窗口和摘要压缩结合起来。
4.5 定义 Agent 工具
为了让模型能够调用工具,我们需要定义工具的描述信息和执行函数。下面模拟一个“获取城市天气”和“计算两个日期之间的天数”的工具。
工具描述采用 OpenAI 定义的 JSON Schema 格式:
# 文件路径:ai_assistant/tools.py import datetime # 工具定义,用于告诉模型有哪些函数可以调用 TOOLS = [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气情况", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如 北京、上海" } }, "required": ["city"] } } }, { "type": "function", "function": { "name": "calc_days_between", "description": "计算两个日期之间相差的天数", "parameters": { "type": "object", "properties": { "start_date": { "type": "string", "description": "开始日期,格式 YYYY-MM-DD" }, "end_date": { "type": "string", "description": "结束日期,格式 YYYY-MM-DD" } }, "required": ["start_date", "end_date"] } } } ] # 工具的实际执行函数 def execute_tool(tool_name: str, arguments: dict): """ 根据工具名称和参数执行对应的函数。 实际项目中这里可以根据业务需要扩展更多的工具。 """ if tool_name == "get_weather": city = arguments.get("city", "") # 这里只是模拟数据,实际项目中应接入真实天气 API if "北京" in city: return {"city": city, "weather": "晴", "temperature": 25, "humidity": 40} elif "上海" in city: return {"city": city, "weather": "小雨", "temperature": 22, "humidity": 80} else: return {"city": city, "weather": "未知", "temperature": None, "humidity": None} elif tool_name == "calc_days_between": start_date = arguments.get("start_date", "") end_date = arguments.get("end_date", "") try: d1 = datetime.datetime.strptime(start_date, "%Y-%m-%d") d2 = datetime.datetime.strptime(end_date, "%Y-%m-%d") delta = abs((d2 - d1).days) return {"start_date": start_date, "end_date": end_date, "days": delta} except ValueError: return {"error": "日期格式错误,请使用 YYYY-MM-DD 格式"} return {"error": f"未知工具: {tool_name}"}4.6 实现 Agent 工作流
Agent 的核心是一个循环:调用模型 -> 判断模型是否要求调用工具 -> 执行工具 -> 把结果返回给模型 -> 模型生成最终回答。
# 文件路径:ai_assistant/agent.py import json from llm_client import LLMClient from memory import ConversationMemory from tools import TOOLS, execute_tool class Agent: def __init__(self, system_prompt=""): self.llm = LLMClient() self.memory = ConversationMemory(max_rounds=6, system_prompt=system_prompt) def run(self, user_input: str, max_iterations: int = 5): """ 处理用户输入并返回 AI 回复。 支持工具调用的 Agent 主循环。 """ # 1. 将用户输入加入记忆 self.memory.add_user_message(user_input) messages = self.memory.get_messages() # 2. Agent 循环:最多迭代 max_iterations 次 for _ in range(max_iterations): # 2.1 调用模型,携带工具定义 response = self.llm.chat_with_tools(messages, tools=TOOLS) # 2.2 获取模型返回的消息 message = response.choices[0].message # 2.3 判断模型是否需要调用工具 if message.tool_calls: # 保存模型的工具调用请求到消息历史 messages.append({ "role": "assistant", "content": message.content if message.content else "", "tool_calls": [ { "id": tc.id, "type": "function", "function": { "name": tc.function.name, "arguments": tc.function.arguments } } for tc in message.tool_calls ] }) # 逐个执行工具 for tool_call in message.tool_calls: tool_name = tool_call.function.name try: arguments = json.loads(tool_call.function.arguments or "{}") except json.JSONDecodeError: arguments = {} # 执行工具函数 tool_result = execute_tool(tool_name, arguments) # 把工具执行结果追加到消息历史 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(tool_result, ensure_ascii=False) }) # 继续循环,让模型基于工具结果生成回复 continue # 2.4 模型没有要求调用工具,直接返回内容 assistant_content = message.content or "" self.memory.add_assistant_message(assistant_content) return assistant_content # 超过最大迭代次数,返回提示 return "处理超时,请简化一下请求或稍后重试。"这段代码需要重点理解的地方:
- 当模型返回
tool_calls时,我们需要把模型的带工具调用请求完整地放进消息历史,再逐个执行工具,并把结果以role: "tool"的消息追加回去。这是 OpenAI 工具调用协议的要求。 max_iterations用于防止 Agent 陷入无限循环,例如模型反复调用同一个工具不返回结果。- 工具执行结果使用
json.dumps序列化为字符串,因为模型需要的是一段文本内容。
4.7 提供 HTTP 服务
最后,我们使用 Flask 把 Agent 包装成一个 HTTP 接口,方便后面集成到前端或者其他服务中。
# 文件路径:ai_assistant/app.py from flask import Flask, request, jsonify from agent import Agent app = Flask(__name__) # 创建一个全局 Agent 实例 # 实际项目中需要注意并发安全,建议每个会话独立管理记忆 agent = Agent(system_prompt="你是一个有用的 AI 助手,可以回答问题,也可以调用工具帮你获取天气或计算日期。") @app.route("/chat", methods=["POST"]) def chat(): data = request.get_json() if not data or "message" not in data: return jsonify({"error": "请求体必须包含 message 字段"}), 400 user_message = data["message"] try: reply = agent.run(user_message) return jsonify({"reply": reply}) except Exception as e: # 生产环境应该记录详细堆栈,而不是直接返回给客户端 return jsonify({"error": str(e)}), 500 @app.route("/health", methods=["GET"]) def health(): return jsonify({"status": "ok"}) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=False)注意:这里全局共用一个agent实例,意味着所有用户共享同一份对话记忆。这在实际产品中是不合理的。更好的做法是为每个会话(session)维护独立的记忆实例,例如使用字典以session_id为 key 保存多个 Agent 实例。
4.8 运行与验证
启动服务:
python app.py然后打开另一个终端,使用curl测试接口。
测试普通问答:
curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "你好,请介绍一下你自己"}'预期输出是一个 JSON 对象,reply字段包含模型的回复内容。
测试工具调用:
curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "北京今天天气怎么样?"}'这时 Agent 会先调用get_weather("北京"),然后把结果整理成自然的回答返回。
curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "2024-01-01 到 2024-12-31 之间有多少天?"}'这个请求会触发calc_days_between工具。
测试多轮记忆:
先发送“我的名字叫张三”,再发送“我叫什么名字?”,看看模型是否记得上下文。
如果你使用的是 Ollama 本地模型,需要确认模型支持工具调用。部分开源模型虽然不完全遵循工具调用协议,但可以通过把工具描述写进系统提示词的方式实现类似效果。
5. 常见问题与排查思路
在实际开发过程中,AI 应用最容易踩的坑集中在以下几个方面。我把高频问题整理成表格,方便你遇到问题时快速定位。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 调用 API 报 401 错误 | API Key 错误或已过期 | 检查.env中的 Key 是否有效;确认没有把 Key 硬编码在代码中 |
| 调用 API 报 429 错误 | 触发了限流 | 加入重试机制和退避策略;检查是否在循环中重复调用 |
| 模型返回内容被截断 | max_tokens设置过小 | 调大max_tokens;或者使用流式输出分段展示 |
| 多轮对话中模型“失忆” | 历史消息没有正确传入,或超出窗口被裁剪 | 检查消息拼接逻辑;实现摘要压缩机制 |
| Agent 反复调用同一个工具 | 工具返回结果没有正确传回模型 | 检查role: "tool"消息的tool_call_id是否匹配 |
| 模型输出了无关内容 | 提示词约束不够明确 | 强化提示词中的角色和格式约束;示例引导 |
| 本地模型工具调用不生效 | 模型不支持 function calling | 使用支持工具调用的模型;或者将工具描述写入系统提示词 |
| 服务响应慢 | 模型推理耗时长,或网络延迟 | 考虑流式输出;使用更小模型;增加缓存 |
针对“模型输出不稳定”的问题,这里展开说一下。大模型本身具有随机性,同一问题重复问,可能得到不同的答案。降低不稳定性的常用方法包括:
- 将
temperature调低,例如 0.1 到 0.3。 - 在提示词中要求模型使用固定模板输出。
- 对关键输出使用 JSON 格式约束,方便程序解析。
- 建立评测集,每次修改提示词后用回归测试验证效果。
另外,如果你在调试 Agent 时发现工具调用链路不对,建议分步排查:
- 先单独测试工具函数,确认输入输出正确。
- 再直接向模型发送带工具的请求,观察模型返回的
tool_calls内容。 - 最后才测试完整的 Agent 循环,并打印每一步的消息历史。
6. AI 工程化最佳实践
6.1 提示词管理:把 Prompt 当成代码来维护
在简单 demo 中,提示词写在业务代码里问题不大。但到了生产环境,提示词往往是变更最频繁的部分。把提示词硬编码在代码中,会导致每次修改都要重新发布服务,而且无法追踪版本变化。
推荐做法是把提示词模板独立存放,例如放在prompts/目录下,使用 JSON 或 YAML 格式管理。每个提示词都有版本号,修改后保留历史版本,方便回滚。
# 文件路径:prompts/qa_system.yaml version: "1.2" system_prompt: | 你是一个专业的技术问答助手。 请先理解用户的问题,然后用清晰、准确的中文回答。 如果你不确定答案,请直接说“我不确定”,不要编造。加载的时候把配置和提示词解耦,这样产品同学也能独立维护部分提示词内容。
6.2 成本与性能:不是所有请求都该用大模型
大模型 API 按 Token 计费,成本是不可忽视的问题。以下几种策略可以有效控制成本:
- 分流:对简单问题使用小模型(如轻量级模型),对复杂推理使用大模型。
- 缓存:对于相同或相似的问题,缓存模型输出。可以基于向量相似度做语义缓存,命中缓存时直接返回,不调用模型。
- 减少上下文:只传入必要的信息,避免把长篇文档全部塞进提示词。
- 流式输出:让用户看到部分响应,提升体验的同时减少等待焦虑。
性能方面,需要关注模型调用的延迟。如果外部 API 响应不稳定,建议加入超时控制和熔断机制,避免模型服务故障拖垮整个应用。
6.3 数据安全与合规:守住合规底线
AI 应用的数据安全比传统应用更需要重视,因为数据会被发送给第三方模型服务。在接入任何大模型 API 之前,必须明确数据合规边界。
具体建议:
- 对敏感数据脱敏后再发送给模型,例如把手机号、身份证号替换成占位符。
- 在传输中使用 HTTPS,API Key 使用环境变量或密钥管理服务保存,不要提交到代码仓库。
- 日志中不要记录完整的用户输入和模型输出,只记录必要的信息。
- 对模型输出做内容安全校验,防止生成违法违规内容。
- 明确告知用户对话内容可能被用于模型训练,并取得必要授权。
如果业务数据高度敏感,建议使用私有化部署的开源模型,数据不出内网。
6.4 可观测性与评估:让 AI 应用可诊断
AI 应用的输出具有不确定性,因此比传统应用更需要可观测性。至少需要记录以下信息:
- 每条请求的模型名称、Token 消耗、响应延迟。
- 提示词版本和模型参数。
- 用户输入与模型输出的完整内容(在合规前提下)。
- 工具调用链路:调了哪些工具、参数是什么、结果如何。
- 错误类型和发生频率。
评估方面,建议建立一个小型评测集,包含几十到几百条典型问题。每次修改提示词、切换模型、调整 RAG 参数后,跑一遍评测集,对比回答质量。这个评测集可以逐步扩展到线上真实问题样本,形成回归测试机制。
对于 RAG 系统,还需要分别评估“检索质量”(能否找到相关知识)和“生成质量”(回答是否准确、完整、不幻觉)。
6.5 开发流程与版本管理
最后,AI 应用开发也要遵守软件工程的基本规范。我的建议是:
- 代码评审:模型的调用代码、工具函数代码都需要走正常的 Code Review 流程。
- 环境隔离:开发、测试、生产使用不同的 API Key 和配置,避免误操作影响线上。
- 灰度发布:切换模型或修改提示词时,先在一部分流量上验证效果,再全量发布。
- 回滚预案:模型输出质量下降时,能快速切回旧版本。
7. 总结与学习路线
通过这篇文章,我们从“AI 洪流”这个宏观话题切入,梳理了 AI 应用开发的分层体系,再逐步落到环境准备、提示词、RAG、Agent、模型部署等具体技术点,最后用 Python 完成了一个具备多轮记忆和工具调用能力的 AI 助手项目。
你应该能掌握:
- AI 应用开发的基本分层和核心概念。
- OpenAI 兼容接口的调用方式和参数含义。
- 对话记忆管理的基本实现方式。
- Agent 工具调用的完整工作流。
- AI 应用在生产环境中的常见问题和工程化手段。
如果你是从零开始,建议按下面路线继续深入:
- 先把本文的代码跑通,替换成自己的 API Key,调试不同参数的效果。
- 学习提示词工程的高级技巧,例如 Few-shot、Chain-of-Thought,在小项目里做对比实验。
- 选择一款向量数据库(如 Chroma、Milvus),把自己的文档做成知识库,实现一个 RAG 问答系统。
- 研究 LangChain 或 LlamaIndex 这类框架,了解它们如何抽象 Agent、记忆、检索等组件。
- 尝试用 vLLM 部署一个开源模型,逐步理解推理优化和部署运维。
当前 AI 领域的技术迭代非常快,框架和工具层出不穷。但底层的东西变化并不快:模型调用协议、上下文管理思想、检索增强思路、Agent 循环结构,这些都值得花时间吃透。当你理解了这些底层原理,再学任何新框架都会轻松很多。
最后,动手实践永远比看教程重要。你可以从给本文的 Agent 增加一个“查询数据库”的工具开始,把你的真实业务接进来——这才是 AI 应用开发最有趣的地方。如果这篇文章对你有帮助,欢迎收藏备用,也欢迎在评论区交流你在 AI 应用开发中遇到的问题。