资讯动态

Spring AI 2.0 vs AgentScope Java:单智能体增强与多智能体系统建模的本质差异

发布时间:2026/9/12 10:47:00 来源:尧图企业网站定制
1. 项目概述同一个 Agent为什么值得用两种框架各写一遍“同一个 Agent用 Spring AI 2.0 和 AgentScope Java 各实现一遍差的不止代码量”——这个标题一出来我就在团队内部技术分享会上被连问了七次“真有必要吗”“不都是调大模型 API 吗”“Java 写个 HTTP 请求不就完了”我的回答很直接不是在比谁写的代码行数少而是在比谁把“智能体”的边界、责任、协作逻辑和失败韧性真正想清楚了。Spring AI 2.0 和 AgentScope Java 看似都面向 Java 生态的 AI 应用开发但它们解决的是完全不同的问题域。Spring AI 是一个AI 增强型应用框架它的核心使命是让 Spring Boot 工程师能像注入Service一样自然地接入 LLM、Embedding、RAG、Streaming 等能力而 AgentScope Java 是一个多智能体系统MAS运行时框架它默认假设你正在构建一个由多个角色明确、目标分层、通信受控、状态可追溯的智能体组成的协作网络。举个生活化类比Spring AI 就像一套高性能厨房电器套装——破壁机、空气炸锅、智能电饭煲每台设备都极好用你可以单点做豆浆、烤鸡翅、煮米饭AgentScope Java 则是一整套米其林餐厅后厨管理系统——它不只提供灶具还定义了主厨、副厨、备餐员、传菜员之间的工单流转规则、食材库存同步机制、出餐质量回溯路径甚至包含突发状况比如某位厨师临时请假下的任务重分配协议。所以当你用同一个业务场景——比如“用户提交一份技术面试题文档系统自动拆解为知识点图谱 生成三套难度梯度的模拟题 输出个性化复习建议”——分别用两个框架落地时你会立刻发现Spring AI 2.0 的实现重点在单点能力链路的健壮性与可观测性Prompt 模板怎么分层管理ObservationHandler 如何捕获中间推理步骤MCPModel Calling Protocol服务怎么注册和路由LLM 调用失败时 fallback 到哪个本地小模型AgentScope Java 的实现重点在多角色协同的语义建模与执行调度谁负责文档解析ParserAgent谁负责知识抽取KnowledgeMinerAgent谁负责题目生成QuestionGeneratorAgent它们之间用什么消息格式通信任务超时后是否触发重试或降级整个流程的状态快照能否导出供人工复核这正是标题里“差的不止代码量”的深意Spring AI 2.0 下你可能写 300 行代码完成端到端流程AgentScope Java 下你可能写了 800 行但其中 400 行是定义 agent 角色契约、200 行是配置消息总线策略、150 行是编写状态持久化钩子——这些“额外代码”恰恰是生产级多智能体系统不可省略的骨架。适合谁来读这篇如果你正面临以下任一场景这篇文章就是为你写的你已用 Spring AI 快速上线了 RAG 助手但用户开始抱怨“回答忽好忽坏”“追问时上下文丢失”“无法解释答案来源”你想知道更结构化的解决方案你在技术选型会上听到“AgentScope”这个词但翻完官网文档仍不清楚它和 Spring AI 的本质差异不敢拍板引入你正在准备 Java 面试题被问到“如何设计一个可扩展的 AI 应用架构”你不想只背“观察者模式策略模式”而是需要真实框架级的对比视角你是个务实的后端工程师讨厌空谈“智能体范式”只想看同样的输入、同样的输出两套代码到底哪里不同为什么不同换框架后运维成本、调试难度、测试覆盖方式会怎么变接下来我会以一个真实可运行的“技术面试题智能处理 Agent”为蓝本逐层拆解两个框架的实现逻辑。不讲概念不堆术语只呈现我亲手敲过、跑通、压测、线上灰度过的完整方案。2. 核心设计思路对比从单点增强到系统建模2.1 Spring AI 2.0 的设计哲学AI 能力即 Spring BeanSpring AI 2.0 的底层心智模型非常清晰把 AI 相关能力当作 Spring 容器中的一等公民通过标准依赖注入、AOP 增强、事件驱动机制进行编排。它不试图定义“智能体是什么”而是专注解决“如何让 AI 能力在 Spring 生态里不突兀、不脆弱、可观测”。我们先看这个 Agent 的核心功能模块划分Input Processor接收用户上传的 PDF/DOCX 面试题文档提取纯文本Knowledge Graph Builder将文本切片后送入 Embedding 模型存入向量库构建知识点节点与关系边Question Generator基于图谱按“基础-进阶-高阶”三级难度生成模拟题Review Advisor综合用户历史答题记录如有、当前图谱密度、高频错题标签生成复习建议Output Formatter将四部分结果组装为 Markdown 报告并返回。在 Spring AI 2.0 中这五个模块天然对应五个Service类每个类内部使用AiClient或EmbeddingClient进行模型调用。关键设计选择如下提示Spring AI 2.0 强烈推荐使用ObservationHandler替代原始ChatClient。这不是为了炫技而是因为ObservationHandler会自动注入 OpenTelemetry 上下文捕获每一次 LLM 调用的 prompt、response、token 数、耗时、错误码。我在压测时发现当并发达到 50 QPS 时未启用 Observation 的日志根本无法定位是哪次调用触发了 rate limit而启用后通过 Jaeger 查看 trace3 秒内就能定位到具体请求 ID 和失败原因。Prompt 管理采用分层模板根目录src/main/resources/prompts/下按模块组织如knowledge-graph/system.ftl、question-generator/user.ftl。Spring AI 2.0 原生支持 FreeMarker 模板变量自动从Message对象中提取避免字符串拼接导致的 XSS 风险。例如question-generator/user.ftl中请基于以下知识点图谱生成${difficulty}难度的模拟题 ${graphSummary} 要求1. 题目数量为${count}道2. 每道题必须标注对应的知识点ID3. 输出格式为JSON数组每个元素包含question、answer、knowledgeId字段。这样业务代码只需构造MapString, Object传入无需关心转义逻辑。MCPModel Calling Protocol服务注册是关键分水岭Spring AI 2.0 1.0 版本中所有模型调用都硬编码在AiClient构造里2.0 引入 MCP允许你将不同模型能力抽象为独立服务。比如我注册了三个 MCP 服务embedding-service对接 Alibaba Cloud DashScope 的 text-embedding-v3llm-glm4对接智谱 GLM-4 的 chat 接口llm-qwen2对接通义千问 Qwen2-72B 的 chat 接口用于高精度生成。在QuestionGeneratorService中我通过Qualifier(llm-qwen2) AiClient aiClient注入指定服务而不是写死 URL。这样当 GLM-4 接口不稳定时运维只需修改application.yml中spring.ai.mcp.services.llm-qwen2.url无需发版。失败处理不是 try-catch而是策略链Spring AI 2.0 提供RetryPolicy、FallbackPolicy、CircuitBreakerPolicy三类策略。我为KnowledgeGraphBuilder配置了组合策略先重试 2 次间隔 1s应对瞬时网络抖动若仍失败则 fallback 到本地bge-small-zh-v1.5模型通过Ollama部署保证基础功能可用若 5 分钟内失败率超 60%熔断器打开后续请求直接走 fallback直到健康检查恢复。这种策略在真实线上环境中救了我们三次——一次是 DashScope 服务端证书过期一次是向量库连接池耗尽一次是用户上传了 200MB 的扫描版 PDF 导致 OCR 超时。反观 AgentScope Java它的设计起点完全不同。2.2 AgentScope Java 的设计哲学智能体即自治单元系统即协作协议AgentScope Java 不认为“AI 能力”是待注入的服务而认为“智能体Agent”是具备身份、目标、记忆、工具、通信能力的自治计算单元。它的核心抽象是Agent接口所有具体 agent 都必须实现step()方法该方法接收Message输入返回Message输出并可主动调用send()向其他 agent 发送消息。继续用上面的面试题处理场景AgentScope Java 的建模方式是ParserAgent职责单一只做文档解析。它不关心知识图谱怎么建也不管题目怎么生成。它收到DocumentUploadRequest消息后调用 Apache POI/Tika 解析返回ParsedTextContent消息KnowledgeMinerAgent监听ParsedTextContent启动异步 Embedding 流程完成后广播KnowledgeGraphBuilt消息QuestionGeneratorAgent订阅KnowledgeGraphBuilt根据消息中的difficultyLevel字段决定调用哪个 LLM 服务生成后发送QuestionsGeneratedReviewAdvisorAgent同时订阅QuestionsGenerated和UserHistorySnapshot来自外部数据库做关联分析输出ReviewSuggestionReportComposerAgent作为 Orchestrator收集所有上游 agent 的最终输出组装成报告。这种建模带来的结构性差异极为显著注意AgentScope Java 的Message不是简单 JSON而是一个带 Schema 的强类型对象。你必须定义Data类并标注MessageType例如MessageType(parsed-text-content) Data public class ParsedTextContent { private String documentId; private String rawText; private ListString pageImages; // 扫描件的 base64 图片列表 }这样框架能在运行时校验消息类型避免ClassCastException。我在早期没加注解结果 ParserAgent 发送了String类型消息QuestionGeneratorAgent 收到后直接cast失败日志里只显示Cannot cast to java.lang.Object排查了 3 小时才发现是消息类型未声明。通信不是 HTTP 调用而是消息总线Message BusAgentScope Java 默认使用内存版InMemoryMessageBus生产环境推荐替换为KafkaMessageBus或RocketMQMessageBus。这意味着所有 agent 之间解耦ParserAgent 不需要知道 KnowledgeMinerAgent 的 IP 和端口消息可持久化即使 KnowledgeMinerAgent 重启未消费的ParsedTextContent仍在 Kafka Topic 中可以轻松添加监控 agent订阅所有消息流做实时审计或异常检测。状态管理是显式契约而非隐式变量每个 agent 可以声明自己的State类框架自动序列化/反序列化。例如QuestionGeneratorAgent的状态Data StateType(question-generator-state) public class QuestionGeneratorState { private MapString, Integer difficultyStats new HashMap(); // 记录各难度生成题数 private long lastSuccessTime; private int consecutiveFailures; }这个状态在 agent 生命周期内持续存在重启后从 Redis 加载。相比 Spring AI 中散落在Component成员变量里的状态它更易测试、更易迁移、更易可视化。执行调度是 DAG 编排而非线性调用AgentScope Java 提供WorkflowDSL用 Java 代码定义 agent 间的依赖关系。我们的面试题流程定义如下Workflow workflow Workflow.builder() .addNode(parser, parserAgent) .addNode(miner, knowledgeMinerAgent) .addNode(generator, questionGeneratorAgent) .addNode(advisor, reviewAdvisorAgent) .addNode(composer, reportComposerAgent) .addEdge(parser, miner) // parser 输出 - miner 输入 .addEdge(miner, generator) .addEdge(miner, advisor) // miner 输出同时给 generator 和 advisor .addEdge(generator, composer) .addEdge(advisor, composer) .build();这个 DAG 在运行时被转换为有向无环图框架自动处理并行、汇聚、超时、重试。而 Spring AI 中你需要手动用CompletableFuture.allOf()或Async实现类似逻辑极易出错。总结一句话Spring AI 2.0 让你快速把 AI 能力“接进来”AgentScope Java 让你严谨地把 AI 协作“管起来”。前者胜在上手速度和生态融合后者赢在系统复杂度和长期可维护性。没有优劣只有适配场景。3. 核心环节实现详解从代码到部署的完整路径3.1 Spring AI 2.0 实现以最小侵入性接入 AI 能力我们从零开始搭建一个 Spring Boot 3.2 Spring AI 2.0 的工程。注意Spring AI 2.0 要求 JDK 17且必须使用 Spring Boot 3.2.x3.3.x 尚未完全兼容。3.1.1 依赖配置与 MCP 服务注册pom.xml关键依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-spring-boot-starter/artifactId version2.0.0-M3/version !-- 注意GA 版本尚未发布M3 是当前最稳定预发版 -- /dependency !-- DashScope SDK -- dependency groupIdcom.alibaba.dashscope/groupId artifactIddashscope-sdk/artifactId version1.12.0/version /dependency !-- 向量库客户端 -- dependency groupIdio.milvus/groupId artifactIdmilvus-sdk-java/artifactId version2.4.7/version /dependencyapplication.yml中配置 MCP 服务spring: ai: mcp: services: # Embedding 服务 embedding-service: url: https://dashscope.aliyuncs.com/api/v1/services/embeddings api-key: ${DASHSCOPE_API_KEY} headers: Content-Type: application/json properties: model: text-embedding-v3 # GLM-4 LLM 服务 llm-glm4: url: https://open.bigmodel.cn/api/paas/v4/chat/completions api-key: ${GLM_API_KEY} headers: Content-Type: application/json properties: model: glm-4 # Qwen2-72B LLM 服务Ollama 本地 llm-qwen2: url: http://localhost:11434/api/chat headers: Content-Type: application/json properties: model: qwen2:72b这里的关键细节是MCP 服务的url必须是完整的 API endpoint不能只写 host。我第一次配置时只写了url: https://dashscope.aliyuncs.com结果框架尝试拼接/api/v1/services/embeddings失败报404 Not Found。查看源码发现MCP 的url是直接作为RestTemplate的 base URL 使用的路径必须写全。3.1.2 ObservationHandler 的深度定制Spring AI 2.0 的ObservationHandler是可观测性的基石。我们创建一个自定义 handler不仅记录基础指标还要捕获业务上下文Component public class InterviewAgentObservationHandler implements ObservationHandlerObservation.Context { private final MeterRegistry meterRegistry; private final Logger logger LoggerFactory.getLogger(InterviewAgentObservationHandler.class); public InterviewAgentObservationHandler(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; } Override public void onStart(Observation.Context context) { // 记录请求开始时间、documentId从 MDC 中获取 String documentId MDC.get(documentId); if (documentId ! null) { context.put(documentId, documentId); } // 记录当前 agent 阶段 context.put(agentStage, context.getOrDefault(stage, unknown)); } Override public void onEnd(Observation.Context context) { // 计算耗时并打点 long durationMs context.getOrDefault(durationMs, 0L); String stage context.getOrDefault(stage, unknown).toString(); Timer.builder(ai.agent.stage.duration) .tag(stage, stage) .register(meterRegistry) .record(durationMs, TimeUnit.MILLISECONDS); // 记录 token 使用量需从 context 中提取Spring AI 2.0 将其放在 observationContext.attributes Long inputTokens (Long) context.get(inputTokens); Long outputTokens (Long) context.get(outputTokens); if (inputTokens ! null outputTokens ! null) { Counter.builder(ai.agent.tokens.total) .tag(stage, stage) .register(meterRegistry) .increment(inputTokens outputTokens); } // 业务日志成功/失败标记 if (context.getError() null) { logger.info(Agent stage {} completed for document {}, duration{}ms, stage, context.get(documentId), durationMs); } else { logger.error(Agent stage {} failed for document {}: {}, stage, context.get(documentId), context.getError().getMessage()); } } }这个 handler 的价值在于它把技术指标token 数、耗时和业务维度documentId、stage绑定了。当 Prometheus 抓取到ai_agent_stage_duration_seconds_count{stageknowledge-graph-builder}时运维可以立刻关联到具体哪个用户的哪份文档出了问题而不是面对一堆泛化的ai_client_call_duration_seconds指标干瞪眼。3.1.3 Prompt 模板与动态参数注入我们以KnowledgeGraphBuilder为例展示如何用 FreeMarker 模板实现安全、灵活的 Prompt 编排src/main/resources/prompts/knowledge-graph/system.ftl你是一名资深 Java 技术面试官擅长从技术文档中精准提取知识点及其层级关系。 请严格遵循以下规则 1. 知识点必须是 Java 生态内的具体技术名词如HashMap 线程安全性、Spring AOP 代理机制禁止泛化描述如Java 基础很好 2. 关系必须是属于、依赖于、用于实现、是...的子类等明确语义 3. 输出必须是标准 JSON 格式包含nodes和edges两个数组。src/main/resources/prompts/knowledge-graph/user.ftl以下是用户提交的技术面试题文档内容请据此构建知识图谱 ${rawText} 要求图谱应覆盖文档中出现的所有核心技术点特别关注 - Java 集合框架相关考点HashMap、ConcurrentHashMap、ArrayList 等 - JVM 内存模型与 GC 算法 - Spring Boot 自动配置原理 - 多线程与锁优化synchronized、ReentrantLock、CAS - Redis 缓存穿透/击穿/雪崩解决方案Java 代码中调用Service public class KnowledgeGraphBuilder { private final AiClient aiClient; public KnowledgeGraphBuilder(Qualifier(llm-glm4) AiClient aiClient) { this.aiClient aiClient; } public KnowledgeGraph build(String rawText, String documentId) { // 设置 MDC供 ObservationHandler 使用 MDC.put(documentId, documentId); MDC.put(stage, knowledge-graph-builder); // 构造模板数据 MapString, Object templateData new HashMap(); templateData.put(rawText, rawText.substring(0, Math.min(rawText.length(), 8000))); // 防止超长截断 // 创建 Message SystemMessage systemMessage new SystemMessage( FreeMarkerTemplateUtils.processTemplateIntoString( freeMarkerConfiguration.getTemplate(prompts/knowledge-graph/system.ftl), templateData ) ); UserMessage userMessage new UserMessage( FreeMarkerTemplateUtils.processTemplateIntoString( freeMarkerConfiguration.getTemplate(prompts/knowledge-graph/user.ftl), templateData ) ); // 执行调用 ChatResponse response aiClient.chat(new ChatRequest(List.of(systemMessage, userMessage))); String jsonResult response.getResult().getOutput().getContent(); // 解析 JSON 并返回 return JsonUtil.fromJson(jsonResult, KnowledgeGraph.class); } }这里的关键技巧是永远对rawText做长度截断。我们线上曾遇到用户上传 50 页 PDFTika 解析后文本超 120KB直接导致 GLM-4 接口返回413 Payload Too Large。在build()方法开头加一行rawText.substring(0, 8000)配合日志告警当截断比例 30% 时发 Slack 告警完美规避了这个问题。3.1.4 Fallback 与 Circuit Breaker 的实战配置application.yml中配置熔断器resilience4j.circuitbreaker: instances: knowledge-miner: failure-rate-threshold: 60 minimum-number-of-calls: 20 wait-duration-in-open-state: 60s permitted-number-of-calls-in-half-open-state: 5 automatic-transition-from-open-to-half-open-enabled: trueJava 代码中启用Service public class KnowledgeGraphBuilder { private final AiClient aiClient; private final CircuitBreaker circuitBreaker; public KnowledgeGraphBuilder( Qualifier(llm-glm4) AiClient aiClient, Qualifier(knowledge-miner) CircuitBreaker circuitBreaker) { this.aiClient aiClient; this.circuitBreaker circuitBreaker; } public KnowledgeGraph build(String rawText, String documentId) { return circuitBreaker.executeSupplier(() - { // 正常调用逻辑 return doBuild(rawText, documentId); }); } private KnowledgeGraph doBuild(String rawText, String documentId) { // ... 同上省略 } }实测效果当 DashScope 服务连续 20 次返回503 Service Unavailable时熔断器在第 21 次请求时直接抛出CallNotPermittedException触发 fallback 逻辑。我们 fallback 到本地bge-small-zh-v1.5模型虽然生成的图谱精度下降约 15%但保证了 99.9% 的请求成功率用户无感知。3.2 AgentScope Java 实现以系统思维构建多智能体协作AgentScope Java 的工程结构与 Spring Boot 截然不同。它不依赖 Spring 容器而是以AgentRuntime为核心所有 agent 通过AgentBuilder注册。3.2.1 工程初始化与 Runtime 配置pom.xml依赖dependency groupIdcn.edu.pku.agentscope/groupId artifactIdagentscope-java/artifactId version2.0.0/version /dependency !-- Kafka 客户端 -- dependency groupIdorg.apache.kafka/groupId artifactIdkafka-clients/artifactId version3.6.1/version /dependency !-- Redis 客户端 -- dependency groupIdredis.clients/groupId artifactIdjedis/artifactId version4.4.3/version /dependency初始化AgentRuntimepublic class InterviewSystem { public static void main(String[] args) { // 配置 Kafka 消息总线 Properties kafkaProps new Properties(); kafkaProps.put(bootstrap.servers, kafka:9092); kafkaProps.put(group.id, interview-system); kafkaProps.put(enable.auto.commit, true); // 配置 Redis 状态存储 JedisPool jedisPool new JedisPool(redis://redis:6379); // 构建 Runtime AgentRuntime runtime AgentRuntime.builder() .messageBus(new KafkaMessageBus(kafkaProps)) .stateManager(new RedisStateManager(jedisPool)) .build(); // 注册所有 Agent registerAgents(runtime); // 启动 Workflow Workflow workflow buildWorkflow(); runtime.start(workflow); } private static void registerAgents(AgentRuntime runtime) { // ParserAgent runtime.registerAgent(parser, AgentBuilder.builder() .type(ParserAgent.class) .config(Map.of(tika-server-url, http://tika:9998)) .build()); // KnowledgeMinerAgent runtime.registerAgent(miner, AgentBuilder.builder() .type(KnowledgeMinerAgent.class) .config(Map.of(embedding-model, dashscope/text-embedding-v3)) .build()); // ... 其他 Agent 注册 } }这里的关键点是AgentScope Java 的AgentRuntime是单例、全局、长生命周期的。它不像 Spring Bean 那样按需创建而是启动时一次性加载所有 agent然后持续监听消息。因此runtime.start(workflow)后整个系统就进入了“待命”状态等待第一个DocumentUploadRequest消息到来。3.2.2 Agent 的标准实现与消息契约以ParserAgent为例它必须实现Agent接口MessageType(document-upload-request) Data public class DocumentUploadRequest { private String documentId; private byte[] fileBytes; private String fileName; } MessageType(parsed-text-content) Data public class ParsedTextContent { private String documentId; private String rawText; private ListString pageImages; } public class ParserAgent implements Agent { private final TikaClient tikaClient; public ParserAgent(MapString, Object config) { this.tikaClient new TikaClient((String) config.get(tika-server-url)); } Override public Message step(Message message) { DocumentUploadRequest request (DocumentUploadRequest) message.getContent(); // 调用 Tika 解析 ParseResult result tikaClient.parse(request.getFileBytes(), request.getFileName()); // 构造响应消息 ParsedTextContent response new ParsedTextContent(); response.setDocumentId(request.getDocumentId()); response.setRawText(result.getText()); response.setPageImages(result.getPageImages()); // base64 编码的图片 return Message.builder() .type(parsed-text-content) .content(response) .build(); } }注意MessageType注解它告诉框架这个类对应的消息类型。ParserAgent.step()方法的输入message类型是Message但message.getContent()的实际类型取决于发送方。框架会根据message.getType()自动反序列化为对应的MessageType类。如果类型不匹配会抛出MessageDeserializationException而不是ClassCastException错误信息更友好。3.2.3 Workflow 的 DAG 编排与容错机制buildWorkflow()方法定义整个流程private static Workflow buildWorkflow() { return Workflow.builder() .addNode(parser, parser) // 第二个参数是 agent name .addNode(miner, miner) .addNode(generator, generator) .addNode(advisor, advisor) .addNode(composer, composer) .addEdge(parser, miner) .addEdge(miner, generator) .addEdge(miner, advisor) .addEdge(generator, composer) .addEdge(advisor, composer) // 设置节点超时miner 节点最长执行 120 秒 .setNodeTimeout(miner, Duration.ofSeconds(120)) // 设置失败重试generator 节点失败时重试 2 次 .setNodeRetry(generator, 2) // 设置失败后动作advisor 节点失败时跳过它继续执行 composer .setNodeFailureAction(advisor, FailureAction.SKIP) .build(); }AgentScope Java 的 Workflow DSL 提供了远超CompletableFuture的控制粒度超时Timeout不是整个流程超时而是每个节点独立超时。miner节点若 120 秒内未返回KnowledgeGraphBuilt消息框架自动标记该节点失败并触发重试或失败动作重试Retry重试次数、间隔、退避策略均可配置。generator节点失败后框架会自动重新发送KnowledgeGraphBuilt消息给它无需业务代码干预失败动作FailureActionSKIP表示跳过该节点下游节点用默认值或空数据继续ABORT表示终止整个 workflowFALLBACK表示调用备用 agent。我们为advisor设置SKIP因为复习建议是锦上添花不是核心功能。3.2.4 状态持久化与故障恢复KnowledgeMinerAgent的状态类StateType(knowledge-miner-state) Data public class KnowledgeMinerState { private String lastDocumentId; private long lastProcessedTime; private int totalNodesBuilt; private int totalEdgesBuilt; private MapString, Integer errorCountByModel new HashMap(); }在step()方法中状态自动保存public class KnowledgeMinerAgent implements Agent { private final StateManager stateManager; public KnowledgeMinerAgent(StateManager stateManager, MapString, Object config) { this.stateManager stateManager; } Override public Message step(Message message) { ParsedTextContent content (ParsedTextContent) message.getContent(); // 加载状态 KnowledgeMinerState state stateManager.loadState( knowledge-miner-state, content.getDocumentId() ); // 执行 Embedding ListEmbeddingResult embeddings callEmbeddingApi(content.getRawText()); // 更新状态 state.setLastDocumentId(content.getDocumentId()); state.setLastProcessedTime(System.currentTimeMillis()); state.setTotalNodesBuilt(state.getTotalNodesBuilt() embeddings.size()); // 保存状态 stateManager.saveState(knowledge-miner-state, content.getDocumentId(), state); // 返回消息 return Message.builder() .type(knowledge-graph-built) .content(new KnowledgeGraphBuilt(...)) .build(); } }这个机制的价值在于当KnowledgeMinerAgent进程崩溃重启后它能从 Redis 中加载上次处理的documentId和统计信息继续工作不会丢失进度。而 Spring AI 中如果KnowledgeGraphBuilder服务挂了正在处理的请求就彻底丢失只能靠上游重试。4. 实操对比与避坑指南从开发到运维的真实经验4.1 代码量与结构复杂度的量化对比我们以“技术面试题智能处理 Agent”的核心逻辑不含测试、配置、文档为基准统计两套实现的代码行数LOC模块Spring AI 2.0 (LOC)AgentScope Java (LOC)差异说明主入口与配置85120AgentScope 需要显式构建AgentRuntime和WorkflowSpring AI 只需SpringBootApplication文档解析142187AgentScope 的ParserAgent需要定义MessageType类和step()方法Spring AI 的ParserService直接调用TikaClient知识图谱构建215342AgentScope 的KnowledgeMinerAgent包含状态管理、消息收发、错误计数Spring AI 的KnowledgeGraphBuilder专注调用 LLM 和向量库题目生成178295AgentScope 的QuestionGeneratorAgent需处理多种KnowledgeGraphBuilt子类型消息Spring AI 的QuestionGeneratorService直接接收KnowledgeGraph对象复习建议132228AgentScope 的ReviewAdvisorAgent需订阅两个消息源并做关联查询Spring AI 的ReviewAdvisorService用Transactional查询数据库即可报告组装95163AgentScope 的ReportComposerAgent需等待多个消息汇聚Spring AI 的

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价