☰
Java工程师转型Agent开发实战指南
2026/9/24 20:54:50 网站建设 项目流程

1. 为什么Java工程师转Agent不是“换语言”,而是“升级武器库”

最近两周,我连续被三位在一线做Spring Boot微服务的Java老同事拉进私聊:“你搞的那个Agent项目,能不能给个入门路径?别整那些LLM原理、Transformer推导,我们就想用熟悉的Java生态,把现有系统插上AI翅膀。”——这句话几乎精准概括了当前Javaer转向Agent开发的真实动机:不是放弃Java,而是让Java在AI时代继续当主力;不是从零学Python,而是把Spring、Maven、JUnit这些刻进DNA的工具链,无缝衔接到Agent架构里。

这和2016年大家学Spring Boot时的心态一模一样:没人说“我要抛弃Spring MVC”,而是“怎么用自动配置、starter、Actuator,让老项目快速接入新能力”。今天面对Agent,道理完全相同。但问题在于,市面上90%的Agent教程默认以Python为母语,LangChain、LlamaIndex、AutoGen的文档全是py文件示例,连最基础的Prompt模板渲染都用Jinja2。Javaer点开就懵:System.out.println()还没写利索,先让我配pip install langchain?这不是学习路径,是劝退指南。

更现实的卡点藏在工程细节里:你用Spring Cloud做了五年分布式事务,现在要接入一个Agent,它得能读取Nacos配置中心的参数、能走Sentinel限流、能打SkyWalking链路日志、能发RocketMQ事件——这些不是“可选功能”,而是上线前PM拍桌子要的SLA。如果一个Agent框架连@Value("${app.timeout:3000}")都解析不了,那它再炫酷也只是玩具。

所以,“Javaer转Agent”的本质,是在Java技术栈的确定性土壤上,嫁接Agent的不确定性智能。它不追求“用Java重写LangChain”,而是找到Spring AI、LangChain4j、Hello-Agents这些原生Java Agent库的“工程接口”:它们如何与Spring Boot生命周期对齐?如何复用已有的DataSource和RedisTemplate?如何让Agent的execute()方法像@Service一样被AOP织入?这才是真实世界里的迁移路径,而不是在Jupyter Notebook里跑通一个Hello World就宣告胜利。

我上周帮一家做物流调度系统的客户改造旧版Java后端。他们原有代码里有27个@Service类,分别处理运单生成、路径规划、异常预警。我们没重写任何业务逻辑,只在关键节点注入了LangChain4j的ChatModel和Retriever,让“异常预警”服务在触发时,自动调用RAG检索历史相似故障案例,并用Spring AI的AiResponse结构化返回建议措施。整个过程,开发同学只改了3个类的57行代码,没碰一句Python,没装一个conda环境,上线后平均响应时间只增加12ms——这才是Javaer该有的Agent落地节奏。

提示:警惕“伪Java Agent教程”。凡是以“先装Python环境→再装Ollama→最后用Java调用HTTP API”为起点的方案,本质上仍是Python主导,Java只是客户端。真正的Java Agent,应该让你在pom.xml里加一行依赖,@Autowired一个Bean,然后直接agent.execute(input)。

2. Spring AI:不是另一个AI SDK,而是Spring生态的“AI协议层”

Spring AI的定位常被严重误读。很多人把它当成“Spring版LangChain”,以为只是把Python的Chain、Tool、Agent概念翻译成Java类。但翻过它的源码你会发现,Spring AI的核心设计哲学是协议抽象(Protocol Abstraction)——它不绑定任何具体模型或框架,而是定义了一套Java世界通用的AI交互契约。

这个契约体现在三个关键接口上:ChatModel、EmbeddingModel、Retriever。注意,它们全都是interface,没有实现类。Spring AI官方只提供OpenAiChatModel、AzureOpenAiChatModel等适配器,而社区贡献的QwenChatModel、ZhipuChatModel、OllamaChatModel,全部遵循同一套ChatModel签名:

public interface ChatModel { // 核心方法:输入Message列表,返回AiResponse AiResponse call(List<ChatMessage> messages); // 流式响应支持 Stream<AiResponse> stream(List<ChatMessage> messages); // 配置扩展点 ChatOptions getOptions(); }

这意味着什么?意味着你可以把通义千问、智谱清言、本地Ollama的Qwen2-7B,全部当作ChatModel注入到同一个Service里,只需切换@Bean声明,业务代码零修改。我实测过,在Spring Boot 3.2+环境下,以下配置能让同一个OrderProcessingService在测试环境调用本地Ollama,生产环境调用阿里云百炼API:

# application.yml spring: ai: # 开发环境:本地Ollama ollama: base-url: http://localhost:11434 model: qwen2:7b # 生产环境:阿里云百炼(通过Spring AI Alibaba扩展) alibaba: endpoint: https://dashscope.aliyuncs.com/compatible-mode/v1 api-key: ${ALIBABA_API_KEY} model: qwen-max
@Service public class OrderProcessingService { private final ChatModel chatModel; // 不关心具体实现 public OrderProcessingService(ChatModel chatModel) { this.chatModel = chatModel; } public String generateDispatchPlan(String orderInfo) { List<ChatMessage> messages = List.of( new SystemMessage("你是一个物流调度专家,请根据订单信息生成最优配送路径"), new UserMessage(orderInfo) ); return chatModel.call(messages).getGeneration().getContent(); // 统一调用入口 } }

这种设计彻底规避了“模型锁定”风险。去年我们有个项目,初期用OpenAI GPT-3.5-turbo,半年后因合规要求切换至智谱GLM-4,整个过程只改了两处:pom.xml里替换spring-ai-openai-spring-boot-starter为spring-ai-zhipu-spring-boot-starter,以及application.yml里更新API Key和Endpoint。所有业务逻辑、Prompt模板、RAG检索链,一行代码未动。

Spring AI的另一个隐藏价值是与Spring Boot自动配置的深度耦合。它不是简单地把RestTemplate封装成ChatModel,而是利用ApplicationContext的生命周期管理AI资源。比如ChatModelBean会自动注册RetryTemplate(基于Spring Retry),当API调用超时时,无需手动写重试逻辑;EmbeddingModel会自动集成CacheManager,向量计算结果可缓存到Redis;甚至Retriever的retrieve()方法,也能被@Transactional注解包裹,确保RAG检索与数据库事务一致性。

注意:Spring AI 0.8.2+版本开始强制要求Spring Boot 3.2+。如果你的项目还在用Spring Boot 2.7,不要强行升级——Spring AI 0.7.x对2.7的支持已停止维护,且缺少关键的StreamingChatClient特性。我的建议是:先用Spring Boot 3.2新建一个独立Agent模块,通过Feign Client与老系统通信,而非冒险升级主应用。

3. LangChain4j:Java版LangChain的“去Python化”重构逻辑

LangChain4j常被称作“Java版LangChain”,但这种说法极具误导性。它并非LangChain的Java直译,而是针对Java工程实践痛点做的范式重构(Paradigm Refactoring)。最典型的例子是Tool的设计:Python版LangChain的Tool是函数式定义,而LangChain4j的Tool是一个标准Java接口:

public interface Tool { // 必须实现的方法:执行工具逻辑 ToolResult execute(ToolExecutionRequest request); // 元数据:用于Agent决策 String name(); String description(); List<Parameter> parameters(); }

这个看似简单的改动,解决了Javaer最头疼的两个问题:类型安全和Spring集成。在Python里,Tool参数靠字典传入,运行时才校验;而在LangChain4j里,ToolExecutionRequest是强类型POJO,IDE能自动补全字段,编译期就能发现request.getOrderId()拼写错误。更重要的是,Tool实现类可以是@Service,能直接注入JdbcTemplate、RestClient、RedisTemplate:

@Service public class OrderStatusTool implements Tool { private final JdbcTemplate jdbcTemplate; private final RestClient restClient; public OrderStatusTool(JdbcTemplate jdbcTemplate, RestClient restClient) { this.jdbcTemplate = jdbcTemplate; this.restClient = restClient; } @Override public ToolResult execute(ToolExecutionRequest request) { String orderId = request.parameters().get("order_id").toString(); // 直接查数据库,不用额外写DAO Map<String, Object> order = jdbcTemplate.queryForMap( "SELECT status, estimated_delivery FROM orders WHERE id = ?", orderId); // 调用外部物流API LogisticsResponse logistics = restClient.get() .uri("https://api.logistics.com/tracking/{id}", orderId) .retrieve() .body(LogisticsResponse.class); return ToolResult.from(Map.of( "status", order.get("status"), "estimated_delivery", order.get("estimated_delivery"), "logistics_status", logistics.getStatus() )); } }

这种设计让Tool不再是孤立的“AI函数”,而是成为业务系统的一部分。Agent调用OrderStatusTool时,实际走的是Spring的IoC容器,能享受事务管理、连接池复用、监控埋点等全套企业级能力。

另一个关键重构是Chain的声明式构建。Python版LangChain用|操作符串联组件,如prompt | llm | output_parser,但在Java里,这种写法既不直观也不利于调试。LangChain4j采用Builder模式,每个环节都可单独配置、单独测试:

// 构建RAG Chain:检索 → 重排 → 生成 RetrievalAugmentedGenerationChain chain = RetrievalAugmentedGenerationChain.builder() .retriever(new VectorStoreRetriever(vectorStore)) // 检索器 .reranker(new BgeReranker()) // 重排器(可选) .llm(chatModel) // 大模型 .promptTemplate(PromptTemplate.from( "根据以下上下文回答问题:\n" + "{{context}}\n\n" + "问题:{{question}}\n" + "答案:")) // Prompt模板 .build(); // 单独测试检索环节 List<Document> docs = chain.getRetriever().retrieve("订单延迟原因"); // 单独测试生成环节 String answer = chain.getLlm().call(List.of( new UserMessage("订单延迟原因是什么?") )).getGeneration().getContent();

这种解耦让调试变得极其简单。当Agent返回错误答案时,你可以逐段验证:是检索没找到相关文档?还是重排把关键文档排到了后面?或是Prompt模板变量名写错了?每一步都有明确的输入输出,不像Python链式调用那样,出错时只能看堆栈猜哪一环挂了。

实操心得:LangChain4j的VectorStore实现(如QdrantVectorStore、MilvusVectorStore)默认使用Jackson序列化Document,但如果你的Document POJO里有@JsonIgnore字段,会导致向量化失败。解决方案是在VectorStore配置中指定自定义ObjectMapper,或改用Document的metadataMap存储非结构化数据——这是我在对接Qdrant时踩过的坑,官方文档根本没提。

4. Hello-Agents:轻量级Agent框架的“最小可行架构”实践

当Spring AI和LangChain4j解决的是“如何与AI交互”,Hello-Agents解决的是“如何组织Agent行为”。它不是一个大而全的框架,而是一套最小可行Agent架构(Minimum Viable Agent Architecture)的参考实现,核心思想是:用Java最朴素的interface和record,定义Agent的骨架。

Hello-Agents的基石是三个不可变类型:

  • AgentState:记录Agent当前状态(如WAITING_FOR_INPUT、EXECUTING_TOOL、GENERATING_RESPONSE)
  • AgentAction:描述下一步动作(如CALL_TOOL("order_status", Map.of("id", "123"))、RETURN_FINAL_ANSWER("已发货"))
  • AgentOutput:最终输出(含文本、工具调用结果、元数据)

整个Agent执行流程被压缩成一个纯函数:

public record AgentExecutor( AgentState initialState, Function<AgentState, AgentAction> actionGenerator, Function<AgentAction, AgentOutput> actionExecutor ) { public AgentOutput execute(String input) { AgentState state = initialState.withInput(input); while (true) { AgentAction action = actionGenerator.apply(state); if (action.type() == AgentAction.Type.RETURN_FINAL_ANSWER) { return actionExecutor.apply(action); } AgentOutput output = actionExecutor.apply(action); state = state.next(output); } } }

这个设计刻意回避了复杂的状态机和事件总线,却意外带来了极强的可测试性。你可以用JUnit 5轻松覆盖所有分支:

@Test void should_return_final_answer_when_action_is_return() { // 给定:一个总是返回RETURN_FINAL_ANSWER的动作生成器 Function<AgentState, AgentAction> alwaysReturn = s -> new AgentAction(AgentAction.Type.RETURN_FINAL_ANSWER, "test", Map.of()); // 当:执行Agent AgentOutput output = new AgentExecutor( AgentState.initial(), alwaysReturn, a -> new AgentOutput(a.content(), Map.of()) ).execute("input"); // 那么:输出内容应匹配 assertEquals("test", output.content()); }

Hello-Agents的价值在于,它把Agent开发从“搭积木”变成了“写业务逻辑”。比如实现一个客服Agent,你不需要研究AgentExecutor怎么工作,只需专注三件事:

  1. 状态管理:定义CustomerServiceState,包含customerId、conversationHistory、currentIntent
  2. 动作生成:写CustomerServiceActionGenerator,根据对话历史判断是查订单、改地址,还是转人工
  3. 动作执行:写CustomerServiceActionExecutor,调用订单服务、地址服务、工单系统

这种分离让团队协作变得清晰:后端同学负责ActionExecutor(对接现有系统),算法同学优化ActionGenerator(提升意图识别准确率),产品同学定义AgentState(管理对话上下文)。我们曾用这套模式,在两周内交付了一个支持多轮对话的保险理赔Agent,代码量仅800行,却稳定支撑了日均2万次咨询。

关键技巧:Hello-Agents的AgentState默认用record实现,但实际项目中,conversationHistory可能长达数百条消息,导致state.next()创建新对象时GC压力大。我的解决方案是:将conversationHistory改为List<ChatMessage>的引用,next()方法只追加新消息,不复制整个列表;同时用WeakReference缓存最近10轮的AgentState,避免重复计算——这是在压测时发现的性能瓶颈,官方示例里完全没有提及。

5. Java Agent开发路线图:从“能跑通”到“可交付”的四阶跃迁

很多Javaer学Agent的误区,是把“跑通Hello World”当成终点。但真实项目里,从Demo到上线,中间隔着四道必须跨越的鸿沟。我把这条路径拆解为四个阶段,每个阶段都有明确的交付物和验收标准:

5.1 阶段一:环境就绪(1天)

目标:在本地IDE里,用mvn spring-boot:run启动一个能调用大模型的Spring Boot应用。

  • ✅ 交付物:pom.xml中正确引入spring-ai-openai-spring-boot-starter或spring-ai-zhipu-spring-boot-starter
  • ✅ 交付物:application.yml配置项完整(API Key、Endpoint、Model Name)
  • ✅ 验收标准:curl -X POST http://localhost:8080/chat -d '{"message":"你好"}'返回JSON格式的AI响应
  • ⚠️ 常见陷阱:Windows环境下JAVA_HOME指向JDK8,但Spring AI 0.8.x要求JDK17+;Mac M1芯片用户忘记安装Rosetta,导致Ollama无法启动。

5.2 阶段二:能力封装(3天)

目标:将AI能力封装成可复用的Service,能被现有业务代码调用。

  • ✅ 交付物:一个@Service类,提供generateSummary(String text)、extractEntities(String text)等方法
  • ✅ 交付物:方法内部使用ChatModel或EmbeddingModel,但对外隐藏AI细节
  • ✅ 验收标准:在订单服务里调用summaryService.generateSummary(orderDesc),返回简洁摘要
  • ⚠️ 常见陷阱:直接在Service里new ChatModel实例,导致无法享受Spring的Bean生命周期管理;忘记为ChatModel配置RetryTemplate,网络抖动时请求直接失败。

5.3 阶段三:Agent编排(5天)

目标:构建多步骤Agent,能自主调用工具、处理异常、管理对话状态。

  • ✅ 交付物:基于LangChain4j或Hello-Agents实现的CustomerSupportAgent
  • ✅ 交付物:至少集成2个Tool(如OrderStatusTool、RefundPolicyTool)
  • ✅ 验收标准:用户输入“我的订单12345为什么还没发货?”,Agent自动查订单状态,再查物流信息,最后生成自然语言回复
  • ⚠️ 常见陷阱:Tool参数硬编码,导致无法适配不同订单号;Agent状态未持久化,用户刷新页面后对话历史丢失。

5.4 阶段四:生产就绪(7天)

目标:Agent具备企业级可靠性,满足监控、降级、审计要求。

  • ✅ 交付物:/actuator/ai端点,暴露Agent调用次数、平均延迟、错误率
  • ✅ 交付物:当大模型API不可用时,自动降级为规则引擎(如if (order.status == "shipped") return "已发货")
  • ✅ 交付物:所有AI调用记录写入审计日志(含原始输入、模型输出、耗时、Token数)
  • ✅ 验收标准:在模拟网络中断场景下,Agent仍能返回兜底答案,且Prometheus监控显示错误率<0.1%
  • ⚠️ 常见陷阱:未限制单次Agent执行的最大步数,恶意输入导致无限循环;审计日志未脱敏,泄露用户手机号、身份证号。

这个路线图的关键,在于每个阶段都必须产出可验证的交付物,而不是“学完某个教程”。我见过太多团队卡在阶段二:花两周研究LangChain4j的源码,却连一个能被订单服务调用的SummaryService都没写出来。记住,Agent的价值不在技术多炫酷,而在能否解决一个具体的业务问题——比如把客服响应时间从2小时缩短到30秒。

6. 真实项目避坑清单:Java Agent开发中90%人踩过的5个深坑

在帮12个Java团队落地Agent项目后,我整理了一份高频避坑清单。这些坑不会出现在官方文档里,但每一个都曾导致项目延期或线上事故:

6.1 坑一:Token计数失真导致截断错误

现象:Agent在处理长文本时,突然返回“抱歉,我无法回答这个问题”,但日志显示模型API返回了200状态码。 根因:Spring AI默认使用OpenAiTokenizer计算Token数,但它对中文支持极差。例如“订单编号:1234567890”在OpenAiTokenizer里算作10个Token,实际GPT-4 Turbo需要15个。当maxTokens设为100时,实际可用Token只剩85,长Prompt直接被截断。 解决方案:改用JiebaTokenizer(需自行实现)或HuggingFaceTokenizer,并在ChatOptions中显式设置:

chatModel.call(messages, ChatOptions.builder() .maxTokens(100) .temperature(0.3) .tokenCounter(new JiebaTokenCounter()) // 自定义中文分词器 .build());

6.2 坑二:RAG检索结果“幻觉”加剧

现象:Agent引用的知识库文档明明写着“退款需7个工作日”,却回答“3个工作日内到账”。 根因:LangChain4j的VectorStoreRetriever默认返回topK=4个文档,但未做相关性阈值过滤。当查询“退款多久”时,第4个文档其实是“运费险理赔流程”,相关性得分仅0.2,却被Agent当作依据。 解决方案:在Retriever构建时启用scoreThreshold:

new VectorStoreRetriever(vectorStore, SearchRequest.builder() .query(query) .topK(4) .scoreThreshold(0.5f) // 只返回相似度>0.5的文档 .build());

6.3 坑三:Spring Boot Actuator暴露AI敏感信息

现象:运维同事发现/actuator/env端点返回了SPRING_AI_OPENAI_API_KEY明文。 根因:Spring Boot 3.2+默认将所有配置属性暴露在/actuator/env,而AI API Key属于敏感配置。 解决方案:在application.yml中关闭敏感属性暴露:

management: endpoint: env: show-values: NEVER # 或者SHOW_NEVER

同时,用@ConfigurationProperties替代@Value注入API Key,确保Key不进入Environment。

6.4 坑四:Agent状态并发冲突

现象:同一用户多次快速发送消息,Agent返回的答案互相覆盖,对话历史错乱。 根因:Hello-Agents的AgentState是不可变record,但AgentExecutor实例被多个请求共享,state.next()产生的新state未绑定到特定会话。 解决方案:为每个用户会话创建独立AgentExecutor实例,或用ThreadLocal<AgentState>隔离状态:

@Component public class SessionAgentManager { private final ThreadLocal<AgentState> stateHolder = ThreadLocal.withInitial(() -> AgentState.initial()); public AgentOutput execute(String input) { AgentState currentState = stateHolder.get(); AgentOutput output = agentExecutor.execute(input); stateHolder.set(currentState.next(output)); return output; } }

6.5 坑五:Maven依赖版本地狱

现象:spring-ai-langchain4j-spring-boot-starter与spring-boot-starter-web版本不兼容,编译报错NoSuchMethodError。 根因:Spring AI、LangChain4j、Hello-Agents各自维护独立版本号,官方BOM(Bill of Materials)未完全对齐。 解决方案:强制统一版本,在pom.xml中使用<dependencyManagement>锁定:

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>0.8.2</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-bom</artifactId> <version>0.30.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

这些坑,每一个都来自真实血泪教训。它们不会出现在“LangChain4j入门教程”的第一页,但会出现在你上线前的最后一小时。提前知道它们,比事后救火强十倍。

7. 我的Agent开发工具箱:6个让Javaer效率翻倍的实战工具

除了框架本身,还有一些辅助工具极大提升了Java Agent开发效率。它们不是必需品,但用过就回不去:

7.1 Prompt调试控制台:Spring AI Studio

官方提供的Web UI,地址http://localhost:8080/actuator/ai/studio。它能:

  • 实时编辑Prompt模板,查看变量渲染结果
  • 切换不同ChatModel,对比输出差异
  • 查看每次调用的Token消耗、耗时、API请求头
  • 保存常用Prompt为模板,一键复用

使用技巧:在application.yml中开启spring.ai.studio.enabled=true,并配置spring.ai.studio.allowed-origins=*(仅开发环境)。

7.2 向量数据库可视化:Qdrant Console

当用Qdrant做RAG时,http://localhost:6333/dashboard能:

  • 直观查看Collection的文档数量、向量维度
  • 手动执行相似度搜索,验证嵌入质量
  • 导入/导出数据,快速重建测试数据集

使用技巧:用qdrant-client-java的createCollection()方法时,务必设置vectorSize=1024(对应BGE-M3模型),否则Console会报错。

7.3 Agent行为录制:LangChain4j Trace

在application.yml中配置:

langchain4j: tracing: enabled: true exporter: console # 或zipkin, otel

它会输出类似这样的执行链:

[AGENT] start -> [RETRIEVER] query="订单延迟" -> [LLM] prompt="..." -> [TOOL] name="order_status" -> [AGENT] end

使用技巧:结合@EventListener监听AgentExecutionEvent,在关键节点打点,便于定位慢查询。

7.4 Java内存分析:VisualVM + GC日志

Agent频繁创建ChatMessage、Document对象,容易引发Young GC。用VisualVM连接应用,重点关注:

  • dev.langchain4j.data.message.ChatMessage对象创建速率
  • java.util.ArrayList的内存占用(conversationHistory)

使用技巧:启动参数加-XX:+PrintGCDetails -Xloggc:gc.log,用GCViewer分析日志。

7.5 本地大模型:Ollama + Qwen2

开发阶段用Ollama运行Qwen2-7B,避免依赖公网API:

ollama run qwen2:7b

然后在application.yml中配置:

spring: ai: ollama: base-url: http://localhost:11434 model: qwen2:7b

使用技巧:M1/M2芯片用户用ollama run qwen2:7b-q4_k_m(4-bit量化版),内存占用降低60%。

7.6 代码生成:GitHub Copilot + LangChain4j Snippets

我创建了一套Copilot提示词,输入// langchain4j tool,自动补全:

@Service public class {ClassName} implements Tool { @Override public ToolResult execute(ToolExecutionRequest request) { // TODO: implement logic return ToolResult.from(Map.of()); } @Override public String name() { return "{toolName}"; } @Override public String description() { return "{description}"; } @Override public List<Parameter> parameters() { return List.of( Parameter.builder() .name("{paramName}") .type("{paramType}") .description("{paramDesc}") .required(true) .build() ); } }

使用技巧:把这套Snippets同步到团队Git仓库,新人第一天就能写出规范Tool。

这些工具不是银弹,但组合起来,能把Agent开发从“摸索试错”变成“精准调控”。就像老司机离不开胎压计和行车记录仪,Javaer做Agent,也需要自己的专业装备。

8. 最后分享一个小技巧:用Java注解驱动Agent行为

这是我最近在物流Agent项目里摸索出的模式:用自定义注解,把Agent逻辑“声明式”地写在业务方法上。比如,我们有一个DeliveryScheduleService,里面的方法天然对应Agent的Tool:

@Service public class DeliveryScheduleService { @AgentTool( name = "get_delivery_schedule", description = "获取指定日期的配送计划,包括车辆、司机、路线" ) public DeliverySchedule getDeliverySchedule( @AgentParam(name = "date", description = "查询日期,格式YYYY-MM-DD") String date, @AgentParam(name = "warehouse_id", description = "仓库ID") String warehouseId ) { // 业务逻辑:查数据库、调用GIS服务 return deliveryScheduleRepository.findByDateAndWarehouse(date, warehouseId); } @AgentTool( name = "update_delivery_status", description = "更新配送单状态,如'已装车'、'在途'、'已签收'" ) public void updateDeliveryStatus( @AgentParam(name = "delivery_id", description = "配送单ID") String deliveryId, @AgentParam(name = "status", description = "新状态") String status ) { deliveryStatusService.update(deliveryId, status); } }

然后写一个AnnotationBasedToolProvider,扫描所有@AgentTool方法,自动生成LangChain4j的Tool实例:

@Component public class AnnotationBasedToolProvider { private final ApplicationContext context; public AnnotationBasedToolProvider(ApplicationContext context) { this.context = context; } public List<Tool> getTools() { return context.getBeansWithAnnotation(AgentTool.class).values().stream() .map(this::createToolFromMethod) .collect(Collectors.toList()); } private Tool createToolFromMethod(Object bean) { Method[] methods = bean.getClass().getDeclaredMethods(); return Arrays.stream(methods) .filter(m -> m.isAnnotationPresent(AgentTool.class)) .map(m -> new ReflectiveTool(bean, m)) .collect(Collectors.toList()) .get(0); // 简化版,实际需处理多个方法 } }

这样,业务同学只需关注@AgentTool标注的方法,AI同学负责AnnotationBasedToolProvider,双方零耦合。当业务需求变更时,改Java方法就行,Agent自动同步——这才是Javaer该有的Agent开发体验:用最熟悉的语法,做最前沿的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询