如何通过Spring AI Alibaba的Human-in-the-Loop技术提升AI可靠性?

摘要:本文详解 Spring AI Alibaba 人工介入 Hook 实战,通过 Human-in-the-Loop 实现 AI 智能体执行暂停、人工审批与流程恢复,让 AI 应用更安全可控。
引言 在构建AI智能体应用时,我们经常面临一个关键挑战:如何让AI在执行某些敏感操作前获得人工确认?Spring AI Alibaba框架提供了强大的人工介入(Human-in-the-Loop)机制,让开发者能够精确控制AI工具的执行流程,在关键节点引入人工审批环节。 本文将通过一个完整的实战示例,详细介绍如何在Spring AI Alibaba应用中实现人工介入功能。 什么是人工介入? 人工介入是一种机制,它允许AI智能体在执行特定工具前暂停执行,等待人工审批后再继续。这种机制特别适用于: 敏感操作:如数据删除、资金转账等 内容生成:如文章发布、诗歌创作等需要质量把控的场景 权限控制:某些需要特定权限才能执行的操作 审计要求:需要记录人工决策过程的场景 实战示例:诗歌创作的人工审批 让我们通过一个具体的例子来理解人工介入Hook的使用。这个示例展示了如何让AI在创作诗歌前获得人工确认。 1. 项目依赖配置 首先,确保你的项目中包含了Spring AI Alibaba相关依赖: <dependencies> <!-- Spring AI Alibaba Agent Framework --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-agent-framework</artifactId> <version>1.1.2.0</version> </dependency> <!-- DashScope ChatModel 支持(如果使用其他模型,请跳转 Spring AI 文档选择对应的 starter) --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> <version>1.1.2.0</version> </dependency> </dependencies> 2. 代码实现解析 步骤1:构建AI模型 // 构建DashScope API对象 DashScopeApi dashScopeApi = DashScopeApi.builder() .apiKey(System.getenv("AliQwen_API")) .build(); // 创建聊天模型 ChatModel chatModel = DashScopeChatModel.builder() .dashScopeApi(dashScopeApi) .build(); 步骤2:配置工具 public class PoetTool implements BiFunction<String, ToolContext, String> { public int count = 0; public PoetTool() { } @Override public String apply( @ToolParam(description = "The original user query that triggered this tool call") String originalUserQuery, ToolContext toolContext) { count++; System.out.println("Poet tool called : " + originalUserQuery); return "在城市的缝隙里, \n" + "一束光悄悄发芽, \n" + "穿过钢筋水泥的沉默, \n" + "在风中轻轻说话。 \n" + "\n" + "夜色如墨,却不再黑, \n" + "星星点亮了每一个角落, \n" + "我站在时间的边缘, \n" + "等一朵云,轻轻落下"; } public static ToolCallback createPoetToolCallback() { return FunctionToolCallback.builder("poem", new PoetTool()) .description("用来写诗的工具") .inputType(String.class) .build(); } public static ToolCallback createPoetToolCallback(String name, PoetTool poetTool) { return FunctionToolCallback.builder(name, poetTool) .description("用来写诗的工具") .inputType(String.class) .build(); } } 步骤3:构建带有Hook的智能体 // 这里我们配置了poem工具需要人工审批,并提供了审批时的描述信息。 Map<String, ToolConfig> approvalOn = Map.of( "poem", ToolConfig.builder() .description("请确认诗歌工具执行") .build() ); ReactAgent agent = ReactAgent.builder() .name("single_agent") .model(chatModel) .saver(new MemorySaver()) // 使用内存保存状态 .tools(List.of(createPoetToolCallback())) // 添加诗歌创作工具 .hooks(HumanInTheLoopHook.builder() .approvalOn(approvalOn) // 添加人工介入Hook .build()) .outputKey("article") .build(); 步骤4:创建会话配置 String threadId = "user-session-001"; RunnableConfig config = RunnableConfig.builder() .threadId(threadId) .build(); 步骤5:执行并处理中断 // 第一次调用 - 触发中断 Optional<NodeOutput> result = agent.invokeAndGetOutput( "帮我写一首100字左右的诗", config ); // 检查是否触发中断 if (result.isPresent() && result.get() instanceof InterruptionMetadata) { InterruptionMetadata interruptionMetadata = (InterruptionMetadata) result.get(); System.out.println("检测到中断,需要人工审批"); // 获取工具反馈信息 List<InterruptionMetadata.ToolFeedback> toolFeedbacks = interruptionMetadata.toolFeedbacks(); for (InterruptionMetadata.ToolFeedback feedback : toolFeedbacks) { System.out.println("id: " + feedback.getId()); System.out.println("工具: " + feedback.getName()); System.out.println("参数: " + feedback.getArguments()); System.out.println("描述: " + feedback.getDescription()); } // 模拟人工决策(批准) InterruptionMetadata.Builder feedbackBuilder = InterruptionMetadata.builder() .nodeId(interruptionMetadata.node()) .state(interruptionMetadata.state()); toolFeedbacks.forEach(toolFeedback -> { InterruptionMetadata.ToolFeedback approvedFeedback = InterruptionMetadata.ToolFeedback.builder(toolFeedback) .result(InterruptionMetadata.ToolFeedback.FeedbackResult.APPROVED) .build(); feedbackBuilder.addToolFeedback(approvedFeedback); }); InterruptionMetadata approvalMetadata = feedbackBuilder.build(); // 使用人工反馈恢复执行 RunnableConfig resumeConfig = RunnableConfig.builder() .threadId(threadId) .addMetadata(RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY, approvalMetadata) .build(); Optional<NodeOutput> finalResult = agent.invokeAndGetOutput("", resumeConfig); if (finalResult.isPresent()) { System.out.println("执行完成"); // 因为创建智能体的时候,指定了outputKey,所以这里我们直接获取 Object article = finalResult.get().state().data().get("article"); System.out.println("最终结果: " + article); } } 3. 执行流程分析 这个示例的执行流程如下: 触发阶段:用户请求AI创作诗歌 中断阶段:AI检测到poem工具需要人工审批,暂停执行 审批阶段:系统展示工具信息,等待人工决策 恢复阶段:人工批准后,AI继续执行并生成诗歌 完成阶段:返回最终结果 高级特性 多工具审批 你可以为多个工具配置审批: Map<String, ToolConfig> approvalOn = Map.of( "poem", ToolConfig.builder().description("诗歌创作工具").build(), "delete", ToolConfig.builder().description("数据删除工具").build(), "publish", ToolConfig.builder().description("内容发布工具").build() ); 审批结果类型 支持多种审批结果: APPROVED:批准执行 REJECTED:拒绝执行 MODIFIED:修改参数后执行 最佳实践 1. 明确审批策略 只为真正需要人工确认的工具配置审批 提供清晰的审批描述信息 考虑审批的时效性 2. 用户体验优化 提供友好的审批界面 支持批量审批操作 记录审批历史便于审计 3. 错误处理 try { Optional<NodeOutput> result = agent.invokeAndGetOutput(request, config); // 处理中断和结果 } catch (GraphRunnerException e) { // 处理执行异常 log.error("智能体执行失败", e); } 4. 状态管理 // 使用合适的Saver .saver(new MemorySaver()) // 内存存储,适合开发测试 .saver(new RedisSaver()) // Redis存储,适合生产环境 .saver(new DatabaseSaver()) // 数据库存储,适合需要持久化的场景 5. 执行结果 拓展 Spring Ai Alibaba还为我们内置了几个其他的Hook SummarizationHook(消息压缩) 当对话很长时,自动压缩对话历史,防止超出模型上下文限制 ModelCallLimitHook(模型调用限制) 防止Agent无限调用模型,控制成本 另外,我们也可以自定义Hook,这部分内容如果大家感兴趣的话,后面可以单独介绍一下下~ 参考资料 HumanInTheLoopHook API文档