1. 从一次真实的踩坑说起为什么单智能体搞不定旅游规划你可能也遇到过这种场景想用一个大模型一次性生成北京三日游的完整方案结果它要么把天气编得离谱要么景点和行程对不上号改一处又崩另一处。我试过把提示词写到八百字模型依然会在下雨天安排户外徒步这种低级错误上翻车。根本原因不在于模型不够强而在于一个智能体同时扛了太多职责——它既要查天气、又要选景点、还要排日程注意力被稀释任何一环出错都会污染整条链路。多智能体系统Multi-Agent System解决的正是这个问题。它的思路很朴素把复杂任务拆成若干子任务每个子任务交给一个专家智能体专家之间通过结构化的数据传递协作最后由汇总智能体整合输出。就像一家旅行社有专门查天气的、专门做景点调研的、专门排行程的各司其职再拼成完整方案。Google 开源的 ADK for JavaAgent Development Kit把这套协作模式做成了可编排的组件LlmAgent定义单个智能体SequentialAgent和ParallelAgent负责编排执行顺序outputKey机制让上游智能体的输出能被下游用占位符引用。对 Java 开发者来说这意味着不用切换到 Python 生态就能在企业级项目里落地多智能体。这篇内容适合三类人一是已经会写 Java、想入门 Agent 开发的后端工程师二是想给现有系统加 AI 能力的架构师三是被单智能体幻觉折磨过、想找结构化方案的同学。下面我会给出可复制的项目骨架、三个智能体的完整配置、运行验证步骤以及如何通过 TaoToken 统一 Key/API 通道接入模型让你跑通一次完整的协作规划任务。2. TaoToken 前置把模型接入这件事先理顺在写智能体代码之前有个容易被忽略但很关键的前置问题模型通道。ADK for Java 默认走 Google AI Studio 的 API Key但在实际项目里你往往需要统一管理多个模型的调用、控制成本、或者让团队共用一套凭证。这时候用 TaoToken 做统一入口会省很多事。TaoToken 是一个模型 API 聚合通道官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它提供 OpenAI 兼容的接口格式API 端点是 https://taotoken.net/api 。对 ADK 项目来说你只需要把 base URL 和 Key 配好智能体里的模型调用就会走这条通道不用改业务代码。具体操作分三步。第一步登录控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在API Keys页面生成一个 Key 并复制保存。第二步如果你不确定该用哪个模型可以先去模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 试跑一下确认模型能正常响应再写进代码。第三步把 Key 和 base URL 写进项目的配置文件后面智能体初始化时会读取。注意API Key 属于敏感凭证不要硬编码进 Java 源码提交到仓库。建议用环境变量或application.properties配合.gitignore管理。如果你后续要做长期编码或 Agent 开发可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它针对高频调用场景做了额度优化。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数问题可以对照查。3. 可复制配置ADK 项目骨架与三个智能体3.1 Maven 依赖与项目结构先建一个标准 Maven 项目pom.xml里加入 ADK 和 Jacksondependencies dependency groupIdcom.google.adk/groupId artifactIdadk-java/artifactId version0.3.0/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.15.2/version /dependency /dependencies项目结构建议这样组织职责清晰后面加智能体不会乱travel-planner-multiagent/ ├── pom.xml ├── src/main/java/com/example/travel/ │ ├── TravelPlannerApplication.java │ ├── agents/ │ │ ├── WeatherAgent.java │ │ ├── AttractionAgent.java │ │ └── ItineraryAgent.java │ └── tools/ │ └── WeatherTool.java └── src/main/resources/application.propertiesapplication.properties里配置 TaoToken 通道taotoken.api.key你的_API_KEY taotoken.api.basehttps://taotoken.net/api taotoken.modelgemini-2.0-flash3.2 工具类WeatherTool智能体要调用外部能力得先有工具。这里用一个模拟天气查询的工具实际项目里替换成真实天气 API 即可package com.example.travel.tools; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.Random; public class WeatherTool { private static final ObjectMapper MAPPER new ObjectMapper(); private static final Random RANDOM new Random(); public JsonNode getWeather(String city) { String[] conditions {晴朗, 多云, 小雨, 大雨, 阴天}; String condition conditions[RANDOM.nextInt(conditions.length)]; int temperature 5 RANDOM.nextInt(30); ObjectNode result MAPPER.createObjectNode(); result.put(city, city); result.put(condition, condition); result.put(temperature, temperature); return result; } public String getWeatherAdvice(JsonNode weather) { String condition weather.get(condition).asText(); if (condition.contains(雨)) { return 建议携带雨具选择室内活动; } else if (condition.equals(晴朗)) { return 适合户外活动注意防晒; } return 天气适宜可正常出行; } }3.3 三个智能体的配置天气智能体负责查天气并输出建议关键是outputKey(weatherInfo)这个键会被下游引用package com.example.travel.agents; import com.example.travel.tools.WeatherTool; import com.google.adk.agents.LlmAgent; public class WeatherAgent { public static LlmAgent create() { WeatherTool tool new WeatherTool(); return LlmAgent.builder() .name(weather-agent) .description(查询目的地天气并提供出行建议) .instruction( 你是一个专业的天气查询助手。 根据用户提供的城市名称使用WeatherTool查询天气 返回包含温度、天气状况和出行建议的格式化报告。 ) .model(gemini-2.0-flash) .tools(tool) .outputKey(weatherInfo) .build(); } }景点智能体通过{weatherInfo}占位符拿到天气结果再结合用户偏好推荐package com.example.travel.agents; import com.google.adk.agents.LlmAgent; public class AttractionAgent { public static LlmAgent create() { return LlmAgent.builder() .name(attraction-agent) .description(根据天气和用户偏好推荐景点) .instruction( 你是一个景点推荐专家。 根据天气信息 {weatherInfo} 和用户偏好如历史文化、美食等 推荐3-5个适合的景点包含简介和建议游览时间。 ) .model(gemini-2.0-flash) .outputKey(attractions) .build(); } }行程智能体整合前两者的输出生成每日行程package com.example.travel.agents; import com.google.adk.agents.LlmAgent; public class ItineraryAgent { public static LlmAgent create() { return LlmAgent.builder() .name(itinerary-agent) .description(整合天气和景点信息生成完整行程) .instruction( 你是一个行程规划师。 根据天气信息 {weatherInfo} 和景点推荐 {attractions} 结合用户天数要求生成详细的每日行程包含餐饮和交通建议。 ) .model(gemini-2.0-flash) .outputKey(itinerary) .build(); } }3.4 主类并行 顺序混合编排这是整个系统的核心。天气和景点两个智能体没有依赖关系可以并行跑行程智能体依赖前两者的结果必须等它们完成后再执行。ADK 用ParallelAgent和SequentialAgent嵌套就能表达这个流程package com.example.travel; import com.example.travel.agents.AttractionAgent; import com.example.travel.agents.ItineraryAgent; import com.example.travel.agents.WeatherAgent; import com.google.adk.agents.LlmAgent; import com.google.adk.agents.ParallelAgent; import com.google.adk.agents.SequentialAgent; import com.google.adk.runners.FlowRunner; import com.google.adk.sessions.InMemorySessionStore; public class TravelPlannerApplication { public static void main(String[] args) { LlmAgent weatherAgent WeatherAgent.create(); LlmAgent attractionAgent AttractionAgent.create(); LlmAgent itineraryAgent ItineraryAgent.create(); ParallelAgent researchAgent ParallelAgent.builder() .name(research-agent) .description(并行收集天气和景点信息) .subAgents(weatherAgent, attractionAgent) .build(); SequentialAgent travelPlanner SequentialAgent.builder() .name(travel-planner) .description(旅游规划师) .subAgents(researchAgent, itineraryAgent) .build(); FlowRunner runner FlowRunner.builder() .sessionStore(new InMemorySessionStore()) .build(); String userQuery 我想去北京旅游3天喜欢历史文化和美食帮我规划一下; System.out.println(用户输入: userQuery); String result runner.run(travelPlanner, userQuery); System.out.println(最终行程:\n result); } }4. 验证请求跑通一次完整的协作规划代码写完后直接运行TravelPlannerApplication的main方法。如果 TaoToken 通道配置正确你会看到控制台先打印用户输入然后经过几秒的模型调用输出类似下面的行程用户输入: 我想去北京旅游3天喜欢历史文化和美食帮我规划一下 最终行程: 【第一天】 上午参观故宫博物院历史文化的精髓建议3-4小时 中午在故宫附近的四季民福烤鸭店享用北京烤鸭 下午游览景山公园俯瞰故宫全景 晚上逛王府井小吃街品尝北京特色小吃 【第二天】 上午前往颐和园皇家园林建议3-4小时 中午在颐和园附近品尝老北京炸酱面 下午参观圆明园遗址公园 晚上观看老舍茶馆的京剧表演 【第三天】 上午游览天坛公园了解古代祭天文化 中午在前门大街品尝爆肚、卤煮等小吃 下午逛大栅栏商业街购买伴手礼验证成功的标志有三个一是天气信息被景点推荐正确引用比如天气是小雨景点推荐里应该出现室内场馆二是景点列表和行程里的地点能对应上三是行程天数符合用户要求的 3 天。如果这三点都满足说明outputKey的传递链路是通的。想单独验证模型通道是否正常可以先去模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 发一条测试消息确认返回正常再排查代码。如果要做更复杂的编码类 AgentCoding Plan 的额度更适合高频场景。5. 本篇常见错排查报错一outputKey引用的占位符为空。现象是行程智能体拿到的{weatherInfo}是空字符串。原因通常是上游智能体的outputKey名字和下游instruction里的占位符拼写不一致比如一个写weatherInfo、另一个写weather_info。排查方法把三个智能体的outputKey和占位符列出来逐一比对大小写敏感。报错二并行智能体输出互相覆盖。如果两个并行智能体用了同一个outputKey后完成的会覆盖先完成的。解决方法是给每个智能体分配唯一的 key比如weatherInfo和attractions分开。报错三模型调用返回 401 或 403。这是 TaoToken 通道配置问题。检查application.properties里的 Key 是否复制完整、base URL 是否写成https://taotoken.net/api注意不要多加路径。如果 Key 没问题去控制台 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 确认 Key 状态是否正常、额度是否充足。报错四FlowRunner报 session 相关异常。多半是InMemorySessionStore没有正确初始化或者多次运行共用了同一个 store 导致状态污染。每次运行前新建一个 store 实例即可。报错五行程智能体忽略了天气建议。这不是代码错误而是提示词问题。在instruction里明确要求如果天气为雨行程必须包含室内活动模型会更严格地遵守约束。6. 继续深入从 Demo 到可用系统跑通这个 Demo 后你可以沿着几个方向扩展。一是把WeatherTool换成真实天气 API让数据不再是随机的。二是引入LoopAgent让行程智能体在检测到景点距离过远时自动重新规划。三是用 Sub-Agents 模式让主智能体根据用户问题动态决定调用哪些子智能体而不是固定流程。如果你打算把这套东西接入现有 Java 企业应用建议先把模型通道统一到 TaoToken这样后续换模型、加额度、做监控都不用改业务代码。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 有完整的参数说明Claude Code 相关的接入示例在 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 也能找到参考。多智能体的价值不在于智能体数量多而在于职责边界清晰、数据流转可控。把这三个智能体的协作跑顺了你再去拆解更复杂的业务场景思路会清晰很多。
