Spring AI Alibaba 1.x 系列【42】多智能体 - 顺序执行、并行执行:用 TaoToken 统一 Key 跑通 SequentialAgent 编排

发布时间:2026/10/9 23:40:19
Spring AI Alibaba 1.x 系列【42】多智能体 - 顺序执行、并行执行:用 TaoToken 统一 Key 跑通 SequentialAgent 编排
1. 多智能体编排里最容易被忽略的坑凭证散落Spring AI Alibaba 1.x 的SequentialAgent和ParallelAgent解决的是「多个 Agent 怎么按顺序或并行跑」的问题但真正落地时很多人卡住的不是编排逻辑而是每个子 Agent 各自持有不同的模型凭证。你写四个ReactAgent如果每个都单独配dashscopeChatModel一旦要换 endpoint 或换 Key就得改四处甚至更多。多智能体场景下凭证散落带来的维护成本比单 Agent 高出一个量级。这篇要解决的就是这件事用SequentialAgent为主线把顺序执行和并行执行的差异讲清楚同时把模型调用统一收敛到 TaoToken 的 endpoint 和 Key 上。TaoToken 是一个模型调用聚合入口适合多智能体这种「多个 Agent 共享同一套模型凭证」的场景你只需要在配置层写一次 Base URL 和 Key所有子 Agent 复用同一个ChatModel实例即可。适合谁看已经在用 Spring AI Alibaba 写单 Agent、准备上多智能体编排的 Java 开发者或者你已经在用SequentialAgent但每次换 Key 都要全局搜索替换。读完你能拿到可复制的依赖坐标、SequentialAgent与并行分支的配置片段、统一 Key 的 settings 写法以及一次顺序/并行对比运行的验证动作。先说结论顺序执行适合有严格依赖链的审核、校验、汇总类流程并行执行适合多个独立子任务同时跑再合并结果的场景。两者的模型调用都可以指向同一个 TaoToken endpoint不需要为每个 Agent 单独配 Key。2. 前置准备依赖坐标与 TaoToken 统一 Key在动手写SequentialAgent之前先把依赖和模型凭证这两件事定下来。依赖坐标决定了你能用哪些 Agent 类型统一 Key 决定了后面所有子 Agent 能不能共享同一个ChatModel。2.1 Maven 依赖坐标Spring AI Alibaba 1.x 的多智能体能力在spring-ai-alibaba-agent-framework里配合 DashScope 的 starter 使用。下面是我实测可用的依赖片段直接贴到pom.xmldependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId version1.0.0-M6.1/version /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-agent-framework/artifactId version1.0.0-M6.1/version /dependency版本号以你本地仓库实际拉到的为准M6.1 这个版本里SequentialAgent、ParallelAgent、MergeStrategy都已经可用。如果你用的是 Gradle把 group/artifact/version 对应换成implementation即可。2.2 统一 Key 的 settings 写法关键点在这里不要让每个ReactAgent各自去 new 一个ChatModel。正确做法是在配置类里创建一个ChatModelBean所有子 Agent 注入同一个实例。TaoToken 的 endpoint 和 Key 只在这个 Bean 里出现一次。# application.yml spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: claude-sonnet-4-20250514然后在 Java 配置类里声明Configuration public class ModelConfig { Bean public ChatModel chatModel(OpenAiChatProperties properties) { return OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() .baseUrl(properties.getBaseUrl()) .apiKey(properties.getApiKey()) .build()) .defaultOptions(OpenAiChatOptions.builder() .model(properties.getChat().getOptions().getModel()) .build()) .build(); } }这样写的好处是base-url和api-key只在application.yml里出现一次四个子 Agent 全部注入同一个chatModel。换 Key 时只改环境变量TAOTOKEN_API_KEY不用动任何 Agent 代码。如果你还没有 Key可以在 TaoToken 的 API Keys 页面创建一个然后在接入文档里确认 endpoint 路径。注意base-url写https://taotoken.net/api不要带多余的路径后缀OpenAI 兼容层会自动拼接/v1/chat/completions。2.3 为什么不在每个 Agent 里单独配我试过在每个ReactAgent.builder()里单独.model(...)一开始觉得灵活后来发现三个问题一是换模型要改多处二是并行执行时多个 Agent 同时初始化各自的 HTTP 客户端连接池利用率低三是日志里分不清哪个请求来自哪个 Agent。统一成一个ChatModelBean 之后这三个问题都消失了。多智能体场景下凭证收敛到一处是刚需不是优化项。3. 可复制配置SequentialAgent 与并行分支这一节给出两套完整配置一套是顺序执行的SequentialAgent一套是并行执行的ParallelAgent。两套都复用第 2 节的chatModelBean你只需要把chatModel注入进来。3.1 顺序执行四步合同审核流程场景是合同审核必须按 1→2→3→4 的顺序走前一步的输出是后一步的输入。用outputKey把每个 Agent 的结果存进状态下游用{key}占位符引用。Component public class SequentialAuditFlow { private final ChatModel chatModel; public SequentialAuditFlow(ChatModel chatModel) { this.chatModel chatModel; } public SequentialAgent build() { ReactAgent basicCheck ReactAgent.builder() .model(chatModel) .name(contract_basic_check) .description(校验合同必填字段、格式完整性) .outputKey(basic_check_result) .returnReasoningContents(true) .instruction( 你是企业行政审核专员仅执行「合同基础格式校验」 1. 检查甲方、乙方、有效期、签字位置是否完整 2. 仅输出「缺失/错误」清单无问题则输出「基础校验通过」 ) .build(); ReactAgent businessRisk ReactAgent.builder() .model(chatModel) .name(contract_business_check) .description(审核付款、账期、违约金等商务条款) .outputKey(business_check_result) .returnReasoningContents(true) .instruction( 你是企业商务经理基于基础校验结果执行「商务条款审核」 上游基础校验结果{basic_check_result} 审核范围付款方式、结算周期、违约金、报价合理性 输出要求标记风险条款 风险等级 修改建议 ) .build(); ReactAgent legalCheck ReactAgent.builder() .model(chatModel) .name(contract_legal_check) .description(法务合规审核排查违法、霸王条款) .outputKey(legal_check_result) .returnReasoningContents(true) .instruction( 你是企业法务基于前序结果执行「合规性审核」 基础校验{basic_check_result} 商务审核{business_check_result} 审核范围无效条款、霸王条款、违法表述 输出要求列出违规条款 法律风险 强制修改建议 ) .build(); ReactAgent reportSummary ReactAgent.builder() .model(chatModel) .name(contract_report_summary) .description(整合全流程审核结果生成最终报告) .outputKey(final_audit_report) .returnReasoningContents(true) .instruction( 你是合同审核汇总专员整合全部审核结果 基础校验{basic_check_result} 商务校验{business_check_result} 合规校验{legal_check_result} 必须输出固定结构 1. 审核结论通过/整改/驳回 2. 风险等级低/中/高 3. 问题汇总精简罗列所有问题 4. 处理意见明确修改要求 ) .build(); return SequentialAgent.builder() .name(contract_sequential_audit) .description(基于顺序智能体完成合同四步标准化审核) .subAgents(List.of(basicCheck, businessRisk, legalCheck, reportSummary)) .build(); } }这里List.of(...)的顺序就是实际执行顺序SequentialAgent内部会按列表顺序线性串联前一个 Agent 的输出通过outputKey写入状态后一个 Agent 用{basic_check_result}这种占位符读取。returnReasoningContents(true)让中间推理过程也保留在消息历史里方便排查。3.2 并行执行多创作任务同时跑并行场景是主题创作三个子任务互不依赖同时执行后合并结果。ParallelAgent用mergeOutputKey指定合并结果的 keymergeStrategy决定怎么合并。Component public class ParallelCreativeFlow { private final ChatModel chatModel; public ParallelCreativeFlow(ChatModel chatModel) { this.chatModel chatModel; } public ParallelAgent build() { ReactAgent proseWriter ReactAgent.builder() .model(chatModel) .name(prose_writer_agent) .description(专门写散文的 AI 助手) .instruction(你是散文作家用户主题{input}创作一篇 100 字左右的散文。) .outputKey(prose_result) .build(); ReactAgent poemWriter ReactAgent.builder() .model(chatModel) .name(poem_writer_agent) .description(专门写现代诗的 AI 助手) .instruction(你是现代诗人用户主题{input}创作一首现代诗。) .outputKey(poem_result) .build(); ReactAgent summaryAgent ReactAgent.builder() .model(chatModel) .name(summary_agent) .description(专门做内容总结的 AI 助手) .instruction(你是内容分析师用户主题{input}对这个主题做简要总结。) .outputKey(summary_result) .build(); return ParallelAgent.builder() .name(parallel_creative_agent) .description(并行执行多个创作任务) .mergeOutputKey(merged_results) .subAgents(List.of(proseWriter, poemWriter, summaryAgent)) .mergeStrategy(new ParallelAgent.DefaultMergeStrategy()) .build(); } }ParallelAgent的subAgents顺序不影响执行三个 Agent 同时拿到{input}并各自处理。合并策略有三种可选ConcatenationMergeStrategy拼接字符串适合文本汇总ListMergeStrategy输出不可变 List适合二次处理DefaultMergeStrategy输出 HashMap保留键值映射。上面用的是默认策略合并结果从merged_results里取。3.3 顺序与并行的选型对照维度SequentialAgentParallelAgent执行方式按 subAgents 列表顺序线性执行所有子 Agent 同时执行数据依赖下游可读上游 outputKey子 Agent 之间不共享中间结果结果获取从 messages 或各 outputKey 取从 mergeOutputKey 取合并结果适用场景审核、校验、汇总等有依赖链的流程多方案生成、多角度分析等独立任务耗时特征累加N 个 Agent 约 N 倍单次耗时取最慢的那个接近单次耗时选型判断很简单如果后一个 Agent 需要读前一个 Agent 的输出用顺序如果几个 Agent 各自独立、最后只要合并结果用并行。两者可以嵌套比如一个SequentialAgent的某个子 Agent 本身是ParallelAgent但嵌套层数建议不超过两层否则状态传递会变得难排查。4. 验证请求顺序与并行对比运行配置写完之后跑一次对比验证确认统一 Key 生效、两种编排都能正常返回。4.1 顺序执行验证SpringBootTest class SequentialAuditFlowTest { Autowired private SequentialAuditFlow sequentialAuditFlow; Test void testSequentialAudit() { SequentialAgent agent sequentialAuditFlow.build(); OptionalOverAllState result agent.invoke( 甲方XX科技 乙方 合作有效期无固定期限 付款条款项目完工后随意付款无违约处罚 服务费用暂定后期口头协商 权责甲方拥有无条件单方解约权乙方不得提出异议 ); result.ifPresent(state - { ListMessage messages (ListMessage) state.value(messages).orElse(List.of()); System.out.println(消息数量: messages.size()); state.value(final_audit_report).ifPresent(r - System.out.println(最终报告: r)); }); } }预期输出messages里会包含四个 Agent 的完整响应final_audit_report里是结构化结论风险等级应该是「高」因为合同里出现了「无条件单方解约权」这种明显不对等的条款。如果final_audit_report为空说明outputKey没写对或者占位符引用错了。4.2 并行执行验证SpringBootTest class ParallelCreativeFlowTest { Autowired private ParallelCreativeFlow parallelCreativeFlow; Test void testParallelCreative() { ParallelAgent agent parallelCreativeFlow.build(); OptionalOverAllState result agent.invoke(AI); result.ifPresent(state - { state.value(merged_results).ifPresent(r - System.out.println(合并结果: r)); }); } }预期输出merged_results里包含三个子任务的结果散文、现代诗、总结各一份。注意messages里只有输入消息输出要从merged_results取这是ParallelAgent和SequentialAgent在结果获取上的关键差异。4.3 统一 Key 生效的确认方式跑完之后去 TaoToken 的 console 看调用记录应该能看到四个顺序或三个并行请求都来自同一个 Keyendpoint 都是https://taotoken.net/api。如果看到多个不同 Key 的调用说明某个 Agent 没有复用chatModelBean回去检查是不是在 builder 里又单独.model(...)了。5. 本篇常见错排查多智能体编排 统一 Key 的组合报错集中在几个固定位置。下面按真实报错对照排查。5.1 401 Unauthorized最常见的是 Key 没读到。检查application.yml里的api-key: ${TAOTOKEN_API_KEY}确认环境变量真的注入了。如果你在 IDE 里跑环境变量可能没配到 Run Configuration 里。另一个可能是base-url写成了https://taotoken.net/api/v1多了一层/v1导致拼接后路径变成/api/v1/v1/chat/completions。正确写法是https://taotoken.net/api。5.2 local proxy failed / connection refused这个报错通常出现在你本地有代理设置但代理没启动。Spring AI 的 HTTP 客户端会读取系统代理如果你之前配过http_proxy环境变量现在代理关了就会连不上。排查方式在启动参数里加-Dhttp.proxyHost和-Dhttp.proxyPort清空或者直接检查环境变量里有没有残留的代理配置。TaoToken 的 endpoint 是直连的不需要额外代理。5.3 reading choices 相关报错Error reading choices或choices is null一般说明返回体不是标准的 OpenAI 格式。可能原因base-url指向了错误的路径或者模型名写错了。确认model字段是你账号下可用的模型 ID不要写一个不存在的名字。如果返回体里带error字段先把完整响应打出来看通常是模型名或权限问题。5.4 OAuth / token 过期类报错如果你用的是需要 OAuth 的模型接入方式报错会提示 token 无效或过期。TaoToken 的 API Key 是长期有效的不存在 OAuth 刷新问题。如果你看到 OAuth 相关报错说明请求根本没走到 TaoToken检查base-url是不是被其他配置覆盖了。Spring AI 里多个 starter 可能同时注册ChatModel用Primary标注你的 Bean 避免冲突。5.5 outputKey 取不到值顺序执行里final_audit_report为空先确认outputKey(final_audit_report)写在了最后一个 Agent 上再确认state.value(final_audit_report)的 key 拼写一致。占位符{basic_check_result}引用时上游 Agent 的outputKey必须完全匹配大小写敏感。5.6 并行结果合并异常ParallelAgent的mergeOutputKey如果和某个子 Agent 的outputKey重名合并结果会被覆盖。确保mergeOutputKey用一个独立的名字比如merged_results。另外DefaultMergeStrategy返回的是 HashMap如果你期望的是字符串拼接换成ConcatenationMergeStrategy。6. 把 Key 收敛到一处编排才跑得稳多智能体编排的复杂度不在 Agent 数量而在状态传递和凭证管理。SequentialAgent和ParallelAgent把执行顺序这件事抽象掉了但模型调用这一层如果还是每个 Agent 各自为政换一次 Key 就是一次全局手术。把ChatModel收敛成一个 Beanendpoint 和 Key 只写一次后面加多少个 Agent 都不用动配置。如果你准备长期跑多智能体流程建议把 Coding Plan 用起来配合统一的 endpoint 做长任务编排会更顺。需要确认模型可用性的话模型对话页面可以直接试。Key 的管理在 API Keys接入细节看接入文档。