Agent 设计模式系列

Agent 设计模式 9:人机协同:为流程保留人工决策

通过控制台输入演示人工介入,再用条件工作流处理继续、拒绝或暂缓等指令。

在招聘示例中,最终是否录用仍由人决定。本讲为流程保留人工介入入口,即 human-in-the-loop,再根据人工输入选择后续分支。

示例代码:

@SpringBootApplication
public class _9a_HumanInTheLoop_Simple_Validator {

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.class, args);
        ChatModel model = context.getBean("ollamaChatModel", ChatModel.class);
        RagProvider ragProvider = context.getBean("ragProvider", RagProvider.class);

        // 3. 创建相关智能体
        InterviewOrganizer interviewOrganizer = AgenticServices.agentBuilder(InterviewOrganizer.class)
                .chatModel(model)
                .tools(new OrganizingTools())
                .contentRetriever(ragProvider.loadHouseRulesRetriever())
                .build();

        EmailAssistant emailAssistant = AgenticServices.agentBuilder(EmailAssistant.class)
                .chatModel(model)
                .tools(new OrganizingTools())
                .build();

        HiringDecisionProposer decisionProposer = AgenticServices.agentBuilder(HiringDecisionProposer.class)
                .chatModel(model)
                .outputKey("modelDecision")
                .build();

        // 2. 定义人工验证环节
        HumanInTheLoop humanValidator = AgenticServices.humanInTheLoopBuilder()
                .description("验证模型提出的招聘决策")
                .inputKey("modelDecision")
                .outputKey("finalDecision") // 由人工检查
                .requestWriter(request -> {
                    System.out.println("AI招聘助手建议: " + request);
                    System.out.println("请确认最终决定。");
                    System.out.println("选项: 邀请现场面试 (I), 拒绝 (R), 暂缓 (H)");
                    System.out.print("> "); // 在实际系统中需要输入验证和错误处理
                })
                .responseReader(() -> new Scanner(System.in).nextLine())
                .build();

        // 3. 将智能体链接成工作流
        UntypedAgent hiringDecisionWorkflow = AgenticServices.sequenceBuilder()
                .subAgents(decisionProposer, humanValidator)
                .outputKey("finalDecision")
                .build();

        // 4. 准备输入参数
        Map<String, Object> input = Map.of(
                "cvReview", new CvReview(0.85,
                        """
                                技术能力强,但缺乏所需的React经验。
                                似乎是快速独立学习者。文化契合度良好。
                                工作许可可能存在潜在问题,但似乎可以解决。
                                薪资期望略高于计划预算。
                                决定继续进行现场面试。
                                """)
        );

        System.out.println(input + "\n");

        // 5. 运行工作流
        String finalDecision = (String) hiringDecisionWorkflow.invoke(input);

        System.out.println("\n=== 人工最终决定 ===");
        System.out.println("(邀请现场面试 (I), 拒绝 (R), 暂缓 (H))\n");
        System.out.println(finalDecision);

        UntypedAgent candidateResponder = AgenticServices
                .conditionalBuilder()
                .subAgents(agenticScope -> finalDecision.contains("I"), interviewOrganizer)
                .subAgents(agenticScope -> finalDecision.contains("R"), emailAssistant)
                .subAgents(agenticScope -> finalDecision.contains("H"), new HoldOnAssist())
                .build();

        String candidateContact = StringLoader.loadFromResource("/documents/candidate_contact.txt");
        String jobDescription = StringLoader.loadFromResource("/documents/job_description_backend.txt");


        Map<String, Object> arguments = Map.of(
                "candidateContact", candidateContact,
                "jobDescription", jobDescription
        );

        // 6. 根据人工最终决定,进行下一步操作
        candidateResponder.invoke(arguments);

        // 注意:人工参与和人工验证通常需要较长时间等待用户响应。
        // 在这种情况下,建议使用异步智能体,这样它们不会阻塞工作流的其余部分,
        // 这些部分可以在用户回答之前潜在执行。
    }
}

解释一下:

代码第 27 行通过 AgenticServices.humanInTheLoopBuilder() 声明人工协助环节,第 29 行使用 new Scanner(System.in).nextLine() 读取控制台输入。原文也提出可以改为读取数据库或调用其他系统接口,具体接入未在此实现。

第 67—71 行根据人工指令选择响应,沿用前面介绍的条件工作流。输入 H 时执行下面的 Agent:

public class HoldOnAssist {

    @Agent(description = "招聘流程暂缓")
    public void abort() {
        System.out.println("招聘流程暂缓");
    }
}

输出日志:

展开原文日志或请求/响应(未复测)
{cvReview=
CvReview:  - score = 0.85
- feedback = "技术能力强,但缺乏所需的React经验。
似乎是快速独立学习者。文化契合度良好。
工作许可可能存在潜在问题,但似乎可以解决。
薪资期望略高于计划预算。
决定继续进行现场面试。
"
}

AI招聘助手建议: **招聘理由汇总:**

**优势:** 技术能力强,文化契合度良好,学习能力佳

**风险:** 缺乏React经验,薪资略超预算,潜在工作许可问题  

**建议:** 继续现场面试评估,面试后综合决定
请确认最终决定。
选项: 邀请现场面试 (I), 拒绝 (R), 暂缓 (H)
> H

=== 人工最终决定 ===
(邀请现场面试 (I), 拒绝 (R), 暂缓 (H))

H
招聘流程暂缓

人机协同的控制台交互示例

原文配图:人机协同的控制台交互示例。

时序图(简化版,原文标注为 AI 生成)

人机协同:为流程保留人工决策:简化时序图

原文配图:人机协同:为流程保留人工决策:简化时序图。

时序图(详细版,原文标注为 AI 生成)

人机协同:为流程保留人工决策:详细时序图

原文配图:人机协同:为流程保留人工决策:详细时序图。