招聘流程中并非每一步都需要模型。例如,汇总 HR、经理和团队的评分,再按平均分判断申请状态,可以直接用 Java 实现。原文把这类参与编排、但不直接调用 LLM 的操作称为“非 AI 智能体”。
AgenticServices.agentBuilder(XXX)
.chatModel(model)
.outputKey("xxx")
.build();
先定义2个不需要AI参与的处理环节(也称非AI智能体)
/**
* 非AI智能体,将多个简历评审聚合成一个综合评审。
* 这演示了普通Java操作符如何作为一等智能体用于智能体工作流,
* 使它们能够与AI驱动的智能体互换使用。
*/
public class ScoreAggregator {
@Agent(description = "将HR/经理/团队评审聚合成综合评审", outputKey = "combinedCvReview")
public CvReview aggregate(@V("hrReview") CvReview hr,
@V("managerReview") CvReview mgr,
@V("teamMemberReview") CvReview team) {
System.out.println("ScoreAggregator被调用,参数:hrReview=" + hr +
", managerReview=" + mgr +
", teamMemberReview=" + team);
double avgScore = (hr.score + mgr.score + team.score) / 3.0;
String combinedFeedback = String.join("\n\n",
"HR评审: " + hr.feedback,
"经理评审: " + mgr.feedback,
"团队成员评审: " + team.feedback
);
return new CvReview(avgScore, combinedFeedback);
}
}
类上仍使用 @Agent 参与统一编排,但这一步由普通 Java 方法处理,不需要 LLM 直接参与。
/**
* 非AI智能体,根据评分更新申请状态。
* 这演示了普通Java操作符如何作为一等智能体用于智能体工作流,
* 使它们能够与AI驱动的智能体互换使用。
*/
public class StatusUpdate {
@Agent(description = "根据评分更新申请状态")
public void update(@V("combinedCvReview") CvReview aggregateCvReview) {
double score = aggregateCvReview.score;
System.out.println("StatusUpdate被调用,评分: " + score);
if (score >= 8.0) {
// 示例用的模拟数据库更新
System.out.println("申请状态更新为: 已邀请面试");
} else {
// 示例用的模拟数据库更新
System.out.println("申请状态更新为: 已拒绝");
}
}
}
完整的串起来,示例如下:
/**
* 这里展示如何在智能体工作流中使用非AI智能体(普通Java操作符)。
* 非AI智能体只是普通的方法,但可以像其他类型的智能体一样使用。
* 它们非常适合确定性的操作,如计算、数据转换和聚合,这些操作不需要LLM参与。
* 将更多步骤外包给非AI智能体,你的工作流将更快、更准确、更经济。
* 对于需要强制确定性执行的步骤,非AI智能体比工具更受青睐。
* 在这个例子中,我们希望评审者的综合评分是确定性计算的,而不是由LLM计算。
* 我们同样基于综合评分确定性地更新数据库中的申请状态。
* by 菩提树下的杨过(yjmyzz.cnblogs.com)
*/
@SpringBootApplication
public class _8_Non_AI_Agents {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.class, args);
ChatModel model = context.getBean("ollamaChatModel", ChatModel.class);
// 2. 构建并行评审步骤的AI子智能体
HrCvReviewer hrReviewer = AgenticServices.agentBuilder(HrCvReviewer.class)
.chatModel(model)
.outputKey("hrReview")
.build();
ManagerCvReviewer managerReviewer = AgenticServices.agentBuilder(ManagerCvReviewer.class)
.chatModel(model)
.outputKey("managerReview")
.build();
TeamMemberCvReviewer teamReviewer = AgenticServices.agentBuilder(TeamMemberCvReviewer.class)
.chatModel(model)
.outputKey("teamMemberReview")
.build();
// 3. 构建组合的并行智能体
var executor = Executors.newFixedThreadPool(3); // 保留引用以便后续关闭
UntypedAgent parallelReviewWorkflow = AgenticServices
.parallelBuilder()
.subAgents(hrReviewer, managerReviewer, teamReviewer)
.executor(executor)
.build();
// 4. 构建包含非AI智能体的完整工作流
UntypedAgent collectFeedback = AgenticServices
.sequenceBuilder()
.subAgents(
parallelReviewWorkflow,
new ScoreAggregator(), // 非AI智能体不需要AgenticServices构建器。outputKey 'combinedCvReview' 已在类中定义
new StatusUpdate(), // 以'combinedCvReview'作为输入,不需要输出
AgenticServices.agentAction(agenticScope -> { // 添加非AI智能体的另一种方式,可以操作AgenticScope
CvReview review = (CvReview) agenticScope.readState("combinedCvReview");
agenticScope.writeState("scoreAsPercentage", review.score * 100); // 当不同系统的智能体通信时,通常需要输出转换
})
)
.outputKey("scoreAsPercentage") // outputKey在ScoreAggregator.java中的非AI智能体注解中定义
.build();
// 5. 加载输入数据
String candidateCv = StringLoader.loadFromResource("/documents/tailored_cv.txt");
String candidateContact = StringLoader.loadFromResource("/documents/candidate_contact.txt");
String hrRequirements = StringLoader.loadFromResource("/documents/hr_requirements.txt");
String phoneInterviewNotes = StringLoader.loadFromResource("/documents/phone_interview_notes.txt");
String jobDescription = StringLoader.loadFromResource("/documents/job_description_backend.txt");
Map<String, Object> arguments = Map.of(
"candidateCv", candidateCv,
"candidateContact", candidateContact,
"hrRequirements", hrRequirements,
"phoneInterviewNotes", phoneInterviewNotes,
"jobDescription", jobDescription
);
// 6. 调用工作流
double scoreAsPercentage = (double) collectFeedback.invoke(arguments);
executor.shutdown();
System.out.println("=== 百分比形式的评分 ===");
System.out.println(scoreAsPercentage);
// 从日志中我们可以看到,申请状态也已经相应更新
}
}
代码第 49—54 行演示两种接入方式:直接创建对象,或使用 AgenticServices.agentAction(agenticScope -> {...}) 包装操作。
原文输出(未复测):
展开原文评分与状态输出
-——————————–
ScoreAggregator被调用,参数:hrReview=
CvReview: - score = 0.82
- feedback = “优点:简历清晰专业,联系方式完整。拥有比利时公民身份,合法工作许可。荷兰语母语水平,英语C1水平符合要求。工作履历稳定,无职业空白期,每段工作经历均超过1年。技术技能匹配度高,具有4年以上Java/Spring Boot开发经验,职责范围从单纯开发扩展到指导实习生,体现了成长。
不足之处:薪资期望75,000欧元略高于55,000-70,000欧元范围。可入职时间需要3个月通知,与理想的1-2个月入职时间存在差距。分布式系统经验有限,Kafka仅为基础水平。
技能评估:技术栈符合要求但深度不够,特别是分布式系统和现代微服务架构经验不足。指导他人的经验是加分项。
综合评估:候选人基础条件良好,技能匹配度较高,但薪资和入职时间需要进一步沟通。如果能协调薪资水平或缩短入职等待时间,建议进入面试环节。”
, managerReview=
CvReview: - score = 0.82
- feedback = “约翰·多伊是一位非常匹配的候选人。他在BrightPay Systems的工作经历特别出色,恰好满足职位的核心要求:具备Java/Spring Boot后端开发经验,成功完成SOAP到REST的API迁移,开发了定价工具系统,并取得了显著的量化成果(报价时间缩短35%)。地理位置理想,语言技能完全符合要求(荷兰语母语+英语C1)。技术栈匹配度高,包括PostgreSQL、CI/CD(GitHub Actions)、Docker等。个人项目展现了对Spring生态系统的深度理解。
主要不足包括:Kafka仅停留在概念验证阶段,缺乏生产级经验;对Kubernetes的实际应用经验不足;从金融科技角度来说,工作经验主要集中在定价工具开发,缺乏完整的支付与对账系统经验。但考虑到整体匹配度很高,这些不足完全可以通过培训和实践来弥补。建议邀请参加现场面试,重点评估其在支付系统架构设计方面的思维能力和学习潜力。”
, teamMemberReview=
CvReview: - score = 0.75
- feedback = “简历整体质量良好,展现了扎实的后端开发基础。优点:4年经验符合岗位要求,技术栈匹配(Java, Spring Boot, PostgreSQL),有具体量化成就(报价时间缩短35%),完成API现代化迁移,建立CI/CD流程,多语言能力突出。不足:Kafka只有基础水平且只是概念验证,个人项目描述过于简单,缺少测试经验(TDD、单元测试等),没有云平台经验(AWS/GCP/Azure),技术栈相对传统。需要改进:增加具体的技术实现细节,补充测试和云平台技能,完善项目描述。风险信号:涉及前端开发可能分散后端专业性,职业转换路径(机械工程→编程)需要进一步了解深度。”
StatusUpdate被调用,评分: 0.7966666666666665
申请状态更新为: 已拒绝
=== 百分比形式的评分 ===
79.66666666666666
-————————————-
时序图(简化版,原文标注为 AI 生成)

原文配图:非 AI 智能体:用 Java 完成确定性操作:简化时序图。
时序图(详细版,原文标注为 AI 生成)

原文配图:非 AI 智能体:用 Java 完成确定性操作:详细时序图。