两行注解把企业 RPC 接口变成 AI 工具

张开发
2026/4/21 17:52:36 15 分钟阅读

分享文章

两行注解把企业 RPC 接口变成 AI 工具
标签JavaAgentj-langchainRPCDubboFeignAgentToolParamParamDesc前置阅读AgentExecutor用一行代码启动 ReAct Agent适合人群希望把已有 Dubbo / Feign / gRPC 等 RPC 服务快速接入 AI Agent 的 Java 开发者一、背景企业里有大量存量 RPC 服务想让 AI Agent 调用这些服务最自然的期望是不改动已有 RPC 接口定义只做最少的配置而不是重写一套工具层不管底层是 Dubbo 还是 Feign接入方式应该一致j-langchain 的AgentToolParam/ParamDesc体系正好满足这个需求。同一套工具包装类既可以交给AgentExecutorReAct也可以交给McpAgentExecutorFunction Calling无需任何修改。二、参数描述的三种方式工具的参数 Schema 是给 LLM 看的框架按以下优先级生成优先级方式适用场景1AgentTool.params内联ParamDescVO 来自第三方包无法修改字段2VO 字段上的Param自己定义的 VO3方法参数上的Param简单基础类型参数三种方式互为补充向下兼容按实际情况选择即可。三、场景一Dubbo — 第三方 VO AgentTool.params 内联描述VO 来自合作方三方包字段上无法加Param。改用AgentTool.params内联描述效果完全相同。工具包装类ComponentpublicclassEcommerceDubboTools{DubboReferenceprivateOrderFacadeorderFacade;DubboReferenceprivateRefundFacaderefundFacade;DubboReferenceprivateLogisticsFacadelogisticsFacade;AgentTool(value查询用户订单信息,params{ParamDesc(nameorderId,desc订单ID格式ORD-2024-XXXXXX如不知道可留空),ParamDesc(nameuserId,desc用户ID格式USR-XXXXXX如不知道可留空),ParamDesc(namequeryType,desc查询类型LATEST最近一笔/ ALL全部订单默认 LATEST)})publicStringqueryOrder(OrderQueryRequestrequest){returnorderFacade.queryOrder(request).toString();}AgentTool(value提交退款申请,params{ParamDesc(nameorderId,desc订单ID格式ORD-2024-XXXXXX),ParamDesc(namereason,desc退款原因如质量问题 / 物流超时 / 不想要了 / 收到错误商品),ParamDesc(nameamount,desc退款金额元不填则申请全额退款)})publicStringapplyRefund(RefundRequestrequest){returnrefundFacade.applyRefund(request).toString();}AgentTool(value查询物流配送状态,params{ParamDesc(nameorderId,desc订单ID格式ORD-2024-XXXXXX),ParamDesc(nametrackingNo,desc快递单号可选不填则通过订单ID自动关联查询)})publicStringtrackLogistics(LogisticsQueryRequestrequest){returnlogisticsFacade.track(request).toString();}}框架读取AgentTool.params自动生成如下 Schema查询用户订单信息 Input JSON keys (OrderQueryRequest): - orderId (String): 订单ID格式ORD-2024-XXXXXX如不知道可留空 - userId (String): 用户ID格式USR-XXXXXX如不知道可留空 - queryType (String): 查询类型LATEST最近一笔/ ALL全部订单默认 LATEST Action Input format: JSON, e.g. {orderId: ..., userId: ..., queryType: ...}示例用例使用 AgentExecutor — ReAct对应dubboAgentDemo()TestpublicvoiddubboAgentDemo(){AgentExecutoragentAgentExecutor.builder(chainActor).llm(ChatAliyun.builder().model(qwen-plus).temperature(0f).build()).tools(ecommerceDubboTools).maxIterations(8).build();agent.invoke(我的订单 ORD-2024-001 付款已经好几天了显示运输中但迟迟没有送达帮我查一下1) 这个订单的基本信息2) 目前物流到哪了3) 如果快递确实异常帮我提交一个退款申请原因是物流超时。);}同一套ecommerceDubboTools也可以直接传给McpAgentExecutor工具定义无需修改。执行过程[思考] 先查询订单基本信息。 Action: query_order {orderId: ORD-2024-001} [服务返回] 订单 ORD-2024-001「索尼 WH-1000XM5 耳机」¥2199运输中已超时2天 [思考] 订单确实超时查询物流详情。 Action: track_logistics {orderId: ORD-2024-001} [服务返回] SF12345678902024-03-12 09:00 在成都青羊区网点此后无更新 [思考] 物流异常提交退款。 Action: apply_refund {orderId: ORD-2024-001, reason: 物流超时} [服务返回] 退款申请已提交¥2199全额工单号 TKT-20240316-8821 Final Answer: 已为您处理完毕...四、场景二Feign — 自有 VO Param 标注字段VO 是自己团队定义的可以直接在字段上加Param工具方法本身保持干净。VO 定义DatapublicclassProductDetailRequest{Param(商品ID格式PROD-XXXXXX)privateStringproductId;Param(商品类目如手机/笔记本/耳机可留空)privateStringcategory;}DatapublicclassInventoryQueryRequest{Param(商品SKU格式SKU-XXXXXX)privateStringsku;Param(仓库区域EAST/WEST/SOUTH/NORTH可留空查全部仓库)privateStringregion;}DatapublicclassPriceQueryRequest{Param(商品SKU格式SKU-XXXXXX)privateStringsku;Param(用户等级VIP/NORMAL影响折扣默认 NORMAL)privateStringuserLevel;}工具包装类ComponentpublicclassRetailFeignTools{AutowiredprivateProductFeignClientproductFeignClient;AutowiredprivateInventoryFeignClientinventoryFeignClient;AutowiredprivatePricingFeignClientpricingFeignClient;AgentTool(查询商品详情)publicStringgetProductDetail(ProductDetailRequestrequest){returnproductFeignClient.getProductDetail(request).toString();}AgentTool(查询商品库存)publicStringqueryInventory(InventoryQueryRequestrequest){returninventoryFeignClient.queryInventory(request).toString();}AgentTool(查询商品价格)publicStringqueryPrice(PriceQueryRequestrequest){returnpricingFeignClient.queryPrice(request).toString();}}框架扫描 VO 字段上的Param自动生成如下 Schema查询商品库存 Input JSON keys (InventoryQueryRequest): - sku (String): 商品SKU格式SKU-XXXXXX - region (String): 仓库区域EAST/WEST/SOUTH/NORTH可留空查全部仓库 Action Input format: JSON, e.g. {sku: ..., region: ...}示例用例使用 McpAgentExecutor — Function Calling对应feignAgentDemo()TestpublicvoidfeignAgentDemo(){McpAgentExecutoragentMcpAgentExecutor.builder(chainActor).llm(ChatAliyun.builder().model(qwen3.6-plus).temperature(0f).build()).tools(retailFeignTools).maxIterations(8).build();agent.invoke(我想买索尼 WH-1000XM5 耳机商品IDPROD-SONY-001SKUSKU-SONY-WH1000XM5帮我查一下1) 这款产品的详情2) 华东仓库是否有库存3) 我是 VIP 用户购买价格是多少);}同一套retailFeignTools也可以直接传给AgentExecutor工具定义无需修改。执行过程[思考] 先查询商品详情。 Action: get_product_detail {productId: PROD-SONY-001} [服务返回] 索尼 WH-1000XM5主动降噪30h 续航黑色/银色 [思考] 查华东仓库库存。 Action: query_inventory {sku: SKU-SONY-WH1000XM5, region: EAST} [服务返回] EAST 仓库黑色 47 件银色 12 件均可当日发货 [思考] 查 VIP 价格。 Action: query_price {sku: SKU-SONY-WH1000XM5, userLevel: VIP} [服务返回] ¥19799折 VIP 优惠立省 ¥220 Final Answer: 索尼 WH-1000XM5 详情、库存、VIP 价格已查完...五、两种场景对比维度DubboAgentTool.paramsFeignParam on fieldVO 是否可修改否二方包提供是自有定义参数描述位置工具方法注解VO 字段注解LLM 看到的 Schema完全相同完全相同AgentExecutor 可用✓✓McpAgentExecutor 可用✓✓两种方式生成的 Schema 对 LLM 完全透明差别仅在于描述写在哪里。六、总结自有 VOParam标注在字段上描述集中在数据模型层第三方 VOAgentTool.params内联ParamDesc描述集中在工具方法上AgentExecutor 与 McpAgentExecutor 通用同一套工具定义无需为不同执行器维护两份代码框架透明Schema 生成、JSON 反序列化、方法调用全部自动处理完整示例Article19RpcMcpTools.javaj-langchain GitHubhttps://github.com/flower-trees/j-langchainj-langchain Gitee 镜像https://gitee.com/flower-trees-z/j-langchain

更多文章