在招聘示例中,最终是否录用仍由人决定。本讲为流程保留人工介入入口,即 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 生成)

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