很多人学 AI 大模型应用开发,卡住的从来不是模型知识,而是“不知道自己到底该学什么”。今天看到一段教程讲 LLM,明天看到一个视频讲 RAG,后天又刷到一个项目讲 Agent,每个概念单独看都能看懂,但合在一起就不知道它们之间是什么关系,更不知道放在自己的业务里该怎么串起来。更麻烦的是,国内很多团队的技术栈是分裂的:一部分用 Python 搞算法,一部分用 Java 写业务,选哪套方案都意味着另一半人要重新学。
这篇文章要讲的,就是一条从 0 到 1 的 AI 大模型应用实战路线,覆盖 LLM、RAG、Agent、MCP、工作流、工具调用六大核心能力,并且提供 FastAPI 和 Spring Boot 双版本对照。它真正想解决的,不是某个模型的 prompt 怎么写,而是帮你建立一张能力地图:每个组件解决什么问题、在什么场景下出场、两个技术栈里各自怎么落地。读完你会得到一套可以直接跑通的骨架工程,也能搞清楚自己当前的项目该从哪里切入。
1. 从 0 学 AI 大模型应用,为什么多数人半途而废
先给一个判断:大模型应用开发的大坑,在于“技术点太多,主线太少”。
你以为要学的是模型推理,实际上项目里 80% 的工作是工程问题:数据怎么切分、向量库怎么组织、工具返回的 JSON 怎么解析、多次调用怎么衔接、Agent 怎么避免死循环、MCP 协议怎么接入。这些知识分散在 LangChain、LlamaIndex、Spring AI、向量数据库、消息队列等一堆框架里,每个框架的文档都只讲自己,没有人帮你拼出全貌。
学习路径上的第二个卡点,是确定性思维和不确定性思维的冲突。传统后端开发追求可预期:输入固定,输出固定。而大模型应用里,同样的输入每次输出可能都不一样,而且会失败、会格式错、会“一本正经地胡说八道”。没有从工程角度去驯服这种不确定性,写出来的 demo 只能停留在本地跑通,根本不敢上生产。
第三个卡点,是语言生态的选择困难。Python 在 AI 生态上最丰富,但很多企业后端是 Java/Spring 体系,AI 能力和现有服务之间隔着一层技术债。近两年 Spring AI 的成熟,让 Java 团队终于有了一个相对标准的接入方式,但网上的实战资料明显少于 Python。这也正是本文采用“双版本对照”写法的主要原因:不是告诉你哪个语言更好,而是帮你理解背后的设计模式是相通的,换语言只是换 API。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 六大能力拆解:LLM、RAG、Agent、MCP、工作流、工具调用
开始写代码之前,必须先把六块拼图的关系搞清楚。很多人分不清 RAG 和 Agent 谁先谁后,也不理解 MCP 和工具调用是不是一回事。
2.1 LLM:整个体系的计算核心
LLM(大语言模型)负责的是“根据上下文生成文本”。它本身只有对话能力,不存储你的业务数据,也不能直接操作外部系统。在实际项目里,LLM 是作为推理引擎被调用的,你给它 system prompt、用户输入、参考资料,它返回回答或者结构化 JSON。它的缺点也很明显:知识截止时间固定、会产生幻觉、无法实时获取数据。所以大模型应用不能只靠 LLM,必须给它接上外部能力。
2.2 RAG:给模型外挂知识库
RAG(检索增强生成)解决的是“模型不知道你的私有数据”的问题。核心思路是:先把文档切块并向量化,存入向量数据库;用户提问时,把问题向量化,检索出最相关的若干文本块;把这些文本块拼进 prompt,再交给 LLM 生成答案。它让模型在不重新训练的情况下具备领域知识,同时因为答案是基于检索结果的,幻觉会明显减少。适合企业知识库问答、客服系统、文档辅助阅读等场景。
2.3 Agent:让模型具备自主决策能力
Agent 可以理解为一个“会调用工具的 LLM 循环”。模型自身不能查天气、不能查数据库、不能发请求,但通过 Function Calling 机制,它可以识别用户意图,输出一个结构化的“我想调用某个工具,参数是这些”的请求,然后由代码执行工具,把结果返回给模型,模型再基于结果继续推理。这个过程可以循环多次,直到模型认为任务完成。
2.4 工具调用:Agent 的手和脚
只有 LLM 加 Function Calling 还不够,你还必须把真实能力包装成函数。工具调用就是把外部能力描述给模型,比如“get_weather(city)” “query_order(order_id)”。模型只负责决定调用哪个工具和传什么参数,真正执行的是你的后端代码。这里最大的工程点在于:工具描述的清晰度、参数定义的正确性、返回内容的大小控制,直接决定 Agent 的效果。
2.5 MCP:工具调用的标准化协议
MCP(Model Context Protocol)的出现,是因为每个模型接入外部工具时都有自己的格式,同样是查数据库,OpenAI 的 function 格式和 Claude 的工具格式并不完全一致。MCP 把这些统一成一套标准协议:模型客户端通过 MCP Client 连接 MCP Server,MCP Server 负责暴露工具和数据源。接入成本明显降低,同一个工具服务可以被多个模型复用。实际项目中,你不一定需要自研 MCP Server,但了解它的分层对理解未来的趋势很重要。
2.6 工作流:把确定流程固化成管线
Agent 适合“步骤不确定”的场景,但很多业务场景恰恰是“步骤确定”的,比如先检索、再总结、再翻译、再审核。这种场景交给 Agent 自由发挥反而不可控,应该用工作流把步骤固定下来:每个节点做什么、顺序是什么、失败怎么办。工作流和 Agent 并不冲突:确定性的流程用工作流,开放性的任务用 Agent,两者可以嵌套使用。
六者合起来,才是一个完整的 AI 应用:LLM 是大脑,RAG 是长期记忆,工具调用是手脚,MCP 是标准化连接层,Agent 是调度逻辑,工作流是固定业务路径。
3. 技术选型:FastAPI 和 Spring Boot 该怎么选
这个全家桶项目支持 FastAPI 和 Spring Boot 双版本,不是要制造选择焦虑,而是两者定位确实不同。
如果你的场景是算法团队主导、快速验证原型、数据处理复杂度高、向量化逻辑经常调整,选 FastAPI 最合适。Python 生态的优势在 AI 侧几乎是碾压级的:LangChain 的分支更细、社区示例更多、数据处理库齐全。FastAPI 本身的异步特性也适合做 AI 网关类服务。缺点是工程化能力弱于 Java,大型团队协作时需要你自己补上接口规范、代码检查、编译期校验等机制。
如果你们的业务系统本身就是 Spring 体系,AI 功能只是其中一个模块,希望直接融入现有的服务发现、配置中心、监控、事务管理体系,那么 Spring Boot 是正确的选择。Spring AI 正在快速补齐对话、Embedding、向量存储、Agent 等方面的能力。它可以让你在熟悉的环境里做 AI 开发,团队不需要分裂成 Python 和 Java 两拨人。缺点是目前有些组件的演进速度不如 Python 生态,遇到偏门问题能查到的资料更少。
从架构视角看,两者并不互斥。很多企业的最终形态是:Python 做 AI 能力层,Spring Boot 做业务聚合层,中间通过 REST 或消息队列通信。本文的双版本演示,本质上是用两种语言实现了同一套模式,建议至少完整跟通一个版本,再对照看另一个版本,理解会更深。
4. 环境准备与项目初始化
开始写代码前,先把环境梳理清楚。你可以选择 Python 或 Java 任一条主线,但核心概念和流程是重叠的,下面会并行给出两边的初始化方式。
4.1 推荐的环境清单
| 组件 | FastAPI 版本 | Spring Boot 版本 |
|---|---|---|
| 语言 | Python 3.10+ | JDK 17+ |
| Web 框架 | FastAPI | Spring Boot Web |
| 模型接入 | OpenAI SDK 或兼容网关 | Spring AI |
| 向量库 | Chroma / Milvus / pgvector | 同左(通过 REST 或 SDK) |
| 依赖管理 | pip / poetry | Maven / Gradle |
版本号建议以你实际使用的为准,因为模型 SDK 和 Spring AI 的迭代都很快。本文重点演示的是稳定不变的设计思路,不是某个具体版本 API。
4.2 FastAPI 项目初始化
bash复制mkdir ai-app-practice
cd ai-app-practice
python -m venv venv
source venv/bin/activate
pip install fastapi uvicorn openai langchain langchain-openai langchain-community chromadb python-dotenv
创建一个入口文件 main.py:
python复制# 文件路径:ai-app-practice/main.py
from fastapi import FastAPI
from dotenv import load_dotenv
load_dotenv()
app = FastAPI(title="AI 应用实战全家桶", version="0.1.0")
@app.get("/health")
def health():
return {"status": "ok", "message": "AI app practice is running"}
bash复制uvicorn main:app --reload --port 8000
访问 http://localhost:8000/health,能看到 {"status": "ok"} 就说明骨架启动成功。需要注意 .env 文件里配置模型 API Key 和 Base URL 等敏感信息,并且不要提交到 Git 仓库。
4.3 Spring Boot 项目初始化
如果用 Spring Boot,推荐通过 Spring Initializr 创建,依赖选择 Spring Web 和 Spring AI。实际版本以官方依赖管理为准,一般写法是:
yaml复制# 文件路径:pom.xml 关键依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置文件 application.yml:
yaml复制# 文件路径:src/main/resources/application.yml
spring:
application:
name: ai-app-practice
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: ${OPENAI_BASE_URL:https://api.openai.com}
chat:
options:
model: gpt-4o-mini
temperature: 0.7
上述配置只是示例,如果你接的是其他模型服务,把 spring.ai.openai 替换成对应的供应商配置即可。核心思想是把模型供应商封装在配置层,业务代码不感知。
5. 第一步实战:LLM 接入与结构化输出
很多人最开始的误区是“直接用原始 SDK 拼 prompt 就完事”。在生产项目里,你至少要做三件事:把模型客户端封装成可替换的组件、把 prompt 模板化、强制模型输出结构化内容。
5.1 FastAPI 版本:封装统一 ChatService
python复制# 文件路径:ai-app-practice/services/chat_service.py
import json
from openai import OpenAI
class ChatService:
def __init__(self, api_key: str, base_url: str = None):
self.client = OpenAI(api_key=api_key, base_url=base_url)
def chat(self, system_prompt: str, user_message: str) -> str:
response = self.client.chat.completions.create(
model="gpt-4o-mini",
temperature=0.3,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def chat_json(self, system_prompt: str, user_message: str) -> dict:
response = self.client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message},
],
)
return json.loads(response.choices[0].message.content)
注意这里用了 response_format={"type": "json_object"},这是 OpenAI 系 SDK 自带的结构化输出能力,但要求 prompt 里明确出现 JSON 字样。关键提醒:并不是所有模型网关都支持这个参数,如果不支持,就必须在 system prompt 里强调“只输出 JSON”,并在代码里做异常兜底。
5.2 Spring Boot 版本:用 ChatClient 实现同样的能力
java复制// 文件路径:src/main/java/com/example/aiapp/service/ChatService.java
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String chat(String systemPrompt, String userMessage) {
return chatClient.prompt()
.system(systemPrompt)
.user(userMessage)
.call()
.content();
}
}
Spring AI 把模型调用收敛成 ChatClient,和 FastAPI 版本的核心逻辑一致。你在做企业项目时,关键不是记住某个 API,而是保持“业务层只面对 ChatService,不直接接触 SDK”的分层习惯。以后换模型供应商,只需要改配置和少量实现,不用动业务代码。
小结论:LLM 接入层最简单的做法是包一层 Service,对外暴露 chat() 和 chat_json(),同时把模型名、温度、超时时间放到配置文件中。这一步是在为后面的 RAG 和 Agent 打地基。
6. 第二步实战:RAG 从零到可用的完整链路
RAG 的核心链路是固定的:加载文档 → 切分 → 向量化 → 存入向量库 → 检索 → 组装 prompt → 生成回答。很多教程只讲了“切分和检索”,却忽略了两个工程细节:文档切分的粒度决定检索质量,检索结果的排序和过滤决定最终答案的可靠性。
下面用一个最小案例演示 FastAPI 版本的 RAG 链路,Spring Boot 版本逻辑完全一致,只是换成了对应的 VectorStore API。
6.1 文档加载与切分
python复制# 文件路径:ai-app-practice/services/rag_service.py
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
class RagService:
def __init__(self, embedding_model, vector_store):
self.embedding_model = embedding_model
self.vector_store = vector_store
def load_documents(self, file_path: str):
loader = TextLoader(file_path, encoding="utf-8")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
)
return splitter.split_documents(docs)
def index(self, file_path: str):
docs = self.load_documents(file_path)
self.vector_store.add_documents(docs)
return {"indexed": len(docs)}
这里的 chunk_size=500 和 chunk_overlap=100 不是固定值。对技术文档来说 500 字左右比较合适,对客服 FAQ 可以更小。过大的 chunk 会导致检索命中不精准,过小的 chunk 会导致上下文信息不足。
6.2 检索增强生成
python复制# 继续在 rag_service.py 中追加
def query(self, question: str) -> str:
similar_docs = self.vector_store.similarity_search(question, k=4)
context = "\n\n".join([doc.page_content for doc in similar_docs])
system_prompt = f"""你是企业内部知识库助手。请严格基于以下材料回答用户问题。
如果材料中没有相关信息,请明确回答“根据当前资料无法确认”,不要编造。
参考资料:
{context}
"""
return self.chat_service.chat(system_prompt, question)
这段代码里最重要的一行是“如果材料中没有相关信息,请明确回答无法确认”。这句 prompt 的作用是抑制幻觉,它不完美,但比默认对话模式可靠得多。实际项目中,通常还会在检索后加过滤:按相似度阈值过滤低质量命中、按知识库范围过滤、对敏感问题做拦截。
6.3 RAG 效果验证需要检查什么
跑通流程只是第一步,判断 RAG 到底好不好用,要关注四个指标:
| 验证维度 | 观察方式 | 常见问题 |
|---|---|---|
| 命中率 | 打印检索回的原文片段 | 切分粒度不合适导致上下文缺失 |
| 相关性 | 对比问题和命中片段主题 | embedding 模型选择不适合领域 |
| 幻觉率 | 检查答案是否有材料外内容 | 检索了但 prompt 没强调约束 |
| 响应时延 | 观察首 token 时间 | 检索太慢或传给模型的 context 太长 |
上面四类问题中,第一个问题的排查优先级最高。检索不到正确资料时,后续所有环节都无从谈起。
7. 第三步实战:Agent、工具调用与 MCP
很多人做 Agent 时只调用了模型自带的联网搜索或计算器,真正的价值并不在这里。Agent 最有价值的地方,是把手里的业务系统暴露成工具,让模型学会按需调用,比如查订单、查库存、创建工单。下面用 FastAPI 和 Spring Boot 各演示一个最小 Agent。
7.1 FastAPI 版本:基于 Function Calling 的 Agent
python复制# 文件路径:ai-app-practice/services/agent_service.py
import json
from openai import OpenAI
# 1. 定义一个查询订单的包装函数
def get_order_status(order_id: str) -> str:
# 真实项目里这里会查数据库或调用订单服务
db = {"A1001": "已发货", "A1002": "待付款", "A1003": "已完成"}
return json.dumps({"order_id": order_id, "status": db.get(order_id, "未找到")})
tools = [
{
"type": "function",
"function": {
"name": "get_order_status",
"description": "根据订单号查询订单状态",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "订单号,例如 A1001"}
},
"required": ["order_id"],
},
},
}
]
def run_agent(client: OpenAI, user_message: str) -> str:
messages = [{"role": "user", "content": user_message}]
for _ in range(5):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
if not msg.tool_calls:
return msg.content
messages.append(msg)
for tool_call in msg.tool_calls:
result = get_order_status(
json.loads(tool_call.function.arguments)["order_id"]
)
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
}
)
return "Agent 执行达到最大轮数,请稍后重试"
这个例子虽然只有几十行,但已经包含了 Agent 的完整骨架:循环推理、工具执行、结果回填、终止条件。最大轮数这里设置为 5,防止模型在复杂任务中反复调用工具不结束,这是生产环境必须加的限制。
7.2 工具调用最常见的问题
第一个坑:工具描述写得太模糊。模型是靠 description 来理解何时调用这个工具的,描述不明确,模型就不会调用。比如“查询订单状态”和“根据订单号查询订单的最新物流状态”,后者明显更容易被正确触发。
第二个坑:工具参数设计得过于复杂。模型生成参数不是无限精确的,尤其是嵌套 JSON 参数,很容易生成结构错误。建议所有工具参数尽量扁平化、字符串化,复杂结构让后端代码去解析。
第三个坑:工具返回的内容不可控。如果工具返回一整个大对象的 JSON,会撑爆上下文,而且让模型难以聚焦。推荐的模式是:工具只返回必要字段,长文本在返回前先做截断或摘要。
7.3 MCP 实际接入思路
MCP 解决的正是多个模型复用同一套工具的问题。当模型和工具数量增长时,工具描述、鉴权、参数校验都容易混乱。使用 MCP 后,工具变成独立的 Server,通过协议向客户端暴露能力。实际项目中的落地路径一般是:
- 项目初期:先用代码内部函数工具,跑通业务闭环;
- 工具数量超过 5 个:引入 MCP 结构化管理工具定义和鉴权;
- 多模型并存:统一通过 MCP Server 暴露工具,避免每个模型各写一套接入代码。
很多人以为 MCP 是 Agent 的必选项,其实不是。它更准确的定位是“工具调用的标准化层”。如果你的工具只有一个,直接写函数就够了。如果工具多了、模型多了、复用多了,MCP 才体现出价值。
7.4 Spring Boot 版本的工具调用
Spring AI 提供了类似的 Function Calling 支持,核心做法是定义一个 @Bean 或工具类,然后在 ChatClient 上调用了:
java复制// 文件路径:src/main/java/com/example/aiapp/service/OrderAgentService.java
@Service
public class OrderAgentService {
private final ChatClient chatClient;
public OrderAgentService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@Tool(description = "根据订单号查询订单状态")
public String getOrderStatus(String orderId) {
Map<String, String> db = Map.of(
"A1001", "已发货",
"A1002", "待付款"
);
return db.getOrDefault(orderId, "未找到");
}
public String chat(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.tools(new OrderAgentService()) // 注册工具对象
.call()
.content();
}
}
这段代码只是入门写法,生产项目会更推荐把工具方法拆分到独立的 @Component,避免 Agent 服务类越来越臃肿。不过两种语言的本质是一样的:把 Java 方法当作可被模型调用的工具,Spring AI 负责把方法定义转换成模型可识别的 tool schema。
8. 第四步实战:把流程编排成工作流
Agent 适合开放任务,但它不适合所有场景。如果你的流程是“先检索 → 再总结 → 再翻译 → 再发送”,每一步都是固定的,用 Agent 反而会增加不可控性。正确做法是设计一个显式工作流。
8.1 为什么工作流比“一个超强 Prompt”稳定
工作流的本质是“把不确定性控制在节点之间”。每个节点只负责一件事,输入输出都有明确约定。比如 RAG 工作流:
第一步:接收用户问题。
第二步:判断是否包含敏感词,包含则直接拦截。
第三步:向量检索,获取资料。
第四步:调用 LLM 生成答案。
第五步:把答案和引用来源一起返回。
这一步里没有让模型自由发挥的空间。它的优点是:每一环都可测试、可监控、可回滚。改动其中一个节点不会影响整个流程。在真实业务中,质检、审核、人工兜底这类环节必须用工作流实现,不能完全交给模型。
8.2 FastAPI 版本:用代码实现一个简单工作流
python复制# 文件路径:ai-app-practice/services/workflow_service.py
class RAGWorkflow:
def __init__(self, rag_service, sensitive_words=None):
self.rag_service = rag_service
self.sensitive_words = sensitive_words or ["违法", "赌博", "涉密"]
def run(self, question: str):
# 节点1:敏感词检查
for word in self.sensitive_words:
if word in question:
return {"code": 400, "message": "问题包含敏感词,已拦截"}
# 节点2:检索增强生成
answer = self.rag_service.query(question)
# 节点3:答案长度检查(简单的兜底校验)
if len(answer) < 5:
return {"code": 500, "message": "生成结果异常,请稍后重试"}
return {"code": 200, "data": {"answer": answer}}
每个节点都可以扩充成独立模块,加上日志、重试、熔断。这种显式的顺序流程对于生产系统最友好,因为你可以对每个节点写单测,而不是在 prompt 里碰运气。
8.3 工作流和 Agent 如何共存
业务里最常见的是混合架构:主流程用工作流控制,某个特殊节点交给 Agent。比如“用户咨询订单问题”入口走固定流程,先做用户意图分类;如果判断为售后投诉,此时才启动 Agent,让它结合订单数据和售后政策生成处理建议。这种设计既保留了工作流的可观测性,又具备 Agent 的灵活性。
9. 常见问题与排查方法
实验过程中踩坑是必然的,下面把最常遇到的几类问题整理成清单,方便定位。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 调用模型报 401 | API Key 配置错误或过期 | 检查环境变量和配置中心 | 重新生成 Key,确认加载了正确的配置 |
| RAG 回答与资料无关 | 切分粒度不合适或向量化效果差 | 打印相似度搜索命中的原文 | 调整 chunk 大小,更换领域匹配的 embedding 模型 |
| 模型输出不是 JSON | 模型不支持结构化输出参数 | 检查网关文档和原始返回 | 改用 prompt 强约束,并增加 JSON 解析失败兜底 |
| Agent 反复调用工具 | 工具描述不清或缺少终止条件 | 查看工具调用日志 | 优化工具 description,设置最大迭代轮数 |
| Spring Boot 依赖冲突 | Spring AI 与 Spring Boot 版本不兼容 | 查看依赖树和启动日志 | 统一版本管理,以官方 BOM 为准 |
| 向量检索结果为空 | 文档切分失败或向量库未正常写入 | 查看向量库集合数量 | 确认 embedding 成功后检查索引状态 |
| 响应时间过长 | 没有缓存或上下文过大 | 压测和链路追踪 | 添加缓存、压缩 context、使用流式输出 |
针对“模型输出不是 JSON”这个问题,最稳妥的排查路线是:先不经过任何框架,直接用原始 SDK 调用一次,确认模型是否稳定输出。如果原始调用也不稳定,就说明问题出在模型或 prompt,而不是你的框架封装。
10. 工程化最佳实践与生产级建议
跑通 demo 之后,要上生产还需要过一道工程化关卡。这里给出六个可以直接落地的建议。
10.1 配置与密钥分离
模型 Key、Base URL、模型名、向量库连接串不要写死在代码里。开发环境用 .env,生产环境走配置中心或环境变量。同时要把 Key 设置为只读权限,避免误操作。
10.2 日志必须记录上下文和 Token
AI 应用的排查难度远高于传统接口。建议在调用 LLM 的位置记录:system prompt 摘要、用户输入、模型输出、token 消耗、耗时。不需要记录完整 prompt 的话,至少把关键节点和错误信息打出来。否则出问题时根本无从下手。
10.3 为每个外部调用设置超时和重试
模型服务不是本地函数,网络抖动、限流、服务端过载都可能发生。所有模型调用都应该配置连接超时和读取超时,并对可重试的错误做退避重试,同时对不可重试的错误做快速失败。
10.4 限制输入长度和控制输出格式
用户的输入内容不能无限制传给模型。生产项目里一般会先做长度截断、敏感词过滤、内容安全审核。输出端则必须做格式校验和异常兜底,防止解析 JSON 失败直接 500。
10.5 用版本化管理 Prompt
Prompt 也是代码,应该纳入版本管理。可以定义独立的 prompts/ 目录,每个 Prompt 文件包含模板和配套说明。改 Prompt 要做对比验证,不要直接在生产上盲改。调试时先脱离业务代码单独测试 Prompt,再集成到服务里。
10.6 双版本团队协作的一些建议
如果团队同时维护 FastAPI 和 Spring Boot 两个版本,更推荐抽象出一份“能力规格说明”,把每个接口的输入输出、每个工具的定义、工作流的节点顺序用文档统一起来。两种语言只是实现,规格必须先一致。否则会出现 Python 版本已经改了某个逻辑,Java 版本还在用旧逻辑的分裂状态。
11. 总结与后续学习方向
这篇内容最想传达的,其实不是某一个框架的用法,而是一条完整的学习主线:先搞懂 LLM 的特性,再掌握 RAG 的外挂知识能力,然后用 Function Calling 把 Agent 接进业务系统,再通过 MCP 让工具能力标准化,最后用工作流把确定流程固化下来。这六块拼图组合在一起,才是一个真实可用的 AI 应用,而不是只有 ChatGPT 风格的聊天框。
建议你按照下面这条路径去实践:
- 第一步:先跑通 LLM 接入,重点练习结构化输出;
- 第二步:实现一个最小 RAG,上面放一份你自己的技术文档,问几个问题看效果;
- 第三步:对照两个版本的 Agent 代码,把业务里的一个查询接口包装成工具;
- 第四步:把你目前业务中最常被问的问题设计成固定工作流;
- 第五步:如果工具超过 5 个,再考虑引入 MCP 做标准化。
其中最容易给你正反馈的是 RAG,因为效果是可以直接被验证的。最应该耐心钻研的是工具调用,它是 Agent 能否真正落到业务上的关键。至于双版本,建议先跟通一边,回头再看另一边时关注模式对比,而不是逐行翻译代码。
AI 应用开发看起来很复杂,但本质仍然是工程问题:合理拆解能力、控制不确定性、逐步建立可观测性。把今天这张能力地图记住,按图施工,就足够支撑你从零开始做一个有实际价值的 AI 项目。后面再往深走,可以去研究多轮对话的记忆策略、Agent 的规划算法、RAG 的重排序优化、模型微调与 RAG 的结合,这些方向都是在今天这个全家桶骨架上的延伸。
