Spring AI Alibaba实战:构建Human-in-the-Loop智能客服系统
1. 项目缘起:当AI决策需要一双“人眼”时
最近在折腾一个基于Spring AI Alibaba的智能客服项目,遇到了一个挺典型的问题。我们让AI模型去处理用户的工单分类和初步回复,效果在大部分情况下都还不错。但有一次,一个用户输入了“我的订单显示已发货,但物流三天没更新了,而且我地址填错了,能改吗?”,AI的回复是:“您好,物流停滞问题建议您联系快递公司查询。地址修改请在订单发货前操作。” 这个回复,从技术上看,它识别出了“物流”和“地址”两个关键词,并给出了标准答案。但从业务角度看,它完全搞错了重点和优先级——用户的核心诉求是“地址填错”这个紧急且需要人工介入的异常,而“物流停滞”可能只是地址错误导致的表象。AI把两个问题割裂处理,给出了一个正确但无用的答案。
这就是纯粹自动化AI的局限性:它缺乏对复杂、模糊或高价值场景的上下文理解和责任判断。如果让这个回复直接发给用户,轻则体验糟糕,重则可能导致用户流失或产生客诉。于是,“Human-in-the-Loop”(人机回环,简称HITL)就成了我们必须引入的架构模式。这不是说AI不行,恰恰相反,是为了让AI在关键环节变得更可靠、更负责任,把机器的效率与人类的判断力结合起来。Spring AI Alibaba作为一套整合了阿里云灵积等大模型能力的框架,为我们实现这种模式提供了非常优雅的支撑点。今天,我就结合实战,聊聊如何在Spring AI Alibaba项目中,系统性地设计和落地HITL,让AI真正成为业务得力的“副驾驶”,而不是一个可能闯祸的“自动驾驶”。
2. 理解HITL:不止是“审核一下”那么简单
很多人一听HITL,第一反应就是“加个审核流程”。这没错,但太片面了。HITL是一个系统性的设计哲学,核心思想是在AI系统的关键决策点上,引入人类的监督、纠正或指导,并将这些人类反馈重新注入系统,用于改进后续的AI表现。在Spring AI Alibaba的上下文中,我们可以把它拆解为几个层次。
2.1 HITL的三种核心介入模式
根据介入的时机和主动性,HITL通常有三种模式,我们需要根据业务场景灵活选用或组合。
2.1.1 被动介入(审核与纠正)这是最常见的形式,即AI先给出输出,再由人类进行审核。适用于结果影响重大、容错率低的场景。比如:
- 内容安全:AI生成的营销文案、客服回复,需要确保无违规、无歧义。
- 关键决策:AI对贷款申请的初步风险评估、对医疗影像的辅助诊断建议。
- 法律合规:AI起草的合同条款、生成的专利摘要。
在Spring AI Alibaba中,这意味着我们需要在ChatClient或PromptTemplate调用之后,不是直接将结果返回给前端或下游系统,而是将其送入一个待审核队列(可以是数据库表、消息队列等),并触发一个审核任务(如通知运营人员的企业微信/钉钉机器人)。
2.1.2 主动介入(引导与约束)在AI生成过程开始前或进行中,人类就提供明确的指令、范例或边界条件。这能极大提升AI输出的相关性和质量。例如:
- 复杂任务分解:人类先将一个“写一份季度市场分析报告”的复杂任务,分解为“分析行业趋势”、“对比竞争对手Q1动-作”、“总结我方销售数据”、“提出下季度建议”四个子任务,再让AI分步执行。
- 提供参考信息:在让AI总结一份会议纪要前,人类先提供关键参会人名单、核心议题列表。
- 设定输出格式:明确要求AI以JSON格式输出,包含
title,summary,action_items等字段。
Spring AI Alibaba的PromptTemplate和SystemMessage是实现主动介入的利器。我们可以设计动态的Prompt,将人类提供的约束条件(来自前端表单或上一次交互的上下文)作为变量注入。
2.1.3 协同介入(混合倡议)这是一种更高级的模式,AI和人类在同一个界面上协同工作,交替贡献。AI可以实时补全代码、建议下一句话,人类则可以随时修改、接受或拒绝AI的建议。这更像是“结对编程”或“协同编辑”。虽然Spring AI Alibaba主要关注服务端交互,但我们可以通过流式响应(Streaming Response)配合前端,实现类似体验。例如,AI流式生成客服回复时,客服人员可以在中途输入新的指令进行引导。
2.2 为什么Spring AI Alibaba适合做HITL?
Spring AI Alibaba不是一个孤立的AI模型调用包,它是Spring生态的一部分,这带来了几个天然优势:
- 声明式客户端与AOP支持:
ChatClient可以被Spring AOP轻松切面环绕。我们可以写一个@Around切面,在call()方法执行前后无缝插入审核逻辑、日志记录和反馈收集,业务代码几乎无侵入。 - 完善的上下文管理:通过
Conversation、Message等抽象,可以轻松维护多轮对话的上下文。这对于HITL至关重要,因为人类的纠正反馈需要和原始的AI请求上下文关联起来,用于后续的模型微调或Prompt优化。 - 与Spring生态无缝集成:审核队列可以用
Spring Data JPA管理,审核通知可以用Spring Integration或消息模板发送,任务调度可以用Spring Scheduler或Quartz。整个HITL工作流可以借助Spring State Machine或Flowable这样的流程引擎来编排,实现状态管理(如:已生成->待审核->审核通过/驳回->已修正)。 - 多模型路由与降级:Spring AI的
ChatClient抽象允许我们配置多个模型连接(如通义千问、DeepSeek等)。在HITL场景下,如果主模型对某个问题置信度低(可以通过模型返回的metadata判断,部分模型支持),可以自动路由到备用模型进行“二次会诊”,如果置信度依然不高,则直接提升给人工处理。
3. 实战架构:构建一个可落地的HITL系统
光有概念不够,我们直接来看一个为智能客服场景设计的、基于Spring AI Alibaba的HITL系统架构。这个架构包含了从AI调用到人工干预再到反馈学习的完整闭环。
[用户请求] -> [Spring MVC Controller] | v [Service层: HITL Orchestrator] | /----------|----------\ | | v v [AI处理管道] [人工审核后台] (ChatClient + Prompt) (Web UI) | | v v [结果+置信度] [人工审核/修正] | | \----------|----------/ | v [决策路由器 (Router)] / | \ / | \ v v v [直接回复] [修正后回复] [请求人工客服] (高置信度) (人工修正) (低置信度/复杂) | v [反馈学习循环] (存储交互数据用于优化)3.1 核心领域模型设计
首先,我们需要设计数据库表来支撑这个流程。这里简化出几个核心实体:
-- AI交互请求记录表 CREATE TABLE ai_interaction ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_id VARCHAR(64) NOT NULL, -- 会话ID user_input TEXT NOT NULL, -- 用户原始输入 prompt_used TEXT, -- 实际使用的Prompt模板及变量 model_name VARCHAR(50), -- 使用的模型,如 qwen-turbo ai_raw_response TEXT, -- AI原始响应 confidence_score DECIMAL(3,2), -- 置信度分数 (如果模型提供) status VARCHAR(20) DEFAULT 'PENDING', -- 状态: PENDING, AUTO_APPROVED, NEED_REVIEW, HUMAN_REVISED, REJECTED created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_status (status), INDEX idx_session (session_id) ); -- 人工审核任务表 CREATE TABLE human_review_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, interaction_id BIGINT NOT NULL, -- 关联的AI交互记录 reviewer_id VARCHAR(50), -- 审核人ID review_result TEXT, -- 审核意见/修正后的内容 review_action VARCHAR(20), -- 动作: APPROVED, MODIFIED, REJECTED reviewed_at TIMESTAMP, FOREIGN KEY (interaction_id) REFERENCES ai_interaction(id) ); -- 反馈学习数据表 (用于后续的Prompt优化或微调) CREATE TABLE feedback_learning ( id BIGINT PRIMARY KEY AUTO_INCREMENT, interaction_id BIGINT NOT NULL, original_input TEXT, original_ai_output TEXT, final_correct_output TEXT, -- 最终正确的输出(可能是AI的,也可能是人修的) feedback_type VARCHAR(20), -- 类型: CORRECTION, CONFIRMATION, REJECTION used_for_training BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );3.2 实现HITL编排器(Orchestrator)
这是我们系统的“大脑”。它负责协调整个流程。我们将其实现为一个Spring Service。
@Service @Slf4j public class HitlOrchestratorService { @Autowired private ChatClient chatClient; // Spring AI Alibaba 的 ChatClient @Autowired private AiInteractionRepository interactionRepo; @Autowired private HumanReviewTaskRepository reviewTaskRepo; @Autowired private NotificationService notificationService; @Value("${ai.confidence.threshold:0.8}") private double confidenceThreshold; @Transactional public ProcessResult processUserQuery(String sessionId, String userInput) { // 1. 构建Prompt(这里可以加入主动介入的要素,比如从会话历史中提取约束) Prompt prompt = new Prompt(new UserMessage(userInput)); // 可以添加SystemMessage来约束AI行为 // Prompt prompt = new Prompt(Arrays.asList(new SystemMessage("你是一个专业的客服助手..."), new UserMessage(userInput))); // 2. 调用AI ChatResponse response = chatClient.call(prompt); String aiRawResponse = response.getResult().getOutput().getContent(); // 3. 解析置信度 (假设模型返回的metadata中包含) // 注意:并非所有模型/API都返回置信度,这里需要根据实际API响应调整 Double confidence = extractConfidence(response); // 4. 保存交互记录 AiInteraction interaction = new AiInteraction(); interaction.setSessionId(sessionId); interaction.setUserInput(userInput); interaction.setAiRawResponse(aiRawResponse); interaction.setConfidenceScore(confidence); interaction.setModelName("qwen-plus"); // 实际从配置或路由中获取 interaction.setStatus(AiInteractionStatus.PENDING); interaction = interactionRepo.save(interaction); // 5. 基于置信度的路由决策 if (confidence != null && confidence >= confidenceThreshold) { // 高置信度,自动通过 interaction.setStatus(AiInteractionStatus.AUTO_APPROVED); interactionRepo.save(interaction); return ProcessResult.autoApproved(aiRawResponse); } else { // 低置信度或无法判断,需要人工审核 interaction.setStatus(AiInteractionStatus.NEEDS_REVIEW); interactionRepo.save(interaction); // 创建审核任务 HumanReviewTask task = new HumanReviewTask(); task.setInteractionId(interaction.getId()); reviewTaskRepo.save(task); // 发送通知(例如,到钉钉群) notificationService.sendReviewAlert(interaction.getId(), userInput, aiRawResponse); return ProcessResult.needReview(interaction.getId(), "已提交人工审核,请稍候"); } } private Double extractConfidence(ChatResponse response) { // 这是一个示例,实际需根据阿里云灵积API返回的metadata结构解析 // 例如, response.getMetadata().get("confidence") // 如果API不提供,可以尝试其他方法,如使用一个轻量级模型对答案进行二次评分 return null; // 暂时返回null,表示不启用置信度路由 } }3.3 实现人工审核后台与反馈闭环
审核后台可以是一个简单的内部管理系统。当审核员打开任务,他看到的是原始的userInput和aiRawResponse,他可以选择:
- 通过:直接采用AI的回答。
- 修改:在AI回答的基础上进行编辑。
- 驳回:AI回答完全不可用,需要转给人工客服或重新生成。
审核完成后,后端服务需要更新状态,并将最终结果返回给用户。同时,最关键的一步是将这次纠正记录到feedback_learning表。
@Service public class ReviewService { @Transactional public void completeReview(Long taskId, Long reviewerId, String revisedResponse, ReviewAction action) { HumanReviewTask task = reviewTaskRepo.findById(taskId).orElseThrow(); AiInteraction interaction = interactionRepo.findById(task.getInteractionId()).orElseThrow(); task.setReviewerId(String.valueOf(reviewerId)); task.setReviewResult(revisedResponse); task.setReviewAction(action); task.setReviewedAt(LocalDateTime.now()); reviewTaskRepo.save(task); // 更新交互记录状态 if (action == ReviewAction.APPROVED) { interaction.setStatus(AiInteractionStatus.HUMAN_APPROVED); } else if (action == ReviewAction.MODIFIED) { interaction.setStatus(AiInteractionStatus.HUMAN_REVISED); interaction.setFinalResponse(revisedResponse); // 保存最终回复 } else { interaction.setStatus(AiInteractionStatus.REJECTED); } interactionRepo.save(interaction); // 记录反馈学习数据 if (action == ReviewAction.MODIFIED || action == ReviewAction.REJECTED) { FeedbackLearning feedback = new FeedbackLearning(); feedback.setInteractionId(interaction.getId()); feedback.setOriginalInput(interaction.getUserInput()); feedback.setOriginalAiOutput(interaction.getAiRawResponse()); feedback.setFinalCorrectOutput(revisedResponse != null ? revisedResponse : "[REJECTED]"); feedback.setFeedbackType(action == ReviewAction.MODIFIED ? "CORRECTION" : "REJECTION"); feedbackLearningRepo.save(feedback); } // TODO: 通知网关或前端,审核已完成,可以推送最终结果给用户 } }4. 进阶策略:让HITL更智能、更高效
基础的审核流程搭建好后,我们会发现,如果所有低置信度的请求都走人工,审核人员的压力会很大。我们需要让HITL本身也“智能”起来,减少不必要的介入。
4.1 基于规则和分类的预过滤
在调用大模型之前,先用简单的规则或轻量级文本分类模型过滤掉明显不需要AI处理或必须人工处理的情况。
@Component public class RequestPreFilter { public FilterResult filter(String userInput) { // 规则1:包含敏感词 -> 直接转人工 if (containsSensitiveWords(userInput)) { return FilterResult.directToHuman("包含敏感内容"); } // 规则2:简单问候/感谢 -> 固定回复,不走AI if (isSimpleGreeting(userInput)) { return FilterResult.cannedResponse("您好,很高兴为您服务!"); } // 规则3:意图明确为“转人工” -> 直接转 if (userInput.contains("转人工") || userInput.contains("找真人")) { return FilterResult.directToHuman("用户明确要求"); } // 可以用一个简单的本地分类模型(如FastText)判断问题领域 // String domain = lightweightClassifier.predict(userInput); // if ("complex_refund".equals(domain)) { return FilterResult.directToHuman("复杂售后问题"); } return FilterResult.proceedToAI(); // 通过过滤,交给AI } }4.2 动态置信度阈值与分级审核
不要用一个固定的置信度阈值(如0.8)。可以根据问题类型、用户价值、时间成本动态调整。
- 高风险领域(如金融建议、医疗咨询):阈值设为0.95,几乎全部审核。
- 常规问答(产品信息、操作指南):阈值设为0.7。
- 夜间模式:凌晨时段,审核人员少,可以适当提高自动通过阈值,或只将极高风险问题转人工,其余先由AI回复并标注“答案仅供参考,白天将由人工确认”。
4.3 利用反馈数据迭代优化
feedback_learning表是金矿。定期(比如每周)分析这些数据:
- Prompt工程优化:如果发现AI在某一类问题上(如“地址修改”)频繁被纠正,可以针对性优化Prompt。例如,在System Message中强调:“当用户同时提到多个问题时,优先处理需要人工介入的异常或变更类请求。”
- 构建拒绝分类器:用被
REJECTED的数据作为负样本,被APPROVED的数据作为正样本,训练一个二分类模型。这个模型可以在AI调用前运行,预测当前请求被人工拒绝的概率,如果概率过高,直接跳过AI,节省成本并提升体验。 - 少样本学习:从
MODIFIED的数据中,提取<user_input, corrected_response>对,作为few-shot examples,在下一次类似的Prompt中注入,直接提升AI表现。
// 示例:优化后的Prompt构建,注入历史修正案例 public Prompt buildPromptWithFewShot(String currentInput, List<FeedbackLearning> similarPastCorrections) { List<Message> messages = new ArrayList<>(); messages.add(new SystemMessage("你是一个客服助手。以下是一些正确回答的示例:")); for (FeedbackLearning fb : similarPastCorrections) { messages.add(new UserMessage(fb.getOriginalInput())); messages.add(new AssistantMessage(fb.getFinalCorrectOutput())); } messages.add(new UserMessage(currentInput)); return new Prompt(messages); }5. 踩坑实录:HITL实践中必须避开的雷区
在实际部署HITL系统的过程中,我们遇到了不少坑,这里分享几个关键的,希望能帮你省点时间。
5.1 状态一致性与并发问题
审核流程涉及多张表的状态变更(ai_interaction,human_review_task),并且前端可能轮询状态。必须保证在一个数据库事务内完成状态更新和相关记录的创建,避免出现“任务创建了但状态没更新”的中间状态。我们曾因为@Transactional注解使用不当,导致部分情况下审核任务通知发出了,但数据库状态回滚,审核员点进链接找不到任务。
解决方案:确保Orchestrator和ReviewService中的核心方法都有明确的@Transactional注解,并仔细检查方法内是否有非受检异常(RuntimeException)被捕获而未抛出,这会导致事务无法回滚。
5.2 审核时效性与用户体验
用户提问后,如果进入审核队列,不能让他无限期等待。我们最初的设计是“审核完成才回复”,导致一些简单但被误判的问题让用户等了十几分钟,体验极差。
解决方案:引入“异步响应”机制。当请求进入审核时,立即给用户一个友好提示:“您的问题比较复杂,正在为您加急处理,请稍等片刻(通常1-5分钟)。” 同时,在审核后台高亮显示等待时间。可以为审核任务设置SLA(如5分钟),超时未处理则自动升级(如通知主管),或先由一个降级策略(如返回一个更保守的AI答案并注明“答案待确认”)来响应。
5.3 反馈数据的质量与清洗
不是所有的人工修改都是“正确”的。审核人员水平参差不齐,有时会引入拼写错误,或者把原本正确的AI答案改错。如果盲目地将所有MODIFIED数据都用于训练,会污染模型。
解决方案:
- 双人复核:对于用于训练数据的关键修正,可以要求二级复核。
- 数据评分:让审核员在修正时,对AI原答案的“错误程度”和自身修正的“把握度”进行简单评分(如1-5星)。优先采用高把握度的修正数据。
- 定期抽样审计:算法或团队负责人定期抽样检查
feedback_learning中的数据质量,及时剔除错误样本。
5.4 Spring AI Alibaba客户端配置与超时
当引入HITL后,AI调用可能处于一个同步链路上(先等AI结果,再判断是否审核)。如果AI服务响应慢或超时,会阻塞整个用户请求。我们在压力测试时就遇到了因模型端延迟导致的线程池耗尽。
解决方案:
- 合理设置超时:在配置
ChatClient(如AlibabaDashScopeChatClient)时,务必设置连接超时和读取超时。spring: ai: alibaba: dashscope: chat: options: connect-timeout: 5s read-timeout: 30s # 根据模型复杂度调整 - 异步化AI调用:对于非实时性要求极高的场景,可以考虑将AI调用也异步化。用户请求进来后,立即返回“正在处理中”,然后通过消息队列触发后台的AI处理和HITL流程,处理完毕后再通过WebSocket或推送通知用户。这大大提升了系统的吞吐量和响应韧性。
6. 效果衡量与未来展望
引入HITL后,不能光凭感觉,需要用数据说话。我们建立了几个核心指标:
- AI直接采纳率:
(AUTO_APPROVED) / TOTAL_REQUESTS。这个比率会随着Prompt优化和模型迭代逐步上升,是我们的核心优化目标。 - 人工修正率:
(HUMAN_REVISED) / NEEDS_REVIEW。反映了AI在“存疑问题”上的可修正性,修正率越低,说明AI在边界问题上的表现越好。 - 平均审核时间(AHT):从任务创建到审核完成的时间。关乎运营成本和用户体验。
- 用户满意度(CSAT):对比引入HITL前后,相同渠道的用户满意度调查分数变化。
经过两个月的运行,我们的AI直接采纳率从初期的65%提升到了82%,人工修正率从40%下降到了15%。更重要的是,由于拦截了可能出错的回答,关于“AI答非所问”的投诉下降了90%。
HITL不是终点,而是一个让AI系统安全、稳健成长的“训练轮”。随着反馈数据的积累和模型的迭代,我们期望需要人工介入的场景会越来越少,但介入的机制会永远存在,作为系统最后也是最重要的安全阀。Spring AI Alibaba提供的灵活性和Spring生态的完整性,让这套机制的实现变得清晰而高效。最终,我们构建的不是一个替代人的AI,而是一个增强人、与人协同的智能系统。
