这次我们来看一个基于 Spring AI Alibaba Graph 构建 HR 自动化 AI Agent 的实战项目。对于 Java 开发者而言,直接上手大模型应用开发往往面临框架选择、流程编排、成本控制等门槛。这个项目提供了一个清晰的落地案例,将 Spring AI 的便捷性与 Alibaba Graph 的编排能力结合,实现了一个能处理简历筛选、面试邀约等任务的智能体。核心不是概念讲解,而是如何一步步搭建、运行并优化一个可工作的 AI Agent。
本文将带你从零开始,完成一个跨行业通用的 HR 自动化 AI Agent 搭建。你会了解到如何用 Java 代码连接大模型、如何设计 Agent 的工作流、如何通过图编排减少不必要的 Token 消耗,以及如何应对开发中常见的 OOM、版本兼容等问题。无论你是想将 AI 能力集成到现有系统,还是为面试准备一个亮眼的项目经验,这篇文章都能提供直接的代码和思路。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 技术栈 | Java 17+, Spring Boot 3.x, Spring AI, Alibaba Graph (用于智能体编排) |
| 核心功能 | 构建具备多步骤推理和工具调用能力的 HR 自动化 AI Agent(如简历解析、人岗匹配、面试问题生成) |
| 大模型接入 | 支持通过 Spring AI 标准接口接入 OpenAI、通义千问、DeepSeek 等主流模型,便于切换和测试 |
| 编排引擎 | 使用 Alibaba Graph 进行 Agent 工作流编排,实现条件分支、循环、并行等复杂逻辑,优化 Token 使用 |
| 显存/资源需求 | 本项目为后端应用,资源消耗取决于 Spring Boot 应用本身。大模型调用为远程 API 请求,本地无需 GPU。 |
| 启动方式 | 标准 Spring Boot 应用,可通过mvn spring-boot:run或打包成 Jar 后java -jar启动。 |
| 是否支持 API | 是,提供 RESTful API 供前端或其他系统调用 AI Agent 服务。 |
| 是否支持批量任务 | 是,可通过设计异步任务或批处理接口,实现简历的批量处理。 |
| 适合场景 | Java 开发者学习 AI 应用开发、企业 HR 系统智能化升级、面试项目实战、Spring AI 生态技术调研。 |
2. 适用场景与使用边界
这个项目主要面向两类人群:一是希望将 AI 能力融入现有 Java 技术栈的开发者;二是正在寻找具有竞争力的实战项目以应对面试的求职者。它演示了如何将一个业务场景(HR自动化)转化为由 AI Agent 驱动的标准化流程。
它能解决什么问题?
- 自动化简历初筛:Agent 可以解析简历文本,提取关键信息(技能、经验、学历),并与职位要求进行匹配打分。
- 智能面试邀约:根据候选人情况和岗位特点,自动生成个性化的面试邀约邮件或消息。
- 面试问题生成:针对特定职位和简历内容,生成专业、有深度的面试问题。
- 面试反馈整理:将面试官的文本记录或语音转文本内容,整理成结构化的面试评价。
不适合什么场景?
- 完全离线的本地模型推理:本项目默认采用调用远程大模型 API 的方式,如需完全本地部署模型,需要额外集成本地模型服务(如 Ollama),并调整 Spring AI 的配置。
- 非结构化的复杂决策:对于涉及大量公司内部潜规则、高度依赖人际判断的终极录用决策,AI Agent 目前仅能作为辅助工具。
- 零代码需求:这是一个需要开发、部署和维护的 Java 项目,不是开箱即用的 SaaS 产品。
合规与安全边界:
- 数据隐私:处理简历等个人信息时,必须确保符合相关数据保护法规(如 GDPR、个人信息保护法)。所有数据需加密传输和存储,并明确告知用户数据用途。
- 模型偏见:大模型可能隐含训练数据带来的偏见,在简历筛选中需谨慎设置评判标准,建议“人机协同”复核,避免歧视性筛选。
- 授权使用:确保用于生成内容(如面试问题)的模型 API 调用是合法且已获得授权的。
3. 环境准备与前置条件
在开始编码之前,请确保你的开发环境满足以下要求。这是项目能成功启动和运行的基础。
Java 开发环境:
- JDK 17 或更高版本:这是 Spring Boot 3.x 的强制要求。使用
java -version确认。 - 常见问题:如果遇到
java: 警告: 源发行版 17 需要目标发行版 17这类错误,请在 IDE(如 IntelliJ IDEA)的 Project Structure 和 Settings for New Projects 中,将项目的 SDK 和 Language level 均设置为 17。Maven 的pom.xml中也需要配置对应的maven-compiler-plugin。
- JDK 17 或更高版本:这是 Spring Boot 3.x 的强制要求。使用
构建工具:
- Maven 3.6+或Gradle:本文以 Maven 为例。使用
mvn -v确认。
- Maven 3.6+或Gradle:本文以 Maven 为例。使用
IDE(可选但推荐):
- IntelliJ IDEA, VS Code, Eclipse 等。确保已安装 Lombok 插件(如果项目使用了 Lombok),以避免 Getter/Setter 报错。
大模型 API 密钥:
- 你需要准备一个或多个大模型的 API Key,用于 Spring AI 进行调用。例如:
- 阿里云灵积:用于通义千问等模型。
- OpenAI:用于 GPT 系列模型。
- DeepSeek:或其他支持 OpenAI 兼容接口的模型服务。
- 将 API Key 保存在安全的地方,我们将在配置文件中使用。
- 你需要准备一个或多个大模型的 API Key,用于 Spring AI 进行调用。例如:
网络环境:
- 确保你的开发机器能够访问你所选大模型的 API 服务地址。
4. 项目初始化与依赖配置
我们从一个标准的 Spring Boot 3.x 项目开始。你可以通过 Spring Initializr 生成,或直接使用以下pom.xml核心依赖配置。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>hr-ai-agent</artifactId> <version>0.0.1-SNAPSHOT</version> <name>hr-ai-agent</name> <description>HR Automation AI Agent with Spring AI Alibaba Graph</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <!-- 使用较新稳定版 --> <relativePath/> </parent> <properties> <java.version>17</java.version> <spring-ai.version>0.8.1</spring-ai.version> <!-- 确认使用最新稳定版 --> </properties> <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI - 核心依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Spring AI - OpenAI 兼容接口 (以阿里云为例) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-alibaba-ai-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Alibaba Graph (用于Agent编排) --> <!-- 注意:Spring AI 的 Graph 功能可能仍在演进,请查阅官方文档确认最新依赖 --> <!-- 此处假设通过 spring-ai-alibaba-graph 或类似模块引入 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-alibaba-graph</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- 工具类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>17</source> <target>17</target> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>关键依赖说明:
spring-ai-alibaba-ai-spring-boot-starter:用于连接阿里云的大模型服务。spring-ai-alibaba-graph:这是实现 Agent 工作流编排的核心。Alibaba Graph 提供了一种声明式的方式来定义 AI Agent 的执行流程,包括顺序、分支、循环和并行节点,这对于构建复杂的 HR 自动化逻辑至关重要。
接下来,配置application.yml来设置模型连接和 Graph 定义。
# application.yml spring: application: name: hr-ai-agent ai: # 配置阿里云灵积作为模型提供商 alibaba-ai: # 从环境变量或配置中心读取更安全 api-key: ${ALIBABA_AI_API_KEY:your-api-key-here} # 通义千问 Max 模型 chat-options: model: qwen-max # 其他参数如 temperature, top_p 等 temperature: 0.7 # 阿里云灵积 endpoint base-url: https://dashscope.aliyuncs.com/compatible-mode/v1 # Alibaba Graph 配置 (示例,具体配置项需参考官方文档) graph: # 可以在这里定义全局的Graph配置,如执行器线程池等 enabled: true # 自定义配置 hr: agent: # 匹配度阈值,高于此值才进入下一轮 resume-match-threshold: 0.75 # 默认的职位描述文件路径 default-jd-path: classpath:jd/template_jd.md # 服务器端口 server: port: 8080 # 日志级别,调试时可开启DEBUG logging: level: org.springframework.ai: DEBUG com.example.hragent: DEBUG5. 核心概念与领域模型设计
在编写业务代码前,我们需要定义清晰的数据结构和 Agent 所要处理的领域模型。这有助于后续工具(Tools)和流程(Graph)的设计。
// 简历实体 @Data public class Resume { private String candidateId; private String candidateName; private String email; private String phone; private String rawText; // 简历原始文本 private String structuredJson; // 解析后的结构化JSON private List<WorkExperience> workExperiences; private List<Education> educations; private List<String> skills; private String selfEvaluation; } // 职位描述实体 @Data public class JobDescription { private String jobId; private String jobTitle; private String department; private List<String> requiredSkills; private List<String> preferredSkills; private String experienceRequirement; private String educationRequirement; private String responsibilities; } // 匹配结果 @Data public class MatchResult { private String candidateId; private String jobId; private Double matchScore; // 匹配度分数 0-1 private Map<String, String> scoreDetails; // 各项得分详情,如技能匹配度、经验匹配度 private String evaluationSummary; // AI生成的综合评价摘要 private MatchStatus status; // 枚举:PASS, REJECT, REVIEW_NEEDED } // 面试问题 @Data public class InterviewQuestion { private String questionId; private String questionText; private QuestionType type; // 枚举:TECHNICAL, BEHAVIORAL, CULTURAL private String referencePoint; // 问题参考点,如针对简历中某段经历 private String expectedAnswerHint; // 期望回答要点(供面试官参考) }6. 构建 AI Agent 工具(Tools)
AI Agent 的强大之处在于它能调用工具(Tools)来执行具体操作。在 Spring AI 中,工具通常是一个带有@Tool注解的 Bean 方法。我们将为 HR Agent 创建几个核心工具。
6.1 简历解析工具
这个工具接收原始简历文本,调用大模型将其解析为结构化的Resume对象。
@Component public class ResumeParserTool { @Autowired private ChatClient chatClient; // Spring AI 注入的 ChatClient @Tool(name = "parseResume", description = "解析一份简历的原始文本,提取候选人姓名、联系方式、工作经历、教育背景、技能列表和自我评价等信息,并返回结构化的JSON数据。") public String parseResume(@Param(description = "简历的原始文本内容") String rawResumeText) { // 构建一个强引导的提示词,让模型输出标准JSON String systemPrompt = """ 你是一个专业的简历解析专家。请将以下简历文本解析为结构化的JSON格式。 JSON结构必须严格遵循以下Schema: { "candidateName": "字符串", "email": "字符串", "phone": "字符串", "workExperiences": [ { "company": "字符串", "position": "字符串", "duration": "字符串", "description": "字符串" } ], "educations": [ { "school": "字符串", "degree": "字符串", "major": "字符串", "duration": "字符串" } ], "skills": ["字符串数组"], "selfEvaluation": "字符串" } 只输出JSON,不要有任何其他解释性文字。 """; UserMessage userMessage = new UserMessage(rawResumeText); SystemMessage systemMessage = new SystemMessage(systemPrompt); Prompt prompt = new Prompt(List.of(systemMessage, userMessage)); ChatResponse response = chatClient.call(prompt); String jsonOutput = response.getResult().getOutput().getContent(); // 这里可以添加JSON格式校验和日志 return jsonOutput; } }6.2 人岗匹配评估工具
这个工具接收结构化的简历和职位描述,计算匹配度并给出评价。
@Component public class JobMatchEvaluatorTool { @Autowired private ChatClient chatClient; @Tool(name = "evaluateJobMatch", description = "评估一份简历与一个职位描述的匹配程度。需要输入结构化的简历JSON和职位描述JSON,返回一个包含匹配分数(0-1)、详细得分项和综合评价的JSON。") public String evaluateJobMatch( @Param(description = "结构化的简历信息,JSON格式") String resumeJson, @Param(description = "结构化的职位描述信息,JSON格式") String jobDescriptionJson) { String systemPrompt = """ 你是一个资深的HR专家和技术面试官。请基于提供的简历和职位描述,进行综合匹配度评估。 评估维度包括: 1. 技能匹配度 (权重0.4) 2. 工作经验匹配度 (权重0.3) 3. 教育背景匹配度 (权重0.2) 4. 自我评价与岗位文化契合度 (权重0.1) 请为每个维度打分(0-1),并计算加权总分。 同时,生成一段不超过200字的综合评价,突出候选人的优势和潜在风险。 输出必须为严格的JSON格式: { "overallScore": 0.85, "scoreDetails": { "skillMatch": 0.9, "experienceMatch": 0.8, "educationMatch": 0.7, "cultureFit": 0.8 }, "evaluationSummary": "候选人具备岗位所需的Java和Spring核心技术栈,并有电商项目经验...", "recommendation": "PASS" // 或 "REJECT", "REVIEW_NEEDED" } 只输出JSON。 """; String userInput = String.format("简历信息:%s\n\n职位描述信息:%s", resumeJson, jobDescriptionJson); Prompt prompt = new Prompt(List.of(new SystemMessage(systemPrompt), new UserMessage(userInput))); ChatResponse response = chatClient.call(prompt); return response.getResult().getOutput().getContent(); } }6.3 面试问题生成工具
为通过初筛的候选人,生成定制化的面试问题。
@Component public class InterviewQuestionGeneratorTool { @Autowired private ChatClient chatClient; @Tool(name = "generateInterviewQuestions", description = "根据候选人的简历和职位要求,生成一组面试问题。问题应涵盖技术、行为和文化适配等方面。") public String generateInterviewQuestions( @Param(description = "结构化的简历信息") String resumeJson, @Param(description = "职位描述信息") String jobDescriptionJson, @Param(description = "需要生成的问题数量,默认5个") @Nullable Integer questionCount) { int count = (questionCount != null && questionCount > 0) ? questionCount : 5; String systemPrompt = String.format(""" 你是一个技术面试官。请根据以下简历和职位描述,生成%d个高质量的面试问题。 问题应多样化,包括: - 2-3个针对简历中具体项目经历的技术深度问题。 - 1-2个行为面试问题(如团队合作、冲突解决)。 - 1个关于职业规划或文化适配的问题。 为每个问题标注其类型(TECHNICAL, BEHAVIORAL, CULTURAL)并提供一个简短的“面试官参考要点”,说明考察意图。 输出格式为JSON数组: [ { "questionText": "请详细描述你在XX项目中如何解决YY技术难题?", "type": "TECHNICAL", "referencePoint": "简历中的‘XX电商平台’项目", "expectedAnswerHint": "考察微服务架构设计、问题排查思路" }, ... ] 只输出JSON。 """, count); String userInput = String.format("简历:%s\n\n职位描述:%s", resumeJson, jobDescriptionJson); Prompt prompt = new Prompt(List.of(new SystemMessage(systemPrompt), new UserMessage(userInput))); ChatResponse response = chatClient.call(prompt); return response.getResult().getOutput().getContent(); } }7. 使用 Alibaba Graph 编排 HR Agent 工作流
有了工具,我们需要一个“大脑”来协调它们。这就是 Alibaba Graph 发挥作用的地方。Graph 允许我们以可视化的思维定义 Agent 的执行逻辑。在代码中,我们通过定义Graph和Node来实现。
下面是一个简化的 HR 初筛工作流 Graph 定义:
@Configuration public class HrScreeningGraphConfig { @Autowired private ResumeParserTool resumeParserTool; @Autowired private JobMatchEvaluatorTool jobMatchEvaluatorTool; @Bean public Graph hrScreeningGraph() { // 1. 定义节点 (Node) // 输入节点:接收原始简历和职位描述 Node startNode = new StartNode("start"); // 工具节点:解析简历 Node parseResumeNode = new ToolNode("parseResume", context -> { String rawResume = context.get("rawResume", String.class); String parsedJson = resumeParserTool.parseResume(rawResume); context.put("parsedResumeJson", parsedJson); return parsedJson; }); // 工具节点:评估匹配度 Node evaluateMatchNode = new ToolNode("evaluateMatch", context -> { String parsedResumeJson = context.get("parsedResumeJson", String.class); String jobDescriptionJson = context.get("jobDescriptionJson", String.class); String evaluationResult = jobMatchEvaluatorTool.evaluateJobMatch(parsedResumeJson, jobDescriptionJson); context.put("evaluationResult", evaluationResult); return evaluationResult; }); // 条件判断节点:根据匹配分数决定流程分支 Node decisionNode = new DecisionNode("decision", context -> { String evalResult = context.get("evaluationResult", String.class); // 简单解析JSON获取总分,实际应用建议用JsonPath或对象映射 Double score = parseScoreFromJson(evalResult); // 假设这是一个解析方法 String thresholdStr = context.get("threshold", String.class); double threshold = thresholdStr != null ? Double.parseDouble(thresholdStr) : 0.75; return score >= threshold ? "pass" : "reject"; }); // 输出节点:通过分支 Node passNode = new EndNode("pass", context -> { context.put("finalDecision", "PASS"); context.put("message", "候选人通过初筛,进入面试问题生成环节。"); return context; }); // 输出节点:拒绝分支 Node rejectNode = new EndNode("reject", context -> { context.put("finalDecision", "REJECT"); context.put("message", "候选人未达到初筛标准。"); return context; }); // 2. 构建图并连接节点 Graph graph = Graph.builder() .addNode(startNode) .addNode(parseResumeNode) .addNode(evaluateMatchNode) .addNode(decisionNode) .addNode(passNode) .addNode(rejectNode) // 设置边(连接关系) .link(startNode, parseResumeNode) // start -> parseResume .link(parseResumeNode, evaluateMatchNode) // parseResume -> evaluateMatch .link(evaluateMatchNode, decisionNode) // evaluateMatch -> decision // 决策节点的条件边 .link(decisionNode, "pass", passNode) // decision(pass) -> pass .link(decisionNode, "reject", rejectNode) // decision(reject) -> reject .build(); return graph; } // 辅助方法:从评估结果JSON中解析分数 private Double parseScoreFromJson(String json) { try { ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(json); return root.path("overallScore").asDouble(0.0); } catch (Exception e) { return 0.0; } } }Graph 编排的核心优势:
- 可视化逻辑:复杂的业务逻辑(如条件判断、循环)可以通过图清晰地表达。
- 减少 Token 消耗:通过精确的流程控制,只在必要的节点调用大模型,避免了在单个提示词中堆砌所有逻辑导致的长文本和高 Token 消耗。例如,只有在解析完简历后,才将结构化的数据(而非原始长文本)传递给匹配评估节点。
- 易于维护和扩展:新增一个筛选环节(如薪资期望匹配)只需在图中插入一个新节点并调整连接即可。
8. 暴露 RESTful API 与服务启动
最后,我们需要一个控制器(Controller)来接收外部请求,触发 Graph 执行,并返回结果。
@RestController @RequestMapping("/api/hr-agent") @Slf4j public class HrAgentController { @Autowired private Graph hrScreeningGraph; // 注入我们定义的Graph @PostMapping("/screen") public ResponseEntity<Map<String, Object>> screenCandidate(@RequestBody ScreeningRequest request) { log.info("收到简历初筛请求,候选人:{}, 职位:{}", request.getCandidateName(), request.getJobId()); // 1. 准备执行上下文 (Execution Context) Map<String, Object> contextMap = new HashMap<>(); contextMap.put("rawResume", request.getRawResumeText()); contextMap.put("jobDescriptionJson", request.getJobDescriptionJson()); contextMap.put("threshold", String.valueOf(request.getThreshold())); // 可从请求传入阈值 // 2. 执行 Graph ExecutionResult result; try { result = hrScreeningGraph.execute(contextMap); } catch (Exception e) { log.error("执行HR筛选Graph时发生错误", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(Map.of("error", "AI Agent处理失败", "detail", e.getMessage())); } // 3. 从结果上下文中提取输出 Map<String, Object> outputContext = result.getOutput(); String finalDecision = (String) outputContext.get("finalDecision"); String message = (String) outputContext.get("message"); String evaluationResult = (String) outputContext.get("evaluationResult"); // 4. 构建响应 Map<String, Object> response = new HashMap<>(); response.put("requestId", UUID.randomUUID().toString()); response.put("candidateId", request.getCandidateId()); response.put("jobId", request.getJobId()); response.put("decision", finalDecision); response.put("message", message); if (evaluationResult != null) { try { response.put("evaluationDetails", new ObjectMapper().readTree(evaluationResult)); } catch (JsonProcessingException e) { response.put("evaluationDetails", evaluationResult); } } response.put("timestamp", Instant.now()); return ResponseEntity.ok(response); } } // 请求体定义 @Data class ScreeningRequest { private String candidateId; private String candidateName; private String jobId; private String rawResumeText; private String jobDescriptionJson; private Double threshold = 0.75; // 默认阈值 }启动服务: 在项目根目录下,执行以下命令启动 Spring Boot 应用:
mvn clean spring-boot:run或者先打包再运行:
mvn clean package java -jar target/hr-ai-agent-0.0.1-SNAPSHOT.jar启动成功后,控制台会输出类似Started HrAiAgentApplication in X.XXX seconds的日志。应用将在http://localhost:8080运行。
9. 功能测试与效果验证
服务启动后,我们可以通过 API 测试工具(如 Postman、curl)或编写单元测试来验证 Agent 的功能。
9.1 单元测试示例
@SpringBootTest @AutoConfigureMockMvc class HrAgentControllerTest { @Autowired private MockMvc mockMvc; @Test void testScreenCandidate_Pass() throws Exception { String requestJson = """ { "candidateId": "123", "candidateName": "张三", "jobId": "JD_Java_01", "rawResumeText": "张三,男,25岁...精通Java、Spring Cloud、MySQL...有3年电商后端开发经验...", "jobDescriptionJson": "{\\"jobTitle\\":\\"Java高级开发工程师\\",\\"requiredSkills\\":[\\"Java\\",\\"Spring Boot\\",\\"MySQL\\"]}", "threshold": 0.7 } """; mockMvc.perform(MockMvcRequestBuilders .post("/api/hr-agent/screen") .contentType(MediaType.APPLICATION_JSON) .content(requestJson)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.decision").exists()) .andExpect(MockMvcResultMatchers.jsonPath("$.evaluationDetails.overallScore").isNumber()); } }9.2 使用 curl 进行接口测试
curl -X POST http://localhost:8080/api/hr-agent/screen \ -H "Content-Type: application/json" \ -d '{ "candidateId": "456", "candidateName": "李四", "jobId": "JD_Java_01", "rawResumeText": "李四,计算机科学硕士,擅长Python和机器学习,但Java经验较少...", "jobDescriptionJson": "{\"jobTitle\":\"Java高级开发工程师\",\"requiredSkills\":[\"Java\",\"Spring Boot\",\"MySQL\"]}", "threshold": 0.8 }'预期响应:
{ "requestId": "a1b2c3d4...", "candidateId": "456", "jobId": "JD_Java_01", "decision": "REJECT", "message": "候选人未达到初筛标准。", "evaluationDetails": { "overallScore": 0.65, "scoreDetails": { "skillMatch": 0.3, "experienceMatch": 0.6, "educationMatch": 0.9, "cultureFit": 0.7 }, "evaluationSummary": "候选人教育背景优秀,但核心技能(Java/Spring)与岗位要求差距较大...", "recommendation": "REJECT" }, "timestamp": "2024-05-27T10:30:00Z" }9.3 验证要点
- 服务连通性:确保
localhost:8080可访问,且/actuator/health端点返回UP。 - API 功能:调用
/api/hr-agent/screen接口,能收到结构化的 JSON 响应。 - 逻辑正确性:输入高匹配度的简历,返回
decision: “PASS”;输入低匹配度的简历,返回decision: “REJECT”。 - Graph 执行:观察应用日志,确认
parseResume、evaluateMatch、decision等节点被依次执行。 - Token 消耗优化:通过日志或模型服务商控制台,对比使用单一复杂提示词与使用 Graph 分步调用所产生的 Token 数量,验证优化效果。
10. 性能优化与常见问题排查
在实际使用中,你可能会遇到以下问题。这里提供排查思路和优化建议。
10.1 性能与资源优化
| 问题现象 | 可能原因 | 优化建议 |
|---|---|---|
| API 响应慢 | 1. 大模型 API 网络延迟高。 2. Graph 中串行节点过多。 3. 提示词过于复杂,导致模型响应慢。 | 1. 选择地理距离近或响应速度快的模型服务。 2. 分析 Graph,将无依赖的节点改为并行执行(如果 Graph 支持)。 3. 精简提示词,使用更明确的指令,并设置合理的 max_tokens限制。 |
| 应用内存占用高(OOM) | 1. 同时处理大量简历,数据堆积在内存。 2. 解析大尺寸简历文本(如包含图片base64)。 3. 未及时清理缓存。 | 1. 实现异步批处理,并控制并发度。 2. 在调用模型前,对简历文本进行预处理和截断,只保留关键部分。 3. 使用流式处理,边处理边输出结果,避免全量加载。 4. 调整 JVM 堆参数( -Xmx)。 |
| Token 消耗过大,成本高 | 1. 每次调用都传入完整的原始简历和 JD。 2. 提示词冗余信息多。 3. 未利用好模型的上下文缓存(如 OpenAI 的 seed)。 | 1.这是 Graph 的核心价值:将流程拆分,上游节点的结构化输出作为下游节点的输入,避免重复传递原始长文本。 2. 优化提示词,删除不必要的描述。 3. 对于固定不变的 JD,可以提前向量化,在匹配时使用向量相似度进行初筛,再调用大模型进行精评。 |
10.2 常见错误排查
| 问题现象 | 排查步骤 | 解决方案 |
|---|---|---|
启动失败:java: 警告: 源发行版 17 需要目标发行版 17 | 检查 IDE 和 Maven 的 Java 版本配置。 | 1. IDE: File -> Project Structure -> Project SDK & Language Level -> 17。 2. Maven: pom.xml中配置maven-compiler-plugin的source和target为 17。3. 命令行: 确认 JAVA_HOME指向 JDK 17。 |
| 调用大模型 API 超时或失败 | 1. 检查网络连通性。 2. 检查 API Key 是否正确且有余额。 3. 查看 Spring AI 的 DEBUG 日志。 | 1.curl测试模型 API 端点。2. 登录模型服务商控制台检查密钥状态和用量。 3. 在 application.yml中配置合理的超时时间(如果 Spring AI 客户端支持)。 |
| Graph 执行卡在某个节点 | 查看该节点工具的日志,检查输入输出。 | 1. 确认该节点工具方法的输入参数类型和内容符合预期。 2. 检查工具方法内部是否有异常被吞没。 3. 在 Graph 配置中增加更详细的执行日志。 |
| 返回的 JSON 解析失败 | AI 模型没有严格遵守输出格式要求。 | 1. 在系统提示词中更加强调“只输出 JSON”。 2. 在代码中增加 JSON 格式校验和修复逻辑(如尝试提取 ````json 标记内的内容)。<br>3. 使用更强大的模型或在请求参数中降低temperature` 以提高输出稳定性。 |
OutOfMemoryError: Java heap space | JVM 堆内存不足。 | 1. 增加 JVM 堆内存:java -Xmx2g -jar your-app.jar。2. 优化代码,避免在内存中缓存大量 ChatResponse等大对象。3. 分析堆转储文件,查找内存泄漏。 |
11. 扩展方向与最佳实践
这个基础项目可以沿多个方向扩展,以适应更复杂的生产需求。
- 集成向量数据库:将职位描述和解析后的简历技能向量化,存入 Milvus、Chroma 等向量数据库。在调用大模型进行深度评估前,先通过向量相似度进行快速粗筛,大幅降低成本和延迟。
- 实现异步与批量处理:将
/api/hr-agent/screen接口改造为异步。提交任务后立即返回一个taskId,通过 WebSocket 或轮询另一个接口获取结果。同时,支持上传一个 ZIP 文件包含多份简历进行批量处理。 - 增加多模态能力:如果简历包含图片或 PDF,可以集成 Spring AI 的图片理解或文档解析能力,先提取文字再进行处理。
- 构建更复杂的决策 Graph:当前的 Graph 比较简单。可以扩展为包含多轮交互的 Agent,例如:匹配度中等时,自动生成几个 clarifying questions(澄清问题),通过邮件发送给候选人,根据回复再决定是否通过。
- 加入人工复核环节:在 Graph 中设计一个
“human_review”节点。当 AI 的置信度不高时,将任务挂起,并通知 HR 在管理后台进行人工复核,复核后再继续流程。 - 完善的监控与可观测性:
- 记录每一次模型调用的 Prompt、Response、Token 使用量和耗时。
- 为 Graph 的执行过程添加 Trace ID,便于追踪一个请求的完整生命周期。
- 暴露关键指标(如平均处理时长、通过率、模型调用错误率)到 Prometheus 和 Grafana。
安全与合规最佳实践:
- 密钥管理:永远不要将 API Key 硬编码在代码或配置文件中。使用环境变量、配置中心或 Kubernetes Secrets。
- 数据脱敏:在日志中记录任何候选人个人信息(如姓名、电话、邮箱)时,必须进行脱敏处理。
- 审计日志:记录所有 AI 决策的输入、输出和操作员,满足合规审计要求。
- 定期评估:定期抽样检查 AI 的筛选结果,评估其公平性和准确性,防止偏见固化。
通过这个项目,你不仅学会了如何使用 Spring AI 和 Alibaba Graph 搭建一个实用的 AI Agent,更重要的是掌握了一套将复杂业务逻辑“翻译”成可编排、可观测的智能工作流的方法。这套方法可以平移到客服、运维、内容审核等多个领域。建议你在理解的基础上,动手调整 Graph 结构、增加新的工具,并思考如何将其集成到你熟悉的业务系统中,这才是应对面试和解决实际问题的关键。