掌握 Harness Engineering:結合 Ralph Loop 與 MemRL,打造具備持續學習能力的 AI Agent

1 前言
- Agent 發展的典範轉移: 從單純追求「更聰明的大模型」,轉向「建構更完善的系統工程」。真正的智能不僅來自 Weights,更來自系統的記憶與反饋機制。
- 核心痛點: AI Agent 在工業界落地時面臨的兩大難題:
- 無狀態與幻覺: Agent 容易在同一個坑裡跌倒兩次,且單純的 RAG 會引發「語義噪音」。
- 規模化與穩定性: 複雜任務導致 Context Rot (上下文腐爛),且微調 (Fine-tuning) 模型成本極高、容易引發災難性遺忘。
- 本筆記目標: 結合 《MemRL》論文 的演算法設計,以及 LangChain 的 《Agent Harness》 與 《Continual Learning》 系統觀,提供一份從「記憶檢索」到「執行腳手架」的全方位架構藍圖。
2 Agent 系統的三層架構解剖
在傳統軟體開發中,我們關注「程式碼 + 資料庫」;在早期的 AI 開發中,我們關注「模型」。但在 2026 年的 Agent 工程實踐中,我們必須遵循這個黃金公式:
Agent = Model + Harness + Context
這三者的關係可以類比為:Model 是大腦的神經元,Harness 是肉體與感官,Context 則是記憶與當下的感知。
2.1 Model (模型層):凍結的大腦與推理引擎
Model 是系統的決策核心,負責將輸入的文字序列轉化為邏輯推理或工具呼叫。
- 工業界現況:凍結 (Frozen) 是常態
在生產環境中,我們通常將 Model 視為「不可變的基礎設施 (Immutable Infrastructure)」。原因在於:
- 災難性遺忘 (Catastrophic Forgetting): 微調 (Fine-tuning) 模型來學習新規則,極易破壞它原本的基礎推理能力。
- 算力與延遲: 頻繁微調模型會導致 MLOps 成本飆升。
- 模型與腳手架的深度耦合 (Post-training Alignment):
這是目前最硬核的觀察。如 Claude 3.5 Sonnet 這類頂尖模型,在後訓練階段 (RLHF) 就已經被放進特定的 Harness 中練習。
- 案例: Claude 3.5 被訓練為高度偏好
str_replace或apply_patch這種特定的檔案修改格式。如果你換成 OpenAI 模型,即使模型智商一樣,它可能因為無法精準產生符合該格式的字串而導致 Harness 解析失敗。這就是所謂的 「腳手架依賴 (Harness-specific optimization)」。
- 案例: Claude 3.5 被訓練為高度偏好
2.2 Harness (腳手架層):Agent 的維生系統與控制中樞
Harness 是所有包裝在模型外圍的程式碼與基礎設施。它是 Agent 能夠從「說說而已」變成「實際行動」的關鍵。
2.2.1 Infrastructure
- Filesystem 模型是無狀態的 (Stateless)。Harness 必須提供一個持久化的工作目錄。Agent 的進度不應存在 Prompt 裡,而應存在磁碟上的檔案(如
CLAUDE.md或TODO.json)。 - Sandboxes:Harness 負責調度 Docker 或虛擬機,讓模型產生的 Bash 指令或 Python 代碼能安全執行。
2.2.2 Action Space & MCP
- 工具封裝:Harness 將 API 封裝成模型可理解的 JSON Schema。
- 漸進式揭露 (Progressive Disclosure): 為了節省 Token,Harness 不會一次給模型 100 個工具,而是先給一個
list_tools,讓模型需要時才動態載入特定工具的定義(Just-in-Time Injection)。
2.2.3 Orchestration & Hooks
- Middleware (中介軟體):Harness 會在模型輸出後進行「格式攔截」。
- 案例:如果模型輸出了一個超長的日誌 (Log),Harness 會主動執行 Tool Call Offloading,只把 Log 的頭尾存入 Context,其餘存入檔案,避免模型因上下文過載而變笨。
- 自我驗證 (Self-Verification):Harness 可以在模型回覆使用者前,自動跑一遍測試腳本 (Test Suite),如果報錯,直接把 Error 拋回 Ralph Loop。
2.3 Context (上下文/記憶層):動態經驗的注入
Context 是 Harness 根據當前任務,從外部動態拼湊出來給 Model 的輸入。
- Hierarchical Memory Isolation:
在企業級應用中,記憶體必須根據隱私權限進行分層:
- Agent-level: 通用的最佳實踐、安全性原則。
- Org-level: 內部專案標準、專屬 API 文檔。
- User-level: 使用者的編碼偏好、歷史操作習慣。
- Injection Logic: Harness 會在發送請求給 Model 前,執行一次檢索(例如用 MemRL 算法),將最相關的高效能經驗 (High-utility experiences) 注入 Context。
2.4 Harness Engineering Pseudo-code
# Harness 內部的控制邏輯 (Pseudo-code)
def run_agent_loop(user_input):
context = load_hierarchical_memory(user_id, org_id) # 載入多層級記憶
base_tools = ["list_skills", "read_file"] # 初始只給最少量的工具
while True:
# 1. 拼湊最終給模型的 Prompt
prompt = assemble_prompt(user_input, context, base_tools)
# 2. 模型決策
response = model.call(prompt)
# 3. Harness 攔截處理 (Hooks)
if response.calls_tool == "list_skills":
# 漸進式揭露:發現模型需要資料庫工具,動態注入定義
db_tool_definition = fetch_tool_schema("database_query_tool")
base_tools.append(db_tool_definition)
continue # 重新開始迴圈,不消耗外部回覆
if response.has_long_output:
# 防腐機制:自動截斷過長輸出 (Head/Tail Truncation)
response.content = truncate_log(response.content)
# 4. 執行並獲得反饋 (Observation)
result = sandbox.execute(response.action)
context.update_short_term_memory(response.action, result)3 Context 層的持續學習 —— MemRL 演算法實踐
3.1 核心哲學:從「相似性」轉向「實用性」 (Utility-Driven)
傳統的 RAG 記憶體有一個根本性的缺陷:語義相似 (Semantic Similarity) 解決問題的效用 (Functional Utility)。
- 痛點: 如果 Agent 曾經用一個錯誤的方法嘗試解決某個 SQL 查詢任務,傳統 RAG 下次遇到相似任務時,依然會因為「語義接近」而把錯誤的經驗檢索出來餵給模型。
- 解法 (M-MDP): 將記憶建模為 Memory-Based MDP,每一條經驗不再只是
(Vector, Content),而是三元組:(Intent, Experience, Utility/Q-value)。- Intent (意圖): 原始任務的向量特徵。
- Experience (經驗): 執行的軌跡 (Traces)、成功的代碼或失敗的反思。
- Utility/Q-value (效用值): 該經驗在歷史中幫助任務成功的機率與回報。
3.2 雙階段檢索機制 (Two-Phase Retrieval)
為了平衡「搜尋速度」與「經驗質量」,我們在 Harness 中實作兩階段過濾:
3.2.1 Phase A: Similarity-Based Recall (語義召回 - 粗篩)
- 動作: 在 Vector DB 中執行 Top-k1 的 Cosine Similarity 搜尋。
- 目的: 作為「守門員」,確保撈出來的經驗在業務邏輯上是相關的。
- 工程 Trick: 設定一個硬性的相似度閾值 (例如 0.38),低於此值的經驗直接丟棄,防止 Agent 被完全不相干的「高分回憶」干擾。
3.2.2 Phase B: Value-Aware Selection (價值感知選擇 - 精排)
- 動作: 對候選經驗進行加權打分。
- 公式:
- 意義: 控制了 Agent 的「執拗程度」。
- 若 ,系統會同時考慮「長得像不像」與「過去成不成功」。
- 這能有效過濾掉那些「語義噪音」——即長得像但實際上會導致失敗的經驗。
3.3 Q-Value 動態更新規則 (Non-Parametric Learning)
這是 Agent 自進化的燃料。我們不更新權重,而是更新 Vector DB 裡的 Metadata (Q-value 欄位)。
更新公式 (Monte Carlo / EMA):
- (Reward): 環境給予的反饋(成功 = +1, 失敗 = -1)。
- (Learning Rate): 記憶的更新速度。
工程實作 (Runtime Update): 當 Ralph Loop 完成一次任務並獲得驗證結果(例如 Test Suite 通過),Harness 會立刻回頭更新剛才被檢索出來的那幾條記憶。
- 優勢: 這是 “Hot-path” (即時學習)。Agent 下一秒處理相似任務時,Q-value 已經更新,它會立刻避開剛才失敗的路徑。
3.4 學習失敗:高價值失敗經驗 (Near-Misses)
MemRL 論文中最具工程啟發的點在於:失敗的經驗可能比成功的更有價值。
- Failure Reflection (失敗反思):
當任務失敗時,Harness 派一個 LLM 去分析 Traces,生成一段
[PATTERN TO AVOID]。 - 記憶體寫入: 這段失敗反思會被賦予一個初始 Q-value。即使它是「失敗」的,但因為它明確告訴 Agent「不要做什麼」,它在相似情境下的決策效用極高。
- 案例:
- 經驗 A (成功): 「直接用
sed替換 config。」 (Q: 0.8) - 經驗 B (失敗反思): 「注意:
sed會略過被註釋掉的行,建議先取消註釋再替換。」 (Q: 0.95) - 結果: 下次 Agent 會優先讀取經驗 B,避免了潛在的 Bug。
- 經驗 A (成功): 「直接用
3.5 實作範例:記憶體層級與更新時機
在 2026 年的企業架構中,我們會結合 Hot-path 與 Dreaming:
| 層次 | 學習時機 (When) | 處理內容 (What) | 目的 |
|---|---|---|---|
| Hot-path | 任務完成的瞬間 (Runtime) | 更新現有記憶的 Q-value | 即時糾錯,避免連續犯錯。 |
| Dreaming | 離線批次 (Offline/Daily) | 掃描 Traces,將多條相似經驗 總結(Summarize) 成一條高階規則 | 壓縮 Context 空間,對抗「記憶膨脹」。 |
3.6 MemRL Q-Value Update Pseudo Code
def post_task_learning(task_trace, feedback_reward):
# 1. 取得本次任務中「被選中並注入 Context」的經驗 IDs
retrieved_memory_ids = task_trace.metadata["retrieved_ids"]
# 2. 執行 Non-Parametric RL 更新
alpha = 0.3 # 學習率
for mem_id in retrieved_memory_ids:
old_q = vector_db.get_metadata(mem_id, "q_value")
# 計算新的 Q 值
new_q = old_q + alpha * (feedback_reward - old_q)
# 寫回 Vector DB 的 Metadata
vector_db.update_metadata(mem_id, {"q_value": new_q})
# 3. 如果失敗,觸發失敗反思並寫入新經驗
if feedback_reward < 0:
reflection = llm.generate_reflection(task_trace)
vector_db.add_new_experience(
intent=task_trace.intent,
content=reflection,
initial_q=0.5 # 給予中等初始權重,引發下次嘗試
)4 Harness 層工程技巧
4.1 Ralph Loop:The Orchestrator
Ralph Loop 是一種對抗「多智能體複雜度陷阱」的架構心法。它主張 「大道理不如一個強健的迴圈」。
- 核心理念:Monolithic > Multi-Agent
- 痛點: 過早引入多個 Agents 互相對話會增加系統的隨機性 (Entropy),導致除錯困難與不穩定。
- Ralph 模式: 讓一個強大的模型 (如 Claude 3.5 或 GPT-5) 在一個單純的
while迴圈中運作。每個迴圈只做一件事:「觀察 → 思考 → 行動 → 獲得反饋」。
- 「丟回轉盤上 (Throw it back on the wheel)」:
- 當 Agent 在迴圈中失敗或報錯時,Harness 不會崩潰,而是捕獲 Error,重新組裝上下文,然後「再次拋入迴圈」。這讓軟體像陶土一樣,透過重複的 Loop 不斷被修整,直到符合驗證標準 (Validation)。
4.2 Battling Context Rot
上下文空間(Context Window)是極度昂貴且脆弱的資源。過長的上下文會導致模型的 Reasoning Degradation。Harness 必須擔任「過濾器」。
4.2.1 Tool Call Offloading
- 工程 Trick:Head/Tail Truncation
- 如果 Agent 執行
cat log.txt回傳了 5 萬字,Harness 會攔截這個輸出。 - 作法: 只保留 Log 的 前 500 字 (Head) 與 後 500 字 (Tail) 丟入 Prompt,其餘內容直接存入硬碟。
- 理由: 錯誤訊息的核心通常在開始(指令狀態)與結尾(Stacktrace),中間的冗長內容只會干擾模型注意力。
- 如果 Agent 執行
4.2.2 Just-In-Time (JIT) Tool Injection
- 作法: 初始 System Prompt 只給予最基礎的導航工具。當 Agent 意識到需要特定技能時(例如:需操作 PDF),Harness 才動態將 PDF 處理工具的 JSON Schema 注入 Context。
- 目的: 減少開局的 Token 負擔,確保模型保留最高智商處理核心邏輯。
4.3 Git-based State & Sandboxes
Agent 需要一個「輸得起」的環境。Harness 必須提供強大的撤銷與分支能力。
4.3.1 Rollback & Branching
- 進階實作: Harness 將 Agent 的工作目錄與 Git 綁定。
- 機制: 當 Agent 準備進行高風險的重構 (Refactor) 時,Harness 先偷偷執行
git checkout -b experiment。 - 自動回滾: 如果執行測試失敗且 Agent 無法在 3 個迴圈內修復,Harness 自動執行
git reset --hard。這讓 Agent 能在失敗後「無痛重啟」,徹底解決長線任務卡死的難題。
4.3.2 Sandbox Fan-out
- 作法: 利用 Docker 或 VM 提供獨立執行環境。
- 價值: 當 Agent 需要嘗試多種解法時,Harness 可以瞬間啟動 5 個平行沙箱,讓 Agent 同時測試 5 種程式碼分支,最後取成功的那一個合併回主幹。
4.4 Harness Meta-Optimization
當 Agent 表現不好時,工程師不應優先去改模型,而是 「編程這個迴圈 (Programming the loop)」。
- 修復失效域 (Failure Domain):
- 如果 Agent 總是看不懂某個 API 的報錯訊息,你應該去修改 Harness 裡該工具的
Error Message定義,讓報錯變得更具「指導性」。
- 如果 Agent 總是看不懂某個 API 的報錯訊息,你應該去修改 Harness 裡該工具的
- ACI (Agent-Computer Interface) 優化:
- 研究顯示,給模型一個「專為它設計的文字介面(如簡化的
ls -R)」比給它一個「人類用的 GUI 截圖」效能更高。這就是 Harness Engineering 的核心價值。
- 研究顯示,給模型一個「專為它設計的文字介面(如簡化的
4.5 Ralph Loop Framework Pseudo Code
def ralph_loop_harness(task_goal):
# 1. 準備 Git 乾淨環境與 Sandbox
repo = git.initialize_workspace()
sandbox = docker.create_sandbox()
while not task_completed:
# 2. 準備上下文 (包含 Log 截斷處理)
obs = repo.get_current_state()
context = prepare_lean_context(task_goal, obs)
# 3. LLM 思考與行動
thought_and_action = model.generate(context)
# 4. 攔截並執行 (Git Branching 為例)
if is_high_risk_action(thought_and_action):
repo.create_checkpoint_branch()
# 5. 執行獲取反饋 (Observation)
result = sandbox.execute(thought_and_action.code)
# 6. 自動化檢核 (Self-Verification)
if result.exit_code != 0:
# 失敗了?不要崩潰,把錯誤丟回 Loop 讓它修
# 如果失敗次數過多,觸發 Harness 層的回滾
if retry_count > threshold:
repo.rollback_to_last_stable()
task_goal.append_hint("Last attempt failed, try a different approach.")
continue
task_completed = verify_with_tests(repo)5 結論
5.1 核心觀念收斂:Agent 工程的範式轉移
在 2026 年的 Agent 開發語境下,我們必須承認一個現實:單純依賴「更強大的模型」已經無法解決複雜的長線任務 (Long-horizon tasks)。 真正的勝負在於系統工程的深度。
- Model 是凍結的引擎: 我們不再幻想透過微調 (Fine-tuning) 來解決每天都在變化的業務邏輯,因為代價太高、風險太大。
- Harness 是強健的骨架: 透過 Ralph Loop 的單體迴圈、Git 的狀態回滾、以及 Sandbox 的平行擴展,我們為 Agent 創造了一個「輸得起、能試錯」的環境。這將 Agent 的表現從「隨機」推向了「確定性」。
- Context 是進化的靈魂: 透過 MemRL 的雙階段檢索與 Q-value 動態更新,我們讓 Agent 具備了「吃一塹,長一智」的能力,這是在不觸動模型權重的前提下,實現持續學習 (Continual Learning) 的最優解。
5.2 Traces 是唯一的真理
在這套架構中,不論是模組一、二還是三,它們的運作全部依賴同一個底層基礎設施:結構化的執行日誌 (Structured Traces)。
如果沒有完整的 Trace 記錄(包含:輸入、思考、工具調用、報錯、最終結果),你將無法:
- 離線做夢 (Dreaming): 無法總結高階規則。
- 熱路徑學習 (Hot-path): 無法更新 MemRL 的 Q-value。
- Harness 優化: 無法發現 Agent 在哪個環節鬼打牆。
「先有觀測性 (Observability),才有智能的進化。」 這是每一位 Engineer 都必須銘記在心的格言。
5.3 行動清單
如果你今天要開始實作自己的 Agent,請遵循以下步驟,不要過早複雜化:
- 第一步:啟動你的 Ralph Loop
- 寫一個 300 行的 Python 腳本,用
while(True)跑起你的 Agent。 - 給它一個標準的 Sandbox 環境(甚至是本地的一個 Temp Dir)與一套 Bash 工具。
- 寫一個 300 行的 Python 腳本,用
- 第二步:建置 Trace 收集系統
- 引入 LangSmith 或 Langfuse。確保每一次的工具調用回傳值(尤其是 Error)都被完整記錄。
- 實作 Head/Tail Log 截斷,防止 Context 爆炸。
- 第三步:實作動態經驗庫 (MemRL Lite)
- 在你的 Vector DB 增加
q_valueMetadata。 - 手動寫入 10 個「成功的範例」與 10 個「失敗的反思」。
- 在檢索時,引入簡單的加權分數 ()。
- 在你的 Vector DB 增加
- 第四步:戴上「工程師帽子」進行迭代
- 盯著 Traces 看。如果 Agent 在同一個地方錯兩次,去修改 Harness 裡的 Tool 描述或 System Prompt。
5.4 結語:編程迴圈,而不是編程邏輯
未來的軟體開發,將會從「一磚一瓦寫 if-else」轉向「設計一套能自我進化的系統」。你不再是程式碼的創造者,你是 「Ralph Loop 的編程者」 與 「經驗的策展人」。
正如 Geoffrey Huntley 所言,這是一個「陶土拉胚」的過程。你的 Harness 越穩,你的 Context 越純淨,你的 Agent 就能在無限的循環中,逼近完美的解法。