Human-in-the-Loop
HITL 安全機制,涵蓋讀寫分類策略、工具包裝攔截、前後端狀態同步,以及對話式人工介入流程設計。
設計問題
AI 助理能幫你查信件、搜資料、回答問題。但如果它從 email 裡找到了 deadline,接著還能幫你「發信」「建行事曆」「刪任務」呢?
這些操作一旦執行就無法輕易撤銷。一個誤判可能導致:
- 發了一封錯誤的郵件給老闆
- 把明天的重要會議刪掉了
- 在不對的時間建了行事曆事件
核心問題:如何在 AI 的自主性和安全性之間取得平衡?讓它能做事,但重要的事要人類先確認。
原則:讀取自由,寫入需要 HITL
最簡單的分界線:
讀取操作 → 自動執行
寫入操作 → 需要用戶確認但實務上沒這麼簡單。有些「讀取」也涉及隱私(查看行事曆日程),有些「寫入」的風險很低(標記 email 已讀)。
分類策略
策略一:名稱模式匹配
工具名稱包含特定動詞 → 需要人工介入:
create, update, delete, edit, add, book, move,
reschedule, cancel, complete, remove, write, send這是一個粗糙但有效的啟發式規則。它不需要人工維護每個工具的分類,只要新接入的工具遵守命名慣例,就能自動被正確分類。
策略二:明確列表
某些工具名稱不包含上述動詞,但仍需人工介入:
findCalendarEvents ← 名字像「讀取」,但涉及日程隱私
retrieveEventById ← 同上這是對策略一的補充,用來覆蓋命名慣例無法捕捉的例外。
兩層組合:先匹配模式,再查例外列表。大部分工具由模式自動分類,只有少數需要手動維護。
HITL(人工介入)流程設計
整體流程
用戶發送請求
用戶:「幫我把 John 那封信裡提到的 deadline 加到行事曆」
Approval Agent 快速判斷
判斷這個請求需要行事曆工具,工具被呼叫但被 HITL 攔截。
前端顯示人工介入請求
┌─────────────────────────────────┐
│ 建立行事曆事件 │
│ 參數:下週五 17:00, 合約截止日 │
│ │
│ [Approve] [Reject] │
└─────────────────────────────────┘用戶決定
- 批准 → 執行工具,返回結果
- 拒絕(附帶原因)→ Agent 看到原因,調整策略或放棄
關鍵設計:工具包裝
HITL 的核心技巧是工具包裝。它會把需要人工介入的工具「包」一層,讓工具看起來一模一樣(相同的名稱、描述、參數 schema),但執行時不真正運行,而是拋出一個信號。
原始工具:
findCalendarEvents.execute(input)
→ 呼叫 Google Calendar API
→ 返回事件列表
HITL 包裝後的工具:
findCalendarEvents.execute(input)
→ 不呼叫 API
→ 記錄工具名稱和參數
→ 拋出 ApprovalRequiredErrorAgent 完全不知道工具被包裝了。它照常決定要呼叫什麼工具、傳什麼參數,真正執行時才會被攔截。
和其他章節的關係
這篇專注在「HITL 機制怎麼做」。如果你想知道 Agent 為什麼會先找 email、再提出建立行事曆的請求,可以搭配閱讀 Agent Orchestration 與 External Integration。
為什麼用包裝而非 Agent 層面的判斷?
替代方案:在 system prompt 中告訴 Agent「這些工具需要 HITL,先問用戶再呼叫」。問題是:
- 不可靠:LLM 可能忽略指示,直接呼叫
- 不安全:安全機制不應該依賴 prompt compliance
- 多餘的 token:每次都要在 prompt 中列出哪些工具需要人工介入
包裝的方式是架構層面的保證。即使 LLM 選擇呼叫工具,程式碼也會阻止它直接執行。
前後端狀態同步
人工介入的生命週期
每個 HITL 請求都有一個唯一 ID,經歷以下狀態:
requested → approved → executed
→ failed
→ rejected狀態追蹤
前端需要追蹤哪些 HITL 請求還沒處理:
所有 HITL 請求的 ID → Set A
所有已決定的 ID → Set B
未決定 = A - B(Set difference)如果還有未處理的人工介入請求,輸入框就會被禁用。用戶必須先處理完所有待確認事項。
為什麼要禁用輸入?
如果允許用戶在人工介入尚未完成時繼續對話:
- Agent 會困惑:「用戶之前要我建行事曆,但還沒說 OK,現在又問了新問題」
- 狀態管理變複雜:這個決定要插到哪裡?
- 用戶體驗差:多條待處理的事項混在一起
禁用輸入是最清晰的 UX,一次只處理一件事。
拒絕與回饋
用戶拒絕時,可以附帶原因:
用戶拒絕:"不要明天,改成下週一。"這個原因會被轉成文字,注入後續的 Agent 上下文:
"The user rejected the tool: 不要明天,改成下週一。"Agent 看到後可以自動調整參數,重新提出人工介入請求。這形成了一個自然的對話式 HITL 流程。
訊息歷史的處理
問題
人工介入的 data parts(請求、決定、結果)不是標準的 LLM 訊息格式。LLM 不認識 data-approval-request 這種型別。
解法:文字化
在送給 LLM 之前,把 data parts 轉成文字:
data-approval-request → "The assistant requested to run findCalendarEvents
with input: { start: 'tomorrow', end: 'tomorrow' }"
data-approval-decision → "The user approved the tool."
or "The user rejected the tool: [reason]"
data-approval-result → "The tool returned this result: { events: [...] }"這樣 LLM 就能理解完整的人工介入歷史,在後續對話中做出合理的判斷。
HITL 的 UI 設計
資訊充分
用戶看到人工介入請求時,必須有足夠的資訊做出決定:
┌──────────────────────────────────────────┐
│ 🔧 Approval Request: findCalendarEvents │
│ │
│ Parameters: │
│ { │
│ "start": "tomorrow at 3:00pm", │
│ "end": "tomorrow at 4:00pm", │
│ "searchTerm": "team meeting" │
│ } │
│ │
│ [Approve] [Reject] │
└──────────────────────────────────────────┘用戶應該能回答:「這個工具要做什麼?用什麼參數?我同意嗎?」
拒絕流程
拒絕時,輸入框切換為「填寫原因」模式:
原本:[What would you like to know?]
切換:[Why are you rejecting this tool?]placeholder 的變化提示用戶「現在要給回饋」。
生產考量
人工介入超時
如果用戶一直不處理人工介入請求怎麼辦?
- 前端:確認按鈕持續顯示,輸入保持禁用
- 後端:不設超時,因為這是用戶的決定,不應該自動取消
- UX:考慮加入「稍後提醒」或「取消此操作」按鈕
批次人工介入
如果 Agent 需要執行多個寫入操作(如建立 3 個行事曆事件)?
- 逐一確認:最安全,但 UX 繁瑣
- 批次確認:一次顯示所有,用戶逐一決定
- 全部批准 / 全部拒絕:最快,但可能不夠細粒度
目前的設計是逐一確認,但保留了擴展到批次確認的可能。
HITL 的安全邊界
HITL 不是萬能的安全機制
它假設:
- 用戶能理解工具的參數和後果
- 用戶會認真審查而非盲目批准
- 工具的 description 正確描述了它的行為
對於高風險操作(如金融交易),可能需要更強的保護(如二次確認、限額)。
重點總結
| 設計決策 | 選擇 | 原因 |
|---|---|---|
| 分類策略 | 動詞模式 + 例外列表 | 自動覆蓋大部分工具,手動處理例外 |
| 攔截方式 | 工具包裝 | 架構層面保證,不依賴 prompt compliance |
| 控制流 | ApprovalRequiredError | 利用框架的錯誤管道,無需額外中斷機制 |
| 前端狀態 | 禁用輸入直到全部決定 | 簡化狀態管理,清晰的 UX |
| 拒絕機制 | 附帶原因,注入 Agent 上下文 | 形成自然的對話式 HITL 流程 |
| 歷史處理 | Data parts → 文字化 | 讓 LLM 理解完整的人工介入歷史 |
Last updated on