如果你最近在学大模型应用开发,大概率会遇到一个尴尬的局面:短视频和文章里到处都是“10分钟写出AI应用”的教程,但真到自己动手,却搞不清LLM、RAG、Agent、MCP、工作流到底谁先谁后,也不知道该用Python还是Java,更不知道一个可以上线的AI应用到底长什么样。
这不是你不够努力,而是这个领域的知识太碎。大部分教程只教你某一个点:要么讲怎么调API,要么讲某个框架的配置,要么讲一个炫酷的Demo。真正能称得上“地图”的资料少之又少。这也是为什么,当看到“免费带你做AI大模型应用开发全家桶”这个方向时,我决定把它展开成一篇文章:它本身就是一个把碎片串成体系的机会。
这篇文章会帮你解决三个问题。第一,搞懂AI应用开发到底包含哪些能力,LLM、RAG、Agent、MCP、工作流、工具调用之间是什么关系。第二,给出一套可直接落地的工程骨架,分别用FastAPI和Spring Boot实现,覆盖Python和Java两大主力技术栈。第三,提供一个从“调通大模型”到“做出带RAG和Agent的完整接口”的实践路径,以及一套避坑清单。
先说结论:大模型应用开发早就不是“加个API”那么简单了。真正的门槛不是算法,而是工程化能力。谁能把模型调用、知识检索、Agent决策、工具协议、任务编排这几层串起来,谁才能在项目里真正落地AI。
1. 这篇文章真正要解决的问题
回到开头那个场景:为什么很多开发者学完后还是写不出项目?
核心原因是学习路径是碎片化的。
你学会了大模型API怎么调用,但不知道回答不准时要接入RAG;你学会了向量数据库,但不知道Agent为什么需要它;你了解了Agent,但不知道它怎么和外部系统交互;你写了几个工具函数,却不知道MCP解决的是“工具标准不统一”的问题。最后你会发现自己什么名词都听过,但一到动手就卡壳。
这套AI应用开发全家桶的价值,就在于它提供了一个完整的能力框架:
- LLM负责“理解与生成”,是大脑。
- RAG负责“知识与事实”,是外挂记忆。
- Agent负责“目标拆解与决策”,是执行者。
- MCP负责“工具调用的统一协议”,是连接器。
- 工作流负责“步骤与状态的编排”,是生产线。
- 工具调用负责“访问外部系统”,是手和脚。
如果你能把这六个能力装进一个工程里,并且用FastAPI或Spring Boot对外暴露成接口,你才算真正掌握了AI应用开发。而不是只会在Notebook里调一次模型。
文章会沿着这条主线展开:从概念地图到架构图,从环境准备到代码实现,从验证方法到生产环境注意事项。无论你之前只写Python,还是长期在Java生态里,都能找到对应的一条落地方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础概念:六个关键词,从“会名词”到“会串”
这一节先把概念讲清。不是为了凑术语,而是因为后面所有代码都建立在这六个概念之上。
2.1 LLM:大语言模型
LLM全称Large Language Model,是AI应用的核心推理引擎。你可以把它理解成一个能力极强但记忆不可靠的“实习生”:它懂语言、懂逻辑、能总结、能改写,但它不知道你私有的业务数据,也可能一本正经地胡说八道。
在工程上,你通常通过HTTP API调用它,输入一段消息,拿到一段补全后的文本。模型本身不是你要开发的东西,你要开发的是“怎么用好它”的周边系统。
一个最常见的误区是:把所有业务逻辑都塞进提示词里。为了省事,有人把几千字的企业文档直接放进Prompt,结果要么超长被截断,要么费用暴涨。这时候就需要RAG。
2.2 RAG:检索增强生成
RAG全称Retrieval-Augmented Generation,核心思路是“先检索,再生成”。模型回答前,先从你自己的知识库中检索出与问题最相关的内容片段,把这些片段作为上下文拼进Prompt,再让模型参照这些材料回答。
没有RAG时,你要靠模型内部的参数记忆回答问题,这对私有数据几乎无效。有了RAG,你可以把产品文档、FAQ、工单记录、合同条款放进去,模型就能基于这些资料回答。
RAG不是单个技术,而是一条流水线:文档解析 → 文本切片 → Embedding向量化 → 存入向量数据库 → 查询时召回 → 重排 → 拼接上下文 → 生成答案。后面章节会用代码演示最小实现。
2.3 Agent:智能体
Agent比“对话机器人”多一个能力:目标执行。它不只是回答你的问题,而是把一个大目标拆成子任务,决定先做什么后做什么,并调用工具拿到结果,再继续推进。
一个典型例子是“帮我订下周去北京的机票”。普通聊天机器人只能回答“好的,我建议你下载XXApp”。Agent则会规划:查询日期 → 查询航班 → 筛选价格 → 确认身份 → 调用预订接口 → 返回订单号。
Agent的基础能力包括:
- 规划(Planning):把复杂任务拆解成步骤。
- 记忆(Memory):记住已经完成的步骤和结果。
- 工具使用(Tool Use):调用计算器、搜索、数据库、API等外部能力。
- 反思(Reflection):根据执行结果修正下一步。
实现Agent并不一定要用复杂框架。很多生产系统里,Agent只是一段“意图识别 + 工具路由 + 循环执行”的状态机代码。这一点很多人误解,以为Agent必须是个AI框架。
2.4 MCP:模型上下文协议
MCP全称Model Context Protocol,定位是“AI应用与世界之间的标准化接口”。它解决的是一个很现实的工程问题:每个AI应用都要对接大量工具,如果每一个工具都自定义一套协议,集成成本会非常高。MCP试图统一“模型如何发现工具、如何调用工具、工具如何返回结果”的交互方式。
可以把MCP理解成AI界的USB-C接口。以前每个设备一个充电头,现在统一了标准,同一个线能充不同设备。MCP的意义不是某个具体功能,而是一套互操作协议。你在全家桶里学习的不是某个厂商的私有工具链,而是“如何让AI应用稳定地接入外部工具”,这是工程通用能力。
2.5 工作流:任务编排
工作流解决的是“步骤顺序和分支条件”的问题。比如一个客服应用:用户输入问题 → 先做敏感词检查 → 再查意图 → 如果是业务咨询就检索知识库 → 如果是转人工就创建工单。每个节点的执行顺序、重试次数、超时时间、失败降级,都需要编排。
工作流和Agent的区别值得反复琢磨:
- 工作流是确定性编排:每个节点做什么是提前画好的。
- Agent是动态决策:下一步由模型根据当前状态来选。
实际项目中两者经常结合:外层用工作流保证可靠,内层用Agent处理开放性问题。确定的部分交给工作流,不确定的部分交给Agent。
2.6 工具调用:把模型和应用系统连起来
工具调用(Tool Calling / Function Calling)是指让模型输出一个结构化指令,再由你的程序去执行。比如用户问“北京今天气温多少”,模型不直接编答案,而是输出一个调用天气查询工具的JSON指令,你的后端拿到指令后去请求真实天气API,最后把结果交给模型润色回答。
没有工具调用,模型只能“说”;有了工具调用,模型才能“做”。工具调用是Agent的底层能力,也是MCP要标准化的核心对象。
为了让这六个概念一目了然,我把它们整理成一张对照表:
| 概念 | 解决的痛点 | 类比 | 工程形态 |
|---|---|---|---|
| LLM | 语言理解与生成 | 大脑 | API调用、Prompt工程 |
| RAG | 知识不新、没有私域数据 | 外挂记忆 | 向量库 + 检索 + 生成 |
| Agent | 任务无法自动执行 | 项目经理 | 规划循环 + 工具路由 |
| MCP | 工具接口标准不统一 | USB-C接口 | 协议 + 服务端/客户端 |
| 工作流 | 步骤与状态难以管理 | 生产线 | 状态机 / DAG编排 |
| 工具调用 | 模型与现实世界隔离 | 手和脚 | 函数注册 + 结构化输出 |
3. 整体架构:一个AI应用到底由哪些层组成
概念讲完之后,需要把它们装进一个真实架构里。一个完整的AI应用,从请求进来到响应回去,通常会经过六层。
第一层是接入层:对外提供HTTP接口,统一鉴权、限流、参数校验,对应FastAPI路由或Spring Boot Controller。
第二层是编排层:决定这个请求走“普通对话”还是“RAG流程”还是“Agent执行”,对应工作流与Agent逻辑。
第三层是模型层:负责调用大模型,管理模型名称、温度、超时、重试、Token消耗。
第四层是知识层:负责加载文档、切片、Embedding、检索,对应RAG的向量数据库部分。
第五层是工具层:封装计算、数据库查询、外部API等能力,供模型调用,对应Tool Calling与MCP。
第六层是存储层:保存历史会话、用户信息、知识库文件、日志,支撑业务闭环。
这六层是逻辑上的划分,不是强制代码结构。小项目可以只有接入层加模型层,但凡是接近生产的项目,几乎都会逐步把后面几层补上。
3.1 两条技术路线:FastAPI与Spring Boot怎么选
标题里特别提到“FastAPI和Spring Boot双版本”,说明这套全家桶默认面向两类开发者。选哪条路线,不应该看跟风,而应该看你的团队和业务环境。
FastAPI的优势是:纯Python生态,和AI库天然同构。你写向量化、写数据处理、做文本切片,都可以用同一门语言完成,不用做跨语言调用。对原型验证、算法团队、个人开发者非常友好。
Spring Boot的优势是:企业级基础设施非常成熟。统一的依赖管理、配置中心、监控、注册发现、事务管理,Java技术栈在企业后端已经沉淀了很多年。如果你要把AI能力集成进已有Java微服务体系,用Spring Boot几乎是必选项。
一个稳妥的判断标准:如果你的核心业务是“AI能力本身”,优先选FastAPI;如果你的核心业务是“把AI嵌入成熟的后端系统”,优先选Spring Boot。
两种路线在工程上的最终目标一致:对外暴露稳定的API,内部按层组织代码,并处理好配置、密钥、日志和异常。下面的实战环节,我会先带你搭一个最小骨架,再分别用两种技术栈跑通大模型调用。
4. 环境准备:先把工程骨架搭起来
在写业务代码之前,先确认环境。这一节不写死具体版本,因为依赖更新太快,重点是让你明白每一步在干什么。
4.1 基础环境要求
Python路线需要的东西:
- Python,建议使用3.10及以上版本。
- pip或Poetry,用于依赖安装。
- uvicorn,用于启动FastAPI应用。
- 一个支持虚拟环境的IDE,比如VS Code或PyCharm。
Java路线需要的东西:
- JDK,推荐使用17及以上版本。
- Maven或Gradle,用于项目构建。
- 一个IDE,比如IntelliJ IDEA。
- 通常还会用Spring Initializr生成项目骨架。
大模型API接入需要准备一个可用的API Key和Base URL。不同厂商的兼容地址会不一样,但绝大多数大模型服务都提供OpenAI兼容的HTTP接口,这一点让代码实现可以保持统一。为了不泄露密钥,使用 .env 文件或系统环境变量保存敏感信息,并在 .gitignore 里排除它。
建议先把工程跑通,再逐步添加RAG、Agent和MCP相关依赖。不要第一步就把全套框架引入,否则一旦启动失败,很难判断是哪一层出了问题。
4.2 目录结构示例
下面是一个简化的双技术栈目录结构,先建立一个心智模型:
text复制ai-app-fullstack/
├── python-fastapi/
│ ├── app/
│ │ ├── main.py # FastAPI入口
│ │ ├── config.py # 配置读取
│ │ ├── routers/
│ │ │ └── chat.py # 对话接口
│ │ ├── services/
│ │ │ ├── llm_service.py
│ │ │ └── rag_service.py
│ │ └── tools/
│ │ └── registry.py # 工具注册
│ ├── .env.example
│ └── requirements.txt
└── java-springboot/
├── src/main/java/com/example/ai/
│ ├── AiApplication.java
│ ├── controller/AiController.java
│ ├── service/LlmService.java
│ └── config/RestTemplateConfig.java
├── src/main/resources/
│ └── application.yml
└── pom.xml
这个结构看起来简单,但它完成了“接入层、编排层、服务层、工具层”的初步分离。后面无论功能怎么增长,只要你守住“Controller不写业务、Service里面写逻辑、工具独立注册”这条纪律,项目就不会烂。
4.3 统一的大模型调用约定
为了让两个技术栈共用一套请求格式,推荐使用当前主流的对话补全接口风格:
text复制POST {base_url}/v1/chat/completions
Authorization: Bearer {api_key}
Content-Type: application/json
{
"model": "your-model-name",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "你的问题"}
],
"temperature": 0.7
}
响应体里需要关注几个字段:choices[0].message.content 是模型回复内容;usage.prompt_tokens 和 usage.completion_tokens 是Token消耗;id 是请求唯一标识,生产环境建议把它记录进日志。
注意:这里的 your-model-name 是占位符,实际以你接入的服务商文档为准。
5. 实战一:使用FastAPI接入大模型API
这一节的目标是花最少的代码跑通一个“对话接口”。它虽然简单,但后续所有功能都会挂在这条主链路上。
5.1 创建虚拟环境并安装依赖
bash复制python -m venv .venv
source .venv/bin/activate # Windows下执行 .venv\Scripts\activate
pip install fastapi uvicorn openai pydantic python-dotenv
这里使用OpenAI官方Python SDK,但它本身允许你通过 base_url 指向任何兼容服务,所以代码具备通用性。
在项目根目录创建 .env 文件:
properties复制# .env
LLM_API_KEY=sk-your-key
LLM_BASE_URL=https://api.your-llm-provider.com
LLM_MODEL=your-model-name
5.2 FastAPI应用代码
文件路径:python-fastapi/app/main.py
python复制import os
from dotenv import load_dotenv
from fastapi import FastAPI
from openai import OpenAI
from pydantic import BaseModel
load_dotenv()
app = FastAPI(title="AI Application Fullstack Demo")
client = OpenAI(
api_key=os.getenv("LLM_API_KEY"),
base_url=os.getenv("LLM_BASE_URL"),
)
class ChatRequest(BaseModel):
prompt: str
temperature: float = 0.7
class ChatResponse(BaseModel):
reply: str
model: str
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest):
resp = client.chat.completions.create(
model=os.getenv("LLM_MODEL"),
messages=[
{"role": "system", "content": "你是AI应用开发助手。"},
{"role": "user", "content": request.prompt},
],
temperature=request.temperature,
)
return ChatResponse(
reply=resp.choices[0].message.content,
model=os.getenv("LLM_MODEL"),
)
这段代码做了三件事:读取配置、创建LLM客户端、暴露POST /chat接口。没有写任何复杂的业务逻辑,但它是一个可以被后续扩展的地基。
有一点需要提醒:目前这个接口是“直连模型,无状态”的。用户上一次说了什么,接口不知道。要实现多轮对话,你得把messages历史传进来,这就涉及会话存储了,属于后续工程化的范畴。
5.3 启动并验证
bash复制uvicorn app.main:app --reload --port 8000
用curl测试:
bash复制curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"prompt": "用一句话解释RAG"}'
如果一切正常,你会收到一个包含reply字段的JSON响应。
6. 实战二:使用Spring Boot接入大模型API
Java侧的思路和Python没有本质区别:发一个HTTP请求,解析响应JSON。差别只在于你是通过依赖管理和类型体系去组织代码。
6.1 创建工程并添加依赖
建议通过Spring Initializr创建一个Spring Boot项目,依赖选择Spring Web即可。关键是把 RestTemplate 或 WebClient 配置成一个Bean。
配置文件 src/main/resources/application.yml:
yaml复制server:
port: 8080
llm:
api-key: sk-your-key
base-url: https://api.your-llm-provider.com
model: your-model-name
6.2 RestTemplate配置
文件路径:java-springboot/src/main/java/com/example/ai/config/RestTemplateConfig.java
java复制package com.example.ai.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
6.3 AI对话Controller
文件路径:java-springboot/src/main/java/com/example/ai/controller/AiController.java
java复制package com.example.ai.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/api/ai")
public class AiController {
private final RestTemplate restTemplate;
@Value("${llm.api-key}")
private String apiKey;
@Value("${llm.base-url}")
private String baseUrl;
@Value("${llm.model}")
private String model;
public AiController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@PostMapping("/chat")
public Map<String, Object> chat(@RequestBody ChatRequest request) {
Map<String, Object> payload = new HashMap<>();
payload.put("model", model);
payload.put("messages", List.of(
Map.of("role", "system", "content", "你是AI应用开发助手。"),
Map.of("role", "user", "content", request.prompt())
));
payload.put("temperature", request.temperature());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(payload, headers);
String url = baseUrl + "/v1/chat/completions";
Map<String, Object> response = restTemplate.postForObject(url, entity, Map.class);
return response;
}
}
对应的请求DTO单独放在一个Java文件中:
java复制package com.example.ai.controller;
public record ChatRequest(String prompt, double temperature) {
}
这段代码和FastAPI版本是同一个套路:构建消息体、设置鉴权头、发起请求、返回结果。区别在于Java的类型定义更显式,同时需要更注意RestTemplate的线程安全与超时配置,否则高并发下容易出问题。
6.4 启动并验证
bash复制mvn spring-boot:run
bash复制curl -X POST http://localhost:8080/api/ai/chat \
-H "Content-Type: application/json" \
-d '{"prompt": "用一句话解释RAG", "temperature": 0.7}'
到这里,两种技术栈的大模型调用都已经跑通了。接下来才是全家桶的核心:给这个“只会聊天”的接口加上RAG和Agent能力。
7. 实战三:构建最小RAG与Agent链路
当你发现靠Prompt上下文已经无法满足业务时,就该上RAG了。这里用一个简化代码解释流程,重点是理解“先检索、再生成”的骨架。
7.1 最小RAG流程
python复制# 简化版RAG流程:伪代码 + 核心步骤说明
documents = load_documents("docs/") # 1 加载业务文档
chunks = split_to_chunks(documents, size=600, overlap=80)
# 2 切片:保证单段文本长度适中,避免向量化与截断
# 3 为每个切片生成向量表示,存入向量数据库
for chunk in chunks:
vector = embedding_model.embed(chunk.text)
vectorstore.add(id=chunk.id, text=chunk.text, vector=vector)
# 4 查询时把问题向量化,并检索最相似的K个片段
question_vector = embedding_model.embed(question)
hits = vectorstore.query(question_vector, top_k=5)
# 5 把检索到的文本拼进Prompt,限制模型只能依据资料回答
context = "\n".join(hit.text for hit in hits)
rag_prompt = f"请根据以下资料回答问题:\n{context}\n\n问题:{question}"
answer = llm_chat(rag_prompt)
真正生产环境里,还需要处理文档格式解析、清洗乱码、按语义切割、重排去重、引用出处等细节。但如果你现在能把这个简化版本跑通,就已经解决了很多业务里“模型不了解内部数据”的核心痛点。
这里容易踩的坑是:有人把整个PDF原文塞进向量库,不切片也不清洗。结果检索出来的片段往往又长又乱,模型阅读困难,回答质量自然差。投入一点时间设计切片逻辑,比换更强的大模型更划算。
7.2 一个极简Agent实现
Agent不一定要引入重量级框架。你可以用一个循环来实现最朴素的ReAct思路:让模型判断下一步调用哪个工具,执行工具,把结果交给模型,直到模型认为任务完成。
python复制# 极简Agent:意图路由 + 工具调用示意
TOOLS = {
"query_db": query_database, # 查询数据库
"calculator": run_calculator, # 数学计算
"search_web": search_web, # 外部搜索
}
def run_agent(user_input: str) -> str:
steps = []
while True:
# 让模型决定:继续思考、调用工具或给出最终答案
decision = llm_decide(user_input=user_input, context=steps, tools=TOOLS)
if decision["action"] == "final_answer":
return decision["answer"]
# 校验工具名,不允许调用列表之外的能力
tool_name = decision["tool_name"]
if tool_name not in TOOLS:
return "错误:工具不存在或不在白名单内。"
tool_result = TOOLS[tool_name](**decision["tool_params"])
steps.append({"tool": tool_name, "result": tool_result})
Agent的工程化要点有三条。一是循环必须有最大轮数限制,否则模型可能无限循环,消耗Token。二是工具注册表必须有白名单,不能把任意系统命令暴露给模型。三是每轮调用要把历史步骤传给模型,否则它记不住自己做过什么。
工具调用为什么要单独拎出来讲?因为只有工具足够多、足够稳定,Agent才真正“有用”。一个只会聊天的Agent,和能查库存、算价格、下订单的Agent,工程复杂度完全不同。
7.3 MCP在全家桶里的位置
MCP不是替代Agent,而是解决“Agent接入工具的成本”。如果没有MCP,你每接一个新工具都要写一套独立的解析和调用代码;有了MCP这一类标准化协议之后,Agent与工具之间的交互格式可以统一。
在全家桶的练习中,建议你按这个顺序理解MCP:
- 先把工具封装成标准接口:输入JSON、输出JSON。
- 再让模型通过结构化输出选择工具。
- 最后再考虑接入MCP SDK,用协议管理工具发现与调用。
如果一上来就研究MCP协议细节,很容易陷入底层而忽略核心业务。先完成工具调用的手工链路,再引入协议抽象,理解会更扎实。
7.4 用工作流把各层串起来
一个真实接口通常是这样编排的:接收请求 → 识别意图 → 判断是否需要RAG → 检索知识库 → 判断是否需要工具调用 → 执行工具 → 生成回答 → 记录日志。
python复制def handle_chat_request(user_input: str):
intent = intent_recognize(user_input)
if intent == "knowledge":
context = rag_retrieve(user_input)
return llm_generate_with_context(user_input, context)
if intent == "business_tool":
decision = llm_plan_tool_call(user_input)
result = execute_tool(decision)
return llm_generate_from_tool_result(user_input, result)
return llm_chat(user_input)
工作流的价值在于:它让系统行为可预测、可观测、可回滚。Agent负责那些“可以灵活发挥”的节点,工作流负责“必须严格按顺序执行”的节点。两者结合,才能让AI应用既智能又稳定。
8. 运行结果与效果验证
当你把上面的FastAPI版链路跑起来后,建议按照下面的顺序验证。
第一步,验证普通对话:
bash复制curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"prompt": "你好"}'
预期输出中应包含reply字段,状态码为200。
第二步,验证RAG链路。给知识库里导入一份“产品退换货规则”,然后问“七天无理由退货有什么条件”。预期回答内容应该和文档一致,而不是模型自由发挥。
第三步,验证工具调用。配置一个返回当前时间的工具,问“现在几点”,观察响应是否正确。如果模型输出了错误的工具参数,可以调整工具描述或增加参数示例。
判断成功的关键标准很简单:回答内容不是胡编,而是来自知识库或工具的真实返回。如果模型依然靠记忆乱答,说明RAG链路没有把检索结果传进Prompt,优先检查上下文拼接是否正确。
如果请求失败,第一步不是重新生成代码,而是按这个顺序排查:
- 看API Key是否有效、Base URL是否正确。
- 看请求体格式是否符合服务商要求。
- 看返回错误信息是权限问题、模型不存在,还是Token超限。
- 看日志里是否记录了完整的请求ID和状态码。
加日志是排错的基础。FastAPI里可以增加一个中间件打印耗时和状态码,Spring Boot里可以用AOP或过滤器记录请求参数与响应摘要。没有日志,面对大模型这种“非确定性系统”,排错会非常痛苦。
9. 常见问题与排查思路
AI应用开发的错误往往是多层嵌套的:可能是代码问题,可能是模型问题,也可能是外部工具问题。下表是这套全家桶实践中最高频的几类问题。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 请求返回401 | API Key错误或未设置 | 检查环境变量是否加载,打印密钥前缀 | 重新配置.env,重启进程 |
| 请求返回Model Not Found | 模型名称不对或没有访问权限 | 核对服务商文档与账号权限 | 换成有权限的模型名 |
| 响应内容截断 | 超出模型上下文长度上限 | 统计Token用量,查看usage字段 | 缩减Prompt或切片长度 |
| RAG回答和资料无关 | 检索结果未进入Prompt | 打印检索到的上下文片段 | 检查向量库查询结果与拼接逻辑 |
| Agent调用工具失败 | 工具参数格式不匹配 | 打印模型输出的工具参数JSON | 优化工具描述,增加参数示例 |
| FastAPI启动端口占用 | 8000端口被占用 | 查看启动日志 | 换端口:uvicorn app.main:app --port 8001 |
| Spring Boot启动失败 | 依赖冲突或配置缺失 | 查看堆栈第一行 | 使用spring-boot:run排查依赖树 |
| 高并发下响应变慢 | RestTemplate配置了默认超时 | 压测与日志分析 | 设置连接池与读写超时 |
这里还要多说一条:大模型应用的问题是概率性的。同样的输入,两次结果可能不同。因此在验证阶段,不要只测一次就下结论,而是用一组稳定的测试用例跑多轮,看趋势,看稳定性。
10. 最佳实践与工程建议
从“能跑通的Demo”到“可以上线的系统”,中间还有很多工程细节。这一节值得收藏,等真正做项目时再来对照。
10.1 密钥与配置安全
API Key绝不允许硬编码进代码仓库。统一走环境变量、配置中心或密钥管理服务。提醒一句:即使代码里的密钥是测试用的,也会在Git历史里留下痕迹,清理成本很高。比较好的做法是从项目一开始就使用.env.example模板,把真实密钥排除在版本控制之外。
10.2 成本控制与Token优化
大模型调用不是免费的,Token就是钱。常见优化手段包括:设置 max_tokens 上限;对历史消息做滑动窗口截断;RAG场景下只传检索到的TopK片段;对简单意图直接走固定话术,不调用大模型。每次调用后记录 usage,定期统计成本分布。
10.3 可观测性
给每个请求分配一个Trace ID,贯穿从HTTP入口到模型调用再到工具返回的全过程。日志里至少记录:
- 请求参数摘要,注意脱敏。
- 模型名称、Token消耗、耗时。
- 工具调用名称、参数、返回状态。
- 最终响应摘要。
大模型系统最大的风险是“黑盒化”:你只知道用户输入和模型输出,不知道中间发生了什么。可观测性就是对抗黑盒的武器。
10.4 测试与评测
对AI应用做测试,不能只看“有没有报错”,还要看“回答质量是否稳定”。建议建立三类测试:
- 单元测试:测试切片、向量化、工具函数等确定性逻辑。
- 回归测试:用固定的问题集,验证不同版本答案质量不降级。
- 评测集:准备一批带标准答案的题目,对比模型答案的准确率和召回率。
RAG场景尤其要注意“检索召回率”:正确文档有没有被检索出来。没有评测集,你很难知道换一个Embedding模型后到底变好还是变坏。
10.5 安全与合规边界
给Agent开放工具能力时,必须按最小权限原则设计。工具白名单要严格限制,不允许模型直接调用删除、支付、导出等高风险能力。涉及用户隐私的请求要脱敏,涉及生产数据库的操作必须在测试环境充分验证,并保留人工审批链路。
Prompt注入是一个真实威胁。攻击者可能在用户输入里写“忽略之前所有指令”。应对方法是:不要把翻译、格式转换等系统级指令直接暴露给用户输入;平衡RAG场景时,明确“资料内容仅作为参考,不能覆盖系统指令”;对模型输出做敏感信息检测。
10.6 从全家桶到生产项目的落地节奏
不建议一上来就搭建全家桶。推荐顺序是:
- 跑通单模型调用。
- 加上RAG解决私有知识问题。
- 加上工具调用让模型能行动。
- 按业务需求引入工作流编排。
- 工具数量增多后,再引入MCP统一管理。
每完成一步,就上线一个可用的功能。这个节奏的好处是:每次改动都是可测试的,出问题知道去哪定位。
11. 总结与后续学习方向
这套AI大模型应用开发全家桶,表面上是一组技术名词,实际上是“从模型到产品”的完整工程链路。LLM解决理解与生成,RAG解决知识与事实,Agent解决任务执行,MCP解决工具标准化,工作流解决步骤编排,工具调用解决系统连接。掌握了这条链路,你就不再只会调API,而是能自己设计一个AI应用的整体结构。
FastAPI和Spring Boot的双版本实践,给了你两条可选路径:如果你在AI原生项目里,用Python路线更顺;如果你在成熟的后端体系里,用Java路线更容易融入现有设施。两条路线最终是同一套架构思想。
接下来你可以做三件事。第一,把本文的FastAPI示例或Spring Boot示例完整跑通,这一步半小时内就能完成。第二,找一个真实业务文档,做一个最小RAG,让它回答业务问题。第三,新增一两个工具函数,让Agent能真正执行动作。这三步做完,你再回头看那些零散的高级资料,会发现它们不再是孤立知识点,而是这条链路中的某一个环节。
需要提醒的是,AI应用开发迭代非常快,框架和SDK经常升级。“免费”只是入场券,真正让你有竞争力的是对架构和流程的理解。建议把这篇文章作为一张知识地图收藏,在动手实践时随时回来对照。
