压Agent别再烧钱跑大模型!0 Token开销|可写简历,这套Mock LLM仿真压测方案无敌
上一篇我们聊透了一件反常识的事:直接拿JMeter压Agent对外HTTP接口,根本不算真正的Agent压测。Agent内部多轮推理、循环工具调用带来的资源放大效应,HTTP层监控完全感知不到。
很多同学看完埋点方案之后,马上遇到第二个拦路虎:想要验证这套埋点、验证Agent在高并发下会不会死循环、验证熔断逻辑,就得不停调用LLM接口。
随便跑一轮并发,几十上百块Token费用就出去了;如果要反复调试、回归测试,成本根本扛不住。
还有一类麻烦:开发环境没有外网LLM网关权限、内网隔离,想做Agent并发测试,连模型都调不通。这也是做大模型工程化落地时,绝大多数团队会卡很久的痛点。
💡 框架迁移提示:本文Mock仿真思路是通用方案,示例基于Spring AI实现;如果你使用 LangChain4j、AgentScope 等其他Java Agent框架,只需要替换框架对应的Model Provider实现,业务压测逻辑不变。
你有没有试过这种窘境:
想验证“连续调用检索工具会不会触发上下文超长”,压测跑5分钟,账单告警先来了;想测极限并发下的熔断、最大推理轮次限制,不敢开大并发,只能小流量手工测试,风险根本暴露不出来。
真实模型压测适合上线前最后一轮验收,而日常迭代、回归、容量预演,必须要有一套不依赖真实LLM的仿真方案。
今天这篇,我们搭建一套Spring AI下的Mock LLM能力,可控返回推理结果、可控模拟工具调用、可控模拟超时和异常;配合JMeter发起并发,和真实Agent调用链路完全一致,对接上一篇我们写的Agent全链路埋点,全程零Token消耗。
本文代码基于Spring AI稳定版本,可直接运行,无编造API;文末附带简历片段、JMeter脚本模板。

一、先分清:Mock LLM仿真压测 ≠ 简单Mock Controller
不少人的第一想法:直接在Controller层写Mock返回,把接口写死返回固定字符串。
这种方式非常廉价,但完全没有价值:
- 绕过了Agent的思考逻辑、工具路由、上下文拼接、多轮循环
- 无法复现Function Calling Java工具调用链路
- 埋点采集的推理轮次、Token统计、工具调用指标全部无效
合格的Agent仿真Mock,必须侵入在Model层:
Agent框架照常走完整链路:prompt组装→传给Model→解析返回的tool_call→执行工具→把结果塞回上下文→下一轮推理;
只是模型的输出由我们程序控制,不走远程LLM接口。
链路和生产环境1:1对齐,只有模型返回内容是预制仿真数据,这样我们之前搭建的Agent可观测埋点体系,才能采集到真实有效的大模型压测指标。
| 测试类型 | 优点 | 短板 | 适合场景 |
|---|---|---|---|
| Controller层Mock | 实现最简单 | 跳过Agent内部闭环,指标失真 | 只测网关、鉴权 |
| Model层Mock LLM仿真 | 全链路保真、零成本、可模拟异常 | 需要实现Model适配 | 开发回归、容量压测、熔断校验 |
| 真实LLM压测 | 真实效果 | Token成本高、限流不可控 | 上线前最终验收 |
二、Spring AI Mock Model 设计思路

Spring AI 的核心抽象是 ChatModel,我们自定义一个实现类 MockChatModel,实现ChatModel接口。
我们可以灵活控制:
- 指定本轮返回普通文本回答,结束Agent流程
- 指定本轮返回Function Calling工具调用指令,驱动Agent继续循环
- 模拟模型超时、返回格式异常、返回超长上下文
- 自定义返回的token usage,用来模拟不同消耗的请求,测试指标看板
和上一篇的Advisor埋点完全兼容,不需要改动之前的Agent全链路埋点代码。
三、完整可运行源码
1. pom.xml(昨天的Pom,无需新增依赖)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0)"
xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)"
xsi:schemaLocation="[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0)
[https://maven.apache.org/xsd/maven-4.0.0.xsd](https://maven.apache.org/xsd/maven-4.0.0.xsd)">
<modelVersion>4.0.0</modelVersion>
<groupId>com.agent.demo</groupId>
<artifactId>agent-pressure-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M1</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. MockChatModel.java(核心仿真模型)
package com.agent.demo.model;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionMessage;
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionMessage.Role;
import org.springframework.ai.openai.api.OpenAiApi.ChatCompletionTool;
import org.springframework.ai.openai.api.OpenAiApi.FunctionToolCall;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class MockChatModel implements ChatModel {
// 可配置:是否返回工具调用,制造多轮循环
private boolean enableMockToolCall = true;
@Override
public ChatResponse call(Prompt prompt) {
if(enableMockToolCall){
// 仿真:返回工具调用,驱动Agent继续调用工具,形成多轮链路
FunctionToolCall toolCall = new FunctionToolCall("queryWeather", "{\"city\":\"北京\"}", "call_1");
ChatCompletionMessage msg = new ChatCompletionMessage("", Role.ASSISTANT, List.of(toolCall), null);
Generation gen = new Generation(ModelOptionsUtils.toJsonString(msg));
return new ChatResponse(List.of(gen));
}else{
// 仿真:直接返回回答,本轮结束
Generation gen = new Generation("北京今天晴,气温22~28℃");
return new ChatResponse(List.of(gen));
}
}
public void setEnableMockToolCall(boolean enableMockToolCall) {
this.enableMockToolCall = enableMockToolCall;
}
}
3. 切换模型配置(区分测试环境/生产环境)
package com.agent.demo.config;
import com.agent.demo.model.MockChatModel;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ChatModelConfig {
@Value("${spring.ai.openai.api-key:}")
private String apiKey;
// test环境使用Mock模型,压测、回归
@Bean
@Profile("test")
public ChatModel mockChatModel(){
return new MockChatModel();
}
// prod环境使用真实OpenAI模型
@Bean
@Profile("prod")
public ChatModel realChatModel(OpenAiChatModel.Builder builder){
return builder.apiKey(apiKey).build();
}
}
4. JMeter测试脚本使用说明
JMeter只需要正常POST请求访问Agent对外接口,和压线上Agent接口写法完全一致:
- 请求体:用户提问文本
- 线程组:设置并发线程数、循环次数
- 后置处理器:不需要特殊改造
- 配套监控:接入我们D1写的埋点指标(Prometheus + Grafana看板)>
测试玩法:
- 开启enableMockToolCall=true,持续触发工具调用,模拟多轮推理,测试循环熔断、轮次上限
- 开启enableMockToolCall=false,单轮直接返回,对比两组指标差异
- 调高并发,验证线程池、上下文存储、trace链路是否泄露
四、压测验证重点校验清单
✅ 并发下埋点指标正常上报:思考轮次、工具调用次数、Token统计持续写入看板
✅ 设置最大推理轮次熔断后,不会出现无限循环
✅ 大量并发下TraceId不混乱、指标不会串请求
✅ 模拟异常返回,Agent异常分支、错误告警逻辑可以正常触发
❌ 不要只看接口RT和错误率,重点看Agent层指标
❌ 不要用Controller Mock替代Model层仿真,指标完全无效
❌ 测试环境和生产环境的Agent超时、最大轮次、工具配置必须保持一致,否则压测结论无参考价值

简历描述片段
基于Spring AI实现Model层Mock LLM仿真方案,搭建零成本Agent压测环境;仿真真实Function Calling调用、多轮推理链路,和生产Agent执行链路对齐;配合JMeter并发测试,对接Agent全链路埋点体系,在不消耗大模型Token的前提下,完成Agent循环熔断、容量、异常场景回归,降低大模型工程化测试成本。
📦 落地资源推荐
跑Agent的应用,尤其是做压力测试,更吃服务器资源,通常本机是不够用的,建议使用云服务器,能大大减少我们做其他事的精力,提高学习代码的专注力和学习AI的效率,免除后顾之忧,新用户首年特惠+专属渠道,https://www.aliyun.com/daily-act/ecs/activity_selection?userCode=fzoabro9。
💡领取完整工程
找到图片上的水印即Java-AI工程师,打出【agent-metric】即可,本篇完整Mock源码+JMeter脚本模板,后台关键词
下期预告
下一篇,我们聚焦压测之后的问题:Agent循环调用、上下文膨胀的熔断策略,教你写生产可用的多层熔断 + 上下文裁剪策略,防止上线后偷偷无限循环吃掉账单。从源头锁死成本上限。
更多推荐
所有评论(0)