1. 项目概述为什么我们需要一个“代码优先”的AI智能体开发套件如果你和我一样在过去一两年里尝试过用各种“低代码”或“可视化”平台来构建AI智能体大概率会经历一个从兴奋到沮丧的过程。一开始拖拽几个模块、配置几个API端点一个能对话的机器人就诞生了感觉非常酷。但当你试图让它处理稍微复杂一点的业务逻辑比如根据用户查询去调用内部CRM系统、处理数据后再调用另一个分析服务最后生成一份结构化的报告时你会发现事情变得棘手起来。流程编排变得笨重调试像在猜谜想把智能体集成到现有的Java后端服务里更是难上加难。你开始怀念写代码的那种清晰、可控和强大的表达能力。这就是Google开源的Agent Development Kit for JavaADK-Java切入的精准痛点。它不是一个试图用界面隐藏一切的黑盒平台而是一个彻头彻尾的“代码优先”工具包。它的核心哲学是将智能体的行为定义、工具调用和流程编排完全交还给开发者熟悉的编程语言和环境。对于Java开发者而言这意味着你可以用你熟悉的Spring Boot、Maven/Gradle、单元测试、IDE调试器来构建和运维复杂的AI智能体系统。ADK-Java提供的不是束缚而是一套强大、灵活的基础构件让你能够像开发普通后端服务一样开发“智能”的服务。简单来说ADK-Java适合这样的你你需要构建的不仅仅是简单的聊天机器人而是能够深度集成Google Cloud服务或其他任何服务、拥有复杂决策逻辑、可能由多个智能体协作完成任务的生产级AI应用。你追求对流程的绝对控制力、代码的可测试性、系统的可维护性以及平滑融入现有技术栈的能力。如果你受够了“魔法黑箱”渴望用工程化的手段驾驭AI能力那么这个工具包值得你深入探索。2. 核心设计理念与架构解析2.1 “代码优先” vs. “配置/界面优先”理念的根本差异在深入ADK-Java的具体用法前我们必须理解其“代码优先”设计理念带来的根本性优势。这与市面上许多通过YAML文件或Web界面配置智能体的方案形成了鲜明对比。“配置/界面优先”方案的典型局限表达能力有限复杂的条件判断、循环、状态管理在图形化或声明式配置中很难优雅实现往往需要绕弯子或根本无法实现。调试困难当智能体行为不符合预期时你很难像调试代码一样设置断点、单步执行、查看中间变量。你只能查看输入输出日志排查过程如同黑盒测试。版本控制与协作弱图形化配置的diff难以阅读合并冲突更是噩梦。而代码可以用Git进行完美的版本管理、代码审查和协作。集成成本高想复用团队现有的认证库、工具类、服务客户端通常需要额外封装或面临兼容性问题。ADK-Java的“代码优先”优势终极灵活性智能体的“大脑”LLM指令、“技能”工具和“身体”执行流程全部用Java定义。你可以使用任何Java语言特性如继承、多态、设计模式来构建智能体。工程化最佳实践你可以为智能体编写单元测试和集成测试确保其行为稳定。可以使用依赖注入如Spring来管理工具和服务的生命周期。可以方便地集成APM工具进行性能监控。无缝云集成作为Google出品它与Google Cloud服务如Vertex AI, Cloud Functions, Cloud Run的集成是“一等公民”级别的但绝不限于此。任何Java能调用的REST API、gRPC服务或数据库都能轻松成为智能体的工具。注意“代码优先”并不意味着你要从零开始写所有东西。ADK-Java提供了大量开箱即用的基础组件如预置工具、标准Agent基类你是在一个强大的框架之上进行开发而非裸奔。2.2 核心架构组件拆解ADK-Java的架构清晰且模块化主要包含以下几个核心部分Agent智能体这是核心抽象。一个智能体封装了特定的目标、指令Instruction和可用的工具Tools。最简单的智能体就是一个LlmAgent它接收用户输入根据指令思考并选择调用工具。ADK支持构建更复杂的智能体如RouterAgent路由到不同子智能体、SequentialAgent按顺序执行多个子智能体等。Tool工具智能体能力的延伸。一个工具本质上是一个可以被智能体调用的函数。ADK-Java中的工具类型非常丰富FunctionTool将任何一个Java方法带Tool注解暴露为工具。这是最灵活的方式。OpenApiTool通过OpenAPI规范文件自动生成调用外部REST API的工具。预置工具如GoogleSearchTool、GoogleSheetsTool等用于快速集成Google服务。自定义工具通过实现Tool接口可以完全控制工具的调用逻辑、输入验证和错误处理。Model模型指大语言模型。ADK-Java通过统一的接口抽象了模型调用目前主要支持Google的Gemini系列模型如gemini-2.0-flash同时也为接入其他模型提供了可能。你可以在代码中指定智能体使用的模型。Orchestrator编排器负责管理智能体与模型、工具之间的交互流程。它处理对话历史管理、工具调用循环模型生成工具调用 - 执行工具 - 将结果返回给模型 - 模型生成下一步直到智能体完成最终响应。开发者通常不需要直接操作编排器但理解其工作原理对调试至关重要。Development UI开发界面这是一个独立的开发时组件google-adk-dev依赖提供了一个本地Web界面。你可以在这里与你的智能体进行交互式对话实时观察它的思考过程、工具调用请求和结果极大提升了开发和调试效率。它不是生产环境的一部分而是像Swagger UI之于API一样是强大的开发辅助工具。这个架构使得构建一个智能体变得像组装乐高积木选择或创建一个Agent为它配置Instruction和Model然后挂载上需要的Tools最后通过Orchestrator运行起来。3. 从零开始构建你的第一个ADK-Java智能体理论说得再多不如动手实践。让我们从一个最简单的例子开始构建一个能够查询天气的智能体。这个例子将涵盖项目初始化、基础依赖、智能体定义和本地测试的全流程。3.1 环境准备与项目初始化假设你使用Maven和Spring Boot这是Java生态中最常见的组合ADK可以无缝集成。创建Spring Boot项目使用 Spring Initializr 或IDE创建新项目。选择Java 17或更高版本添加Spring Web依赖用于后续可能暴露HTTP端点。添加ADK依赖在pom.xml中添加核心依赖和开发UI依赖。dependencies !-- Spring Boot Starter Web (可选用于构建API) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- ADK Core -- dependency groupIdcom.google.adk/groupId artifactIdgoogle-adk/artifactId version1.0.0/version !-- 请检查并使用最新版本 -- /dependency !-- ADK Development UI (用于本地调试scope可为test或provided) -- dependency groupIdcom.google.adk/groupId artifactIdgoogle-adk-dev/artifactId version1.0.0/version scoperuntime/scope !-- 通常只在运行时需要 -- /dependency !-- 其他你可能需要的依赖如Lombok -- /dependencies配置模型API密钥ADK需要调用大语言模型。你需要一个Google Cloud项目并启用Vertex AI API或者使用Gemini API。最简单的方式是通过环境变量设置API密钥。在application.properties或application.yml中配置# 如果你使用Gemini API GOOGLE_API_KEYyour_actual_api_key_here # ADK通常会读取这个环境变量重要安全提示永远不要将API密钥硬编码在代码中或提交到版本控制系统。使用环境变量、云平台的密钥管理服务如Google Cloud Secret Manager或Spring Cloud Config。3.2 定义第一个工具模拟天气查询在让智能体调用真实API前我们先定义一个模拟的天气查询工具来理解工具的定义方式。import com.google.adk.tools.Tool; import com.google.adk.tools.ToolCall; import com.google.adk.tools.ToolResult; import org.springframework.stereotype.Component; Component // 使其成为Spring管理的Bean方便注入 public class MockWeatherTool implements Tool { Override public String name() { return get_weather; } Override public String description() { return 获取指定城市的当前天气情况。输入应为城市名称。; } Override public ToolResult call(ToolCall toolCall) { // toolCall.getArguments() 是一个Map包含了模型传递的参数 String city (String) toolCall.getArguments().get(city); if (city null || city.isBlank()) { return ToolResult.error(城市名称不能为空); } // 模拟查询逻辑 String mockWeather; switch (city.toLowerCase()) { case 北京: mockWeather 晴15°C北风2级; break; case 上海: mockWeather 多云18°C东南风1级; break; case 深圳: mockWeather 阵雨22°C南风3级; break; default: mockWeather String.format(城市【%s】的天气信息晴朗20°C, city); } // 返回成功结果内容可以是字符串或任何可序列化的对象 return ToolResult.success(mockWeather); } }代码解读工具必须实现Tool接口核心是call方法。name和description至关重要。description是给LLM看的需要清晰说明工具的功能、输入格式和输出。LLM根据这个描述来决定是否以及如何调用该工具。ToolCall对象包含了LLM生成的调用参数。你需要从中解析参数并进行验证。ToolResult用于返回结果或错误。错误信息也会反馈给LLM让它有机会修正或采取其他行动。3.3 组装并运行智能体现在我们创建一个配置类或服务类来组装智能体。这里我们在一个Service中创建。import com.google.adk.agents.LlmAgent; import com.google.adk.AgentRunner; import com.google.adk.memory.InMemoryMemory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; Service public class WeatherAgentService { private LlmAgent weatherAgent; Autowired private MockWeatherTool weatherTool; // 注入我们定义的工具 PostConstruct public void init() { // 1. 构建智能体 weatherAgent LlmAgent.builder() .name(WeatherAssistant) .description(一个友好的天气查询助手可以查询全球城市的天气。) .model(gemini-2.0-flash) // 指定使用的模型 .instruction( 你是一个天气查询助手。用户会询问某个城市的天气。 如果你知道城市名就调用get_weather工具来获取天气信息然后以友好、简洁的方式告诉用户。 如果用户没有提供城市名或者城市名不明确请礼貌地询问用户具体是哪个城市。 你的回答应该使用中文。 ) // 给智能体的核心指令 .tools(weatherTool) // 装配工具 .memory(new InMemoryMemory()) // 使用简单的内存记忆记录对话历史 .build(); } public String chat(String userMessage) { // 2. 使用AgentRunner运行智能体 AgentRunner runner new AgentRunner(weatherAgent); // 3. 执行对话 String agentResponse runner.run(userMessage); return agentResponse; } }关键点解析instruction指令这是智能体的“灵魂”。你需要用自然语言清晰、无歧义地定义它的角色、行为规范和目标。好的指令能极大减少智能体的“幻觉”和错误行为。这里我们明确要求它调用工具、用中文回答。model指定底层LLM。gemini-2.0-flash是性价比很高的模型。你也可以根据需求换用gemini-2.0-pro等。memoryInMemoryMemory是一个简单的实现将对话历史保存在内存中使得智能体具备上下文感知能力即“多轮对话”。对于生产环境你可能需要实现持久化存储的Memory。AgentRunner是执行智能体的入口。runner.run()会触发完整的思考-行动循环。3.4 通过开发UI进行测试与调试这是ADK极具魅力的一个环节。你不需要写前端就能获得一个交互式的调试界面。确保google-adk-dev依赖已添加且作用域正确如runtime。启动你的Spring Boot应用。打开浏览器访问http://localhost:8080/adk-ui默认端口是8080如果改了请对应调整。你将会看到一个Web界面。在界面中你应该能看到你定义的WeatherAssistant智能体。在聊天框输入“北京天气怎么样”然后发送。在开发UI中你能看到什么完整的交互过程界面会清晰地展示用户输入、智能体的响应。关键的“思考过程”如果模型支持你可以看到模型在决定调用工具前的推理。工具调用详情当智能体决定调用get_weather工具时界面会显示它准备传递的参数{city: 北京}。工具执行结果显示MockWeatherTool返回的模拟天气数据。最终响应显示智能体整合工具结果后生成的自然语言回复。这个可视化流程对于理解智能体内部工作状态、调试指令instruction的歧义、检查工具参数传递是否正确具有无可替代的价值。它把黑盒变成了灰盒甚至白盒。4. 进阶实战构建多智能体协作系统与集成真实服务单一智能体能做的事情有限。ADK-Java的强大之处在于可以轻松构建多个智能体组成的系统让它们各司其职协作完成任务。同时集成真实的外部服务如数据库、内部API是生产应用的必然要求。4.1 设计一个多智能体协作场景旅行规划助手假设我们要构建一个旅行规划助手它涉及以下任务理解用户需求预算、目的地、兴趣、时间。查询航班信息。查询酒店信息。生成一份整合的旅行计划建议。我们可以设计三个智能体CoordinatorAgent协调员主智能体负责与用户对话理解整体需求并将子任务分派给专家智能体。FlightExpertAgent航班专家专门负责查询和推荐航班。HotelExpertAgent酒店专家专门负责查询和推荐酒店。CoordinatorAgent将作为一个RouterAgent或SequentialAgent根据逻辑复杂程度选择它内部持有对两个专家智能体的引用并在适当时机调用它们。4.2 实现专家智能体与真实工具集成首先我们实现FlightExpertAgent并让它集成一个真实的航班查询API这里以模拟调用为例。步骤一创建真实的航班查询工具这次我们使用OpenApiTool它可以根据OpenAPI规范自动生成客户端。假设我们有一个内部航班查询服务其OpenAPI spec位于classpath:flight-api.yaml。import com.google.adk.tools.OpenApiTool; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import java.io.IOException; Configuration public class ToolConfiguration { Bean public OpenApiTool flightQueryTool() throws IOException { // 从类路径加载OpenAPI规范文件 ClassPathResource specResource new ClassPathResource(apis/flight-api.yaml); return OpenApiTool.fromSpec(specResource.getInputStream()) .baseUrl(https://internal-api.example.com) // 实际服务的基础URL .build(); // ADK会自动解析spec将里面的每个operation如POST /flights/search暴露为一个工具。 // LLM会根据operation的summary/description来决定调用哪个。 } }实操心得使用OpenApiTool是集成现有REST服务的利器。确保你的OpenAPI spec文档规范、描述清晰这能极大提升LLM调用工具的准确性。对于复杂的认证如OAuth 2.0你可能需要在工具层面或通过拦截器进行额外处理。步骤二构建航班专家智能体Service public class FlightExpertAgentService { private LlmAgent flightAgent; Autowired private OpenApiTool flightQueryTool; // 注入上一步定义的OpenApiTool PostConstruct public void init() { flightAgent LlmAgent.builder() .name(FlightExpert) .description(一个专业的航班信息查询专家。) .model(gemini-2.0-flash) .instruction( 你是一个航班查询专家。当被询问时你需要调用合适的航班查询工具来获取信息。 输入可能包含出发城市、到达城市、出发日期、返回日期可选、乘客数量、舱位偏好。 你需要从输入中提取这些参数并调用工具。如果信息不足请询问用户。 工具返回的是结构化的航班数据列表。你需要从中筛选出最符合用户需求的1-3个选项并以清晰、友好的格式总结给用户包括航空公司、航班号、时间、价格和关键特点。 ) .tools(flightQueryTool) .memory(new InMemoryMemory()) .build(); } public LlmAgent getAgent() { return flightAgent; } }步骤三构建协调员智能体使用RouterAgentRouterAgent可以根据当前对话状态或用户输入动态决定将任务交给哪个子智能体处理。Service public class TravelCoordinatorService { private RouterAgent coordinatorAgent; Autowired private FlightExpertAgentService flightExpertService; Autowired private HotelExpertAgentService hotelExpertService; // 假设已类似实现 PostConstruct public void init() { // 获取专家智能体实例 LlmAgent flightExpert flightExpertService.getAgent(); LlmAgent hotelExpert hotelExpertService.getAgent(); coordinatorAgent RouterAgent.builder() .name(TravelCoordinator) .description(旅行规划总协调员负责分派任务给航班和酒店专家。) .model(gemini-2.0-flash) .instruction( 你是旅行规划助手的总协调员。用户会提出旅行需求比如“我想下周末去上海预算5000元”。 你的任务是 1. 理解用户的完整需求目的地、时间、预算、人数、兴趣。 2. 根据需求判断需要调用哪个专家航班、酒店或两者都需要。 3. 将具体的查询任务分派给对应的专家智能体并收集他们的结果。 4. 将专家返回的结果整合成一份完整的、用户友好的旅行计划建议并回复给用户。 如果用户的需求不明确请主动询问以澄清。 ) .routeToAgent(flightExpert) // 注册子智能体 .routeToAgent(hotelExpert) .memory(new InMemoryMemory()) .build(); } public String planTravel(String userRequest) { AgentRunner runner new AgentRunner(coordinatorAgent); return runner.run(userRequest); } }关键机制RouterAgent内部有一个路由逻辑可以是基于LLM判断也可以是基于规则。当coordinatorAgent运行时它会根据instruction和当前对话决定是将整个对话上下文“移交”给flightExpert还是hotelExpert或者先让一个处理完再把结果和上下文交给另一个处理。这实现了一种动态的任务分派和上下文传递。4.3 集成数据库与复杂业务逻辑智能体经常需要访问业务数据。例如酒店专家在推荐前可能需要先查询用户的会员等级以确定可用优惠。这可以通过自定义FunctionTool轻松实现。import com.google.adk.tools.Tool; import com.google.adk.tools.annotation.ToolMethod; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; Component public class UserProfileTool { Autowired private JdbcTemplate jdbcTemplate; // 假设使用Spring JDBC ToolMethod(name get_user_membership_tier, description 根据用户ID查询其会员等级。输入是userId字符串。) public String getUserMembershipTier(String userId) { String sql SELECT membership_tier FROM user_profiles WHERE user_id ?; try { return jdbcTemplate.queryForObject(sql, String.class, userId); } catch (Exception e) { return STANDARD; // 默认等级或根据业务处理 } } ToolMethod(name apply_membership_discount, description 根据会员等级和原始价格计算折扣后价格。输入是tier字符串和originalPrice数字。) public double applyMembershipDiscount(String tier, double originalPrice) { double discountRate switch (tier) { case PLATINUM - 0.15; case GOLD - 0.10; case SILVER - 0.05; default - 0.0; }; return originalPrice * (1 - discountRate); } }使用方法在构建智能体时你可以通过FunctionTool.fromInstance(userProfileTool)来将这个类中所有ToolMethod注解的方法自动转换为工具。这样智能体就能在推理过程中自主决定何时查询用户等级、何时计算折扣使得推荐逻辑更加智能和个性化。注意事项让LLM直接操作数据库存在SQL注入等安全风险。最佳实践是永远不要将原始数据库查询暴露为工具。应该像上面例子一样通过精心设计的、参数化的服务方法ToolMethod来提供数据访问能力并在方法内部做好输入验证、权限检查和错误处理。5. 部署、评估与生产环境考量开发调试完成后下一步就是让智能体服务上线。ADK-Java智能体本质上是Java应用因此可以复用所有成熟的Java部署方案。5.1 部署模式选择嵌入式服务将智能体作为你现有Spring Boot微服务的一部分。例如在TravelCoordinatorService上暴露一个REST端点 (RestController)。这是最常见的方式智能体与你的业务逻辑处于同一进程通信效率最高。RestController RequestMapping(/api/assistant) public class TravelAssistantController { Autowired private TravelCoordinatorService coordinatorService; PostMapping(/plan) public String planTravel(RequestBody UserRequest request) { return coordinatorService.planTravel(request.getQuery()); } }独立服务将智能体系统打包成一个独立的JAR/WAR部署到任何Servlet容器如Tomcat或使用java -jar运行。适合构建专门的AI Agent服务。Serverless函数将智能体逻辑打包部署到Google Cloud Functions、Cloud Run或AWS Lambda。ADK智能体通常是无状态的状态在Memory中可配置为外部存储非常适合Serverless环境。需要注意冷启动时间以及LLM API调用的超时设置云函数通常有执行时间限制。与A2A协议集成对于更复杂的、跨网络的多智能体系统ADK支持与A2AAgent-to-Agent协议集成。这允许智能体以标准化的方式相互发现和调用即使它们部署在不同的机器或网络中。这对于构建大规模、分布式的智能体生态系统至关重要。5.2 性能优化与监控模型选择与缓存gemini-2.0-flash在响应速度和成本间取得了很好平衡。对于复杂推理可使用gemini-2.0-pro。考虑对频繁且结果稳定的LLM调用如某些分类任务实施缓存以降低成本和提高响应速度。异步处理如果智能体任务耗时较长如调用多个慢速外部API应考虑异步处理模式。用户请求触发任务后立即返回一个任务ID通过WebSocket或轮询获取结果。避免HTTP请求超时。监控与可观测性在生产环境中必须对智能体进行监控。日志详细记录每个智能体会话的输入、输出、工具调用详情和耗时。ADK提供了日志接口。指标Metrics使用Micrometer等工具暴露指标如每秒请求数、平均响应时间、工具调用成功率、各模型token使用量、成本等。分布式追踪集成OpenTelemetry追踪一个用户请求穿越多个智能体和工具的完整路径便于排查性能瓶颈和错误。5.3 评估智能体效果即将到来的功能根据官方路线图ADK的评估Evaluation功能“即将到来”。评估是开发生命周期中的关键环节旨在系统性地衡量智能体的准确性回答是否正确安全性是否产生有害或偏见内容可靠性工具调用是否稳定流程是否总能完成用户体验回答是否相关、有用、流畅在没有官方评估套件前我们可以自行搭建评估流程构建测试集整理一批具有标准答案的典型用户问题单元测试。自动化测试编写脚本用测试集问题批量调用智能体收集响应。评估方法人工评估黄金标准但成本高。基于LLM的评估使用另一个通常更强的LLM作为“裁判”根据标准答案和智能体输出按照预设规则如相关性、正确性、完整性进行打分。这可以自动化但需要设计好的提示词Prompt来让裁判LLM保持客观。工具调用正确性评估检查在测试过程中工具是否被正确调用参数是否正确。持续集成将评估流程加入CI/CD管道确保代码更改不会导致智能体性能回归。6. 常见问题、踩坑记录与排查技巧在实际使用ADK-Java的过程中你肯定会遇到各种问题。以下是我从项目实践中总结的一些典型问题和解决方案。6.1 智能体不调用工具或调用错误这是最常见的问题。症状智能体直接用自己的知识回答而不去调用你提供的工具。排查步骤检查工具描述description这是LLM决定是否调用工具的主要依据。描述必须清晰、无歧义准确说明工具的功能、输入和输出。例如“获取天气”就不如“获取指定城市名称的当前天气情况和温度”明确。检查智能体指令instruction指令中必须明确要求智能体在特定条件下使用工具。例如“当用户询问天气时你必须调用get_weather工具”。指令要足够强硬和具体。使用开发UI观察在开发UI中运行对话查看LLM的“思考过程”如果模型支持输出。你可能会发现LLM认为自己知道答案所以跳过了工具调用。这时需要强化指令例如加上“即使你认为你知道答案也必须通过调用工具来确认最新信息”。检查工具参数LLM生成的参数格式必须与工具call方法中期望的格式匹配。如果工具期望{city: string}而LLM生成了{location: 北京}调用就会失败。确保工具描述中的参数名与代码中解析的键名完全一致。6.2 处理长上下文与记忆管理问题对话轮次多了之后智能体可能忘记之前的对话内容或者因为上下文太长导致模型性能下降、成本升高。解决方案选择合适的MemoryInMemoryMemory只适合短期、单次会话的调试。生产环境需要实现持久化Memory如基于数据库Redis, PostgreSQL的Memory将会话历史存储起来并设定TTL或最大轮次。上下文窗口管理LLM有上下文长度限制。当对话历史超过一定长度时需要进行摘要Summarization或选择性遗忘。你可以实现一个自定义的Memory在保存历史时定期将旧的对话内容总结成一段简短的摘要替换掉冗长的原始历史只保留最近几轮完整对话。ADK的Memory接口允许你实现这种逻辑。关键信息提取与存储在长对话中对于关键的用户信息如用户名、偏好、任务ID可以在工具调用或智能体逻辑中主动提取并存储到独立的会话状态Session State中而不是完全依赖LLM的记忆。6.3 错误处理与智能体恢复问题工具调用失败网络超时、API返回错误、参数无效时智能体应该如何应对ADK的机制工具调用返回ToolResult.error(errorMessage)时这个错误信息会作为上下文反馈给LLM。LLM可以据此尝试修复问题例如重新生成参数、选择其他工具、或向用户道歉并说明情况。最佳实践提供有意义的错误信息错误信息应该能指导LLM或用户。不要只返回“Internal Server Error”而是返回“航班查询服务暂时不可用请稍后再试”或“参数‘城市’不能为空”。设置重试机制对于暂时的网络故障可以在工具层面或通过重试库如Resilience4j实现自动重试。降级方案如果核心工具失败智能体指令中可以包含降级逻辑例如“如果航班查询失败请告知用户并建议其通过其他渠道查询同时继续为其推荐酒店”。6.4 安全性与权限控制工具权限不是所有智能体都应该能调用所有工具。例如一个客服智能体不应该有调用“删除用户数据”工具的权限。你需要在工具调用前增加权限校验层。可以在自定义Tool的call方法开头进行校验或者通过Spring Security等框架在更上层进行拦截。输入净化Sanitization对从LLM接收到的、即将传递给工具或数据库的参数进行严格的验证和净化防止注入攻击。输出过滤Filtering对LLM生成的内容进行安全检查过滤掉不适当、有害或敏感的信息。可以集成内容安全API如Google Cloud的Safety Settings或在最终输出前进行后处理。API密钥管理用于调用LLM和第三方工具的API密钥必须通过安全的方管理如环境变量、云密钥管理服务绝不能出现在代码或配置文件中。6.5 成本控制LLM API调用是按Token收费的智能体系统可能产生可观成本。监控与告警如前所述建立成本监控指标设置每日或每月的预算告警。优化指令和上下文精简instruction和memory中的历史消息移除不必要的废话。使用更短的模型如flash进行简单任务。缓存对常见、结果变化不频繁的查询如“公司的退货政策是什么”的LLM响应进行缓存。设置超时和限流为工具调用和LLM调用设置合理的超时防止因个别慢请求阻塞系统。对用户请求进行限流防止滥用。ADK-Java将构建复杂AI智能体的门槛从研究层面拉到了工程实践层面。它没有隐藏复杂性而是提供了驾驭复杂性的工具。拥抱“代码优先”意味着你获得了完全的掌控力同时也承担了架构设计的责任。从定义一个简单的工具开始逐步构建起一个理解业务、能调用服务、可协作、易监控的智能体系统这个过程本身就是一场充满挑战和成就感的工程探险。