惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
P
Privacy & Cybersecurity Law Blog
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
C
CERT Recently Published Vulnerability Notes
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Cisco Blogs
博客园 - 司徒正美
L
LINUX DO - 热门话题
D
Docker
M
MIT News - Artificial intelligence
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
Jina AI
Jina AI
Last Week in AI
Last Week in AI
Security Latest
Security Latest
The Hacker News
The Hacker News
L
Lohrmann on Cybersecurity
Y
Y Combinator Blog
A
Arctic Wolf
小众软件
小众软件
T
Threat Research - Cisco Blogs
Know Your Adversary
Know Your Adversary
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
I
InfoQ
腾讯CDC
Google DeepMind News
Google DeepMind News
B
Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 叶小钗
I
Intezer
NISL@THU
NISL@THU
A
About on SuperTechFans
爱范儿
爱范儿
C
Cybersecurity and Infrastructure Security Agency CISA
D
Darknet – Hacking Tools, Hacker News & Cyber Security
G
Google Developers Blog
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Cyberwarzone
Cyberwarzone
AWS News Blog
AWS News Blog
Engineering at Meta
Engineering at Meta
Latest news
Latest news

博客园 - Gary Zhang

MiniMind 从零训练 LLM — 详细学习笔记 C端产品的三个关键词和B端产品的一个关键词 人工智能相关概念 经济活动原理推导 使用Matlab对账期客户订单的逾期率进行预测 BizTalk RosettaNet 解决方案 - Gary 存储HTTP请求Body部分到文件中 BizTalk Visual Studio 各版本自动部署GAC命令 RosettaNet PIP 清单 BizTalk RosettaNet解决方案与项目调研 ChatGPT可能的影响与机会 使用Google Bigquery快速用SQL查询Excel数据 Azure logic App 构建低成本EDI解决方案(英文) 从 BizTalk 跟踪数据库中清除数据 自助批量下载Confluence文档内容 Elsa 工作流引擎 Hello World Azure 升级订阅服务及服务区别 MIME 消息 BizTalk性能优化配置(建议) Azure Logic APP 与 EDIFACT/X12/RosettaNet/AS2解决方案概述 BizTalk 安装管理 API
Human-in-the-Loop (HITL) 设计文档-学习笔记
Gary Zhang · 2026-06-14 · via 博客园 - Gary Zhang

Human-in-the-Loop (HITL) 设计文档

SpringAI Agent Engine — 设计方案、原理、实现规格与 Demo 图鉴(合并版)
版本:与当前代码库同步(2026-06)
适用范围:backend/ + frontend/ 中的 HITL 子系统

文档结构

章节 内容
§1–2 背景、目标、总体架构
§3–8 sessionId/requestId、数据模型、三通道触发、沙箱续跑、Guard
§9–12 前端设计、REST API、持久化、Agent 线程模型
§13 Demo 1–9 图鉴(含运行截图)
§14–18 配置、源码索引、演进、测试、术语
附录 A 设计原理与状态机

HITL-设计方案与原理.md 已合并至本文附录 A,该文件仅保留跳转说明。


1. 背景与目标

1.1 问题

Agent 执行中需用户确认危险操作、选择方案、补充信息或批量审批。若 LLM 仅在文本里提问,前端无法渲染结构化面板,回答也无法可靠回传给正在运行的 Agent 线程。

1.2 设计目标

目标 说明
结构化输入 confirm / single_choice / multi_choice / freeform_question / batch_confirm
执行挂起 HITL 点阻塞直至用户提交或超时
一问一答绑定 每面板唯一 requestId
会话隔离 sessionId 隔离 pending
多通道触发 ask_user、嵌入 JSON/DSML、沙箱 marker 统一收敛
沙箱续跑 marker 确认后 env 重跑脚本
沙箱防绕过 HitlSandboxGuard
端外快捷审批 Demo 9:消息通知 + 短链 + 处理进度

1.3 非目标

  • WebSocket(使用 SSE + REST)
  • 同 session 并行多个 HITL(串行)
  • 服务端完整对话存储(history 客户端携带)
  • 多租户鉴权

2. 总体架构

React ChatPage / HitlLinkPage
    │ SSE (input_request)          │ REST POST /api/hitl/respond
    ▼                              ▼
AgentService ──► HumanInputService (registerPending / awaitInput / respond)
    ├── AskUserSkillHandler
    └── CodeExecutionSkillHandler + HitlSandboxGuard
            └── HitlLinkService (Demo 9 短链)

2.1 传输层

方向 协议 端点
服务端 → 客户端 SSE POST /api/chat/stream
HITL 回答 REST POST /api/hitl/respond
pending REST GET /api/hitl/pending/{sessionId}
Demo 8 REST POST /api/hitl/demo/large-batch
Demo 9 REST POST /api/hitl/demo/notify-confirm
短链 REST GET/POST /api/hitl/link/{token}
短链页 SPA /h/{token}

3. 核心标识

3.1 sessionId

一次对话生命周期 ID。索引 pending[sessionId]、快照文件、沙箱日志。客户端从 SSE 写入 state 并在后续 chat/hitl 请求复用。

3.2 requestId

单次 HITL「问」的 UUID。SSE input_requestPOST respond 必须一致。CompletableFuture 按 requestId 唤醒。

3.3 history

客户端携带 history[];用户提交后追加 hitl_feedback 消息,保证 LLM 跨轮理解决策。


4. 数据模型

4.1 InputRequest

requestId, sessionId, prompt, interactionType, options, questions, suggestions, defaultTimeoutMs, source, meta

4.2 InputResponse

decision, values, freeText, cancelled, timedOut

4.3 ChatStreamEvent(HITL 相关)

input_request | input_resolved | input_timeout | tool_result(含 _hitl:true


5. 三通道触发

通道 source 恢复方式
ask_user 工具 skill tool_result JSON
嵌入 JSON/DSML llm_structured / llm_dsml UserMessage 合成
沙箱 HITL_REQUEST sandbox_marker env 重跑 + 输出合并

6. HumanInputService

registerPending(req) → futures[requestId] = new CompletableFuture<>()
awaitInput(req)        → future.get(timeoutMs)  // Agent worker 阻塞
respond(resp)        → future.complete(resp)    // 仅首次有效

registerPending 必须在 SSE 推送之前调用,避免用户秒点 404。


7. 多会话一致性

层次 机制
运行时 requestId 精确绑定
会话 sessionId 隔离 pending
语义 history + hitl_feedback

防重复:hitlCompletedRef(前端)、hitlResolvedInTurn(后端 ThreadLocal)。


8. 沙箱续跑与 Guard

环境变量: HITL_PHASE=resume, HITL_DECISION, HITL_VALUES, HITL_FREE_TEXT

Phase 分支(推荐): 首次 print marker;resume 分支执行业务。

HitlSandboxGuard: 拒绝伪造 env、无 phase 分支的 marker 脚本、首次路径 premature「已发送」输出。


9. 前端设计

模块 职责
ChatPage.jsx SSE、Demo 1–9、pending 面板、通知卡片
HumanInputPanel.jsx 五种 UI;可选 respondFn(短链页)
HitlLinkPage.jsx /h/{token} 审批 + 时间线
BatchLargePanel.jsx Demo 8 分页大批量
hitl.js API、normalize、15s 超时、listPending 恢复

pending 恢复: hitlLoading 约 2s 后自动 listPending(sessionId)


10. REST API 规格

POST /api/hitl/respond

Body: { sessionId, requestId, decision, values, freeText, cancelled, timedOut }
200: { status: "accepted" } · 404: pending 不存在

GET /api/hitl/pending/

返回 List<InputRequest>

POST /api/hitl/demo/large-batch · /demo/notify-confirm

Demo 8/9 预设载荷,见 §13

HitlLinkView:含 inputRequest, notification, status, steps[]


11. 持久化

FileSnapshotStore./data/hitl-snapshots/{sessionId}.json
重启后 future 丢失;需 listPending + reload 策略(部分未实现)。


12. Agent 执行模型

HTTP 线程返回 Flux;agent-{sessionId} worker 执行 runConversation,HITL 阻塞在 worker 上。
hitlEnabled=false 时不注册 ask_user、忽略沙箱 marker。


13. Demo 图鉴(1–9)

Cursor 预览:§13 截图使用 ![alt](data:image/...;base64,...) 格式(勿用 HTML <img>,长 base64 会被当纯文本)。图片放在表格外独立块中。源 PNG:docs/images/hitl/

预览测试:

Demo 9 处理完成

13.0 Demo 机制分类

类型 Demo 触发方式 依赖 LLM 面板位置
A. LLM 驱动 1–7 Chat 发送 DEMO_SCRIPT[n] → SSE input_request Chat · HumanInputPanel
B. 后端预设 8 POST /api/hitl/demo/large-batch Chat · BatchLargePanel
C. 消息 + 短链 9 POST /api/hitl/demo/notify-confirm Chat + /h/{token}

flowchart LR subgraph A["A · LLM 驱动 Demo 1-7"] U1[用户点 Demo] --> SSE[SSE input_request] SSE --> P1[HumanInputPanel] P1 --> R1[POST /api/hitl/respond] R1 --> AG[Agent 继续] end subgraph B["B · 后端预设 Demo 8"] U2[Demo 8 按钮] --> API8[POST demo/large-batch] API8 --> BL[BatchLargePanel] end subgraph C["C · 短链 Demo 9"] U3[Demo 9 按钮] --> API9[POST demo/notify-confirm] API9 --> N[通知卡片] API9 --> LINK[/h/token] LINK --> TL[处理时间线] end


13.1 Demo 1 — 单选(single_choice)

入口 Demo 1 - 单选 · DEMO_SCRIPT[0]
通道 ask_user 工具
类型 single_choice
验证 标准 function calling → 挂起 → tool_result 续跑

流程:

sequenceDiagram participant U as 用户 participant FE as ChatPage participant AG as Agent participant H as HumanInputService U->>FE: 发送 Demo 1 脚本 FE->>AG: POST /api/chat/stream AG->>H: registerPending + input_request FE->>U: 单选面板 U->>FE: 点击「快速排序」 FE->>H: POST respond H->>AG: 唤醒 → tool_result AG->>FE: 生成排序代码

运行截图(界面示意 · Demo 1–7 依赖 LLM,实机面板随模型响应变化)

① 用户输入

Demo1 输入

② 选项面板

Demo1 面板

③ 提交结果

Demo1 结果

操作: Chat 页 HITL 开启 → 点 Demo 1 - 单选 → 在 amber 边框面板中点击一项(点击即提交)→ 观察 hitl_feedback 收据与 Agent 继续输出。


13.2 Demo 2 — 沙箱多选(multi_choice)

入口 Demo 2 - 沙箱多选 · DEMO_SCRIPT[1]
通道 沙箱 HITL_REQUEST::...::END::
类型 multi_choice
验证 HITL_PHASE=resume + HITL_VALUES 续跑

运行截图

① 多选面板

Demo2 面板

② 沙箱续跑输出

Demo2 结果

脚本要点: 首次 phase 打印 marker;resume phase 读取 HITL_VALUES.selected 并打印「已启用模块」。


13.3 Demo 3 — 批量确认(batch_confirm)

入口 Demo 3 - 批量确认 · DEMO_SCRIPT[2]
通道 ask_user
类型 batch_confirm(5 个临时文件)
values {id}approved / rejected

运行截图

① 批量面板

Demo3 面板

② 提交收据

Demo3 结果

UI: 支持「全部同意 / 全部拒绝」与逐项切换。


13.4 Demo 4 — 自由问答(freeform_question)

入口 Demo 4 - 自由问答 · DEMO_SCRIPT[3]
通道 ask_user(或 DSML 降级)
类型 freeform_question
特性 suggestions chips 快速填充

运行截图

① 问答面板(输入 + chips)

Demo4 面板


13.5 Demo 5 — 沙箱确认(confirm)

入口 Demo 5 - 沙箱确认 · DEMO_SCRIPT[4]
通道 沙箱 marker
类型 confirm
场景 磁盘扫描 → 用户确认 → 执行清理

运行截图

① 确认面板

Demo5 面板

② 清理执行输出

Demo5 结果


13.6 Demo 6 — 沙箱防绕过(HitlSandboxGuard)

入口 Demo 6 - 防绕过 · DEMO_SCRIPT[5]
通道 沙箱 marker + Guard 静态校验
验证 未确认前不得输出「已发送」;伪造 env 拒绝执行

运行截图

① 发邮件前 confirm

Demo6 面板

② Guard 拦截违规脚本

Demo6 Guard

三层防护: Prompt · HitlSandboxGuard · awaitInput 运行时阻塞。


13.7 Demo 7 — 批量改值(editable 行)

入口 Demo 7 - 批量改值 · DEMO_SCRIPT[6]
类型 batch_confirm 混合 confirm + editable
values {id} + {id}__value
{
  "discount-vip": "approved",
  "discount-vip__value": "90",
  "budget-cap": "approved",
  "budget-cap__value": "800"
}

运行截图

① 可改值批量面板

Demo7 面板


13.8 Demo 8 — 200 行 × 5 字段(BatchLargePanel)

入口 Demo 8 - 200行(REST,不经过 LLM)
API POST /api/hitl/demo/large-batch
后端 HitlDemoPresets.largeBatch200() + HitlBatchExpand
values {id} + {id}__{fieldKey}(最多 1200 键)

问题: LLM 无法可靠输出 200 行 questions[]
方案: 服务端展开;前端行数 ≥ 50 启用 BatchLargePanel(分页 25 行、搜索、5 列编辑)。

实机运行效果

Demo8 实机

界面示意

分页表格示意

Demo8 示意

面板裁剪(实机)

Demo8 实机


13.9 Demo 9 — 消息通知 + 短链接

入口 Demo 9 - 通知 / Demo 9 - 失败
API POST /api/hitl/demo/notify-confirm
短链 GET/POST /api/hitl/link/{token} · 页面 /h/{token}
组件 HitlLinkService · HitlLinkPage

处理步骤(模拟): decision → validate → deploy → verify

sequenceDiagram participant Chat as ChatPage participant API as HumanInputController participant Link as HitlLinkService participant Page as HitlLinkPage Chat->>API: POST /demo/notify-confirm API-->>Chat: 通知 + token + inputRequest Page->>API: GET /link/{token} Page->>API: POST /link/{token}/respond Link->>Link: 异步 processing Page->>Page: 轮询步骤时间线

实机运行效果(推荐)

① Chat 页:消息通知 + 确认面板

Demo9 Chat

② 短链页 /h/{token}:待审批

Demo9 pending

③ 短链页:用户同意后 · 处理完成(四步时间线)

Demo9 success

上图:通知卡片 →「处理完成」→ 收到确认 / 校验 / 部署 / 验证 全部打勾。

④ 失败路径

Demo9 fail

界面示意(补充)

通知卡片示意

通知卡片示意

处理中示意

处理中示意

失败跳转示意

失败跳转示意

双通道: Chat POST /api/hitl/respond 与短链 respond 等价;onExternalRespond 同步启动进度流水线。
限制: token 仅存内存,backend 重启后短链失效。


14. 配置项

hitl:
  snapshot-dir: ${HITL_SNAPSHOT_DIR:./data/hitl-snapshots}
  default-timeout-ms: ${HITL_DEFAULT_TIMEOUT_MS:60000}

15. 关键源码索引

模块 路径
AgentService backend/.../service/AgentService.java
HumanInputService backend/.../service/HumanInputService.java
HumanInputController backend/.../controller/HumanInputController.java
HitlLinkService backend/.../service/hitl/HitlLinkService.java
HitlDemoPresets backend/.../service/hitl/HitlDemoPresets.java
HitlSandboxGuard backend/.../service/hitl/HitlSandboxGuard.java
HitlBatchExpand backend/.../service/hitl/HitlBatchExpand.java
ChatPage frontend/src/pages/ChatPage.jsx
HitlLinkPage frontend/src/pages/HitlLinkPage.jsx
HumanInputPanel frontend/src/components/HumanInputPanel.jsx
BatchLargePanel frontend/src/components/BatchLargePanel.jsx
hitl.js frontend/src/lib/hitl.js
Demo 截图 docs/images/hitl/(与 docs/assets/hitl/ 同步)
截图脚本 docs/scripts/capture-hitl-screenshots.mjs

16. 已知限制与演进

  1. respond 不校验 sessionId 归属
  2. 无服务端 conversation store
  3. 快照 reload 未接入启动
  4. sessionId 未 localStorage 持久化
  5. 短链 token 仅内存(Demo 9)
优先级
P0 localStorage sessionId + mount 时 listPending
P1 respond 校验 sessionId + requestId
P2 HitlLink token 持久化 + TTL

17. 测试覆盖

HumanInputServiceTest, HitlRequestParserTest, HitlSandboxGuardTest, HitlBatchExpandTest, HitlLinkServiceTest, CodeExecutionSkillHandlerHitlTest


18. 术语表

术语 定义
HITL 人机协同挂起点
pending 已创建未响应的 InputRequest
Phase 续跑 HITL_PHASE 区分首次/恢复执行
HitlSandboxGuard 沙箱脚本静态防绕过校验