行底座與編排控制層)
做 Agent 項(xiàng)目的朋友估計(jì)都被 Agent Harness 和 Agent Runtime 這兩個(gè)詞繞暈過同樣是“Agent 相關(guān)的東西”為什么有人天天問“你基于哪個(gè) Runtime”有人卻在說(shuō)“我自己寫了個(gè) Harness”更麻煩的是不同框架文檔里的用法還不一樣有的把 Harness 說(shuō)成 Agent 執(zhí)行器有的把 Runtime 直接當(dāng)成模型調(diào)用客戶端。這篇文章不繞概念直接站在工程落地角度把兩者的邊界、職責(zé)、代碼形態(tài)、排查方式一次說(shuō)清楚。先說(shuō)結(jié)論Agent Runtime 是 Agent 代碼真正執(zhí)行的環(huán)境和基礎(chǔ)設(shè)施負(fù)責(zé)模型通信、工具調(diào)用、記憶讀寫、生命周期內(nèi)的進(jìn)程與資源管理Agent Harness 則是跑在 Runtime 之上、控制 Agent 行為節(jié)奏的編排層它決定 Agent“下一步該推斷還是該調(diào)用工具、什么時(shí)候算完成、異常了怎么糾正”。你可以把 Runtime 理解成攝影棚里的燈光、攝像機(jī)和場(chǎng)地把 Harness 理解成導(dǎo)演手里的分鏡本——兩部戲可以共用一個(gè)攝影棚但分鏡本完全不同。1. 先把定義釘死兩個(gè)詞到底在解決什么問題1.1 一個(gè)馬上能記住的類比電影棚和導(dǎo)演我給人講這個(gè)概念時(shí)最喜歡用拍電影來(lái)比。Runtime 是整個(gè)拍攝現(xiàn)場(chǎng)的基礎(chǔ)設(shè)施攝影機(jī)、燈光、錄音設(shè)備、場(chǎng)務(wù)調(diào)度它們保證“只要演員站在那里就能被拍下來(lái)”。Harness 則是導(dǎo)演手上的劇本分鏡、走位要求和喊停規(guī)則它決定演員先說(shuō)哪句臺(tái)詞、走到哪個(gè)機(jī)位、什么情況下這場(chǎng)戲可以收工。落到 Agent 工程里Runtime 負(fù)責(zé)“能跑”。模型接口是否可用、工具函數(shù)能否在安全容器里執(zhí)行、上一步的對(duì)話歷史能不能被讀出、運(yùn)行時(shí)的指標(biāo)和日志能不能被采集這些都是 Runtime 的活。Harness 負(fù)責(zé)“怎么跑”。先讓模型生成一段推理還是先調(diào)用檢索接口拿到工具返回后要不要再讓模型分析一輪已經(jīng)重復(fù)三次還沒結(jié)果時(shí)是否要強(qiáng)行結(jié)束這些都是 Harness 的活。這個(gè)區(qū)分并不是某些框架發(fā)明的而是軟件工程里“基礎(chǔ)設(shè)施”和“業(yè)務(wù)流程”分層思想在 Agent 領(lǐng)域的自然延伸。只要你寫過異步任務(wù)應(yīng)該能感受到類似的邏輯消息隊(duì)列是 Runtime任務(wù)編排狀態(tài)機(jī)是 Harness數(shù)據(jù)庫(kù)是 Runtime事務(wù)腳本是 Harness。1.2 為什么那么多教程把這兩個(gè)詞混著用說(shuō)句公道話這不能全怪初學(xué)者因?yàn)椴煌蚣軐?duì)術(shù)語(yǔ)的處理確實(shí)不同。有的框架把 Agent Runtime 做成了一個(gè)很薄的服務(wù)端只處理模型調(diào)用和工具執(zhí)行有的框架則把 Harness 封裝成完整的 Agent 類內(nèi)置了 ReAct 循環(huán)、策略注入和記憶壓縮文檔甚至不出現(xiàn) Harness 這個(gè)詞。正因?yàn)榭蚣軐用娴某橄髮蛹?jí)不統(tǒng)一才導(dǎo)致“同樣的詞在不同人嘴里根本不是同一個(gè)東西”。更大的混淆來(lái)源是早期很多 Agent Demo 是單體腳本運(yùn)行時(shí)環(huán)境和控制循環(huán)寫在同一份代碼里。比如 AutoGPT 早期的實(shí)現(xiàn)主循環(huán)、提示詞拼接、文件操作全在一個(gè)進(jìn)程里跑后來(lái)社區(qū)才逐漸把它拆成不同的組件。在這種“先能用、后分層”的歷史背景下你當(dāng)然很難說(shuō)清哪個(gè)部分屬于 Harness、哪個(gè)部分屬于 Runtime。但從工程化角度分層遲早要做。原因很簡(jiǎn)單如果你只是想展示一個(gè) Agent Demo單體代碼無(wú)所謂如果要做多租戶、灰度發(fā)布、穩(wěn)定觀察和權(quán)限隔離你就必須知道哪部分能力應(yīng)該下沉為公共運(yùn)行環(huán)境哪部分策略應(yīng)該保留為每個(gè)業(yè)務(wù)可控的編排層。后面所有內(nèi)容都圍繞這個(gè)“分層必要性”展開。2. Agent RuntimeAgent 真正“跑”起來(lái)的底座2.1 Runtime 不等于“模型 API 封裝”它的范圍更寬很多人第一反應(yīng)是Runtime 不就是那個(gè)調(diào)用 GPT 接口的客戶端嗎這理解太窄了。模型調(diào)用只是 Runtime 的一個(gè)子能力完整的 Agent Runtime 至少應(yīng)該提供四類能力。第一是模型訪問抽象。你的業(yè)務(wù)代碼不該寫死“openai.ChatCompletion.create”而應(yīng)該面向一個(gè)統(tǒng)一接口能隨時(shí)從某通義模型切到某個(gè)開源私有化模型。第二是工具執(zhí)行能力。模型輸出一個(gè)call_search_engine(query...)的指令后Runtime 需要真正把這個(gè)指令落到可信環(huán)境里執(zhí)行并且拿到結(jié)構(gòu)化結(jié)果返回給上層。第三是狀態(tài)與記憶能力。Agent 每一輪的消息歷史、內(nèi)部狀態(tài)、外部記憶向量都需要 Runtime 管理讀寫和過期策略。第四是可觀測(cè)性和容錯(cuò)。調(diào)用模型失敗要重試工具執(zhí)行超時(shí)要熔斷每步的 token 和耗時(shí)要有 trace這些都是 Runtime 該提供的基礎(chǔ)設(shè)施能力。你可以回憶一下自己寫單體 Agent 時(shí)最煩的事切換模型供應(yīng)商要?jiǎng)訕I(yè)務(wù)代碼某個(gè)工具有毛病把整個(gè)進(jìn)程打掛多輪內(nèi)存一長(zhǎng)上下文爆掉沒人處理。這些問題全部指向同一個(gè)根源——你把業(yè)務(wù)控制邏輯和底層執(zhí)行能力耦合在一起了。引入 Runtime 這一層之后業(yè)務(wù)控制邏輯只用關(guān)心“要什么”不用關(guān)心“去哪里取、怎么取更穩(wěn)”。2.2 Runtime 接口設(shè)計(jì)的重要原則用名詞定義能力不要用流程定義能力我在看開源項(xiàng)目 Runtime 接口時(shí)會(huì)特別留意一個(gè)細(xì)節(jié)它暴露出來(lái)的方法到底是穩(wěn)定能力還是臨時(shí)流程。穩(wěn)定能力比如chat(messages, tools)、run_tool(name, args)、save_recall(text)、retrieve_recall(query)這些可以作為 Runtime 接口。而execute_step(observation)這種帶狀態(tài)推進(jìn)語(yǔ)義的接口通常更適合放在 Harness 里因?yàn)椤皥?zhí)行一步”本身就是一種控制策略。用名詞定義能力還有個(gè)好處可替換性更強(qiáng)。比如今天底層模型不支持 Tool Calling只要模型訪問抽象層能把用戶指令改寫為“請(qǐng)輸出JSON格式工具調(diào)用”上層 Harness 就不必大改今天工具執(zhí)行環(huán)境從本地 subprocess 改成遠(yuǎn)端容器只要run_tool接口的入?yún)⒑头祷刂挡蛔僅arness 也無(wú)感知。這就是“底座”該有的樣貌。下面給一個(gè)最小 Python 接口示意幫助你感受 Runtime 抽象# runtime.py class AgentRuntime: def __init__(self, model_client, tool_registry, memory_store): self.model_client model_client self.tool_registry tool_registry self.memory_store memory_store def chat(self, messages, toolsNone, **kwargs): 調(diào)用底層模型并返回完整響應(yīng)不做決策 return self.model_client.complete(messages, toolstools, **kwargs) def run_tool(self, name, args, timeout10): 在受控環(huán)境執(zhí)行工具并返回結(jié)構(gòu)化結(jié)果 executor self.tool_registry.get(name) return executor.run(args, timeouttimeout) def save_memory(self, text): return self.memory_store.save(text) def retrieve_memory(self, query, top_k5): return self.memory_store.search(query, top_ktop_k)看到這里你可能會(huì)說(shuō)這不就是普通函數(shù)封裝嗎對(duì)其實(shí)就是這么簡(jiǎn)單。真正復(fù)雜的 Runtime 當(dāng)然還有調(diào)度隊(duì)列、容器隔離、限流和密鑰管理但在邏輯上它對(duì)外只承諾“我能穩(wěn)定執(zhí)行這些原子能力”。原子能力之上如何組合不是 Runtime 的職責(zé)。2.3 什么時(shí)候該從框架 Runtime 遷到自研 Runtime如果你是剛開始做 Agent 原型的個(gè)人開發(fā)者建議直接用現(xiàn)成的 Runtime。Python 里可以用 LangChain 的 LCEL 和工具抽象也可以直接用輕量函數(shù)實(shí)現(xiàn)底層的調(diào)用封裝端側(cè)可用 Ollama 這類模型運(yùn)行時(shí)它們已經(jīng)幫你把模型請(qǐng)求標(biāo)準(zhǔn)化了。真正需要自研 Runtime 的信號(hào)通常是下面幾類你需要對(duì)工具執(zhí)行做更強(qiáng)的安全隔離比如 Agent 生成的要執(zhí)行的代碼不能直接subprocess跑在業(yè)務(wù)容器里而要進(jìn)入沙箱容器你需要把 Agent 的調(diào)用能力開放給多個(gè)業(yè)務(wù)方每個(gè)業(yè)務(wù)方的模型流量、密鑰、限流規(guī)則各不相同你需要把記憶存儲(chǔ)從“簡(jiǎn)單的列表保存”升級(jí)成多租戶隔離向量庫(kù)并且要求底層存儲(chǔ)可運(yùn)維、可備份、可審計(jì)你需要極致的可觀測(cè)性每次模型調(diào)用和工具調(diào)用都必須有完整 tracetoken 成本要按客戶維度拆分。這些需求出現(xiàn)時(shí)如果你依然在 Harness 代碼里到處調(diào)用模型 SDK、直接操作工具函數(shù)那架構(gòu)基本是一盤散沙。先把 Runtime 能力抽出來(lái)再談上層編排才有的放矢。3. Agent Harness真正控制“智能化行為”的編排層3.1 Harness 的核心是那個(gè)循環(huán)不是模型本身大模型本身只是一個(gè)“單步推理器”給它一段上下文它返回一段文本或一個(gè)工具調(diào)用。真正讓 Agent 持續(xù)工作、能分多步完成任務(wù)的那個(gè)機(jī)制是 Harness 里的循環(huán)邏輯。經(jīng)典的 ReAct 循環(huán)可以概括為觀察當(dāng)前問題 - 讓模型思考 - 如果模型要調(diào)用工具就執(zhí)行 - 把工具結(jié)果返回給模型 - 再觀察 - 直到模型給出最終答案。Harness 就是這段循環(huán)的載體。循環(huán)不是越高深越好但必須滿足工程要求最大步數(shù)、終止條件、異常恢復(fù)、歷史裁剪。我見過不少項(xiàng)目把 Agent 寫得神乎其神最后跑崩的原因卻特別低級(jí)沒有限制最大步數(shù)工具調(diào)用結(jié)果異常后沒有讓模型感知到或者模型已經(jīng)連續(xù)三次給出同一種錯(cuò)誤工具調(diào)用卻沒有打斷機(jī)制。這些都不是模型能力問題而是 Harness 沒有把“套路”寫扎實(shí)。一個(gè)健壯 Harness 的控制偽代碼# harness.py class Harness: def __init__(self, runtime, policy, max_steps10): self.runtime runtime self.policy policy self.max_steps max_steps def run(self, task): history [] observation task for step in range(self.max_steps): # 策略層先判斷是否可以收手 if self.policy.should_stop(observation, history): return self.policy.build_final_answer(observation, history) # Runtime 負(fù)責(zé)讓模型“想一步” response self.runtime.chat( messagesself.policy.build_messages(observation, history), toolsself.policy.get_tools() ) if response.has_tool_call(): # 執(zhí)行工具這一步走 Runtime result self.runtime.run_tool( response.tool_call.name, response.tool_call.args ) observation {tool_result: result} else: # 沒有工具調(diào)用說(shuō)明模型想直接給答案 if self.policy.is_final(response.text): return response.text observation {agent_message: response.text} history.append({ step: step, response: response, observation: observation, }) raise HarnessTimeoutError(f超過 {self.max_steps} 步仍未得出最終答案)注意這個(gè)偽代碼里runtime.chat和runtime.run_tool幾乎沒有業(yè)務(wù)語(yǔ)義它們只是被動(dòng)執(zhí)行而循環(huán)次數(shù)、什么時(shí)候停止、歷史怎么傳給模型、要不要把上一步的工具結(jié)果轉(zhuǎn)成一句話全是 Harness 在管。這就是兩者各自的位置。3.2 Harness 同時(shí)裝著提示詞策略和人類規(guī)則Harness 不只是寫循環(huán)它還承載了很多“規(guī)則”。同一個(gè) Runtime接不同 Harness可以做出完全不同的 Agent 性格和行為。比如客服 Agent 的 Harness 里會(huì)內(nèi)置“先查訂單再安撫用戶實(shí)在解決不了就轉(zhuǎn)人工”的策略代碼助手 Agent 的 Harness 會(huì)內(nèi)置“修改文件前先展示 diff確認(rèn)后才寫入”的審批動(dòng)作數(shù)據(jù)分析 Agent 的 Harness 會(huì)在模型生成 SQL 之后強(qiáng)制執(zhí)行“只讀檢查”禁止DELETE和UPDATE開頭。這些策略放不到 Runtime 里因?yàn)?Runtime 不知道上層業(yè)務(wù)目標(biāo)。但它們非常適合放在 Harness 里通過顯式的 Policy 類注入。我在上面代碼里寫了self.policy就是這個(gè)意思。Policy 可以封裝系統(tǒng)提示詞、工具列表、停止規(guī)則、結(jié)果校驗(yàn)甚至人工審批回調(diào)。把策略從循環(huán)代碼里拆出來(lái)之后測(cè)試和復(fù)用都方便很多。還有一點(diǎn)容易被忽略錯(cuò)誤處理策略也屬于 Harness。比如工具調(diào)用返回了“權(quán)限不足”Harness 要決定是直接反饋給模型讓它換個(gè)方式還是終止任務(wù)并通知管理員。Runtime 只能告訴你工具拋了異常不能替你決定下一步怎么辦。這是“編排放控制層”和“執(zhí)行層”最本質(zhì)的區(qū)別。3.3 開源框架里的 Harness 長(zhǎng)什么樣為了讓你能對(duì)號(hào)入座我列幾個(gè)常見框架里“Harness 角色”的具象化框架典型類型對(duì)應(yīng)內(nèi)容LangGraph圖狀態(tài)機(jī)Agent 節(jié)點(diǎn)、工具節(jié)點(diǎn)、條件邊、循環(huán)限制CrewAICrew / Agent / Task任務(wù)編排、Agent 角色提示詞、流程模式AutoGenGroupChatManager對(duì)話調(diào)度、發(fā)言順序、終止條件Semantic KernelKernel / Planner規(guī)劃器與函數(shù)調(diào)用循環(huán)自研系統(tǒng)Harness / Controller主循環(huán)、策略注入、中斷恢復(fù)拿 LangGraph 來(lái)說(shuō)StateGraph里的add_node、add_edge本身就是 Harness 邏輯的高度抽象而實(shí)際執(zhí)行invoke model或call tool的是 Runtime 層。很多人誤以為 LangGraph 等于 Runtime其實(shí)它更偏 Harness真正的底層模型調(diào)用還是由 LangChain 的 ChatModel 或者獨(dú)立 SDK 完成。當(dāng)你理解了“LangGraph 更像 Harness”之后就不會(huì)再犯一個(gè)典型錯(cuò)誤試圖在 LangGraph 里塞過多底層連接池、超時(shí)重試等基礎(chǔ)設(shè)施邏輯。那些邏輯應(yīng)該在節(jié)點(diǎn)內(nèi)部封裝的 Runtime 客戶端里否則整個(gè)圖會(huì)變成一張又大又脆的蜘蛛網(wǎng)。4. 兩者邊界一圖流按職責(zé)對(duì)照與協(xié)作流程拆解4.1 高頻對(duì)比速查表下面這張表可以保存下來(lái)當(dāng)速查卡每次邊界模糊時(shí)就拿出來(lái)對(duì)一遍對(duì)比維度Agent RuntimeAgent Harness定位執(zhí)行基礎(chǔ)設(shè)施控制編排層回答的問題“能不能跑、穩(wěn)不穩(wěn)”“下一步做什么、什么時(shí)候?!敝饕庋b模型客戶端、工具執(zhí)行器、記憶存儲(chǔ)主循環(huán)、Policy、提示詞策略、終止條件典型載體獨(dú)立服務(wù)、函數(shù)庫(kù)、沙箱進(jìn)程Agent 類、狀態(tài)圖、編排器對(duì)模型影響通過 system 和 tools 傳入但不會(huì)“替模型決策”決定 model 看到什么、能看到幾步歷史故障影響崩潰會(huì)導(dǎo)致所有 Agent 不可用出 bug 會(huì)導(dǎo)致當(dāng)前任務(wù)走偏或死循環(huán)可觀測(cè)對(duì)象token 數(shù)、調(diào)用耗時(shí)、工具執(zhí)行時(shí)延決策軌跡、工具選擇原因、循環(huán)步數(shù)測(cè)試重點(diǎn)接口穩(wěn)定性、超時(shí)和隔離不同策略下的任務(wù)成功率、終止率這不是二選一的關(guān)系而是分層依賴關(guān)系Harness 依賴 Runtime 提供的原子能力Runtime 不依賴任何 Harness 的業(yè)務(wù)策略。如果哪天你發(fā)現(xiàn)自己的 Runtime 代碼里塞了大量“如果用戶問了天氣就優(yōu)先調(diào)用天氣工具”這種邏輯那說(shuō)明 Harness 里的策略漏到了 Runtime。4.2 一次真實(shí)查詢的完整路徑假設(shè)你正在做一個(gè)企業(yè)內(nèi)部知識(shí)庫(kù)問答 Agent用戶問“幫我總結(jié)昨天項(xiàng)目周報(bào)并找出風(fēng)險(xiǎn)項(xiàng)?!?我按一次完整執(zhí)行拆給你看。第一步HTTP 網(wǎng)關(guān)收到請(qǐng)求創(chuàng)建 Session初始化 Harness 和綁定給該 Session 的 Runtime。第二步Harness 調(diào) Policy 構(gòu)造系統(tǒng)提示詞把“你是項(xiàng)目經(jīng)理助理”這類人設(shè)和工具說(shuō)明帶進(jìn)去然后調(diào)用 Runtime.chat 把用戶問題發(fā)給模型。第三步模型返回的不是最終答案而是一個(gè)工具調(diào)用比如search_docs(query項(xiàng)目周報(bào) 2025-04-10, date2025-04-10)。Harness 看到有 tool_call于是轉(zhuǎn)到工具執(zhí)行階段調(diào)用Runtime.run_toolRuntime 去向量庫(kù)檢索并把文本片段返回。第四步Harness 拿到檢索結(jié)果后把工具結(jié)果拼進(jìn) messages再調(diào)用 Runtime.chat。模型這次根據(jù)檢索內(nèi)容生成了總結(jié)和風(fēng)險(xiǎn)點(diǎn)。Harness 判斷文本不是最終答案而是“需要再調(diào)一次 calendar 工具獲取成員日程”的新請(qǐng)求于是再次執(zhí)行工具。第五步直到模型輸出滿足 Policy 的終止條件Harness 把結(jié)果回給 HTTP 網(wǎng)關(guān)。如果第五步發(fā)生了工具調(diào)用異常Harness 會(huì)決定是重試、換工具還是把異常信息返回給模型繼續(xù)推理。從這五個(gè)步驟你應(yīng)該能感覺到用戶感知到的“智能”其實(shí)就是 Harness 對(duì) Runtime 多次調(diào)用后形成的結(jié)果。Runtime 每次執(zhí)行都很快難的是如何編排這些步驟讓模型在正確時(shí)機(jī)看到正確信息。4.3 邊界模糊時(shí)用四個(gè)問題做判斷如果你在代碼評(píng)審時(shí)拿不準(zhǔn)某個(gè)函數(shù)應(yīng)該屬于哪一層可以直接問下面四個(gè)問題。第一個(gè)問題這段邏輯去掉之后Agent 還能用同一個(gè)底層模型和工具嗎如果能它多半屬于 Harness 策略。第二個(gè)問題這段邏輯和具體模型供應(yīng)商強(qiáng)相關(guān)嗎比如某個(gè) API 的 tools 參數(shù)格式轉(zhuǎn)換這屬于 Runtime。第三個(gè)問題這段邏輯需要所有 Agent 類型共用嗎比如統(tǒng)一的限流、鑒權(quán)它是 Runtime 基礎(chǔ)設(shè)施如果只有財(cái)務(wù)分析 Agent 需要審批流程那是 Harness。第四個(gè)問題如果業(yè)務(wù)要新接入一個(gè)完全不同的 Agent 場(chǎng)景你是否希望復(fù)用這段代碼希望復(fù)用的底層資源和穩(wěn)定性邏輯放 Runtime不希望復(fù)用的場(chǎng)景策略放 Harness。這四句話基本能解決 90% 的歸屬爭(zhēng)論。剩下的 10% 可能屬于長(zhǎng)期演進(jìn)產(chǎn)生的中間層比如“會(huì)話路由”到底歸誰(shuí)取決于你的產(chǎn)品形態(tài)但至少你們討論時(shí)能有一個(gè)統(tǒng)一的判斷框架而不是靠感覺。5. 日常開發(fā)中的高頻坑位與排查實(shí)錄5.1 現(xiàn)象一工具調(diào)用一直執(zhí)行但 Agent 每次都說(shuō)“沒找到結(jié)果”這個(gè)坑我見得太多了。表面上看 Runtime 日志里工具執(zhí)行成功了返回內(nèi)容也打印出來(lái)了但模型還是說(shuō)沒找到。排查時(shí)先別懷疑模型重點(diǎn)看 Harness 把工具結(jié)果回傳給模型時(shí)是怎么拼裝的。常見的錯(cuò)誤是Harness 把工具結(jié)果保存在了一個(gè)局部變量里但下一次調(diào)用模型時(shí)忘了把這條消息加到 messages或者加了但消息角色寫成了user而不是tool對(duì)應(yīng)的角色。工具結(jié)果回傳屬于 Agent 編排的核心細(xì)節(jié)。模型廠商對(duì)工具結(jié)果的格式要求可能不同OpenAI 要求用 roletool 的消息并要求提供 tool_call_id其他模型可能只要求在 user 內(nèi)容里塞結(jié)果。這個(gè)差異通常在 Runtime 層做適配但 Harness 要保證“每條 tool_call 都有對(duì)應(yīng)結(jié)果”。如果你發(fā)現(xiàn)工具執(zhí)行成功但 Agent “失憶”建議在 Harness 插入一步檢查統(tǒng)計(jì) messages 里 tool_call 數(shù)量和 tool_result 數(shù)量是否一致。5.2 現(xiàn)象二Agent 永久不終止費(fèi)用飆高這大概率不是 Runtime 問題而是 Harness 的終止條件寫得太寬松。最常見的情況是 Policy 里只判斷“模型輸出是否包含 final 標(biāo)記”但模型在復(fù)雜任務(wù)里就是不輸出這個(gè)標(biāo)記于是一直循環(huán)調(diào)用工具。我的建議是任何 Harness 都必須同時(shí)具備“正向終止”和“強(qiáng)制終止”正向終止由 Policy 判斷任務(wù)目標(biāo)是否完成強(qiáng)制終止由兜底步數(shù)、時(shí)間閾值和 Token 閾值共同實(shí)現(xiàn)。上面代碼里的max_steps就是強(qiáng)制終止。真實(shí)項(xiàng)目里我還會(huì)加一個(gè)熔斷器如果連續(xù)三次工具調(diào)用都返回相同錯(cuò)誤Harness 直接停止并把上下文發(fā)給人工處理。這一步能省下大量調(diào)試成本。5.3 現(xiàn)象三問題定位時(shí)日志到底去 Harness 找還是 Runtime 找很多團(tuán)隊(duì)日志混亂就是因?yàn)闆]有按職責(zé)劃分。我的經(jīng)驗(yàn)是Harness 日志記錄的是“決策軌跡”比如第幾步、模型回復(fù)了哪段思考、為什么調(diào)用某個(gè)工具、終止原因是什么Runtime 日志記錄的是“執(zhí)行指標(biāo)”比如模型接口耗時(shí)、Token 消耗、工具執(zhí)行超時(shí)、網(wǎng)絡(luò)重試次數(shù)。如果任務(wù)結(jié)果不符合預(yù)期先查 Harness 的決策軌跡比如“它到底有沒有理解用戶意圖”如果系統(tǒng)整體卡頓或偶發(fā)失敗再查 Runtime 指標(biāo)比如“是不是模型 API 超時(shí)率變高了”。我把排查順序整理成了一個(gè)速查表現(xiàn)象優(yōu)先排查層典型原因結(jié)論不對(duì)Harness提示詞策略缺失、上下文裁剪過度、工具信息未回傳某一步工具偶發(fā)失敗Runtime工具超時(shí)、服務(wù)不可用、鑒權(quán)過期任務(wù)中途停止Harness異常未處理、終止條件誤判整個(gè)服務(wù)不可用Runtime資源耗盡、下游模型限流未處理響應(yīng)慢但結(jié)果OKRuntime模型調(diào)用串行、工具執(zhí)行慢同一問題多次回答漂移Harness缺少固定的 few-shot、溫度未調(diào)低這里的重點(diǎn)是不要一出現(xiàn)問題就瘋狂打日志定位到每一行而是先判斷“是這一層壞了還是上一層用錯(cuò)了”。層與層之間的語(yǔ)義障礙是最大的排查暗礁。5.4 我的一個(gè)壓箱底調(diào)試技巧給 Harness 裝“步進(jìn)器”不知道你會(huì)不會(huì)遇到這種情況Agent 跑了 20 步之后終于飛了但你想知道它在第 7 步為什么突然檢索了一個(gè)毫不相關(guān)的文檔??赐暾罩咎壑苯诱{(diào)模型接口又脫離真實(shí)場(chǎng)景。我的建議是給 Harness 設(shè)計(jì)一個(gè)可插拔的 StepListener在每一輪循環(huán)開始、模型返回、工具調(diào)用前后觸發(fā)回調(diào)把關(guān)鍵信息渲染成可讀的 JSONL。這樣你可以在本地把一次完整運(yùn)行保存下來(lái)再用腳本按 step 編號(hào)逐層查看。實(shí)際上這也是一種“Harness 與 Runtime 分離”帶來(lái)的紅利Runtime 只記錄底層請(qǐng)求Harness 記錄決策鏈條。把決策鏈條回放一遍你會(huì)瞬間看清是哪條 Prompt 誤導(dǎo)了模型而不是在 Runtime 的幾百條原始請(qǐng)求日志里大海撈針。這個(gè)技巧我?guī)缀踉诿總€(gè)生產(chǎn)級(jí) Agent 項(xiàng)目里都會(huì)用。6. 架構(gòu)決策順序與我的個(gè)人心得6.1 小項(xiàng)目可以先不拆服務(wù)但邏輯必須拆看到這你可能會(huì)緊張是不是必須上容器、上獨(dú)立 Runtime 服務(wù)才算完成了分層大可不必。個(gè)人項(xiàng)目或者五個(gè)以內(nèi)的 Agent 場(chǎng)景完全可以跑在同一個(gè) Python 進(jìn)程里但代碼結(jié)構(gòu)上要拆。我推薦的目錄結(jié)構(gòu)很簡(jiǎn)單app/ runtime/ model_client.py tool_executor.py memory_store.py harness/ base_harness.py policies/ customer_service.py analyst.py loop_listeners.py agents/ customer_service_agent.pyruntime下的模塊不 importharness下的任何內(nèi)容harness只通過 Runtime 對(duì)外暴露的接口方法使用能力agents負(fù)責(zé)把某個(gè) Harness 和 Runtime 組合起來(lái)注冊(cè)工具、綁定模型賬號(hào)。這樣就實(shí)現(xiàn)了邏輯邊界。將來(lái)如果某個(gè) Runtime 模塊需要獨(dú)立成服務(wù)把它抽出來(lái)補(bǔ)一個(gè)網(wǎng)絡(luò)接口層即可不會(huì)牽連 Harness。6.2 演進(jìn)路徑通常從“Harness 吃胖”開始要主動(dòng)給 Runtime 補(bǔ)位在實(shí)際項(xiàng)目里還有一個(gè)很普遍的趨勢(shì)一開始 Harness 和 Runtime 確實(shí)分了層但隨著業(yè)務(wù)迭代大家圖省事開始把“其他模型供應(yīng)商的適配”“工具執(zhí)行的沙箱參數(shù)”“歷史消息的向量化存儲(chǔ)”都寫進(jìn)了 Harness。于是 Harness 越來(lái)越胖Runtime 變成了空殼所謂架構(gòu)分層名存實(shí)亡。我的建議是每?jī)芍茏鲆淮未a評(píng)審時(shí)按第 4.3 節(jié)的判斷標(biāo)準(zhǔn)重新檢查如果同一段“穩(wěn)定執(zhí)行能力”被多個(gè) Harness 復(fù)制粘貼了就把它下沉到 Runtime如果 Runtime 里出現(xiàn)了業(yè)務(wù)相關(guān)的策略 if-else就把它上提到 Harness。這個(gè)動(dòng)作要持續(xù)做而不是只在架構(gòu)設(shè)計(jì)時(shí)做一次。分層不是一錘子買賣它更像廚房里“灶臺(tái)”和“菜單”的關(guān)系灶臺(tái)性能穩(wěn)定菜單卻每周都在換。6.3 最后一個(gè)選型建議先寫壞掉的 Harness再談框架很多朋友在選擇 LangGraph、CrewAI 還是自研時(shí)猶豫很久。我的觀點(diǎn)是如果你連一個(gè)最樸素的while循環(huán) Harness 都沒寫過直接上抽象框架很容易被框架帶著走。先把你想要的一個(gè)場(chǎng)景用最原始的 Runtime Policy 循環(huán)寫出來(lái)跑通一次再去看框架內(nèi)部怎么表達(dá)這些概念你才能判斷它是在替你做決策還是在限制你的表達(dá)。我自己做過兩個(gè)核心 Agent 系統(tǒng)一個(gè)早期重度依賴編排框架后續(xù)為了加“人工審批暫停與恢復(fù)”花了大半個(gè)月改造另一個(gè)一開始就用很樸素的 Runtime 接口 Harness 控制循環(huán)加新策略反而很快。不是框架不好而是框架自帶的那套 Harness 假設(shè)未必匹配你的業(yè)務(wù)狀態(tài)機(jī)。這里沒有銀彈理解分層邏輯比背出任何一家 API 文檔都管用。我個(gè)人最后一次分享一個(gè)真實(shí)體會(huì)Agent 項(xiàng)目的復(fù)雜度從來(lái)不是“模型不夠聰明”而是“環(huán)境不可控、步驟不可回放、策略不可解釋”。把 Runtime 做好解決的是環(huán)境穩(wěn)定性問題把 Harness 做好解決的是步驟可控和策略可解釋問題。兩者都有價(jià)值但如果你今天只打算改一處優(yōu)先把 Harness 的循環(huán)、終止、Policy 和回放日志寫得像樣因?yàn)檫@是你和“玄學(xué) Agent”之間最重要的一道閘門。