把 agent 跑任務的費用降了五倍,模型版本從頭到尾沒動。harness 設計爛是主因。
去年底公司內部做了一個 code review agent,掃 PR、找 security issue、產修復建議。大概跑了兩個月,一直在換模型。gpt-4o 換 claude-3-5-sonnet,再換 gemini flash,結果不是這個貴就是那個準確率爛。最後查了一週才發現,問題根本不在模型,是 harness 每次任務都把整個工作目錄的相關檔案全塞進 context,一個任務 150k token 起跳,模型換再多都是在那堆亂糟糟的 context 裡工作,當然出問題。
Lilian Weng 最近那篇 harness engineering 把這個層次講得蠻系統的,重點是 harness 不只是 prompt 包裝,而是整個執行環境,包含 context 管理邏輯、workflow、permission、memory、subagent 怎麼切。從 infra 維運角度看,這幾個點也剛好是實際差距最大的地方。
Context 裁切:爆了不一定知道
context 爆炸有兩種死法。第一種是費用直接炸,第二種更慘,是模型的注意力稀釋,回答品質開始漂移,但你完全不知道為什麼,只能一直覺得「這個模型最近怎麼變差了」。
最常見的場景是工具讀檔沒控制粒度。一個有 read_file 工具的 agent,如果沒有強制帶 offset 和 limit,稍微大一點的 repo 就完了:
# 沒控制粒度
read_file("src/utils/helpers.ts")
→ 3,200 行,約 48,000 tokens
# 有控制粒度
read_file("src/utils/helpers.ts", offset=120, limit=80)
→ 80 行,約 1,200 tokens
→ 差 40 倍
這還只是單次讀檔。一個任務有十幾個工具呼叫,context 差距快速疊加到 10 倍以上。
更隱蔽的問題是 context 沒有清理機制。很多 harness 只會往 context 裡加東西,跑了 30 個步驟的 agent,context 裡有 20 個步驟的過期工具結果,全部堆著。
我的做法是在每個 major checkpoint 強制做一次 context prune,只保留原始目標、目前狀態快照、最近 N 步的結果,以及標記為重要的 artifact。其餘丟到 file system,需要時才撈。這樣長任務的 context size 可以穩定在 20-30k token,不會無限膨脹。
Subagent 邊界:context 污染才是大問題
spawning subagent 本身不複雜,難的是邊界設計。
最常見的爛設計:parent agent 把自己的整個 context 傳給 subagent,subagent 跑完把整個工作記錄塞回來,最後 main context 裡三個 subagent 的輸出全部混在一起,誰說了什麼根本理不清。
Subagent 要有清楚的 input/output contract。
input:subagent 只需要知道「它的任務是什麼」和「需要哪些 artifact」,不需要 parent 的完整工作歷史。output:subagent 回傳結構化摘要,不是它整個工作記錄。
# subagent input contract
task: "分析這個 PR diff,找出潛在的 security issue"
context_files:
- "pr_diff.txt"
- "security_checklist.md"
# 不傳 parent conversation history
# 不傳其他 subagent 輸出
# subagent output contract
result:
issues: []
severity: "low|medium|high"
confidence: 0.0-1.0
reasoning: "簡短說明"
我實際測過,同一個任務,subagent 帶完整 parent context 跑 vs 帶 trimmed context 跑,任務完成率差了約 35%,費用差了將近一半。帶完整 context 的 subagent 很容易偏題,試圖回應 parent context 裡其他不相關的問題。
說穿了,subagent 要像一個 function call,有明確的 input 和 output,不是讓它去讀你的日記再給你建議。
驗證鍊:沒有驗證的 loop 就是在賭
agent loop 最危險的特性是錯誤會複
利。第一步的錯誤假設,在後面每一步都被當成事實使用,越跑越偏。harness 裡如果沒有在關鍵節點設驗證,就是在賭每一步都剛好對。
實務上我在 harness 裡設三種驗證點:
工具執行後驗證:確認工具輸出符合預期格式。很多工具在邊界情況會回傳奇怪的東西,如果 agent 直接用那個輸出繼續跑就全亂了。
def call_tool_with_validation(tool_name, params, expected_schema):
result = call_tool(tool_name, params)
if not validate_schema(result, expected_schema):
raise ValidationError(f"{tool_name} output schema mismatch")
return result
Milestone 驗證:長任務每個 milestone 做一次 sanity check,確認 agent 還在正確軌道上。不需要很複雜,就是一個「你現在在做什麼、為什麼」的快速確認。
輸出驗證:最終輸出要過獨立的 verifier,不是讓生產它的 agent 自己驗。同一個 agent 在同一個 context 裡驗自己的輸出,確認偏誤很嚴重。
加了驗證鍊之後,那個 code review agent 的 hallucination rate 從 18% 降到 4%。不是換模型換的,是加了 milestone 驗證跟獨立 verifier 換的。
Session Memory:每次都從零開始
大多數 agent 的記憶是暫態的,關了就沒了。每次任務都從零開始,不記得上次踩過什麼坑,不記得什麼方法試過沒用。
我現在把 agent 的 session memory 分三層:
.agent-memory/
├── session-state.json # 當前任務狀態,任務結束後清
├── project-context.md # project 層級的長效記憶
└── learnings/
├── 2026-07-*.md # 每次任務後的 retrospective
└── patterns.md # 跨任務抽出的 pattern
project-context.md 裡記著「這個 repo 的特殊約定」、「這個 team 常踩的坑」、「哪些工具有已知的奇怪行為」。每次新任務開始,agent 先讀這個檔案。這是最便宜也最有效的 memory 機制,不需要 vector search,不需要 embedding,就是 bash 讀一個 markdown 檔案。
實際差距是多少
把這些東西加起來,好的 harness vs 隨便寫的 harness,在同一個任務、同一個模型上的差距大概是這樣:
| 面向 | 爛 harness | 好 harness |
|---|---|---|
| Context size | 150k tokens | 25k tokens |
| 任務完成率 | ~55% | ~85% |
| Subagent 偏題率 | ~40% | ~5% |
| Hallucination rate | ~18% | ~4% |
| 每次任務成本 | $0.45 | $0.08 |
兩組都用 claude-3-5-sonnet,任務是「掃 repo、找 security issue、產修復 PR」。
說穿了,這個差距比換模型省的錢大多了。同一個模型,harness 設計好,成本降 80%,準確率還提升了。如果是靠降低模型等級來省錢,省 60% 費用通常要付出明顯的品質代價,得不償失。
不是要你放棄換模型
這裡說清楚:模型還是重要的,有兩件事 harness 補不了。
第一,複雜的單步推理。這種 harness 幫不到,叫差的模型做複雜的架構決策,它就是做不到。
第二,tool-use 穩定性。差的模型在 structured output 和 tool call format 上容易飄,harness 做 retry 可以補一點但補不完。
那篇文章也提到「harness-benefit 很吃模型是否能正確用工具和遵循長任務」,這點是對的。差的模型,好的 harness 幫助有限。但在中等偏上的模型裡,harness 設計的影響比模型版本差距大得多。
真正的優化順序應該是:先把 harness 設計對
,確認現有模型的 ceiling,再決定要不要升模型。大多數人做反了,harness 爛著,先去換模型,浪費錢還找不到問題所在。
最後一件事,Lilian Weng 那篇文章裡的 meta-harness 那個方向很有意思,把 harness 設計本身變成可以被 agent 優化的搜尋空間。那是研究前沿,實務上先把 context 裁切、subagent 邊界、驗證鍊這三件事做好,就已經比大多數人強很多了。
作者:CtrlC