先用免费模型尝试任务,测试/类型检查/linter作为验证门控,失败才升级到付费强模型,实现成本与质量平衡。
有一项 AI 支出项目在我仔细查看后让我感到困扰:在我发给最强(也是最贵)模型的 prompt 中,有很大一部分是类似"在這些文件中重命名這個字段"或"為這個日誌格式寫一個解析器——這是它必須通過的測試"這樣的任務。這些任務有一個共同點,與難度無關:機器已經可以告訴我答案是否正確。
這個觀察最終演變成了本文中的工作流。我不再靠直覺為每個 prompt 選擇模型,而是讓現有的工具——測試運行器、linter、類型檢查器——擔任守門人的角色。免費/便宜的模型先嘗試任務;驗證器打分;只有不及格才能買到昂貴模型的入場券。我把它想象成一個按價格排列的模型階梯,每一級之間有一個閘機。
這建立在之前寫過的 eval harness 和一次性沙箱設置之上,但它可以獨立運行。這裡沒有綁定特定供應商:我把階梯叫做 tier_a(免費/便宜)和 tier_b(強大),在接入任何東西之前,你應該查看每個提供商文檔中當前的模型名稱、定價和限額——這片領域每個月都在變化。
早期我嘗試按感知難度來路由:簡單的 prompt 往下,困難的往上。這不管用,因為我對難度的猜測在兩個方向上都是錯的——更重要的是,難度並不是決定是否可以使用便宜模型的安全標準。
真正讓它安全的是反饋。考慮兩個工作量相似的任務:
將所有硬編碼的 UI 字串提取到 i18n 目錄中,然後用一個腳本檢查源文件中每個字串現在都有目錄條目。便宜模型在一個文件中搞砸了?檢查器失敗,你升級。錯誤嘗試的代價:一次浪費的 API 調用。
查看一個偶發的生產死鎖並假設其原因。弱模型產生了一個自信、看似合理但錯誤的理論——你的工具鏈中沒有任何東西標記它。你只能在燒了一個下午追查之後才發現問題。
難度差不多,風險狀況卻完全不同。所以階梯的准入規則是:任務只有在輸出存在自動化、客觀的檢查時,才能從便宜的那級進入。主觀工作——設計判斷、根本原因排查奇怪的 bug、安全敏感的代碼——完全跳過階梯,直接進入強模型,因為在那裡錯誤答案的代價遠比 token 費用更高。
為了讓這個概念更具體,以下是我在便宜那級上運行的確切任務形態:
Prompt:「在 src/components/ 中,將每個面向用戶的字串文字替換為 t('key') 調用,並將 key/value 對追加到 locales/en.json。"
模型返回一個補丁,應用於一個一次性容器中(與之前文章中相同的一次性沙箱模式——永遠不要讓未經驗證的補丁接近真實的工作樹)。
驗證器運行三項檢查:tsc --noEmit、eslint src/,以及一個檢查剩餘未翻譯文字的小腳本。
全部綠燈 → 接受,完成,總成本≈零。任何紅燈 → 升級到 tier_b,並將檢查器的錯誤輸出粘貼到升級 prompt 中。
最後這個細節——轉發失敗證據——結果比路由本身更重要。在我的沙箱運行中,將 lint 錯誤和失敗的補丁交給強模型,明顯減少了它重蹈覆轍的情況,相比較於只是重新問原始問題。
底層需要一個零成本且呈現熟悉請求/響應形狀的端點。最近我一直在指向 MonkeyCode 通過其免費服務器選項提供的免費模型訪問,只是因為路由器只關心端點存在並接受標準風格的調用——階梯邏輯對誰在托管它並不關心。
聲明:本文是作為 MonkeyCode 產品推廣的一部分準備的。
明確說一下我沒有說的:我沒有基準測試他們目前的陣容,沒有引用配額或模型名稱,免費可用性條款可能隨時變化。架構上,免費層是一個插槽,不是基礎。如果它下周消失了,我會插入任何取代它的便宜端點,工作流程保持不變。
這個想法的早期版本在一個 Python 文件中。我後來把它拆成了兩部分,而這個拆分就是關鍵:路由策略是你調整的數據,運行器是保持穩定的代碼。
ladder.yaml —— 可調整的部分:
rungs:
- name: tier_a
model_env: CHEAP_MODEL_ENDPOINT # read from env, never hardcode
- name: tier_b
model_env: STRONG_MODEL_ENDPOINT
tasks:
i18n_extract:
ladder: true
verify:
- "tsc --noEmit"
- "eslint src/"
- "python scripts/check_untranslated.py"
sql_migration:
ladder: true
verify:
- "sqlfluff lint migrations/"
- "pytest tests/test_migration_roundtrip.py -q"
incident_hypothesis:
ladder: false # judgment work: straight to the top rung
auth_review:
ladder: false # 'compiles and passes tests' is not 'safe'
ladder.py —— 穩定的部分(結構草圖;運行前填入你的客戶端和補丁應用邏輯):
import os, subprocess, sys, yaml
def call_model(endpoint: str, prompt: str) -> str:
... # any client; both rungs expose the same shape
def checks_pass(checks: list[str], workdir: str) -> tuple[bool, str]:
for cmd in checks:
r = subprocess.run(cmd, shell=True, cwd=workdir,
capture_output=True, text=True, timeout=180)
if r.returncode != 0:
return False, f"$ {cmd}\n{r.stdout}\n{r.stderr}"
return True, ""
def run(task_name: str, prompt: str, workdir: str, policy: dict) -> None:
spec = policy["tasks"][task_name]
rungs = policy["rungs"]
start = 0 if spec["ladder"] and spec.get("verify") else len(rungs) - 1
for rung in rungs[start:]:
endpoint = os.environ[rung["model_env"]]
patch = call_model(endpoint, prompt)
apply_patch(workdir, patch) # throwaway container only
if start == len(rungs) - 1 or not spec.get("verify"):
print(f"[{rung['name']}] top rung: result needs human review")
return
ok, evidence = checks_pass(spec["verify"], workdir)
if ok:
print(f"[{rung['name']}] accepted — all checks green")
return
# Evidence rides along to the next rung.
prompt = (f"{prompt}\n\nAn earlier attempt failed these checks. "
f"Fix the root cause, don't just silence the checker:\n{evidence}")
revert_patch(workdir)
def apply_patch(workdir: str, patch: str) -> None: ...
def revert_patch(workdir: str) -> None: ...
if __name__ == "__main__":
policy = yaml.safe_load(open("ladder.yaml"))
run(sys.argv[1], open(sys.argv[2]).read(), sys.argv[3], policy)
值得借鑒的三個行為,即使你忽略其餘部分:
無驗證器,不上階梯。沒有 verify 塊的任務從頂層開始。這個護欄是結構性的,不是你可以忘記的約定。
失敗輸出是輸入。每次升級都會附帶檢查器實際錯誤重新 prompt。
端點來自環境。以後換免費層是一行配置的改變,不是代碼的改變。
YAML 中的 ladder: true/false 標誌是初始猜測。兩三週後,按任務類型測量:
升級率——便宜那級輸出失敗驗證的頻率。任何高於大約一半的都是信號,表明該任務應該切換到 ladder: false;你正在支付便宜嘗試的來回延遲,同時還是要購買強模型的調用。
靜默通過率——更令人擔憂的一個:便宜輸出通過了檢查但仍然是錯誤的,後來被人類審查發現。如果某個任務類型的這個指標非零,說明你的驗證器對那個任務來說太薄了,解決辦法是更好的檢查,不是更好的模型。
如果你已經為模型比較運行了 eval harness,可以先在歸檔任務上離線評級便宜那級的輸出,然後再信任路由上線——同樣的 harness,新的問題。
階梯的可信度取決於你的檢查。稀疏的測試套件會把便宜那級變成帶著綠色通過標記的錯誤答案生成器。驗證較弱的項目應該首先在那裡投入——而且很方便,即使你從不構建任何這些,這也會有回報。
你在用延遲換金錢。失敗的便宜嘗試在強模型開始之前就已經付出了一個完整的來回代價。對於你盯著加載 spinner 的同步配對場景,那個交換通常是壞的。這個模式更適合批量風格的工作——後台 agent、排隊任務、接近 CI 的自動化——而不是交互式場景。
免費容量不是一個計劃。免費模型端點會在不征求你許可的情況下改變名稱、限額和可用性。保持底層可替換,並確保工作流程在底層變得只是便宜而非免費時仍然有意義。
如果你的工作主要是判斷性的,跳過這個。設計評審、探索性調試、架構辯論——這些都沒有驗證器,所以都不進入階梯。對於那種混合工作,一個有能力的模型加上精心編寫的 prompt 會勝過任何路由方案。
這裡有用的心智轉變不是「使用更便宜的模型」——而是「讓你的工具來裁決模型質量」。你的測試套件已經知道如何評級一個補丁;階梯只是根據它的評分來路由支出。如果你想探究你自己的工作負載是否有一個值得路由的可驗證層,最便宜的可能實驗是底層的免費端點——MonkeyCode 的免費服務器是我一直在用的——少數你最機械化的任務,以及一週的升級日誌。無論你學到什麼,策略文件和路由規範是你保留的部分。