前面的 Agent 已经具备 Memory、RAG、Tool、MCP、Workflow 和 Task State。系统能力越来越丰富以后,会出现一个更加实际的问题:模型回答错了,到底应该修改 Prompt、优化 RAG、调整 Tool,还是直接去微调模型?
大模型应用由多个环节共同产生最终结果。一个错误回答只能说明最终结果出了问题【结果导向】 ,无法直接说明模型本身能力不足。因此,效果优化的第一步通常是定位错误发生在哪一层。

一、先判断问题发生在哪一层
一个典型 Agent 可以简化为:
bash
用户问题
↓
Prompt / Context / Memory
↓
RAG
↓
LLM
↓
Tool / Workflow
↓
业务系统
↓
最终结果
不同错误需要完全不同的解决方法。
例如模型不知道企业内部退款政策,首先应该补充 RAG;知识库存在正确政策,但检索到了旧文件,需要优化切片、Embedding、混合检索、Metadata Filter 或 Rerank;正确资料已经进入 Context,模型依然遗漏关键要求,则需要检查 Prompt、Few-shot、上下文组织和 Structured Output。
如果问题需要查询库存、读取数据库、获取当前用户状态或者执行退款,这已经属于外部真实状态和业务动作,应当使用 Tool Calling 或 Workflow。
因此,大模型应用优化可以先形成一条顺序:
bash
知识不足
→ RAG
检索不准
→ Retrieval 优化
正确知识已经拿到,但使用不好
→ Prompt / Context / Structured Output
需要真实数据或执行动作
→ Tool / Workflow
偶发错误仍然很多
→ Badcase / Eval / 回归测试 / Observability
长期存在稳定能力缺陷
→ SFT / LoRA
成本、延迟、模型体积成为主要问题
→ 量化 / 蒸馏 / 专用小模型
这条顺序很重要,因为越往后,开发成本和数据要求通常越高。
二、什么是大模型应用中的"偶发错误"
传统代码面对同样的输入和状态,通常会进入确定的执行路径。LLM 属于概率性模型,同一个应用可能在绝大多数情况下运行正常,却在某些输入、上下文组合、检索结果或者采样过程中突然产生错误。
例如客服 Agent 测试 100 个问题,其中 95 个正常,另外 5 个出现:
bash
答非所问
遗漏退款条件
引用旧政策
调用错误 Tool
Tool 参数提取错误
正确检索到资料后仍然理解错误
这种问题很容易产生一个误区:发现一个错误以后立即修改 Prompt。
单独修改某一句 Prompt 后,当前案例可能正常了,但无法回答两个更重要的问题:
bash
这个问题真的稳定解决了吗?
修改以后,会不会让原来正常的问题出错?
**【偶发错误如果只修改prompt,会对未来用户产生负担:让用户也遇到错误然后修改么?不会的,所以要稳定解决该问题,并且保证不会产生新问题】**因此,偶发错误越来越多时,开发方式需要从"手工试 Prompt"进入工程化测试。
三、Badcase:把错误变成测试资产
Badcase 就是经过确认的失败案例。
例如:
bash
输入:
"购买7天后还能无理由退款吗?"
期望:
根据最新退款政策判断,并指出适用条件。
实际结果:
模型引用了已经失效的旧政策。
【极为重要!】一个有价值的 Badcase 最好保存:
bash
用户输入
当时的上下文
检索到的文档
Tool Call 与结果
模型最终回答
期望结果
错误类型
这样以后才能判断错误究竟发生在 Retrieval、Prompt、Tool 还是模型推理阶段。
随着系统运行,Badcase 会逐渐形成一套真实测试集。这些案例通常比随意编写几十个测试问题更有价值,因为它们来自系统实际暴露过的问题。
四、Eval:给大模型应用建立"考试"
有了 Badcase,还需要定义怎样判断模型回答是否合格【考试】,这就是 Eval。
例如一个 RAG 问答可以评价:
bash
是否回答了用户问题?
是否与检索到的资料一致?
是否遗漏关键条件?
是否出现资料之外的事实?
Spring AI 提供统一的:
bash
Evaluator
接口,并提供 RelevancyEvaluator 和 FactCheckingEvaluator 等实现。RelevancyEvaluator 可以结合用户问题、RAG Context 和模型回答,判断回答是否与问题及上下文相关;FactCheckingEvaluator 可以检查模型生成的 Claim 是否能够被提供的 Context 支持。
例如一个简化的 RAG Eval:
bash
EvaluationRequest request =
new EvaluationRequest(
question,
retrievedDocuments,
answer
);
RelevancyEvaluator evaluator =
new RelevancyEvaluator(
ChatClient.builder(chatModel)
);
EvaluationResponse result =
evaluator.evaluate(request);
assertTrue(result.isPass());
这里相当于把:
bash
用户问题
+
检索资料
+
模型答案
一起交给 Evaluator 判断。
Eval 不一定全部由另一个 LLM 完成。对于订单金额、JSON 字段、Tool 名称、分类结果等明确规则,普通 Java 断言往往更加稳定 。LLM Evaluator 更适合语义正确性、相关性和内容完整性等难以用固定规则表达的问题。
五、回归测试:防止"修好 A,又弄坏 B"
假设当前已经积累 200 条 Badcase。
修改了:
bash
System Prompt
RAG 切片方式
Embedding Model
Tool 描述
以后,不应该只重新运行刚刚出错的那个问题,而应该重新运行整个测试集:
bash
200 条历史案例
↓
运行新版 Agent
↓
Eval
↓
与旧版本结果比较
例如:
bash
Version 1
通过:176 / 200
通过率:88%
Version 2
通过:191 / 200
通过率:95.5%
同时还需要检查原本通过的案例是否出现退化。
这就是回归测试的核心意义:每一次修改都重新验证历史能力,防止局部优化破坏其他场景。
因此可以把 Agent 开发逐渐转变成:
bash
发现错误
↓
加入 Badcase
↓
建立 Eval
↓
修改系统
↓
重新运行全部案例
↓
比较新旧版本
六、Observability:回答"错误到底发生在哪里"
Eval 可以告诉开发者:
bash
这个结果错了。
Observability 进一步回答:
bash
为什么错?
例如一次退款请求失败,需要能够观察:
bash
用户发送了什么?
↓
实际使用了什么 Prompt?
↓
Memory 提供了哪些历史信息?
↓
RAG 检索了哪些 Document?
↓
LLM 返回了什么?
↓
调用了哪个 Tool?
↓
Tool 参数是什么?
↓
Tool 执行多久?
Spring AI 的 Observability 基于 Spring 生态中的 Micrometer Observations,可以覆盖 ChatClient、Advisor、ChatModel、EmbeddingModel 和 VectorStore 等核心组件;Tool Calling 也会产生独立 observation,并记录工具名称、执行时间以及 tracing 信息。
因此:
bash
Eval
→ 判断结果好不好
Observability
→ 定位为什么不好
二者共同使用时,才真正形成大模型应用的质量控制能力。
七、什么时候才应该考虑 SFT 和 LoRA
完成前面的措施以后,如果模型仍然在一种稳定、重复、可标注的行为上****长期表现不足 ,并且已经积累了足够多高质量训练样本,才开始具备微调价值。
例如长期存在:
bash
固定行业术语抽取错误
某类报告始终无法按照内部格式生成
大量相似输入都存在相同分类偏差
而且已经积累:
bash
输入
→ 高质量标准输出
这样的训练数据,就可以进一步考虑 SFT 或 LoRA。
这里要注意,SFT、LoRA 属于模型训练层面的技术(Fine-tuning),并不是 Spring AI 的核心应用开发能力。Spring AI 更关注如何使用模型、组织 Context、RAG、Tool、Workflow 和 Eval。
因此可以形成一个非常实用的判断:
bash
知识不知道
→ RAG
事情做不了
→ Tool
流程不稳定
→ Workflow
回答偶尔出错
→ Eval + Badcase + Regression
某项能力长期稳定不足
→ Fine-tuning
八、量化和蒸馏解决的是另一类问题
如果当前模型已经能够很好地完成任务**,真正的瓶颈变成【性能成本(时空成本)】**:
bash
推理成本太高
响应时间太长
显存占用太大
端侧无法部署
此时才进入量化、蒸馏或者专用小模型的问题。
这些技术主要优化模型的:
bash
Size
Latency
Compute
Cost
Deployment
它们解决的目标与 RAG、Prompt、Tool、Eval 有明显区别。因此,"模型效果不好"本身通常不足以成为直接做量化或蒸馏的理由。
九、把整个优化过程连接起来
到这里,可以建立一套比较完整的大模型应用优化路径:
bash
用户发现错误
↓
Observability 定位问题
↓
判断错误层级
↓
RAG / Prompt / Tool / Workflow
↓
加入 Badcase
↓
Eval 衡量
↓
Regression Test
验证历史能力
↓
仍存在稳定重复能力缺陷?
↓
是
↓
积累高质量训练数据
↓
SFT / LoRA
↓
性能成本成为瓶颈?
↓
量化 / 蒸馏 / 专用小模型
因此,成熟的大模型应用开发已经非常接近传统软件工程中的质量保障思想 。区别在于系统内部增加了 LLM 这一概率性组件,因此测试对象从"代码路径是否正确"进一步扩展到了"检索内容、上下文、模型判断和工具行为是否共同产生了稳定结果"【路径扩大,问题概率性发生,但始终以结果为导向,找到问题发生的位置并解决】。
最终可以把这一过程简化为一句很实用的工程原则:
发现错误 → Badcase 固化问题 → Observability 定位根因 → 修改对应层 → Eval 验证该问题 → 回归测试验证整个系统。
bash
发现错误
↓
记录 Badcase
↓
Observability / Trace 定位原因
↓
确定错误类型
↓
修改对应层
Prompt / RAG / Tool / Workflow ...
↓
针对该 Badcase 做 Eval
确认问题是否修好
↓
运行完整回归测试
确认没有"修好 A、弄坏 B"
"Badcase 已经知道是错的,为什么还要 Eval?",关键在这里:Badcase 只告诉我们"这个案例曾经失败",Eval 定义"以后怎样客观判断它是否已经修好"。
例如 Badcase 是:
bash
用户问退款规则
模型引用了旧政策
我们已经知道它错了。但是修完 RAG 后,需要一个判断标准:
bash
是否引用最新政策?
是否回答退款条件?
是否出现旧政策内容?
这些标准就是 Eval。它可以是 Java 断言,也可以是 LLM Evaluator。
当这套质量保障循环真正建立以后,大模型应用的优化才逐渐从"凭感觉调 Prompt"进入可测量、可比较、可持续迭代的软件工程过程。