目錄

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

  • Agent 發展的典範轉移: 從單純追求「更聰明的大模型」,轉向「建構更完善的系統工程」。真正的智能不僅來自 Weights,更來自系統的記憶與反饋機制。
  • 核心痛點: AI Agent 在工業界落地時面臨的兩大難題:
    1. 無狀態與幻覺: Agent 容易在同一個坑裡跌倒兩次,且單純的 RAG 會引發「語義噪音」。
    2. 規模化與穩定性: 複雜任務導致 Context Rot (上下文腐爛),且微調 (Fine-tuning) 模型成本極高、容易引發災難性遺忘。
  • 本筆記目標: 結合 《MemRL》論文 的演算法設計,以及 LangChain 的 《Agent Harness》《Continual Learning》 系統觀,提供一份從「記憶檢索」到「執行腳手架」的全方位架構藍圖。

在傳統軟體開發中,我們關注「程式碼 + 資料庫」;在早期的 AI 開發中,我們關注「模型」。但在 2026 年的 Agent 工程實踐中,我們必須遵循這個黃金公式:

Agent = Model + Harness + Context

這三者的關係可以類比為:Model 是大腦的神經元,Harness 是肉體與感官,Context 則是記憶與當下的感知。

Model 是系統的決策核心,負責將輸入的文字序列轉化為邏輯推理或工具呼叫。

  • 工業界現況:凍結 (Frozen) 是常態 在生產環境中,我們通常將 Model 視為「不可變的基礎設施 (Immutable Infrastructure)」。原因在於:
    1. 災難性遺忘 (Catastrophic Forgetting): 微調 (Fine-tuning) 模型來學習新規則,極易破壞它原本的基礎推理能力。
    2. 算力與延遲: 頻繁微調模型會導致 MLOps 成本飆升。
  • 模型與腳手架的深度耦合 (Post-training Alignment): 這是目前最硬核的觀察。如 Claude 3.5 Sonnet 這類頂尖模型,在後訓練階段 (RLHF) 就已經被放進特定的 Harness 中練習。
    • 案例: Claude 3.5 被訓練為高度偏好 str_replaceapply_patch 這種特定的檔案修改格式。如果你換成 OpenAI 模型,即使模型智商一樣,它可能因為無法精準產生符合該格式的字串而導致 Harness 解析失敗。這就是所謂的 「腳手架依賴 (Harness-specific optimization)」

Harness 是所有包裝在模型外圍的程式碼與基礎設施。它是 Agent 能夠從「說說而已」變成「實際行動」的關鍵。

  • Filesystem 模型是無狀態的 (Stateless)。Harness 必須提供一個持久化的工作目錄。Agent 的進度不應存在 Prompt 裡,而應存在磁碟上的檔案(如 CLAUDE.mdTODO.json)。
  • Sandboxes:Harness 負責調度 Docker 或虛擬機,讓模型產生的 Bash 指令或 Python 代碼能安全執行。
  • 工具封裝:Harness 將 API 封裝成模型可理解的 JSON Schema。
  • 漸進式揭露 (Progressive Disclosure): 為了節省 Token,Harness 不會一次給模型 100 個工具,而是先給一個 list_tools,讓模型需要時才動態載入特定工具的定義(Just-in-Time Injection)。
  • Middleware (中介軟體):Harness 會在模型輸出後進行「格式攔截」。
    • 案例:如果模型輸出了一個超長的日誌 (Log),Harness 會主動執行 Tool Call Offloading,只把 Log 的頭尾存入 Context,其餘存入檔案,避免模型因上下文過載而變笨。
  • 自我驗證 (Self-Verification):Harness 可以在模型回覆使用者前,自動跑一遍測試腳本 (Test Suite),如果報錯,直接把 Error 拋回 Ralph Loop。

Context 是 Harness 根據當前任務,從外部動態拼湊出來給 Model 的輸入。

  • Hierarchical Memory Isolation: 在企業級應用中,記憶體必須根據隱私權限進行分層:
    1. Agent-level: 通用的最佳實踐、安全性原則。
    2. Org-level: 內部專案標準、專屬 API 文檔。
    3. User-level: 使用者的編碼偏好、歷史操作習慣。
  • Injection Logic: Harness 會在發送請求給 Model 前,執行一次檢索(例如用 MemRL 算法),將最相關的高效能經驗 (High-utility experiences) 注入 Context。
# 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)

傳統的 RAG 記憶體有一個根本性的缺陷:語義相似 (Semantic Similarity) \neq 解決問題的效用 (Functional Utility)

  • 痛點: 如果 Agent 曾經用一個錯誤的方法嘗試解決某個 SQL 查詢任務,傳統 RAG 下次遇到相似任務時,依然會因為「語義接近」而把錯誤的經驗檢索出來餵給模型。
  • 解法 (M-MDP): 將記憶建模為 Memory-Based MDP,每一條經驗不再只是 (Vector, Content),而是三元組:(Intent, Experience, Utility/Q-value)
    • Intent (意圖): 原始任務的向量特徵。
    • Experience (經驗): 執行的軌跡 (Traces)、成功的代碼或失敗的反思。
    • Utility/Q-value (效用值): 該經驗在歷史中幫助任務成功的機率與回報。

為了平衡「搜尋速度」與「經驗質量」,我們在 Harness 中實作兩階段過濾:

  • 動作: 在 Vector DB 中執行 Top-k1 的 Cosine Similarity 搜尋。
  • 目的: 作為「守門員」,確保撈出來的經驗在業務邏輯上是相關的。
  • 工程 Trick: 設定一個硬性的相似度閾值 δ \delta (例如 0.38),低於此值的經驗直接丟棄,防止 Agent 被完全不相干的「高分回憶」干擾。
  • 動作: 對候選經驗進行加權打分。
  • 公式: Score=(1λ)Normalized_Sim+λNormalized_Q_value Score = (1-\lambda) \cdot \text{Normalized\_Sim} + \lambda \cdot \text{Normalized\_Q\_value}
  • 意義: λ\lambda 控制了 Agent 的「執拗程度」。
    • λ=0.5\lambda=0.5,系統會同時考慮「長得像不像」與「過去成不成功」。
    • 這能有效過濾掉那些「語義噪音」——即長得像但實際上會導致失敗的經驗。

這是 Agent 自進化的燃料。我們不更新權重,而是更新 Vector DB 裡的 Metadata (Q-value 欄位)

  • 更新公式 (Monte Carlo / EMA):

    Qnew=Qold+α(RQold)Q_{new} = Q_{old} + \alpha(R - Q_{old})
    • RR (Reward): 環境給予的反饋(成功 = +1, 失敗 = -1)。
    • α\alpha (Learning Rate): 記憶的更新速度。
  • 工程實作 (Runtime Update): 當 Ralph Loop 完成一次任務並獲得驗證結果(例如 Test Suite 通過),Harness 會立刻回頭更新剛才被檢索出來的那幾條記憶。

    • 優勢: 這是 “Hot-path” (即時學習)。Agent 下一秒處理相似任務時,Q-value 已經更新,它會立刻避開剛才失敗的路徑。

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。

在 2026 年的企業架構中,我們會結合 Hot-pathDreaming

層次學習時機 (When)處理內容 (What)目的
Hot-path任務完成的瞬間 (Runtime)更新現有記憶的 Q-value即時糾錯,避免連續犯錯。
Dreaming離線批次 (Offline/Daily)掃描 Traces,將多條相似經驗 總結(Summarize) 成一條高階規則壓縮 Context 空間,對抗「記憶膨脹」。
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 # 給予中等初始權重,引發下次嘗試
        )

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)。

上下文空間(Context Window)是極度昂貴且脆弱的資源。過長的上下文會導致模型的 Reasoning Degradation。Harness 必須擔任「過濾器」。

  • 工程 Trick:Head/Tail Truncation
    • 如果 Agent 執行 cat log.txt 回傳了 5 萬字,Harness 會攔截這個輸出。
    • 作法: 只保留 Log 的 前 500 字 (Head)後 500 字 (Tail) 丟入 Prompt,其餘內容直接存入硬碟。
    • 理由: 錯誤訊息的核心通常在開始(指令狀態)與結尾(Stacktrace),中間的冗長內容只會干擾模型注意力。
  • 作法: 初始 System Prompt 只給予最基礎的導航工具。當 Agent 意識到需要特定技能時(例如:需操作 PDF),Harness 才動態將 PDF 處理工具的 JSON Schema 注入 Context。
  • 目的: 減少開局的 Token 負擔,確保模型保留最高智商處理核心邏輯。

Agent 需要一個「輸得起」的環境。Harness 必須提供強大的撤銷與分支能力。

  • 進階實作: Harness 將 Agent 的工作目錄與 Git 綁定。
  • 機制: 當 Agent 準備進行高風險的重構 (Refactor) 時,Harness 先偷偷執行 git checkout -b experiment
  • 自動回滾: 如果執行測試失敗且 Agent 無法在 3 個迴圈內修復,Harness 自動執行 git reset --hard。這讓 Agent 能在失敗後「無痛重啟」,徹底解決長線任務卡死的難題。
  • 作法: 利用 Docker 或 VM 提供獨立執行環境。
  • 價值: 當 Agent 需要嘗試多種解法時,Harness 可以瞬間啟動 5 個平行沙箱,讓 Agent 同時測試 5 種程式碼分支,最後取成功的那一個合併回主幹。

當 Agent 表現不好時,工程師不應優先去改模型,而是 「編程這個迴圈 (Programming the loop)」

  • 修復失效域 (Failure Domain):
    • 如果 Agent 總是看不懂某個 API 的報錯訊息,你應該去修改 Harness 裡該工具的 Error Message 定義,讓報錯變得更具「指導性」。
  • ACI (Agent-Computer Interface) 優化:
    • 研究顯示,給模型一個「專為它設計的文字介面(如簡化的 ls -R)」比給它一個「人類用的 GUI 截圖」效能更高。這就是 Harness Engineering 的核心價值
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)

在 2026 年的 Agent 開發語境下,我們必須承認一個現實:單純依賴「更強大的模型」已經無法解決複雜的長線任務 (Long-horizon tasks)。 真正的勝負在於系統工程的深度。

  • Model 是凍結的引擎: 我們不再幻想透過微調 (Fine-tuning) 來解決每天都在變化的業務邏輯,因為代價太高、風險太大。
  • Harness 是強健的骨架: 透過 Ralph Loop 的單體迴圈、Git 的狀態回滾、以及 Sandbox 的平行擴展,我們為 Agent 創造了一個「輸得起、能試錯」的環境。這將 Agent 的表現從「隨機」推向了「確定性」。
  • Context 是進化的靈魂: 透過 MemRL 的雙階段檢索與 Q-value 動態更新,我們讓 Agent 具備了「吃一塹,長一智」的能力,這是在不觸動模型權重的前提下,實現持續學習 (Continual Learning) 的最優解。

在這套架構中,不論是模組一、二還是三,它們的運作全部依賴同一個底層基礎設施:結構化的執行日誌 (Structured Traces)

如果沒有完整的 Trace 記錄(包含:輸入、思考、工具調用、報錯、最終結果),你將無法:

  1. 離線做夢 (Dreaming): 無法總結高階規則。
  2. 熱路徑學習 (Hot-path): 無法更新 MemRL 的 Q-value。
  3. Harness 優化: 無法發現 Agent 在哪個環節鬼打牆。

「先有觀測性 (Observability),才有智能的進化。」 這是每一位 Engineer 都必須銘記在心的格言。

如果你今天要開始實作自己的 Agent,請遵循以下步驟,不要過早複雜化:

  1. 第一步:啟動你的 Ralph Loop
    • 寫一個 300 行的 Python 腳本,用 while(True) 跑起你的 Agent。
    • 給它一個標準的 Sandbox 環境(甚至是本地的一個 Temp Dir)與一套 Bash 工具。
  2. 第二步:建置 Trace 收集系統
    • 引入 LangSmith 或 Langfuse。確保每一次的工具調用回傳值(尤其是 Error)都被完整記錄。
    • 實作 Head/Tail Log 截斷,防止 Context 爆炸。
  3. 第三步:實作動態經驗庫 (MemRL Lite)
    • 在你的 Vector DB 增加 q_value Metadata。
    • 手動寫入 10 個「成功的範例」與 10 個「失敗的反思」。
    • 在檢索時,引入簡單的加權分數 (Score=Sim+QScore = Sim + Q)。
  4. 第四步:戴上「工程師帽子」進行迭代
    • 盯著 Traces 看。如果 Agent 在同一個地方錯兩次,去修改 Harness 裡的 Tool 描述或 System Prompt。

未來的軟體開發,將會從「一磚一瓦寫 if-else」轉向「設計一套能自我進化的系統」。你不再是程式碼的創造者,你是 「Ralph Loop 的編程者」「經驗的策展人」

正如 Geoffrey Huntley 所言,這是一個「陶土拉胚」的過程。你的 Harness 越穩,你的 Context 越純淨,你的 Agent 就能在無限的循環中,逼近完美的解法。