Spring Boot接入大模型:构建HR政策问答机器人实战

发布时间:2026/10/14 8:51:39

Spring Boot接入大模型:构建HR政策问答机器人实战 先聊点实际的。我最近在做一个面向公司内部的 HR 政策问答机器人起因很简单HR 团队每天要回复几十条“年假剩几天”“报销发票怎么贴”“考勤怎么补卡”这类重复问题回答得多了既耗时又容易口径不一。所以我想用 Spring Boot 把大模型的对话接口接进来做一个能基于公司政策回答问题的机器人。这一期是系列第十二篇的第二部分核心目标很明确不搞花活先跑通第一个 AI 对话接口让机器人能正常回答员工提问再谈后续优化。这篇文章适合谁如果你已经会 Spring Boot 的基本用法但还没碰过大模型接口那正好。我会带你从项目拆解、环境配置、核心代码到常见坑位完整走一遍。整个项目我起名叫“HR 政策问答机器人”用的是标准的大模型对话接口兼容大多数厂商的 HTTP 接口格式不需要额外引入复杂框架只要一个 Spring Boot 工程和一点点 JSON 解析的耐心就够了。1. 项目拆解HR 政策问答机器人到底要做什么1.1 核心需求与场景分析先别急着写代码我们得把需求聊透。这个机器人不是简单接个大模型就完事它要解决的真实问题是员工在钉钉、企业微信或者网页端问一句“产假可以休多少天”机器人能立刻给出一段符合公司制度的准确回答。如果员工追问“需要提前多久申请”机器人还要能接着上下文回答而不是每次都当成新问题。从实际场景看HR 政策问答往往有几个特点问题重复率高、答案有明确依据、错误回答的代价大。所以机器人必须被约束在一个“政策范围”内不能让它自由发挥。这就引出了两个关键技术点一是系统提示词System Prompt要给足约束二是对话接口要支持多轮上下文。这一期我们先实现最基础的单轮问答但会在代码结构里预留多轮扩展能力。另外还要考虑接入方式。HR 机器人最常见的入口是聊天工具或内部系统但不管入口是什么后端核心都是同一个能力接收用户消息调用大模型生成回复再返回给前端。所以我把项目定位为一个独立的 REST 接口服务后续不管接到哪个平台只需要加一个适配层即可。1.2 技术选型为什么用 Spring Boot 直接接大模型接口市面上有很多 AI 应用开发框架比如 LangChain、Semantic Kernel 等它们确实能帮你简化调用和组装但对一个内部问答机器人来说直接用 Spring Boot 去调大模型 HTTP 接口反而更灵活、更好维护。原因很简单大模型本质就是一个 HTTP 服务你给它 JSON它还你 JSON。Spring Boot 最擅长的就是处理这类需求而且团队现有技术栈基本都是 Java用 Spring Boot 接入意味着后续维护成本最低。框架不框架不重要关键是先跑通链路等真的需要复杂编排时再引入专门工具也不迟。我选择用RestTemplate来做 HTTP 调用而不是WebClient。因为当前场景是同步的、单次请求RestTemplate 简单直接调试起来也直观。后面如果要做流式输出或者高并发再切 WebClient 也来得及。技术选型的原则是能简单就不要复杂能跑通就先进度优先。1.3 整体架构设计这条链路由三部分组成入口层、业务层、外部接口层。入口层就是一个 Spring MVC Controller接收 POST 请求参数是用户发送的消息。业务层是一个 Service负责构造请求体、调用大模型、解析响应同时把系统提示词拼进去。外部接口层就是大模型 API 本身你可以把它理解成一个黑盒输入一个消息列表输出一个回复。这个架构的好处是边界清晰。Controller 不关心大模型是怎么调的Service 不关心 HTTP 细节后续如果要换模型厂商只需要修改 Service 里的接口地址和参数映射不影响上层逻辑。我把这个架构画成一个箭头用户请求 - Controller - Service - RestTemplate - 大模型API - 响应原路返回就这么简单。同时我在项目里加了合理的分层Controller 和 DTO 放在controller包Service 放在service包HTTP 客户端相关放在client包。虽然项目不大但分好包以后后面加记忆功能、加知识库都会很顺手。2. 环境准备与基础配置2.1 创建 Spring Boot 项目并引入依赖我用的是 Spring Initializr 生成的基础工程Java 17 版本依赖只勾了 Spring Web、Lombok 和 Validation。如果你不想用 Lombok 也行但用了以后实体类会干净很多。实际用到的依赖很简单核心就一个 Web 启动器。pom.xml里关键内容如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.4/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies这个配置在大多数 Spring Boot 3.x 项目里都能直接用。如果你的环境还是 Spring Boot 2.x也只需要把版本号改一下代码几乎不用动。创建好项目之后把目录结构保持清爽我习惯把配置类放在config包后续加配置会比较方便。2.2 配置大模型 API 参数地址、密钥、模型名调用任何大模型接口都需要三个核心参数接口地址URL、API Key、模型名称。这三个参数不能硬编码在代码里我习惯放在application.yml中并且通过环境变量覆盖避免把密钥提交到代码仓库。我这里的配置如下llm: api-url: ${LLM_API_URL:https://api.example.com/v1/chat/completions} api-key: ${LLM_API_KEY:sk-your-key-here} model: ${LLM_MODEL:llm-v1} max-tokens: 500 temperature: 0.3注意api-url我写的是示例地址你需要替换成你实际使用的大模型服务商提供的地址。现在主流的大模型接口大多兼容 Chat Completion 格式请求体中包含model、messages、max_tokens、temperature等字段响应中有一个choices数组里面是生成的回复内容。关于temperature这是一个影响随机性的参数范围通常 0 到 1。HR 政策问答需要准确和稳定所以我把它设为 0.3甚至 0 都不过分。为什么不用更高的值因为政策回答不像写诗不需要创造性越稳定越好。我见过有人把 temperature 设成 0.9结果同一个问题每次答案都不一样员工来了两次发现回答的口径对不上这就出问题了。2.3 定义请求与响应模型大模型的 HTTP 接口需要你发送特定结构的 JSON。拿最常见的格式举例请求体是这样{ model: llm-v1, messages: [ {role: system, content: 你是HR政策助手...}, {role: user, content: 年假还剩几天} ], max_tokens: 500, temperature: 0.3 }响应体大致是{ choices: [ { message: { role: assistant, content: 您好根据公司政策年假剩余天数需要联系HR查询... } } ] }所以我在 Java 里定义了几个 DTOChatRequest包含 model、messages、maxTokens、temperatureChatMessage包含 role、contentChatResponse包含一个 choices 列表每个 choice 里有一个 message。用record来定义最简洁但考虑到前后端交互我用了 Lombok 的Data这样序列化和反序列化都省心。定义完模型后还要在配置类里把RestTemplate注入 Spring 容器并设置合理的超时时间。我设置连接超时 5 秒读取超时 60 秒因为大模型生成回复通常比较慢尤其是长回答。如果读取超时太短容易误判为失败太长用户会等得暴躁。60 秒是常见值实际可以根据模型速度调整。3. 核心代码实现从 Controller 到 Service 到 HTTP 调用3.1 Controller 层设计暴露一个简洁的 POST 接口Controller 是整个服务的入口。我设计了一个接口POST /api/chat入参是ChatRequestDTO里面有一个message字段表示用户发来的文本。返回值是一个统一结构包含机器人回复内容和一个会话标识虽然本期用不到但先留好。ChatController的代码很薄RestController RequestMapping(/api) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } PostMapping(/chat) public ResponseEntityChatResponseDTO chat(RequestBody Valid ChatRequestDTO request) { String reply chatService.getReply(request.getMessage()); ChatResponseDTO response new ChatResponseDTO(); response.setReply(reply); return ResponseEntity.ok(response); } }这里我用了构造函数注入。如果你也喜欢用Autowired当然可以但我个人更推荐构造器注入测试的时候方便也符合 Spring 官方建议。ChatRequestDTO里我还加了一个NotBlank校验防止用户传空消息让服务端白白浪费一次模型调用。这算是一个小细节空请求没意义还费钱。3.2 Service 层组装系统提示词并调用接口Service 是整个逻辑的核心。在这里我把系统提示词、用户消息组装成 messages然后调大模型接口。下面这段代码是简化版但完整展示了思路Service public class ChatService { private static final String SYSTEM_PROMPT 你是一个专业的HR政策问答助手。你的职责是 1. 只根据公司提供的政策内容回答员工问题 2. 如果问题不在政策范围内或者你不确定请回复“我无法回答建议联系HR部门” 3. 回答要简洁、准确不要编造政策 4. 涉及年假、病假、产假、报销、考勤等制度时必须使用公司标准表述。 ; private final RestTemplate restTemplate; private final LlmProperties llmProperties; private final ObjectMapper objectMapper; public ChatService(RestTemplate restTemplate, LlmProperties llmProperties, ObjectMapper objectMapper) { this.restTemplate restTemplate; this.llmProperties llmProperties; this.objectMapper objectMapper; } public String getReply(String userMessage) { ListChatMessage messages new ArrayList(); messages.add(new ChatMessage(system, SYSTEM_PROMPT)); messages.add(new ChatMessage(user, userMessage)); ChatRequest request new ChatRequest(); request.setModel(llmProperties.getModel()); request.setMessages(messages); request.setMaxTokens(llmProperties.getMaxTokens()); request.setTemperature(llmProperties.getTemperature()); String url llmProperties.getApiUrl(); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(llmProperties.getApiKey()); HttpEntityChatRequest entity new HttpEntity(request, headers); ResponseEntityChatResponse responseEntity restTemplate.postForEntity(url, entity, ChatResponse.class); ChatResponse response responseEntity.getBody(); if (response null || response.getChoices() null || response.getChoices().isEmpty()) { throw new RuntimeException(大模型接口返回为空); } return response.getChoices().get(0).getMessage().getContent(); } }注意两个细节第一setBearerAuth是设置 Authorization 头的标准方法大多数大模型接口都接受这种认证方式。如果你对接的是别的认证方式比如api-key放在请求头里那就要改成headers.set(api-key, xxx)。第二ObjectMapper在当前代码里没用到但如果你需要对响应做一些额外处理或者调试时打印报文它会很有用。3.3 HTTP 调用层RestTemplate 的配置与错误处理RestTemplate 默认是没有任何超时设置的这是一个容易踩的坑。如果大模型服务挂了你的接口可能会一直挂起直到客户端超时。所以必须在配置里显式设置超时。我在RestTemplateConfig中做了如下配置Bean public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(60000); return new RestTemplate(factory); }这里使用SimpleClientHttpRequestFactory简单直观。如果你需要连接池管理可以用HttpComponentsClientHttpRequestFactory引入 Apache HttpClient 依赖。但就目前这个项目规模没必要。错误处理是重点。如果调用大模型接口时返回 401认证失败、429限流、500服务内部错误直接用postForEntity会抛出异常但异常信息通常不够友好。我建议在 Service 里至少处理三类异常认证异常检查 API Key 是否正确是否过期。超时异常检查网络是否通模型响应是否太慢。响应结构异常检查映射是否匹配尤其注意 JSON 字段名大小写。为了便于排查我在代码里打印了请求和响应的关键信息但注意不要打印完整 API Key。这一点在后面调试时帮了大忙。3.4 系统提示词设计让 AI 不“胡说八道”这一步是整个机器人能否可用的关键。大模型本身就像一个博览群书但不太懂规矩的实习生你如果不给它划边界它会一本正经地编造公司政策。所以系统提示词必须明确以下几点第一角色定义。告诉它“你是 HR 政策助手”这能让它调用相应的知识范式。第二知识边界。明确说“只能根据公司提供的政策内容回答”凡是不确定的统一回复“无法回答建议联系 HR”。第三回答风格。HR 问答需要礼貌、专业、简洁。第四敏感信息处理。提示它不要询问或收集员工身份证号、银行卡密码等隐私信息。我的参考提示词已经写在代码里。实际项目中你还可以把这些内容放到数据库中做到动态配置这样 HR 团队可以自己更新政策而不用改代码。这是个很好的扩展方向但本期不展开。这里要特别提醒提示词不是万能的。如果模型本身知识不足它会试图用常识回答所以未来必须引入知识库RAG才能真正做到“基于政策回答”。这一期先靠提示词强约束算是一个可以接受的 MVP。4. 跑通全流程与接口调试4.1 本地启动并调用接口所有代码写完以后启动 Spring Boot 应用然后用curl或 Postman 测试。我是用 curl 测试的命令如下curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 我想问一下今年年假还有几天}如果一切正常你会收到类似这样的 JSON{ reply: 您好您今年的年假剩余天数取决于您的入职日期和公司考勤记录建议您通过HR系统查询或联系HR部门获取准确数据。 }第一次跑通这个流程时我整个人是兴奋的但实际调了几个问题后就发现问题了机器人的回答太泛不够具体。后来我把公司政策文本嵌入到提示词里效果稍微好一些但距离“能用”还很远。所以我建议你第一次跑通后先不要急着追求完美先确认链路是通的再逐步优化回答质量。4.2 常见问题排查403、超时、乱码、幻觉我在调试过程中遇到了不少问题挑几个典型的说一说。第一个是 403 或 401。原因十有八九是 API Key 没写对或者请求头没有正确携带。检查方式是在代码里打印Authorization头的前几位和后几位确认没有拼接错误。如果你用的环境变量还要确认环境变量真的被加载了不要直接在 IDEA 里改代码里的默认值。第二个是读取超时。大模型接口在某些时候响应很慢尤其生成内容较长时。如果读取超时设置过短比如 10 秒接口就会频繁报超时。我最终把它调到 60 秒才算稳定。如果你要提供服务给员工用建议前端配合 loading 提示让用户知道系统正在生成回答。第三个是中文乱码。这个问题多发生在 String 里手动构造 JSON 时没有指定 UTF-8 编码。用 RestTemplate 传递HttpEntity的时候要确保Content-Type是application/json;charsetUTF-8。如果你用 ObjectMapper 序列化对象一般不会有乱码问题但如果你从文件读取提示词一定要确认文件编码是 UTF-8。第四个也是最重要的模型幻觉。有一次我测试“离职时未休年假怎么折算”模型给出了一段看起来很合理的政策但我查了公司制度发现完全是编的。这就是没有知识库的典型表现。解决办法除了强化提示词至少要在提示词里写“如果政策内容未提供必须拒绝回答”。我在这上面栽过跟头所以格外强调。4.3 实测效果与优化方向跑通之后我拿几个真实问题做了测试比如“病假需要什么证明”“报销发票丢失了怎么办”“新员工入职多久可以享受年假”。在提示词里加入了简单的政策要点后回答准确率明显提高。但我也发现问题一旦涉及具体数字比如“年假按入职满一年算还是满一年后的下一年起算”模型就容易混淆。所以我给这个项目的下一步优化列了三个方向第一引入长期记忆。现在每次请求都是独立的员工问“我想请假”之后再问“需要审批吗”模型是不知道“请假”这个上下文的。后面要在 Service 层维护一个会话消息列表把每个用户的对话历史存起来再传给模型。第二建立公司政策知识库。用向量数据库存储政策条文提问时先检索相关片段再拼进提示词。这是真正解决幻觉的路径也是这个机器人的最终形态。第三增加人工转接机制。当模型判断回答不了或者员工主动要求“转人工”接口需要返回一个特殊的标识让前端展示转人工按钮。这个对内部服务来说很关键毕竟再好的 AI 也有搞不定的时候。5. 常见问题与避坑指南5.1 问题排查速查表把最常遇到的问题整理成了一张表格方便你快速定位现象可能原因排查与解决方法返回 401/403API Key 错误或未携带打印请求头检查环境变量返回 429超出模型限流增加重试机制降低并发请求数读取超时模型生成慢或网络慢调大读取超时到 60 秒以上响应内容为空模型返回结构异常打印完整响应 JSON检查字段映射回答内容与政策不符模型幻觉将政策文本加入提示词或引入知识库中文乱码编码问题统一使用 UTF-8避免手动拼接 JSON这张表可以贴在你的项目文档里后面团队接手时能省不少事。5.2 安全与合规注意事项内部机器人最容易忽略的是安全问题但恰恰最不能忽略。API Key 属于敏感信息在代码里写死是绝对禁止的。我采用环境变量注入的方式代码仓库只保留占位符。同时调用日志里不能打印完整的 API Key也不能打印员工输入中的敏感信息比如身份证号、银行卡号。另外大模型问答服务的回复内容需要具备可追溯性。我建议在接口返回结构中增加一个requestId每次调用都生成唯一标识方便问题回溯。在正式上线前最好让 HR 团队对常见问题的回答进行一轮审核确认没有原则性错误。5.3 一点真心话跑通第一个 AI 对话接口并不难难的是让机器人在真实业务中真正可用。我这边的体会是先让链路通再让答案准最后做好兜底。不要一开始就追求什么都自动回答宁可让它多问一句“需要我转接 HR 吗”也比给员工一个错误答案强。如果你也在做类似的东西我建议你从今天下午就开始动手用最快的方式把接口调通。不要迷信复杂框架先把 Spring Boot 的工程结构和大模型 API 的调用关系吃透。等基础跑通了再慢慢加知识库、加记忆、加人工兜底那都是水到渠成的事。
延伸阅读

更多相关文章

2026/10/14 8:51:39

Kong API网关10分钟实战:从零部署到JWT鉴权与限流

1. 项目概述:为什么今天必须懂API网关,而不是“先学Spring Boot再补课”你有没有遇到过这样的场景:团队里三个后端小组各自开发了用户服务、订单服务和支付服务,接口地址分别是http://user-api:8080/v1/users、http://order-api:8…

2026/10/14 8:51:39

主机游戏PC模拟器:指令集转译与图形API重映射技术解析

1. 这个项目到底在折腾什么第一次看到这个标题的时候,我正端着咖啡刷技术社区,差点一口喷在屏幕上。让主机游戏跑在PC上,这事儿听起来像是某个论坛里常年飘着的“许愿帖”,但它偏偏是个实打实有十几万星标的开源项目。我花了一个周…

2026/10/14 8:51:39

基于SSM框架的文具商城系统开发实践

1. 项目概述 这个文具学习用品商城系统是一个典型的B2C电子商务平台,采用JavaWeb技术栈构建。作为一名有多年电商系统开发经验的工程师,我认为这种系统虽然看似简单,但实际开发中需要考虑的细节非常多。系统采用SSM(SpringSpringMVCMyBatis)框…

2026/10/14 10:22:42

Java轻量级爬虫框架开发与接口测试实践

1. 项目背景与核心价值 最近在做一个电商价格监控系统时,发现市面上现成的爬虫工具要么功能过剩,要么灵活性不足。于是决定基于Java开发一套轻量级的爬虫API接口测试框架,专门用于快速验证各类网页数据抓取逻辑。这套方案特别适合需要频繁调整…

2026/10/14 10:22:41

校园二手交易平台Java+SSM与Django混合架构实践

1. 项目背景与核心价值校园二手交易平台是解决学生群体闲置物品流转需求的典型应用。每年毕业季和开学季,大量教材、电子产品、生活用品在校园内闲置,传统线下交易存在信息不对称、交易效率低、安全隐患等问题。这个基于JavaSSMDjango的混合架构平台&…

2026/10/14 10:22:41

可微分聚类实现无监督图像分割:原理、实现与调参指南

1. 为什么这篇论文值得反复读图像分割这个方向,做的人多,但真正把"无监督"和"可微分聚类"这两件事捏在一起讲清楚的论文并不多。这篇《Unsupervised Learning of Image Segmentation Based on Differentiable Feature Clustering》我…

2026/10/14 10:22:41

C#调用Windows API设置桌面分辨率:DEVMODE封装与黑屏自救指南

做桌面工具的开发者,迟早会遇到这样一个需求:在C#程序里把Windows桌面分辨率切换到指定数值。我第一次认真研究这个问题,是在做游戏启动器的时候。用户反馈说游戏全屏之后画面被拉伸、边缘发虚,排查到最后发现是启动器没有在进入游…

2026/10/14 10:22:41

巴菲特价值投资:核心理念与实战方法解析

1. 价值投资的核心理念沃伦巴菲特作为当代最成功的投资者之一,其投资哲学的核心在于价值投资。这种理念源于他的导师本杰明格雷厄姆,但巴菲特将其发展得更为完善和系统化。价值投资的本质是寻找市场价格低于内在价值的优质企业,然后长期持有&…

2026/10/14 10:17:41

从长文摘要到知识卡片:一个知识工作插件集的设计与实现

老实说,最开始我并不是为了做一整套插件才开的这个仓库。起因特别朴素——有一天我发现自己的浏览器里开了四十多个标签页,十几篇文献和文档来回切换,脑子里的信息像一堆泡软了的纸,根本没组织起来。我试着把文章一段段粘进某个大…

2026/10/13 5:19:27

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/13 17:09:18

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述:为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中,我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点,结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介:这是一套遵循SCORM标准的交互式课件演示与测试资源,面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者,用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件,约12.07MB,以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑