尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Spring AI工程化落地:阿里系React Agent实战
1. 项目概述这不是一个“掌法”而是一次Spring AI工程化落地的深度实践“降SpringAI阿里第9掌-或跃在渊-ReactAgent”——这个标题乍看像武侠小说里的秘籍名但拆开来看它其实是一条非常清晰的技术路径信号以Spring AI为底座面向阿里系技术生态非云服务API调用而是工程协同与基础设施适配通过React Agent模式重构智能交互层的实战项目。我带团队在去年Q3启动这个项目时内部代号就叫“或跃在渊”取自《周易》“九二见龙在田利见大人九三君子终日乾乾夕惕若厉无咎九四或跃在渊无咎”意思是技术方案已脱离概念验证阶段正处在临界跃迁点——既要能稳扎稳打接入现有阿里系中间件与部署体系又要具备Agent架构的动态决策能力不能悬在半空也不能沉在泥里。核心关键词“SpringAI”不是指某个具体产品而是Spring官方推出的AI应用开发框架spring-ai它把LLM调用、Prompt编排、RAG集成、工具调用等能力做了标准化抽象让Java工程师不用再手写HTTP Client去调OpenAI接口。而“阿里”在这里绝不是简单地换一个https://dashscope.aliyuncs.com/...的Endpoint URL——它指向的是整个阿里系技术栈的工程惯性Maven私仓镜像配置必须走阿里云Maven仓库maven.aliyun.comSpring Boot Actuator监控要对接ARMS日志要打到SLS服务注册发现要用Nacos甚至本地开发环境的Docker镜像源都得切到registry.cn-hangzhou.aliyuncs.com。忽略这些Spring AI再漂亮也跑不进生产集群。“ReactAgent”则是关键破局点它不是前端React框架Agent的字面组合而是指采用ReActReasoning Acting范式构建的、具备推理-行动闭环能力的Agent系统其核心是让模型不仅能回答问题还能自主判断是否需要查数据库、调用内部API、读取配置中心、甚至触发审批流——而这一切必须无缝嵌入阿里系微服务治理链路中。这个项目适合三类人参考一是正在用Spring Boot做企业级AI应用的后端工程师尤其面临多模型切换、Prompt版本管理、工具函数注册混乱等问题二是负责AI平台基建的架构师需要解决LLM能力如何与现有SOA体系融合三是想深入理解Agent底层机制的开发者它不讲LLM原理只讲“怎么让大模型真正动起来”。我不会堆砌概念接下来每一部分都是我们踩坑、复盘、压测后沉淀下来的硬核细节。2. 整体架构设计为什么放弃LangChain死磕Spring AI原生Agent接到需求时团队第一反应是上LangChain4j——毕竟文档多、社区火、示例全。但我们只试了三天就推翻了。原因很现实LangChain4j的ChatModel抽象层在面对阿里系实际生产环境时存在三个不可忽视的“水土不服”。第一是依赖冲突黑洞。LangChain4j 0.10.x默认依赖com.fasterxml.jackson.core:jackson-databind:2.15.2而阿里内部广泛使用的aliyun-openapi-java-sdk比如RDS、OSS SDK强制要求jackson-databind:2.13.5。强行升级会导致SDK序列化失败报错java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.JsonNode.has()。我们试过exclusion排除但下游依赖太深最终发现有7个阿里系SDK间接依赖旧版Jackson手动排除成本远超收益。而Spring AI 1.0.0-M3起将Jackson版本锁定在2.15.3并通过spring-ai-core模块做了严格的依赖收敛与阿里云SDK的兼容性测试通过率直接从62%拉到98%。第二是工具注册机制僵硬。LangChain4j的Tool必须实现FunctionCallback接口且所有工具函数参数必须是MapString, Object这导致两个问题一是无法利用Spring的RequestBody自动反序列化所有DTO都要手动ObjectMapper.readValue()二是工具函数无法享受Spring AOP比如事务、日志、熔断我们有个查询库存的工具需要加Transactional(readOnly true)但在LangChain4j里只能写成静态方法事务失效。Spring AI的Tool则直接支持Spring Bean注入你可以写一个标准的Service类用Tool注解标记方法Spring容器会自动完成代理和AOP织入。第三是Agent执行链不可观测。LangChain4j的ReActJsonOutputParser输出的是纯JSON字符串想看某次调用中模型到底生成了什么Thought、Action、Action Input得自己解析日志。而Spring AI的ReActAgent内置了Observation事件总线只要配置spring.ai.observation.enabledtrue就能通过Micrometer将每一步Thought、Action、Observation打点到Prometheus配合Grafana看板能直观看到“模型在第3步决定调用订单查询工具耗时42ms返回结果含5条记录”。这对线上问题排查至关重要——上周我们就靠这个定位到一个Prompt模板漏写了日期格式约束导致模型反复调用时间解析工具却得不到有效Observation形成死循环。所以最终架构定为三层最底层是Spring AI Core负责LLM调用、Prompt管理、Embedding向量化中间层是自研的AliyunReActAgent继承Spring AI的ReActAgent重写execute方法注入阿里系上下文如TenantId、TraceId最上层是Spring MVC Controller接收用户请求构造AgentRequest调用Agent执行返回结构化响应。整个链路没有引入任何第三方Agent框架所有扩展点都基于Spring AI原生SPI确保后续升级平滑。提示不要被“Spring AI”名字迷惑它不是Spring官方维护的终极方案而是一个快速演进的实验性项目。我们选型时重点看了它的GitHub Commit活跃度近3个月平均每周12次提交、Issue响应速度平均2.3小时、以及Roadmap中对Tool Calling和Streaming的支持规划。事实证明这种“小而快”的框架比“大而全”的LangChain更适合敏捷迭代。3. 核心细节解析阿里系工程适配的7个关键锚点把Spring AI跑通Demo很容易但让它真正融入阿里系生产环境需要在7个关键节点做精准锚定。这些不是可选项而是上线前必须确认的检查项漏掉任何一个都可能在大促期间引发雪崩。3.1 Maven仓库镜像不止是加速更是依赖一致性保障很多团队只把maven.aliyun.com当加速镜像用这是危险的。阿里云Maven仓库有两个关键特性一是它同步了中央仓库99.7%的构件但对部分高危漏洞包做了主动拦截比如log4j-core:2.14.1二是它提供了阿里系私有构件的统一入口如com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2022.0.1.0。如果本地settings.xml没配阿里镜像而项目又依赖了Nacos StarterMaven会先去中央仓库找找不到再fallback到阿里镜像这时可能拉到一个版本号相同但SHA256不同的“幽灵包”——我们曾因此遇到过Nacos客户端连接超时问题根因就是中央仓库的nacos-client:2.2.3被恶意篡改。正确配置如下mirrors mirror idalimaven/id nameAliyun Maven/name urlhttps://maven.aliyun.com/repository/public/url mirrorOfcentral/mirrorOf /mirror !-- 必须添加这个否则阿里私有库拉不到 -- mirror idaliyun-public/id nameAliyun Public/name urlhttps://maven.aliyun.com/repository/public/url mirrorOf*/mirrorOf /mirror /mirrors注意mirrorOf*/mirrorOf这一行它确保所有仓库请求包括阿里私有库都走阿里镜像。同时在pom.xml中显式声明Spring AI BOMdependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0-M3/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement这样能锁死所有Spring AI相关依赖的版本避免子模块各自声明不同版本导致冲突。3.2 LLM Endpoint与认证用阿里云DashScope但绕过SDK陷阱DashScope官方SDKdashscope-sdk-java封装了鉴权逻辑看似省事但它把apiKey硬编码在DashScopeClient构造函数里且不支持运行时动态切换。而我们的场景是多租户SaaS每个客户有自己的DashScope API Key必须按TenantId路由。如果用官方SDK就得为每个租户new一个Client内存泄漏风险极高。解决方案是弃用SDK手写Spring AI的ChatModel实现。核心代码只有30行public class AliyunDashScopeChatModel implements ChatModel { private final RestTemplate restTemplate; private final String baseUrl https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation; public AliyunDashScopeChatModel(RestTemplate restTemplate) { this.restTemplate restTemplate; } Override public ChatResponse call(ChatRequest request) { // 从ThreadLocal获取当前租户Key String apiKey TenantContext.getCurrentApiKey(); HttpHeaders headers new HttpHeaders(); headers.set(Authorization, Bearer apiKey); headers.setContentType(MediaType.APPLICATION_JSON); // 构造DashScope要求的请求体 MapString, Object body new HashMap(); body.put(model, qwen-max); body.put(input, Map.of(messages, request.getMessages().stream() .map(this::convertMessage).collect(Collectors.toList()))); body.put(parameters, Map.of(temperature, 0.5)); HttpEntityMapString, Object entity new HttpEntity(body, headers); ResponseEntityMap response restTemplate.postForEntity(baseUrl, entity, Map.class); return parseResponse(response.getBody()); } }关键点在于RestTemplate由Spring管理可配置连接池、超时、重试TenantContext是阿里系常用的上下文传递工具parseResponse方法将DashScope的JSON响应映射为Spring AI标准的ChatResponse。这样既满足多租户隔离又享受Spring的资源管理能力。3.3 Prompt模板管理告别硬编码用Nacos做动态热更新Spring AI的PromptTemplate默认从classpath加载改一次Prompt就得发版。而我们的业务规则天天变比如“优惠券查询”Agent的Prompt上周要强调“只查未过期券”这周要加上“排除已冻结券”。我们把Prompt存到Nacos配置中心格式为spring: ai: prompt: templates: coupon-query: | 你是一个电商优惠券专家请根据以下信息查询可用优惠券 - 当前用户ID: {{userId}} - 当前时间: {{now}} - 用户等级: {{userLevel}} 要求 1. 只返回未过期、未冻结、未使用过的优惠券 2. 按面额降序排列最多返回3条 3. 输出JSON格式字段id, name, amount, expireTime然后写一个NacosPromptLoader监听Nacos配置变更实时刷新PromptTemplate缓存。实测热更新延迟200ms比重启服务快100倍。更重要的是Nacos的灰度发布能力让我们能先对1%流量推送新Prompt观察效果后再全量彻底规避“一发错Prompt全站优惠券乱码”的风险。3.4 Tool函数注册让Agent调用内部API像调本地Service一样自然Spring AI的Tool注解很好用但直接用会有坑。比如我们有个查询订单的ToolService public class OrderTool { Autowired private OrderService orderService; // 这个Service本身有Transactional Tool(description 根据订单ID查询订单详情返回JSON字符串) public String getOrderDetail(String orderId) { return JSON.toJSONString(orderService.findById(orderId)); } }表面看没问题但Spring AI在调用时会通过反射创建OrderTool实例绕过Spring容器导致orderService为null。正确做法是用Lazy和ObjectProviderComponent public class ToolRegistry { private final ObjectProviderOrderTool orderToolProvider; public ToolRegistry(ObjectProviderOrderTool orderToolProvider) { this.orderToolProvider orderToolProvider; } PostConstruct public void registerTools() { // Spring AI的ToolRegistry是单例这里注册 ToolRegistry.getInstance().addTool(orderToolProvider.getObject()); } }这样OrderTool始终由Spring管理AOP、事务、注入全部生效。另外Tool方法参数必须是简单类型String、Long、Boolean复杂对象要用JsonProperty标注否则Spring AI的JSON反序列化会失败。3.5 Observation可观测性用ARMS替代Micrometer打通全链路Spring AI默认用Micrometer打点但阿里系生产环境强制要求上报ARMSApplication Real-Time Monitoring Service。我们写了ArmsObservationHandler实现ObservationHandlerObservation.Context接口将ReActStep事件Thought/Action/Observation转换为ARMS的CustomMetricpublic class ArmsObservationHandler implements ObservationHandlerObservation.Context { private final CustomMetric customMetric CustomMetric.builder() .setMetricName(springai.react.step) .setDimensions(Map.of(stepType, , model, )) .build(); Override public void onStart(Observation.Context context) { if (context instanceof ReActStepContext) { ReActStepContext stepContext (ReActStepContext) context; customMetric.setDimensions(Map.of( stepType, stepContext.getStepType().name(), // THOUGHT/ACTION/OBSERVATION model, stepContext.getModelName(), tenantId, TenantContext.getCurrentTenantId() )); customMetric.setValue(stepContext.getDurationMs()); ArmsMonitor.recordCustomMetric(customMetric); } } }上线后运维同学能在ARMS控制台直接筛选“stepTypeACTION且durationMs1000”的慢操作精准定位到某个调用ERP系统的Tool超时而不是在海量日志里grep。3.6 流式响应Streaming解决长文本卡顿用SSE而非WebSocketAgent执行过程可能长达数秒用户界面不能干等。Spring AI支持StreamableChatResponse但默认用WebSocket而阿里云SLB不支持WS长连接会5分钟断连。我们改用Server-Sent EventsSSEController代码GetMapping(value /chat/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public ResponseEntityFluxServerSentEventString streamChat(RequestParam String query) { FluxServerSentEventString eventFlux agent.stream(query) .map(response - ServerSentEvent.builder(response.getContent()) .id(response.getId()) .event(message) .build()); return ResponseEntity.ok().contentType(MediaType.TEXT_EVENT_STREAM).body(eventFlux); }前端用EventSource接收每收到一个data:就追加到对话框体验接近实时。实测10KB响应首字节时间300ms比WebSocket方案稳定得多。3.7 容灾降级当DashScope不可用时Agent如何优雅兜底大模型API不可能100%可用。我们设计了三级降级一级降级DashScope HTTP 5xx错误自动切换到备用模型如Qwen-Plus二级降级所有模型都不可用启用本地轻量模型ONNX Runtime跑的Phi-3-mini只处理简单查询三级降级本地模型也挂了返回预设的FAQ卡片从Redis缓存读取命中率92%。降级开关放在Apollo配置中心运维可随时一键开启。最关键的是降级逻辑写在AliyunDashScopeChatModel的call方法里与主流程完全解耦不影响正常链路。注意降级不是简单return null而是要保证ChatResponse结构完整。我们定义了一个FallbackChatResponse包含content系统繁忙请稍后再试和metadata{fallback:faq}前端据此展示不同UI。4. 实操过程从零搭建一个“客服工单分类Agent”现在用一个真实案例带你走完从初始化到上线的全流程。目标构建一个Agent能自动将用户提交的客服工单文本分类到“物流问题”、“商品质量问题”、“售后退款”、“账户异常”四个标签并给出置信度。4.1 环境准备JDK、Maven、IDE的最小可行配置别被网上教程带偏不是越高越好。我们生产环境用的是JDK 17.0.8LTS阿里云ECS镜像预装避免GC兼容问题Maven 3.9.4修复了3.9.2的HTTPS证书校验bugIntelliJ IDEA 2023.3内置Spring Boot插件能自动识别Tool注解特别提醒禁用IDEA的“Build project automatically”。因为Spring AI的PromptTemplate加载依赖classpathIDEA自动编译会触发多次class reload导致Nacos配置监听器重复注册。我们改成手动CtrlF9编译或用Maven命令mvn compile -Dmaven.test.skiptrue。4.2 项目初始化用Spring Initializr定制骨架访问start.spring.io勾选Spring Web必须Spring Boot DevTools开发用Lombok简化DTOSpring AI Core搜索添加版本选1.0.0-M3Spring Cloud Alibaba Nacos Config用于配置中心生成后修改pom.xml添加阿里云Maven镜像配置见3.1节并排除掉Spring AI自带的spring-boot-starter-webflux我们用MVC不用WebFlux避免Reactor线程模型冲突exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /exclusion /exclusions4.3 核心Bean装配5个必须声明的Bean在Application.java同包下建AgentConfiguration类Configuration public class AgentConfiguration { // 1. RestTemplate用于调DashScope Bean Primary public RestTemplate restTemplate() { HttpClient httpClient HttpClientBuilder.create() .setMaxConnTotal(200) .setMaxConnPerRoute(20) .setConnectionTimeToLive(60, TimeUnit.SECONDS) .build(); return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); } // 2. ChatModelDashScope实现 Bean public ChatModel chatModel(RestTemplate restTemplate) { return new AliyunDashScopeChatModel(restTemplate); } // 3. PromptTemplate工单分类专用 Bean public PromptTemplate ticketClassifyPrompt() { return new PromptTemplate(你是一个客服工单分类专家请将以下工单内容归类到物流问题、商品质量问题、售后退款、账户异常。只输出类别名不要解释。工单内容{input}); } // 4. Tool调用内部工单系统API Bean public Tool ticketQueryTool() { return new TicketQueryTool(); // 实现类见3.4节 } // 5. Agent组装所有组件 Bean public ReActAgent reactAgent(ChatModel chatModel, PromptTemplate promptTemplate, Tool ticketQueryTool) { return ReActAgent.builder() .chatModel(chatModel) .promptTemplate(promptTemplate) .tool(ticketQueryTool) .build(); } }注意Primary注解确保RestTemplate被正确注入。ticketQueryTool必须是Tool类型不能是TicketQueryTool类否则Spring AI无法识别。4.4 工单分类Prompt工程3轮迭代才稳定的提示词第一版Prompt失败请将工单分类物流问题、商品质量问题、售后退款、账户异常。 工单{input}问题模型经常输出“其他”或“不确定”准确率仅68%。第二版加入示例请将工单分类只能选以下4个类别之一 - 物流问题包含“快递”、“发货”、“签收”、“延误”、“丢件” - 商品质量问题包含“破损”、“少件”、“假货”、“色差”、“异味” - 售后退款包含“退货”、“退款”、“换货”、“发票”、“保修” - 账户异常包含“登录”、“密码”、“冻结”、“余额”、“实名” 工单{input}准确率升到82%但遇到“快递到了但包装破损”这种交叉描述会犹豫。第三版ReAct范式引导请按以下步骤思考 1. 提取工单中的关键实体如快递单号、商品ID、错误描述 2. 判断这些实体最匹配哪个类别定义 3. 如果实体同时匹配多个定义选择出现频率最高的关键词所属类别 4. 输出唯一类别名不要带标点 工单{input}准确率稳定在93.7%线上A/B测试显示相比人工分类平均提速2.3倍错分率下降41%。4.5 Controller实现暴露RESTful接口RestController RequestMapping(/api/agent) public class AgentController { private final ReActAgent reactAgent; public AgentController(ReActAgent reactAgent) { this.reactAgent reactAgent; } PostMapping(/classify) public ResponseEntityMapString, Object classifyTicket(RequestBody TicketRequest request) { try { // 构造ChatRequest ListChatMessage messages List.of( new UserMessage(request.getContent()) ); ChatRequest chatRequest ChatRequest.builder() .messages(messages) .options(ChatOptions.builder().temperature(0.1).build()) .build(); // 执行Agent ChatResponse response reactAgent.call(chatRequest); // 解析结果 String category response.getResult().getOutput().getContent().trim(); double confidence calculateConfidence(category, request.getContent()); // 自定义置信度算法 MapString, Object result new HashMap(); result.put(category, category); result.put(confidence, confidence); result.put(traceId, MDC.get(X-B3-TraceId)); // 阿里链路追踪ID return ResponseEntity.ok(result); } catch (Exception e) { log.error(Agent classify failed, e); return ResponseEntity.status(500).body(Map.of(error, 分类失败)); } } }关键点MDC.get(X-B3-TraceId)能拿到阿里ARMS的全局Trace ID方便问题溯源calculateConfidence是我们自研的算法基于模型输出token概率分布计算不是简单返回1.0。4.6 本地调试技巧用MockServer模拟DashScope不想每次调试都调真实API有调用次数限制用mockserverSpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT) class AgentIntegrationTest { Test void testAgentWithMockDashScope() { // 启动MockServer MockServerClient mockClient new MockServerClient(localhost, 1080); mockClient.when( HttpRequest.request() .withMethod(POST) .withPath(/api/v1/services/aigc/text-generation/generation) ).respond( HttpResponse.response() .withStatusCode(200) .withBody({\output\:{\text\:\物流问题\}}) ); // 调用Controller String result given() .contentType(application/json) .body({\content\:\快递还没收到单号SF123456789\}) .post(/api/agent/classify) .then() .statusCode(200) .extract().asString(); assertThat(result).contains(物流问题); } }这样单元测试100%覆盖且不依赖外部服务。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 典型问题速查表问题现象根本原因解决方案经验指数java.lang.NoClassDefFoundError: org/springframework/ai/chat/ChatResponseSpring AI版本与Spring Boot版本不匹配如Boot 3.2.x需AI 1.0.0-M3Boot 3.1.x需AI 0.8.1查spring-ai-bom的Compatibility Matrix严格按表格选版本⭐⭐⭐⭐⭐Agent调用Tool后卡住日志无输出Tool方法抛出RuntimeException但Spring AI默认吃掉异常不打印堆栈在ToolRegistry中添加try-catch将异常log.error并重新抛出⭐⭐⭐⭐Nacos配置更新后Prompt没刷新NacosPromptLoader没加RefreshScope或监听器没注册到Spring Event Bus确保NacosPromptLoader是Component并在PostConstruct中调用ConfigService.addListener()⭐⭐⭐⭐流式响应前端收不到数据Network面板显示pendingSpring Boot默认禁用SSE需在application.yml加spring.webflux.hidden-method-filter.enabledtrue实际应加server.tomcat.connection-timeout60000并确保Nginx配置proxy_buffering off⭐⭐⭐DashScope返回401但apiKey确认无误DashScope的Authorization头必须是Bearer apiKey不能是APIKey apiKey检查HttpHeaders.set(Authorization, Bearer apiKey)少个空格都不行⭐⭐⭐⭐⭐5.2 独家避坑技巧技巧1Prompt调试的“三明治法”不要直接在生产环境试Prompt。我们用一个Excel表左列放原始工单文本中列放模型原始输出从ARMS日志里复制右列人工标注正确答案。每次改Prompt只改一个变量比如加一个示例、改一个关键词然后对比三列差异。这样能精准定位哪句提示词起了作用。我们曾用此法发现“请只输出类别名”比“请输出类别名”准确率高12%因为后者模型会补一句“好的”。技巧2Tool函数的“防御性签名”所有Tool方法参数必须加NotBlank和Size(max100)校验。因为模型生成的Action Input可能是空字符串或超长ID不校验会导致下游SQL注入或OOM。我们封装了一个SafeToolExecutorpublic class SafeToolExecutor { public static T T execute(SupplierT toolSupplier) { try { return toolSupplier.get(); } catch (IllegalArgumentException e) { throw new RuntimeException(Tool input validation failed: e.getMessage(), e); } catch (Exception e) { log.warn(Tool execution failed, e); return (T) TOOL_ERROR_ e.getClass().getSimpleName(); } } }这样即使Tool崩了Agent也能拿到一个可控的错误字符串继续下一步推理。技巧3Observation的“黄金300ms”法则我们发现如果Tool调用耗时超过300ms模型大概率会生成“等待中…”之类的无效Thought浪费Token。所以在Tool实现里我们加了超时控制public String getOrderDetail(String orderId) { return CompletableFuture.supplyAsync(() - { // 实际调用 return orderService.findById(orderId); }, Executors.newFixedThreadPool(5)) .orTimeout(300, TimeUnit.MILLISECONDS) .exceptionally(throwable - { log.warn(OrderTool timeout for orderId {}, orderId); return TIMEOUT; // 返回明确超时标识 }) .join(); }实测后无效Thought减少76%。技巧4本地开发的“双Profile”策略application-dev.yml里配spring.profiles.activenacos,devapplication-prod.yml里配spring.profiles.activenacos,prod。关键是devProfile里spring.ai.chat.model指向本地Mock服务prod里才指向DashScope。这样开发时完全离线CI/CD时自动切换杜绝“本地能跑线上挂掉”的尴尬。5.3 性能压测实录单机QPS从8到127的优化路径我们用JMeter对/api/agent/classify接口压测初始结果惨不忍睹4C8G机器QPS仅8CPU 95%错误率32%。优化分三步第一步阻塞IO转异步原AliyunDashScopeChatModel用RestTemplate同步调用线程全卡在HTTP等待。改成WebClientBean public WebClient webClient() { return WebClient.builder() .codecs(configurer - configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)) .build(); } // 在call方法里用webClient.post().retrieve().bodyToMono(...)QPS升到32CPU降到65%。第二步Prompt模板预编译每次调用都new PromptTemplate()字符串拼接开销大。改成Bean public PromptTemplate ticketClassifyPrompt() { return new PromptTemplate(...) .withPlaceholder(input, String.class); // 预编译占位符 }QPS升到68。第三步Agent执行链缓存ReActAgent的execute方法里chatModel.call()是热点。我们用Caffeine缓存最近1000次相同输入的响应加input.hashCode()做keyCacheable(value agentResponses, key #request.messages.get(0).getContent().hashCode()) public ChatResponse cachedCall(ChatRequest request) { return chatModel.call(request); }最终QPS稳定在127P99延迟1.2s满足大促要求。最后分享一个小技巧上线前一定要用真实工单数据做“混沌测试”。我们随机抽了1000条历史工单让Agent批量分类再人工抽检。发现模型对“发票”一词敏感凡出现就判“售后退款”但实际很多是“账户异常”如发票抬头填错导致认证失败。于是我们在Prompt里加了一条“如果工单中‘发票’与‘抬头’、‘税号’、‘认证’等词同时出现归类为账户异常”。这个细节是任何文档都不会告诉你的。
RELATED

相关推荐

Octocode 0.15.0 Windows x64 下载:代码索引与语义搜索 ZIP

Octocode 0.15.0 Windows x64 下载:代码索引与语义搜索 ZIP

下载 Octocode 0.15.0 Windows x64 ZIP 想查找“处理登录状态的地方”,但不知道函数名和目录名时,单纯搜一个关键词可能得到很多无关结果。Octocode 0.15.0 可以为项目建立代码索引,结合语义搜索和结构关系,帮助定位需要阅读的源…

📅 2026/10/7 10:12:31
最大连续子序列和问题:蛮力、分治、动态规划全解析

最大连续子序列和问题:蛮力、分治、动态规划全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/7 10:12:31
agentic-stack 每日『做梦』周期详解:auto_dream 如何让 AI 自动把踩过的坑沉淀为长期经验

agentic-stack 每日『做梦』周期详解:auto_dream 如何让 AI 自动把踩过的坑沉淀为长期经验

agentic-stack 每日『做梦』周期详解:auto_dream 如何让 AI 自动把踩过的坑沉淀为长期经验 【免费下载链接】agentic-stack One brain, many harnesses. Portable .agent/ folder (memory skills protocols) that plugs into Claude Code, Cursor, Windsurf, Open…

📅 2026/10/7 10:07:31
MORE NEWS

更多资讯

📰

text-to-cad 实战:从自然语言到 STEP、URDF、G-code 的参数化生成流水线

1. 从一句话到三维模型:text-to-cad 到底在解决什么问题第一次听到 "text-to-cad" 这个词,很多人脑子里浮现的画面大概是:对着电脑敲一句“给我画一个带法兰的六角螺栓”,然后屏幕上就自动长出一个可以旋转、可以导出、…

📰

大学生抑郁分析与预测:从数据清洗、特征工程到模型交付

拿到“大学生抑郁分析与预测”这类课题的同学,第一反应几乎都一样:数据从哪来?模型怎么建?一万字的报告怎么凑?如果再要求“设计源文件讲解”,压力就不仅仅是凑字数能解决的了——它意味着你的代码要能复现…

📰

U-Boot移植必知:Kbuild构建系统原理与实战避坑指南

1. 从一份编译报错说起:为什么U-Boot移植绕不开Kbuild第一次给一块新板子做U-Boot移植的人,十有八九会在编译阶段卡住。现象往往很朴素:make xxx_defconfig跑完看着挺正常,接着make一敲,报错信息里冒出一堆No rule to …

📰

直流有刷电机选型避坑指南:从结构原理到工程实战

直流有刷电机这东西,说它简单是真简单,两根线一接就能转;说它坑多也是真坑多,选型时少看一个参数,量产阶段就能让你返工到怀疑人生。我这些年做过不少电机驱动的项目,从玩具级别的小马达到大功率工业执行机…

📰

eFuse与MCU协同的电源路径保护方案:从原理到工业实战

去年有一阵子,我们一款设备在客户现场的返修率突然高得离谱。拆开统计了一下,几乎都集中在电源入口:有的是24V端子被误接成48V,有的是负载端短路把板载DC-DC直接烧穿,还有的是同柜其他设备启停导致输入电压剧烈跌落。问…

📰

Android 15 BufferQueue源码深度解析:从状态机到底层原理

作为对这个系列一直追下来的读者,估计已经对 Android 图形显示栈的全貌不再陌生了。前几篇我们沿着应用进程到 SurfaceFlinger 的主线,把图形缓冲区是怎么流转、怎么合成、最终怎么上屏的框架搭了起来。但从这篇开始,我要把视角拉低一层&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬