iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
AI Security

我也希望安全第一——然後我們看看這些鎖是怎麼一個個被撬開的系列 第 4 篇

第 4 篇|三個詞的演進:Prompt → Context → Harness Engineering

  • 分享至 

  • xImage
  •  

三個詞的演進:Prompt → Context → Harness Engineering

[[我也希望安全第一]]|第 4/30 天

這篇先把三個詞放回同一個任務

前三篇一路看資料、對齊與模型內部訊號。到這裡先往外走一層:當模型開始讀檔、執行指令,成敗就不再只由權重決定。

Prompt、Context、Harness 常被當成三波流行語。我比較想知道的是:同一個模型接到同一項任務時,這三層分別改變了什麼?

這篇就看一個修 bug 的示範。模型第一次沒有找到檔案,卻照樣交出答案;第二次只多了一小段工作原則,結果便完全不同。把過程按順序排出來,三個名詞也會清楚不少。

模型會寫程式,卻沒先看資料夾

這個示範讓 Google 的 Gemma 4 2B 修正 parser.py 裡的 bug,並用 verify.py 驗證結果。兩個檔案都放在模型可以操作的工作目錄中,但第一次沒有提供額外的工作引導。

Gemma 沒有先查看目錄。它從題目裡看到 parser.py 和函式名稱,便自行想像檔案內容,重新寫了一份,最後回報任務完成。

它知道 email parser 大概該怎麼寫,實際卡住的是工作順序:它沒有採取人類工程師很自然的第一步,先看看手邊到底有什麼。

第二次,示範者加入不到八十個字的通用原則:開始前先查看目錄、修改前先讀檔、完成後執行驗證。同一個模型這次依序找到檔案、閱讀內容、修改程式並跑完測試。

模型沒有變聰明,工作環境替它補上了一套做事方式。

Prompt、Context、Harness 各自補了什麼

把剛才的任務排在一起看,大致會是這個樣子:

Prompt   「修好 parser.py,讓 verify.py 通過」
   ↓
Context   parser.py、verify.py、專案說明、先前操作結果
   ↓
Harness   查看目錄 → 讀檔 → 修改 → 跑測試 → 根據結果重試

Prompt 給模型一個目標。早期大家最常調整的是問法:角色怎麼設定、步驟怎麼描述、要不要要求模型逐步推理。這些技巧能改善單次回答,卻不會自動把工作目錄和測試結果送進模型。

Context 補上完成任務需要的資訊。對話紀錄、檔案內容、搜尋結果與 RAG 找回來的文件,都屬於這一層。Gemma 第一次只知道檔名,不知道檔案實際內容;有名稱和拿得到內容,是兩回事。

Harness 負責讓整個過程運作起來。它把使用者需求、context、模型與工具接在一起,也安排每輪之間要保留什麼、何時執行工具、失敗後要不要重試,以及最後拿什麼當作完成證據。

三者沒有互相取代。Prompt 仍然說明目標,Context 仍然提供資訊;只是任務從「回答一句話」變成「連續操作一個環境」之後,越來越多成敗落在 Harness。

現在的 Harness 大概長什麼樣

Harness 聽起來像一套很大的平台,實際上可以從一個很小的 agent loop 開始:把任務與當前狀態交給模型,模型選擇回答或呼叫工具,系統執行後把結果放回下一輪。一直到模型說完成,或是用完步數、時間與預算。

User / Task
     ↓
Runner / Orchestrator ── Session、State、Checkpoint
     ↓                         ↑
Context Builder → Model → Tool Request
                              ↓
                    Tool Adapter / MCP
                              ↓
               Shell、Browser、API、Database
                              ↓
                  Result / Error / Evidence

整條路徑另外留下 Trace,必要時停下來交給人。

Runner 是跑這個迴圈的程式;State 是任務現在走到哪裡;Checkpoint 則是可以恢復的中間點。MCP(Model Context Protocol)這類介面解決的是「工具怎麼接進來」,不會自動決定模型該不該用它。

真正實作時,這張圖會往不同方向長。目前常見的不是單一標準架構,而是下面幾種取向:

取向 架構重點 什麼時候有用
線性 agent loop 對話歷程一路往後加,模型在每輪選擇下一個動作 想看懂每一步、做 baseline 或測試模型時
SDK / runner 把工具呼叫、session、handoff 與 trace 收進通用執行層 一般對話、工具使用與多 agent 分工
圖狀工作流 節點、分支、狀態與 checkpoint 都明確表示 任務會跑很久、需要中斷後續跑,或特定步驟要人介入
Agent server + 執行環境 agent 與真正動手的 workspace、container 或 VM 分開 寫程式、用瀏覽器或處理多人長時間任務

為什麼要分這麼細?因為「agent 卡住」的解法差很多。三步就做完的工具呼叫,用線性 loop 最好查;如果任務會跑幾個小時,半夜因 API 逾時斷在第二十七步,有沒有 checkpoint 就會決定 on-call 的人是按一次恢復,還是重跑整件事。

跟著 mini-swe-agent 跑一次

要看 Harness,我反而不會先選功能最多的框架。SWE-agent/mini-swe-agent 的預設 agent 類別只有約兩百行,核心迴圈更短,正好可以對照前面的三層各自落在哪裡。

如果把前面的任務交給它,過程大致如下:

system template +「修好 parser.py,讓 verify.py 通過」
                         ↓
                 model.query(messages)
                         ↓
                    action: ls
                         ↓
              environment.execute(action)
                         ↓
            observation: parser.py  verify.py
                         ↓
                  append to messages
                         └───── 回到下一輪

run() 開始時會先清空 messages,加入 system template 與任務,然後反覆呼叫 step()。而 step() 實際只做兩件事:先用目前全部 messages 詢問模型,再把模型回傳的 action 交給 environment 執行。執行結果會被格式化成 observation,追加回同一份 messages。

前一輪的 ls 結果,就是後一輪的 context。

這篇的名詞 在 mini-swe-agent 裡的位置
Prompt system template 與帶入任務的 instance template
Context 線性增長的 messages,內含模型回應、action 與 observation
Harness run() / step() 迴圈、model 與 environment 介面、上限檢查、錯誤處理與 trajectory 儲存

這個實作還有一個容易被忽略的部分。每次查詢模型前,它會先檢查步數、費用與總執行時間;格式連續出錯也會停下來。這些都不是模型的能力,是 Harness 從外面管住一次 run 的方式。

它的好處也是限制。線性歷程很好追,本地、container 或其他執行環境也能替換;但整個工具面主要是 shell,能做什麼很大程度取決於那個環境原本擁有的權限。messages 一直變長時,也得自己處理 context 成本。它會儲存 trajectory,不等於已有圖狀工作流那種明確的分支與 checkpoint 恢復。

這也是我選它當範例的因素:它沒有用大量框架名詞遮住 Harness 的基本工作。等基本迴圈看懂了,再去看 OpenAI Agents SDK 的通用 runner 與 handoff、LangGraph 的狀態與 checkpoint,或 OpenHands 如何在 agent server、workspace 與多後端控制中心之間分工,會比較知道自己在找什麼。

再看一個極簡方向:Pi 留下哪些東西

Pi 也是刻意做小的 coding harness,但它比 mini-swe-agent 更接近每天真的會用的終端工具。核心預設只交給模型 read、write、edit、bash,同時提供互動、JSON、RPC 與 SDK 模式。任務迴圈沒有因此變神祕:模型讀 context、選工具、取得結果,再決定下一步。

值得看的反而是它選擇不把什麼塞進核心。Sub-agent、plan mode、權限 gate、git checkpoint、sandbox 等能力可以透過 extension 或外部環境加入。這讓基本路徑比較容易追,也把責任露得很清楚:預設有 bash,並不等於預設有安全邊界;extension 又是會在本機執行的程式碼,安裝第三方 package 等於新增一個供應鏈信任決定。

Pi 的 session 用 JSONL tree 保存,可以分支、回到先前節點,也會在 context 接近上限時做 compaction。完整歷程仍在檔案裡,送給下一輪模型的摘要卻是有損的。這個差異後面會再出現:保存過紀錄,不代表模型當下看到的是完整紀錄;能恢復工作,也不代表能安全地恢復原本的權限與意圖。

為什麼這和安全有關

如果模型只在聊天視窗回答,猜錯一個檔案頂多得到一段不可靠的文字。當它能寫檔、執行程式或操作外部服務,同一種猜測就可能真的改變系統。

因此,Harness 會影響任務能否完成,以及錯誤能走多遠。它可以要求模型先讀再改、跑完測試才回報,也可以決定哪些資訊和操作結果會在下一輪重新送回模型。

但這裡先留一條界線。寫在工作原則裡的「不要修改正式資料」,仍然只是一句給模型看的指示;資料庫帳號究竟能不能寫入,要由模型外面的權限系統決定。第 5 篇會分別看工作指引、工具邊界與驗證流程;第 6 篇再看即使沒有人惡意下指令,agent 為了完成任務還是可能自己越界。

前四篇小結:從模型如何長出行為,走到誰能讓它動手

前四篇從訓練資料開始。資料治理能留下來源並降低已知風險,對齊能改變模型在測試中的行為分布,可解釋性開始提供內部訊號。到了 Harness,工程師才開始能在每一次任務外圍安排資訊、工具與驗證流程。

這四層都能降低風險,卻沒有一層可以單獨保證「這一次 tool call 一定安全」。第 5 篇不再問 Harness 是什麼,而是開始逐一看它的工作指引、能力邊界與行為驗證。接著兩篇分別看 agent 自己越界,以及外部內容把它帶走;第 9 篇會再回頭看看,這些限制為什麼不可能把所有路都封死。

前四篇裝了哪些鎖

前四篇從模型裡面一路走到執行迴圈。把它們放在一起看,最大的差別是:有些鎖只能改變模型「比較可能怎麼做」,有些則開始留下可檢查的工程邊界。

鎖 放在哪裡 想攔住什麼 失效時的跡象 怎麼驗證
資料與 adapter lineage 訓練資料、微調產物 來源不明、污染或錯版資料進入行為 找不到某項行為來自哪批資料與版本 固定資料/權重 hash,重跑回歸集
對齊與行為評估 post-training、eval 模型把代理指標當真正目標 換一種說法或環境,安全行為就消失 對抗切片、未見情境與外部效果測試
SAE/activation monitoring 模型內部 危險傾向只在輸出後才被看見 特徵漂移、誤報暴增或危險行為沒有訊號 綁定模型 revision,比較介入前後及副作用
Context 與工作指引 prompt、專案文件、記憶 沒讀環境便猜、忽略工作順序 指令衝突、舊摘要覆蓋新規則 固定任務回放,檢查實際讀取與引用來源
Harness 執行迴圈 模型與工具之間 未取得證據便宣稱完成 回答說成功,卻沒有 diff、測試或工具結果 強制完成證據,故意讓工具失敗再觀察行為

這裡還沒有真正限制 shell、網路和正式系統。後面才開始碰那些比較硬的控制。

參考與來源


上一篇
第 3 篇|能不能「看懂」AI 在想什麼——可解釋性作為安全支柱
下一篇
第 5 篇|模型提出 Tool Call 之後,誰來決定能不能執行?
系列文
我也希望安全第一——然後我們看看這些鎖是怎麼一個個被撬開的 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言