Spring AI Alibaba 1.x 系列【52】Interrupts 中断机制:案例演示

文章目录

  • [1. 概述](#1. 概述)
    • [1.1 Human-in-the-Loop(人在回路)](#1.1 Human-in-the-Loop(人在回路))
    • [1.2 Interrupts(执行中断)](#1.2 Interrupts(执行中断))
    • [1.3 执行中断 VS 人在回路](#1.3 执行中断 VS 人在回路)
  • [2. interruptBefore、interruptsAfter 模式](#2. interruptBefore、interruptsAfter 模式)
    • [2.1 配置项说明](#2.1 配置项说明)
    • [2.2 interruptsBefore 配置](#2.2 interruptsBefore 配置)
    • [2.3 定义中断反馈结果](#2.3 定义中断反馈结果)
    • [2.4 流程中断](#2.4 流程中断)
    • [2.5 流程恢复](#2.5 流程恢复)
    • [2.6 interruptsAfter 配置](#2.6 interruptsAfter 配置)
    • [2.7 interruptBeforeEdge 配置](#2.7 interruptBeforeEdge 配置)
  • [3. InterruptableAction 模式](#3. InterruptableAction 模式)
    • [3.1 定义可中断节点](#3.1 定义可中断节点)
    • [3.2 添加节点](#3.2 添加节点)
    • [3.3 流程中断](#3.3 流程中断)
    • [3.4 流程恢复](#3.4 流程恢复)

1. 概述

1.1 Human-in-the-Loop(人在回路)

Human-in-the-Loop 为智能体的工具调用 增加人工审核机制。当大模型拟执行存在风险的操作(如写入文件、执行 SQL 语句)时,该中间件可暂停流程执行,等待人工决策。

内置三种人工响应中断的决策方式:

决策类型 说明 适用场景示例
✅ 批准(approve) 直接认可原操作,无修改执行 原样发送邮件草稿
✏️ 编辑(edit) 修改工具调用参数后再执行 发送邮件前修改收件人
❌ 拒绝(reject) 驳回工具调用,并在对话中附上拒绝理由 拒绝邮件草稿并说明改写建议

在之前我们介绍过 React 基于 HumanInTheLoopHook 模型后置钩子(模型生成响应后、工具调用执行前触发)实现,处理逻辑:

  1. 智能体调用大模型生成响应
  2. 中间件解析响应中的工具调用
  3. 若需人工介入,构造含操作请求、审核配置的 HITL 请求并触发中断
  4. 智能体挂起,等待人工决策
  5. 根据人工决策:执行批准/编辑的调用、为拒绝操作生成工具消息、将人工回复直接作为工具返回结果,最终恢复流程执行

1.2 Interrupts(执行中断)

执行中断 允许在流程图的指定节点暂停执行,等待外部输入后再继续运行。该机制是实现人在回路 流程的核心,适用于需要外部人工介入才能继续执行的场景。触发中断时,Graph 会通过持久化层保存流程图完整状态,并无限等待直到手动恢复执行。

中断核心价值:暂停执行、等待外部输入

典型应用场景:

  • 审批流程:执行高危操作前暂停(接口调用、数据库变更、金融交易)
  • 多中断并行处理:单次调用批量恢复多个并行分支的中断
  • 审核与编辑:人工审核、修改大模型输出或工具调用参数后再继续
  • 工具调用中断:工具执行前暂停,人工审核编辑后再执行
  • 输入校验:步骤流转前校验人工输入,非法则重新询问

Spring AI Alibaba Graph 提供了提供了两种方式来实现重点:

  • 通过实现 InterruptableAction 接口来控制中断时机,可以在任意时刻返回 InterruptionMetadata 来中断执行
  • 编译 Graph 时指定中断点(interruptBeforeinterruptsAfter),在指定节点执行前后自动中断

两种中断模式对比:

特性 InterruptionMetadata 模式 interruptBefore\ interruptsAfter模式
中断时机 运行时动态决定 编译时预先定义
节点要求 需要实现 InterruptableAction 接口 普通节点即可
灵活性 高,可根据运行时状态动态中断 中等,需在编译时固定中断位置
配置复杂度 较高,需实现接口方法 低,仅配置节点名称即可
适用场景 需依据运行时业务状态动态判断是否中断 业务流程中已知、固定的中断点位

HITL 规定了只能在工具调用前暂停 ,而 Interrupts 更灵活支持任意节点、任意时机、任意触发条件都能暂停

1.3 执行中断 VS 人在回路

对比维度 执行中断(Interrupts) 人在回路(Human-in-the-Loop, HITL)
核心定位 Graph 底层技术原语、基础工具 AI 流程的业务设计模式、人机协作规范
本质 底层实现恢复指令,实现暂停/恢复 受控的人机协作机制,封装了固定的审批逻辑(批准/编辑/驳回/应答)
核心作用 在流程图任意位置暂停执行、保存状态,等待外部输入后恢复运行 将人纳入自动化流程,关键节点(多为工具调用)需人工审批/编辑后才能继续
灵活性 极高,可在任意节点、任意时机、任意条件下触发暂停 较低,仅针对工具调用场景,按预设规则触发暂停,决策方式固定
触发场景 调试断点、多轮对话等待用户输入、任意节点条件暂停(如风险判断)、非工具环节人工确认等 工具调用前审批(如执行SQL、写文件、发邮件)、高危操作把关、合规校验、工具调用参数审核修改
实现方式 执行中断机制,自定义暂停时机和恢复逻辑 集成 HITL 钩子,配置 interrupt_on 定义需审批的工具及允许的决策类型
依赖关系 独立技术能力,不依赖 HITL 依赖 Interrupts 实现暂停功能,是 Interrupts 的业务化封装
核心用途 满足所有需要暂停流程的场景,通用且灵活 生产级安全场景,重点管控工具调用,保障流程合规、安全

一句话总结:

  • InterruptsGraph底层暂停/恢复技术,万能工具
  • HITL :基于 Interrupts人机协作安全模式,专用于执行时人工审批

2. interruptBefore、interruptsAfter 模式

在编译 Graph 时提前指定中断点,在指定节点执行前后自动中断。这种方式适合已知的中断点,配置简单直接。

优势

  • 配置简单:只需在编译配置中指定中断点
  • 无需修改节点:普通节点即可,不需要实现特殊接口
  • 明确的中断点:中断位置在编译时确定,易于理解和维护

2.1 配置项说明

CompileConfig 中断配置项说明:

配置项 类型 作用 示例
interruptsBefore Set<String> 在指定节点执行前中断 interruptBefore("llm_node")
interruptsAfter Set<String> 在指定节点执行后中断 interruptAfter("tool_node")
interruptBeforeEdge boolean 恢复执行后动态计算下一个节点(配合 interruptsAfter interruptBeforeEdge(true)

示例:

java 复制代码
CompileConfig config = CompileConfig.builder()
    // 方式1:执行前中断
    .interruptBefore("approval_node", "review_node")
    
    // 方式2:执行后中断
    .interruptAfter("tool_call_node")
    
    // 方式3:边评估前中断(需配合 interruptsAfter)
    .interruptAfter("decision_node")
    .interruptBeforeEdge(true)
    
    .saverConfig(SaverConfig.builder().register(MemorySaver.builder().build()).build())
    .build();

StateGraph graph = new StateGraph(...)
    .compile(config);

2.2 interruptsBefore 配置

CompileConfig 提供了两种【节点执行前触发中断】的配置方法:

java 复制代码
		/**
		 * 从集合中设置【节点执行前触发】的多个中断点
		 * @param interruptsBefore 存储中断点标识的字符串集合
		 * @return 当前构建器实例,用于方法链式调用
		 */
		public Builder interruptsBefore(Collection<String> interruptsBefore) {
			// 将集合转换为不可修改的Set集合,赋值给中断点配置
			this.config.interruptsBefore = interruptsBefore.stream().collect(Collectors.toUnmodifiableSet());
			return this;
		}

		/**
		 * 通过可变参数方式,设置【节点执行前触发】的单个/多个中断点
		 * @param interruptBefore 一个或多个表示中断点的字符串
		 * @return 当前构建器实例,用于方法链式调用
		 */
		public Builder interruptBefore(String... interruptBefore) {
			// 将可变参数转换为不可变Set集合,赋值给中断点配置
			this.config.interruptsBefore = Set.of(interruptBefore);
			return this;
		}

节点名称就是我们在添加节点时指定的名称:

java 复制代码
workflow.addNode("read_email", readEmail)

ReactAgent 只有模型、工具节点,是在 ReactAgent#initGraph 方法中定义的:

java 复制代码
		graph.addNode(AGENT_MODEL_NAME, node_async(this.llmNode));
		if (hasTools) {
			graph.addNode(AGENT_TOOL_NAME, node_async(this.toolNode));
		}

常量定义在 RunnableConfig 类中:

java 复制代码
	public static final String AGENT_MODEL_NAME = "_AGENT_MODEL_";
	public static final String AGENT_TOOL_NAME = "_AGENT_TOOL_";

自定义状态图、ReactAgent 都需要通过 CompileConfig 配置中断节点,这里的配置标识在 classify_intent 节点执行前进行中断:

java 复制代码
			 // 自定义状态图
        CompileConfig compileConfig = CompileConfig.builder()
                .saverConfig(saverConfig)
                .interruptBefore("classify_intent")
                .store(new MemoryStore())
                .build();
        CompiledGraph compiledGraph = workflow.compile(compileConfig);
			  
			  // ReactAgent
        ReactAgent chatAgent = ReactAgent.builder()
                .name("email-chat-agent")
                .observationRegistry(observationRegistry)
                .compileConfig(compileConfig)
                .enableLogging(true)
                .tools()
                .saver(new MemorySaver())
                .model(chatModel)
                .instruction("你是一个邮件处理助手,可以帮助用户处理邮件分类、文档搜索、Bug追踪和回复起草等问题。请用中文回答用户的问题。")
                .build();

2.3 定义中断反馈结果

HITL 中定义了三种工具审批结果:

java 复制代码
	// ==================== FeedbackResult 枚举 ====================

	/**
	 * FeedbackResult - 工具审批结果枚举
	 *
	 * <p>表示用户对工具调用的审批决定。
	 */
	public enum FeedbackResult {

		/**
		 * 已批准 - 允许执行工具
		 */
		APPROVED,

		/**
		 * 已拒绝 - 拒绝执行工具
		 */
		REJECTED,

		/**
		 * 已编辑 - 用户修改了工具参数后批准
		 */
		EDITED;
	}

Interrupts 更加灵活,我们可以自定义多种处理结果,比如在以上三种基础上加:

决策类型 说明 适用场景示例
💬 回复(respond) 跳过节点执行,人工输入内容直接作为节点返回结果 直接回复「询问用户」类提示

自定义中断反馈结果枚举类:

java 复制代码
public enum InterruptFeedbackResult {

    APPROVED("批准", "APPROVED"),
    EDITED("编辑", "EDITED"),
    REJECTED("拒绝", "REJECTED"),
    RESPOND("回复", "RESPOND");

    private final String name;
    private final String code;

    InterruptFeedbackResult(String name, String code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public String getCode() {
        return code;
    }

    public static InterruptFeedbackResult fromCode(String code) {
        for (InterruptFeedbackResult action : values()) {
            if (action.getCode().equals(code)) {
                return action;
            }
        }
        return null;
    }
}

2.4 流程中断

执行前中断执行逻辑:

  1. 流程在 interruptBefore 调用前精准挂起
  2. 检查点器保存完整状态,支持后续恢复;生产环境需使用数据库等持久化检查点
  3. 返回 InterruptionMetadata 数据,我们需要根据输出类型进行处理

interruptBefore 模式下只要执行 Graph 到中断节点前,一定会执行自动中断返回 InterruptionMetadata 数据,我们需要根据输出类型进行处理:

  1. 如果是 InterruptionMetadata 类型说明流程暂停了
  2. 检测到中断点时,输出相关处理页面

部分处理代码:

java 复制代码
        return stream
                .map(output -> {
                    String nodeName = output.node();
                    OverAllState state = output.state();
                    boolean isInterrupted = output instanceof InterruptionMetadata;

                    StringBuilder result = new StringBuilder();

                    // 检测中断点
                    if (isInterrupted) {
                        result.append("⏸️ 执行已中断,等待审核...\n");
                        result.append("节点: ").append(nodeName).append("\n");
                        result.append("sessionId: ").append(sessionId).append("\n");
                        result.append("\n请选择审核操作:\n");
                        result.append("<div class=\"action-buttons\">\n");
                        result.append("  <button class=\"action-btn approved-btn\" onclick=\"handleInterruption('APPROVED')\">");
                        result.append("✅ 批准 - 继续执行</button>\n");
                        result.append("  <button class=\"action-btn rejected-btn\" onclick=\"handleInterruption('REJECTED')\">");
                        result.append("❌ 拒绝 - 终止执行</button>\n");
                        result.append("  <button class=\"action-btn edited-btn\" onclick=\"showEditStateForm()\">");
                        result.append("✏ 编辑 - 修改状态后继续</button>\n");
                        result.append("</div>\n");
                        result.append("<div id=\"editStateForm\" style=\"display:none;margin-top:10px;\">\n");
                        result.append("  <textarea id=\"editedStateInput\" placeholder='输入JSON格式的状态更新,例如:{\"draft_response\":\"修改后的回复\"}' style=\"width:100%;height:80px;padding:8px;border:1px solid #ddd;border-radius:8px;resize:vertical;\"></textarea>\n");
                        result.append("  <button class=\"action-btn confirm-edited-btn\" onclick=\"handleInterruption('EDITED')\" style=\"margin-top:8px;\">确认修改并继续</button>\n");
                        result.append("</div>\n");

                        // 输出当前状态信息供审核
                        state.value("classification").ifPresent(v -> {
                            result.append("\n分类结果: ").append(v).append("\n");
                        });
                        state.value("draft_response").ifPresent(v -> {
                            result.append("\n回复草稿: ").append(v).append("\n");
                        });
                    } 

InterruptionMetadata 数据:

前端显示效果:

2.5 流程恢复

中断暂停后,通过 RunnableConfig 携带恢复元数据再次调用流程图即可恢复,节点据此继续执行业务逻辑。

恢复中断关键要点:

  1. 恢复时必须使用触发中断时相同的 thread_id
  2. 使用 RunnableConfig.builder() 构建 HUMAN_FEEDBACK 元数据
  3. 如果存在 HUMAN_FEEDBACK ,流程会继续执行

人工批准恢复执行示例:

java 复制代码
                // 获取中断后的当前状态(参考标准中断恢复流程)
                var currentState = emailAgentGraph.getState(config);
                Map<String, Object> stateData = currentState.state().data();
                switch (action) {
                    case APPROVED:
                        // 人工批准:恢复执行
                        RunnableConfig approvedResumeConfig = RunnableConfig.builder()
                                .threadId(sessionId)
                                .resume() // 构建 `HUMAN_FEEDBACK` 元数据
                                .build();
                        // 继续执行 Graph(使用之前的状态)
                        stream = emailAgentGraph.stream(stateData, approvedResumeConfig);
                        log.info("Approved - updated state and resuming graph execution for sessionId: {}", sessionId);
                        break;

如果是人工拒绝,可以在状态中添加一个拒绝标识,会调用检查点进行更新和持久化,下次再执行这个会话时,可以提示当前会话流程已终止:

java 复制代码
                    case REJECTED:
                        // 人工拒绝:更新状态标记拒绝后终止执行
                        emailAgentGraph.updateState(
                                config,
                                Map.of("human_feedback", "rejected"),
                                null
                        );
                        log.info("Rejected - terminating execution for sessionId: {}", sessionId);
                        return Flux.just("❌ 执行已被拒绝终止。");

如果是人工编辑,是需要将状态中的数据展示给用户进行修改,再使用修改后的状态恢复执行,部分示例:

java 复制代码
                    case EDITED:
                        // 人工编辑:合并用户更新与审核标记后恢复执行

                        // 用户修改后的状态
                        @SuppressWarnings("unchecked")
                        Map<String, Object> stateUpdate = (Map<String, Object>) request.get("stateUpdate");
                        if (stateUpdate == null) {
                            stateUpdate = Map.of();
                        }
                        // 构建运行时配置
                        RunnableConfig resumeWithEditConfig = RunnableConfig.builder()
                                .threadId(sessionId)
                                .resume()
                                .build();
                        // 使用更新后的状态执行
                        stream = emailAgentGraph.stream(stateUpdate, resumeWithEditConfig);
                        log.info("Edited state and resuming for sessionId: {}, stateUpdate: {}", sessionId, stateUpdate);
                        break;

2.6 interruptsAfter 配置

interruptsAfter 用于配置【节点执行后】执行中断,指定节点执行完成结束后再暂停流程。

适用场景:

  • 文案生成、合同解析、内容抽取等节点跑完,暂停供人工校对、修改;
  • 节点产出业务结果后,需要外部系统回调、消费数据,再继续往下走;
  • 子图 / 子流程执行完毕后暂停,等待外部指令再进入主流程下一环节;
  • 数据加工、统计计算节点执行完,需先落库、发通知,再手动恢复续跑。

示例,定义classify_intent (邮件分类)节点执行后进行中断:

java 复制代码
        CompileConfig compileConfig = CompileConfig.builder()
                .saverConfig(saverConfig)
                .interruptAfter("classify_intent")
                .store(new MemoryStore())
                .build();

输入测试:

2.7 interruptBeforeEdge 配置

中断时只暂停不规划,恢复时再动态算下一步,专门为「动态条件路由边」设计,等人工改完数据状态后,再让流程做条件判断,保证路由正确。

对配置了 interruptsAfter 的节点,interruptBeforeEdge 可以配置:

  • false(默认值):中断后,直接计算下一个节点
  • true:中断后,不提前计算下一个业务节点,只把 nextNodeId 标记为 __INTERRUPTED__,恢复后才根据当前状态重新计算真正的下一个节点

配置示例:

java 复制代码
        CompileConfig compileConfig = CompileConfig.builder()
                .saverConfig(saverConfig)
                .interruptAfter("read_email")
                .interruptBeforeEdge(true)
                .store(new MemoryStore())
                .build();

如果有【邮件读取】、【邮件分类】两个节点,当配置了【邮件读取】配置了执行后中断时,并开启了 interruptBeforeEdge = true ,下一个节点设置为(__INTERRUPTED__),而不是【邮件分类】:

【邮件读取】执行完成后会进行中断,在恢复执行时,会再将下一个节点设置为本身应该执行的节点(实时计算):

3. InterruptableAction 模式

InterruptionMetadata 模式允许节点在运行时动态决定是否需要中断,提供了最大的灵活性。节点通过实现 InterruptableAction 接口,可以在任意时刻返回 InterruptionMetadata 来中断执行。

优势:

  • 灵活性强:可以在任意节点根据运行时状态决定是否中断
  • 动态控制:中断逻辑由节点自身控制,不需要提前配置
  • 状态感知:可以根据当前状态动态决定是否需要等待用户输入

3.1 定义可中断节点

在之前的邮件处理案例中,我们使用 interruptBefore 模式配置了一个人工审核节点:

java 复制代码
public class HumanReviewNode implements NodeAction {

    private static final Logger log = LoggerFactory.getLogger(HumanReviewNode.class);

    @Override
    public Map<String, Object> apply(OverAllState state) throws Exception {
        EmailClassification classification = state.value("classification")
                .map(v -> (EmailClassification) v)
                .orElse(new EmailClassification());

        // 准备审核数据
        @SuppressWarnings("unchecked")
        Map<String, Object> reviewData = Map.of(
                "email_id", state.value("email_id").map(v -> (String) v).orElse(""),
                "original_email", state.value("email_content").map(v -> (String) v).orElse(""),
                "draft_response", state.value("draft_response").map(v -> (String) v).orElse(""),
                "urgency", classification.getUrgency(),
                "intent", classification.getIntent(),
                "action", "请审核并批准/编辑此响应"
        );

        log.info("Waiting for human review: {}", reviewData);

        // 返回审核数据和下一个节点
        // 注意:在 interruptBefore 模式下,此节点在人工输入后才会执行
        return Map.of(
                "review_data", reviewData,
                "status", "waiting_for_review",
                "next_node", "send_reply"
        );
    }
}

将其改造为使用 InterruptableAction 实现中断,不需要 interruptBefore 配置:

java 复制代码
public class InterruptableHumanReviewNode implements AsyncNodeActionWithConfig, InterruptableAction {

    private static final Logger log = LoggerFactory.getLogger(InterruptableHumanReviewNode.class);

    private final String nodeId;

    public InterruptableHumanReviewNode(String nodeId) {
        this.nodeId = nodeId;
    }

    /**
     * 判断是否需要中断执行
     * @param nodeId 当前节点ID
     * @param state 当前状态
     * @param config 运行配置
     * @return 如果需要中断返回 InterruptionMetadata,否则返回 empty
     */
    @Override
    public Optional<InterruptionMetadata> interrupt(String nodeId, OverAllState state, RunnableConfig config) {
        // 检查是否已经有人工反馈
        boolean hasHumanFeedback = state.value("human_feedback").isPresent();
        // 也可以根据检查用户输入的信息是否完整进行判断
        if (!hasHumanFeedback) {
            // 没有人工反馈,需要中断等待人工输入
            log.info("Node [{}] requires human review, interrupting execution...", nodeId);

            // 准备审核数据
            EmailClassification classification = state.value("classification")
                    .map(v -> (EmailClassification) v)
                    .orElse(new EmailClassification());

            Map<String, Object> reviewData = Map.of(
                    "email_id", state.value("email_id").map(v -> (String) v).orElse(""),
                    "original_email", state.value("email_content").map(v -> (String) v).orElse(""),
                    "draft_response", state.value("draft_response").map(v -> (String) v).orElse(""),
                    "urgency", classification.getUrgency(),
                    "intent", classification.getIntent(),
                    "action", "请审核并批准/编辑此响应,完成后设置 human_feedback 字段"
            );

            // 创建中断元数据对象,包含审核数据供外部展示
            InterruptionMetadata interruptionMetadata = InterruptionMetadata.builder(nodeId, state)
                    .addMetadata("review_data", reviewData)
                    .addMetadata("interruption_type", "HUMAN_REVIEW_REQUIRED")
                    .build();

            return Optional.of(interruptionMetadata);
        }

        // 如果已经有 human_feedback,继续执行节点逻辑
        log.info("Human feedback received, resuming execution of node [{}]", nodeId);
        return Optional.empty();
    }

    /**
     * 节点实际执行逻辑(收到人工反馈后异步执行)
     * @param state 当前状态
     * @param config 运行配置
     * @return 更新后的状态(CompletableFuture 包装)
     */
    @Override
    public CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {
        // 获取人工审核结果
        String humanFeedback = state.value("human_feedback")
                .map(v -> (String) v)
                .orElse("approved");

        // 获取原始邮件分类
        EmailClassification classification = state.value("classification")
                .map(v -> (EmailClassification) v)
                .orElse(new EmailClassification());

        // 根据审核结果决定最终响应
        String finalResponse;
        String status;

        if ("approved".equalsIgnoreCase(humanFeedback)) {
            finalResponse = state.value("draft_response").map(v -> (String) v).orElse("");
            status = "review_approved";
            log.info("Human review approved, using draft response");
        } else if (humanFeedback.startsWith("edit:")) {
            finalResponse = humanFeedback.substring(5);
            status = "review_edited";
            log.info("Human review edited response");
        } else {
            finalResponse = "Request rejected by reviewer";
            status = "review_rejected";
            log.info("Human review rejected request");
        }

        Map<String, Object> result = Map.of(
                "final_response", finalResponse,
                "review_status", status,
                "reviewer_feedback", humanFeedback,
                "next_node", "send_reply"
        );

        return CompletableFuture.completedFuture(result);
    }
}

3.2 添加节点

只需要将可中断的节点添加到状态图中即可:

java 复制代码
var humanReview = new InterruptableHumanReviewNode("human_review");
workflow..addNode("human_review", humanReview)

3.3 流程中断

和上面的模式一样,通过 InterruptionMetadata 输出类型判断是否中断。

3.4 流程恢复

和上面的模式一样,通过会话 ID、修改后的状态执行恢复。

相关推荐
qq_411262421 小时前
基于 ESP32-S3 的四博 AI 双目智能音箱工程方案:四路触摸、IMU 姿态识别、震动反馈、双目屏状态机与语音克隆知识库接入
人工智能·智能音箱
老鱼说AI1 小时前
现代 LangChain 开发指南:从 LCEL 原理到企业级 RAG 与 Agent 实战
java·开发语言·人工智能·深度学习·神经网络·算法·机器学习
百度Geek说1 小时前
Browser Use:为 Agent 构建 Runtime Harness
人工智能
用户4330514143811 小时前
流程控制与并行工作
人工智能
云天AI实战派1 小时前
ChatGPT/API 调用故障排查指南:Realtime 音频、智能体浏览器操作与 AI 编码代理全流程修复手册
人工智能·chatgpt·音视频
水上冰石1 小时前
怎么查看olama是否用到了显卡加速
人工智能·显卡
码点滴1 小时前
用自然语言指挥 K8s 集群:AI 运维 Agent 的架构原理与可运行原型
运维·人工智能·kubernetes
Wanderer X1 小时前
【LLM】PPO
人工智能
霍夫曼vx_helloworld73521 小时前
字符提取与字符识别
图像处理·人工智能·计算机视觉