多步骤 ReAct 实战:让 Agent 自主完成航司比价与订票
标签JavaReActAgentj-langchain多步骤推理工具调用AgentExecutor前置阅读Java 实现 ReAct Agent工具调用与推理循环 → AgentExecutor告别手写 ReAct 循环适合人群已会用AgentExecutor和AgentTool希望串联多个工具完成完整业务流程的 Java 开发者一、从单次调用到多步推理前两篇文章演示的都是一问一答式的工具调用——用户问天气Agent 调一次get_weather就结束了。但真实业务往往不是这样的。考虑这样一个场景用户说“帮我查一下东方航空、国航、南航 2024-03-15 上海飞北京的票价选最便宜的帮我订票。”这句话里藏着四个动作查 MU 票价、查 CA 票价、查 CZ 票价、比价后订票。用户不会逐步确认他只提了一个最终目标中间的推理和决策全部由 Agent 自主完成。这就是多步骤 ReAct的典型场景多轮工具调用逐步积累信息模型根据已有 Observation 决定下一步动作直到目标达成。二、工具设计一个任务四个工具工具拆分的原则是每个工具只做一件事返回结构清晰便于模型在后续 Thought 中消费多条 Observation。staticclassFlightTools{// 演示数据内存 Map 模拟航班数据库生产环境替换为真实 APIprivatestaticfinalMapString,IntegerMU_PRICESMap.of(上海-北京,980,上海-广州,1200,上海-成都,1450);privatestaticfinalMapString,IntegerCA_PRICESMap.of(上海-北京,1150,上海-广州,1080,上海-成都,1380);privatestaticfinalMapString,IntegerCZ_PRICESMap.of(上海-北京,860,上海-广州,1320,上海-成都,1560);// 三个查询工具结构完全一致航司不同AgentTool(查询东方航空MU的机票价格)publicStringqueryMuFlight(Param(出发城市)StringfromCity,Param(目的城市)StringtoCity,Param(日期格式 YYYY-MM-DD)Stringdate){StringroutefromCity-toCity;IntegerpriceMU_PRICES.get(route);if(pricenull){returnString.format(东方航空暂无 %s 航线,route);}returnString.format(东方航空MU%s %s → %s票价 ¥%d,date,fromCity,toCity,price);}AgentTool(查询中国国际航空CA的机票价格)publicStringqueryCaFlight(Param(出发城市)StringfromCity,Param(目的城市)StringtoCity,Param(日期格式 YYYY-MM-DD)Stringdate){StringroutefromCity-toCity;IntegerpriceCA_PRICES.get(route);if(pricenull){returnString.format(国航暂无 %s 航线,route);}returnString.format(中国国航CA%s %s → %s票价 ¥%d,date,fromCity,toCity,price);}AgentTool(查询南方航空CZ的机票价格)publicStringqueryCzFlight(Param(出发城市)StringfromCity,Param(目的城市)StringtoCity,Param(日期格式 YYYY-MM-DD)Stringdate){StringroutefromCity-toCity;IntegerpriceCZ_PRICES.get(route);if(pricenull){returnString.format(南航暂无 %s 航线,route);}returnString.format(南方航空CZ%s %s → %s票价 ¥%d,date,fromCity,toCity,price);}// 订票工具在比价完成后由模型决策调用AgentTool(确认订购机票输入航司、出发城市、目的城市和日期)publicStringbookFlight(Param(航司名称如东方航空、国航、南航)Stringairline,Param(出发城市)StringfromCity,Param(目的城市)StringtoCity,Param(日期格式 YYYY-MM-DD)Stringdate){returnString.format(✅ 订票成功%s %s → %s日期 %s订单号 ORD-%d,airline,fromCity,toCity,date,(long)(Math.random()*900000)100000);}}这里有个设计取舍值得注意三家航司的查询逻辑完全相同也可以合并成一个带airline参数的工具。但拆成三个独立工具有一个好处——模型不需要在参数层面决定调哪家减少一层推理负担在复杂场景下出错率更低。三、构建 Agent只换工具类AgentExecutor的构建方式与上篇完全一致唯一的变化是把工具换成FlightToolsTestpublicvoidflightCompareAndBook(){AgentExecutoragentAgentExecutor.builder(chainActor).llm(ChatAliyun.builder().model(qwen-plus).temperature(0f).build()).tools(newFlightTools())// 传入工具类框架自动扫描 AgentTool 方法.maxIterations(10)// 多步推理需要留足轮次3次查询 1次订票 若干 Thought.onThought(System.out::print).onObservation(obs-System.out.println(Observation: obs)).build();ChatGenerationresultagent.invoke(我要订2024-03-15从上海飞北京的机票请帮我查询东方航空、国航、南航三家的价格选最便宜的那家帮我订票);System.out.println(\n 最终答案 );System.out.println(result.getText());}maxIterations设为 10 留有余量三次查询工具 一次订票工具 至少 4 轮 Action加上每轮的 Thought实际消耗大约 6~8 轮。四、Agent 的完整推理轨迹运行后onThought和onObservation回调会把整个推理过程打印出来Thought: 需要分别查询三家航司的票价。先查东方航空。 Action: query_mu_flight Action Input: {fromCity: 上海, toCity: 北京, date: 2024-03-15} Observation: 东方航空MU2024-03-15 上海 → 北京票价 ¥980 Thought: 东方航空 ¥980继续查国航。 Action: query_ca_flight Action Input: {fromCity: 上海, toCity: 北京, date: 2024-03-15} Observation: 中国国航CA2024-03-15 上海 → 北京票价 ¥1150 Thought: 国航 ¥1150继续查南航。 Action: query_cz_flight Action Input: {fromCity: 上海, toCity: 北京, date: 2024-03-15} Observation: 南方航空CZ2024-03-15 上海 → 北京票价 ¥860 Thought: 三家对比南航 ¥860 最便宜调用订票工具。 Action: book_flight Action Input: {airline: 南航, fromCity: 上海, toCity: 北京, date: 2024-03-15} Observation: ✅ 订票成功南航 上海 → 北京日期 2024-03-15订单号 ORD-342871 Final Answer: 已为您查询三家航司票价东方航空 ¥980、国航 ¥1150、南航 ¥860。 南航价格最低已成功订票订单号 ORD-342871。整个过程中模型做了三件事按顺序收集信息三次查询、在 Thought 中做比较判断、选定最优方案后调用下游工具。这正是 ReAct 框架的核心价值——推理与行动交替进行每一步的 Observation 都成为下一步 Thought 的输入。五、关键设计点总结工具拆分粒度每个工具只做一件事Observation 格式固定统一。工具粒度越细模型在 Thought 中的推理越容易出错率越低。参数设计多参数工具统一用 JSON 格式的 Action InputParam注解的描述要具体尤其是日期格式这类有约束的参数明确写在注解里能显著减少模型的格式错误。maxIterations 留有余量估算最坏情况下的工具调用次数再留 2~3 轮 buffer避免因轮次不足导致推理中断。本例最少需要 4 次 Action设 10 是合理的上限。推理过程可观测onThought和onObservation两个回调不只是调试用途在生产环境中可以接入日志系统完整记录每次工具调用的输入输出方便排查问题。六、扩展思路这个模式可以直接复用到更多业务场景只需替换工具类场景工具组合最终动作比价采购多供应商报价查询 × N下单智能推荐多维度数据查询评分/价格/库存生成推荐报告数据汇总多表/多系统查询写入汇总表审批流程资质查询 规则校验提交审批 / 拒绝并说明原因核心模式是一样的多个信息收集工具 一个执行工具由 Agent 自主决策何时从收集切换到执行。 相关资源完整示例Article10FlightAgent.java对应方法flightCompareAndBook()j-langchain GitHubhttps://github.com/flower-trees/j-langchainj-langchain Gitee 镜像https://gitee.com/flower-trees-z/j-langchain运行环境需配置阿里云 API Key示例模型qwen-plus