iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI Engineering

一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環系列 第 6

Day 6 - 工程師還在看 Log,Agent 就認定 UART 不能碰:Human/Agent 控制權的平衡點

  • 分享至 

  • xImage
  •  

系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。

我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。

本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。

Today’s Change

  • Change Ref: 主線 PR #43-allow agent handoff while human monitors console;後續收斂 PR #65-區分 Human 在場與正在操作、PR #86-多 Agent 重疊時仍能正確歸還控制權

  • Issue: 工程師維持 minicom Console 時,session self-test 會回報 HUMAN_INTERACTIVE_ACTIVE;即使 UART Session 是 READY,Agent Controller 仍會要求 Human 等待或離線。

  • Root Cause: Self-test 只要看到 Human 持有操作權,就直接判定 Agent 不能介入;後續又發現「Console 還連著」與「Human 正在操作」也不能混為一談。

  • Solution: PR #43 先讓 Self-test 可以短暫接手再歸還;PR #65 再依 Human 是否真的在輸入決定誰先讓路;PR #86 補上多 Agent 重疊時的交接保護。

  • Evidence: PR #43 以 Fake Target 驗證預設與 Strict 兩條路徑;PR #65 補上 Human Active/Idle 的 co-work tests;PR #86 驗證多層交接後仍能把控制權還給 Human。這些是受控 Host-side Evidence,不等同所有真機 UART 情境;PR #43 的完整 Test Suite 仍有一項可在乾淨基線重現的既有失敗。


人在看 Log,和人正在操作,是不是同一件事?

處理好 WAL 記錄與測試初始化後,我把 minicom 留著監看,再接上 Reboot Test Flow,問題馬上就來了。

為了觀察 DUT Reboot 前後的 UART 訊息,minicom 當然不能關;另一邊的 Agent Controller 則會先執行 Self-test,確認 COM0、COM1 能不能接手操作。

兩條 Session 明明都是:

READY

Self-test 卻回:

{
  "classification": "HUMAN_INTERACTIVE_ACTIVE",
  "recommended_action": "wait_or_detach_console"
}

翻成白話就是:

有人在 UART 前面。請等他走,或先把他的 Console 拔掉。

問題是,我留下 minicom,就是為了看 Agent 接下來怎麼 Reboot、怎麼 Recovery,以及板子到底吐了什麼東西。

現在為了讓 Agent 開始工作,第一步竟然是先把負責觀察 Agent 的人趕走。

我是要 Human-in-the-loop,不是 Human-quit-the-loop 耶~

乞丐趕廟公喔?

FullRun

Slider 往哪邊偏過頭,都會有人卡在門外

工程師 Attach Console 後,不只會看到 UART Output。為了必要時能直接輸入 Command,serialwrap 也會把互動控制權交給這個 Human Console,形成一個有 Owner、有生命週期的 Interactive Lease。

問題可以想成一條 Slider:

Human Control  ◀─────────────── 綠色區 ───────────────▶  Agent Autonomy

往 Human 端走過頭,只要人還掛著 minicom,Agent 就永遠不能進來。

往 Agent 端走太近,Agent 一開始跑工作,Human 就連必要的介入機會都沒有,只能等自動化自己決定什麼時候結束。

真正可用的位置不是某個固定刻度,而是中間那段可以互相讓路的綠色區:

Human 正在操作:
Human Control  ◀────────●──────────────────────────▶  Agent
                        Agent 先等一下

Agent 正在跑工作:
Human Control  ◀────────────────────────●──────────▶  Agent
                Human 保留監看,輸入先延後

Human 真的在用 UART 時,控制權微微往左,Agent 不要硬搶。

Agent 已經開始執行 Command 或持續跑 Test Flow 時,控制權微微往右,Human 繼續看 Log,但不要在 Command 中間插入新的 Byte。

這才是我想找的甜蜜點,就是要這麼絲滑。


PR #43 先解掉「永遠偏左」

我先說,我是偏右派的,因為偏左表示我要自己來,我不想。

原本的 Self-test 只要看到 Owner 是 human:*,就立刻回:

HUMAN_INTERACTIVE_ACTIVE

後面的設備連線、UART 通道與 Target 回應全部不再檢查。

也就是說,Slider 被固定在 Human 最左邊。

但這個問題感覺似曾相識?

Day 3 已經先決定一條 UART 只能有一個 Writer,Human 與 Agent 的輸入都必須經過 Broker 仲裁。底下其實也已經具備暫時保留 Human 輸入的能力,只是當時文章沒有展開這一層。

它的實際動作大致如下:

暫停 Human 輸入
→ Human 新輸入先保留下來
→ Agent 執行 Command
→ 恢復 Human 輸入
→ 再把剛才保留的內容送出去

暫停的是 Human 寫入 UART 的權利,不是把 Console 關掉。minicom 還是會持續顯示 Target Output;UART 底下則維持 Single-writer,不會把 Human 與 Agent 的兩條 Command 混成一條。

程式內部把這套機制叫作 Suspend/Resume,保留下來的輸入則放在 Deferred Buffer。

PR #43 做的事情,就是讓 Self-test 也走這條既有路徑。

現在 Human Console 還在時,Self-test 不會立刻拒絕,而是繼續確認設備與 UART 是否正常。真的需要送出一條測試指令時,才短暫讓 Agent 使用 TX;完成後,再把輸入權還給 Human。

就算中途發生錯誤,恢復 Human 控制權這件事也不能被跳過。

至於確定不允許任何交接的情境,仍可明確使用:

serialwrap session self-test \
  --selector COM0 \
  --strict-human-lock

這個 PR 解決了 Human Console 一存在,Agent 就永遠站在門外的問題。

但問題永遠都是一體兩面的:

Console 還開著,Human 就一定正在使用嗎?

FullRun

人在場,不代表人正在打字

minicom 留著監看,和工程師正在輸入 Command,表面上都叫作「Human 還連著」,實際需要的處理卻不同。

人在打字時,Agent 當然不應該突然搶走整段互動控制權。

但 Console 只是開著看 Log,Human 已經一段時間沒有輸入,繼續把它當成永久占用也不合理。

這一塊在後續的 PR #65 才補上。

serialwrap 開始記錄 Human 最後一次真正輸入的時間,把「Console 還連著」與「Human 最近仍在操作」分開。程式裡分別叫作 human_attachedhuman_active

概念上變成:

Human 最近仍在操作
→ Agent 不搶互動控制權,先等一下

Human 只是監看,已經沒有繼續輸入
→ Agent 可以暫時接手
→ Console 不斷線
→ 完成後再把控制權還回去

Source Code 裡把後者稱為 Soft Preempt。

名字聽起來很厲害,實際意思只是:

不把人踢出去,也不要求人先關 minicom;只是請目前沒有在輸入的人,暫時讓一下位置。

這才補上 Slider 靠近 Human 那一側的判斷。

不是只要看到人就全面停工,而是先確認人到底有沒有真的在用。


Agent 在跑時,Human 也先不要插隊

Slider 的另一側,則是 Agent 已經開始執行工作。

這時 Human 不需要離場,仍然可以透過 minicom 看 UART Log;但如果正好在 Agent Command 執行到一半時輸入新的內容,那些 Byte 不能立刻送進 UART。

否則 Target 收到的可能不是兩條 Command,而是一條誰都看不懂的混合物。

所以 Agent 執行期間:

Human 繼續看 Log
→ Human 新輸入先保留
→ Agent 完成目前的 Command
→ Human 輸入再接著送出

這裡的「Agent 優先」也不是整個 Full Run 永久鎖住 UART。

serialwrap 是逐條 Command 仲裁;Agent 已經排入的工作會先往下執行,Human 的輸入不會從中間切進去,但在命令邊界之外,控制權仍然可以正常交還。

換句話說,Slider 不會跑到 Agent 最右端。

Human 仍然在場、仍然看得到現場,也保留後續介入能力;只是不要在自動化執行到一半時,把手伸進 UART 裡一起攪拌。

FullRun

這才是現行的甜蜜點

把前後兩邊合起來,現行模型大致如下:

Human 正在操作
→ Agent 先不要搶互動控制權

Human 只是監看
→ Agent 可以暫時接手
→ 完成後還回去

Agent 正在執行 Command
→ Human 持續監看
→ 新輸入先保留
→ Command 完成後再送出

它不是 Human 與 Agent 各拿一半。

也不是誰的權限比較高。

真正的甜蜜點是:

誰正在使用 UART,另一邊就先讓一下;但不需要因此離開現場。

PR #43 先解掉 Self-test 永遠偏向 Human 的問題。

後續的 PR #65 再補上 Human Activity 判斷,讓 Slider 真的能在中間的綠色區裡左右移動。


先證明交接不會把人弄丟

PR #43 的測試先確認三件事:

  • Human Console 留著時,Self-test 仍然可以完成檢查。

  • 明確使用 Strict Mode 時,Agent 不會送出任何 Probe。

  • 不論 Probe 成功或失敗,最後都必須把輸入權還給 Human。

後續 PR #65 再補上「Human 最近是否真的有輸入」與 Soft Preempt 的測試。

再往後,PR #86 又補上多 Agent 同時進入時的保護。否則第二條 Agent 路徑如果再次接手,很可能把前一層記住的 Human 控制權蓋掉,最後沒有人知道該把 UART 還給誰。

這些測試沒有辦法證明所有 UART 情境都已經被收服。

但至少把最重要的底線釘住:

Human 不應該因為 Agent 工作而被踢出現場,Agent 也不應該因為 Human 還在現場就永遠無法開始。


Runtime 會交接了,文件跟得上嗎?

到這裡,Human 正在操作時 Agent 會讓路;Agent 正在執行時,Human 也可以留在旁邊監看。

但 serialwrap 並沒有停在這裡。

隨著遇到的問題愈來愈多,它的操作方式、目錄位置與內部架構也改了好幾次。等我回頭把文件全部攤開來檢查,才發現有些地方還寫著舊路徑、舊指令,甚至還在介紹早已移除的功能。

Code 已經往前走了。

文件卻留在原地。

更精彩的是,Policy Check 仍然是 21 pass / 0 fail,只有一項既有的 Advisory Warning。

下一篇,我們來看看:

Code 都已經活在新世界了,為什麼文件檢查還在舊世界全部 PASS?

Have a nice day.


上一篇
Day 5 - UART 明明還接著,測試一跑 minicom 卻斷線:自動化測試的初始化準備
下一篇
Day 7 - 拿著 Windows 3.1 的說明書來修 Windows 11:如何讓文件與架構對齊?
系列文
一天一個 PR,讓我告訴你嵌入式開發的殘酷:如何為 AI Agent 建立可信的工程閉環8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言