中的自工程完結(jié):從理念到工程實(shí)踐)
1. 從“修水管”視頻到“自工程完結(jié)”一個(gè)被忽視的工程鐵律前幾天我在一個(gè)程序員社區(qū)里刷到一個(gè)挺火的短視頻標(biāo)題大概是“程序員修水管結(jié)果修出個(gè)更大的Bug”。視頻里一個(gè)哥們兒家里的水管漏水他手忙腳亂地?cái)Q緊了某個(gè)閥門結(jié)果水是不漏了但整個(gè)樓層的供水壓力都出了問題鄰居家也跟著遭殃。評(píng)論區(qū)里一片“哈哈哈這很程序員”但笑過之后我心里卻咯噔一下。這不就是我們?nèi)粘i_發(fā)中尤其是現(xiàn)在搞AI Agent這類復(fù)雜系統(tǒng)時(shí)每天都在上演的“經(jīng)典戲碼”嗎為了快速解決眼前的一個(gè)報(bào)錯(cuò)比如某個(gè)API調(diào)用失敗我們可能隨手加個(gè)補(bǔ)丁、繞個(gè)彎子代碼是能跑了卻把更隱蔽、更棘手的問題比如資源泄漏、狀態(tài)不一致像“水壓異?!币粯忧臒o(wú)聲息地傳遞給了下游模塊甚至是最終用戶。這個(gè)場(chǎng)景完美地詮釋了TPS豐田生產(chǎn)方式里一個(gè)核心到不能再核心的理念——“自工程完結(jié)”。最近我在深度參與一個(gè)名為Agent Harness的開源項(xiàng)目時(shí)對(duì)這個(gè)理念有了近乎“刻骨銘心”的體會(huì)。Agent Harness你可以把它理解為一套專門為AI Agent打造的“腳手架”或“基礎(chǔ)設(shè)施層”。它不負(fù)責(zé)替代Agent本身的核心推理邏輯那是LLM和具體技能模塊的事而是專注于解決那些讓Agent能穩(wěn)定、可靠、可觀測(cè)地運(yùn)行起來(lái)的“臟活累活”比如生命周期管理、工具調(diào)用編排、狀態(tài)持久化、異常處理和監(jiān)控等等。那么“自工程完結(jié)”到底是什么意思簡(jiǎn)單粗暴地講就是**“別把Bug或者任何形式的問題、半成品留給下一道工序”**。在制造業(yè)的生產(chǎn)線上這意味著每個(gè)工位的工人必須確保自己經(jīng)手的零件是100%合格的才能流到下一個(gè)工位。在軟件工程特別是我們正在構(gòu)建的AI Agent系統(tǒng)中這意味著每一個(gè)模塊、每一次函數(shù)調(diào)用、每一個(gè)數(shù)據(jù)轉(zhuǎn)換都必須在其邊界內(nèi)盡最大可能處理掉所有它能預(yù)見的異常和問題輸出一個(gè)確定、可靠的結(jié)果給它的調(diào)用者而不是簡(jiǎn)單地拋出一個(gè)錯(cuò)誤或者傳遞一個(gè)模棱兩可的狀態(tài)。這聽起來(lái)像是常識(shí)對(duì)吧但現(xiàn)實(shí)是在追求快速迭代、功能上線的壓力下我們太容易妥協(xié)了。“先讓流程跑通回頭再優(yōu)化”、“這個(gè)異常概率很低先不管了”、“讓調(diào)用方去處理吧”……這些想法就是“Bug流水線”的開端。Agent Harness項(xiàng)目讓我意識(shí)到對(duì)于AI Agent這種高度動(dòng)態(tài)、依賴外部服務(wù)、狀態(tài)復(fù)雜的系統(tǒng)如果不貫徹“自工程完結(jié)”整個(gè)系統(tǒng)就會(huì)像一個(gè)到處漏水的管道網(wǎng)絡(luò)運(yùn)維成本和調(diào)試難度會(huì)呈指數(shù)級(jí)上升。今天我就結(jié)合Agent Harness開發(fā)中的具體案例跟你聊聊為什么這個(gè)來(lái)自傳統(tǒng)制造業(yè)的理念在AI時(shí)代反而成了我們最該撿起來(lái)的“工程利器”。2. 拆解“自工程完結(jié)”它遠(yuǎn)不止是“不要拋出異?!焙芏嗳艘宦牭健皠e把Bug留給下一道工序”第一反應(yīng)就是“哦就是要多做點(diǎn)異常捕獲別亂拋異?!?。這理解得太淺了。在Agent Harness的語(yǔ)境下“自工程完結(jié)”有著更豐富、更嚴(yán)格的內(nèi)涵。我們可以把它分解為三個(gè)遞進(jìn)的層次這恰恰對(duì)應(yīng)了構(gòu)建穩(wěn)健AI Agent基礎(chǔ)設(shè)施的三個(gè)關(guān)鍵維度。2.1 第一層數(shù)據(jù)與狀態(tài)的“完結(jié)”這是最基礎(chǔ)的一層。任何一個(gè)函數(shù)或模塊對(duì)于其輸入必須有明確的契約和驗(yàn)證對(duì)于其輸出必須有確定的格式和狀態(tài)。反面案例在早期版本的Agent Harness中我們有一個(gè)工具調(diào)用路由模塊。它的職責(zé)是根據(jù)Agent的意圖將調(diào)用分發(fā)給不同的工具Tool。最初的設(shè)計(jì)是如果找不到匹配的工具它就返回一個(gè)None。問題來(lái)了調(diào)用方拿到None后該怎么辦是重試是記錄日志后跳過還是向上拋出“工具未找到”錯(cuò)誤每個(gè)調(diào)用方都要重復(fù)實(shí)現(xiàn)這套邏輯而且極易出錯(cuò)。更糟糕的是有些工具調(diào)用后返回的數(shù)據(jù)結(jié)構(gòu)不一致有的返回字典有的返回字符串有的在出錯(cuò)時(shí)返回一個(gè)包含error字段的字典。路由模塊只是簡(jiǎn)單地透?jìng)鬟@些結(jié)果?!白怨こ掏杲Y(jié)”的改造輸入完結(jié)路由模塊強(qiáng)制要求所有注冊(cè)的工具必須提供統(tǒng)一的元數(shù)據(jù)描述名稱、描述、輸入?yún)?shù)schema。在路由前先根據(jù)schema校驗(yàn)輸入?yún)?shù)的合法性和完整性不合法則立即在此模塊內(nèi)反饋“參數(shù)錯(cuò)誤”并附上具體原因而不是把原始參數(shù)丟給工具去報(bào)錯(cuò)。輸出完結(jié)我們定義了一個(gè)統(tǒng)一的ToolResponse對(duì)象。無(wú)論工具內(nèi)部執(zhí)行成功與否路由模塊都必須返回這個(gè)對(duì)象。這個(gè)對(duì)象包含幾個(gè)關(guān)鍵字段success: bool(成功/失敗)data: Any(成功時(shí)的標(biāo)準(zhǔn)化數(shù)據(jù)如將不同工具的原始輸出轉(zhuǎn)換為一個(gè)標(biāo)準(zhǔn)JSON結(jié)構(gòu))error: ToolError(失敗時(shí)的錯(cuò)誤對(duì)象包含錯(cuò)誤碼、錯(cuò)誤信息、可選的原始異常)tool_name: str(調(diào)用的工具名)execution_id: str(本次執(zhí)行的唯一ID用于鏈路追蹤)這樣調(diào)用方通常是Agent的核心邏輯拿到ToolResponse后只需要檢查success字段就能做出確定性的下一步?jīng)Q策。數(shù)據(jù)的形態(tài)和狀態(tài)的語(yǔ)義在路由模塊這一“工序”就完結(jié)了。注意這里“完結(jié)”的不是可能性工具仍然可能因?yàn)榫W(wǎng)絡(luò)、權(quán)限等問題失敗而是信息表達(dá)的完備性和一致性。調(diào)用方無(wú)需猜測(cè)結(jié)果的含義。2.2 第二層邏輯與流程的“完結(jié)”這一層關(guān)注的是業(yè)務(wù)流程或控制流在一個(gè)模塊內(nèi)的完整性。一個(gè)模塊應(yīng)該盡可能處理其業(yè)務(wù)邏輯范圍內(nèi)的所有分支避免將流程控制的復(fù)雜度暴露給上游。反面案例考慮Agent的“記憶”或“狀態(tài)持久化”模塊。假設(shè)Agent執(zhí)行到一半崩潰了重啟后需要恢復(fù)狀態(tài)。最初的簡(jiǎn)單實(shí)現(xiàn)是提供一個(gè)save_state(state)和load_state(agent_id)函數(shù)。如果load_state時(shí)找不到數(shù)據(jù)就返回None。于是Agent的啟動(dòng)邏輯里就需要寫一堆if-elseif state is None: 初始化新會(huì)話 else: 恢復(fù)舊會(huì)話。這看起來(lái)沒問題但把“狀態(tài)不存在時(shí)應(yīng)初始化”這個(gè)業(yè)務(wù)決策從存儲(chǔ)模塊剝離到了核心邏輯模塊。如果未來(lái)我們想改變策略比如狀態(tài)不存在時(shí)從備份中加載就需要修改核心邏輯。“自工程完結(jié)”的改造 我們重構(gòu)了狀態(tài)管理模塊提供一個(gè)get_or_initialize_state(agent_id, initial_state_factory)方法。這個(gè)方法內(nèi)部封裝了完整的邏輯嘗試加載agent_id對(duì)應(yīng)的狀態(tài)。如果加載成功直接返回。如果加載失敗文件不存在、數(shù)據(jù)損壞等它不會(huì)簡(jiǎn)單返回None而是會(huì) a. 首先嘗試記錄錯(cuò)誤和進(jìn)行可能的修復(fù)如讀取損壞文件的備份。 b. 如果最終確定狀態(tài)無(wú)法恢復(fù)則調(diào)用傳入的initial_state_factory函數(shù)創(chuàng)建一個(gè)新的初始狀態(tài)。 c.可選但強(qiáng)烈推薦將這個(gè)新創(chuàng)建的狀態(tài)立即持久化并記錄一條“狀態(tài)初始化”的審計(jì)日志。這樣對(duì)于Agent的核心邏輯來(lái)說它調(diào)用get_or_initialize_state后一定能拿到一個(gè)有效的、立即可用的狀態(tài)對(duì)象。至于這個(gè)狀態(tài)是舊的、新的、還是修復(fù)后的核心邏輯不關(guān)心也無(wú)需包含相關(guān)判斷代碼。整個(gè)“獲取可用狀態(tài)”的流程在狀態(tài)管理模塊內(nèi)部就“完結(jié)”了。2.3 第三層資源與副作用的“完結(jié)”這是最高級(jí)也最容易出問題的一層。它要求一個(gè)模塊必須管理好它申請(qǐng)的所有資源網(wǎng)絡(luò)連接、文件句柄、內(nèi)存、外部服務(wù)會(huì)話等并確保其操作產(chǎn)生的副作用如修改了全局配置、發(fā)送了通知在模塊邊界內(nèi)是可預(yù)測(cè)和可清理的。反面案例經(jīng)典Bug在集成一個(gè)外部知識(shí)庫(kù)查詢工具時(shí)該工具需要維護(hù)一個(gè)昂貴的數(shù)據(jù)庫(kù)連接池。最初的工具實(shí)現(xiàn)提供了一個(gè)query(sql)方法但連接池的初始化(init_pool)和關(guān)閉(close_pool)需要調(diào)用方顯式管理。于是Agent的生命周期管理代碼變得復(fù)雜要在合適的時(shí)候init_pool更要在Agent停止時(shí)或在異常發(fā)生時(shí)記得調(diào)用close_pool。一旦忘記就會(huì)導(dǎo)致連接泄漏。這就像打開了水龍頭沒關(guān)把“關(guān)水”的責(zé)任留給了房子里的下一個(gè)人?!白怨こ掏杲Y(jié)”的改造 我們利用Agent Harness提供的生命周期鉤子Lifecycle Hooks和上下文管理器Context Manager模式對(duì)這類工具進(jìn)行了重構(gòu)。工具自身實(shí)現(xiàn)資源管理工具類實(shí)現(xiàn)__enter__和__exit__方法或者提供start()/stop()方法。在__enter__/start()中初始化連接池在__exit__/stop()中確保關(guān)閉所有連接。Harness框架負(fù)責(zé)調(diào)度Agent Harness在加載該工具時(shí)會(huì)識(shí)別其生命周期接口。當(dāng)Agent啟動(dòng)時(shí)Harness自動(dòng)調(diào)用工具的start()當(dāng)Agent停止無(wú)論是正常結(jié)束還是因異常崩潰Harness都會(huì)保證調(diào)用工具的stop()進(jìn)行清理。對(duì)調(diào)用方透明Agent的核心邏輯在調(diào)用query(sql)時(shí)完全無(wú)需關(guān)心連接池是否存在、是否健康。它假設(shè)工具在任何時(shí)候都是“就緒”的。資源管理的責(zé)任從分散的、易忘的調(diào)用方收歸到了工具模塊內(nèi)部和Harness框架這個(gè)統(tǒng)一的“工序”中。這個(gè)改造直接避免了類似isAutoCloseConnection false這種配置錯(cuò)誤導(dǎo)致的Bug。因?yàn)殛P(guān)閉連接不再是可選的而是框架強(qiáng)制保障的、在“工具使用”這個(gè)工序內(nèi)必須完結(jié)的操作。3. Agent Harness如何將“完結(jié)”理念工程化理解了理念我們來(lái)看看在Agent Harness這個(gè)具體項(xiàng)目中是如何通過設(shè)計(jì)和約定將“自工程完結(jié)”從口號(hào)落地的。這不僅僅是代碼風(fēng)格更是一套嵌入到框架DNA里的約束和最佳實(shí)踐。3.1 通過強(qiáng)類型與契約定義邊界松散的類型是Bug的溫床。Agent Harness廣泛使用像Pydantic這樣的數(shù)據(jù)驗(yàn)證庫(kù)為幾乎所有在模塊間傳遞的數(shù)據(jù)結(jié)構(gòu)定義嚴(yán)格的Schema。工具輸入/輸出契約每個(gè)工具都必須聲明其輸入?yún)?shù)的JSON Schema和輸出數(shù)據(jù)的類型。Harness在調(diào)用前進(jìn)行校驗(yàn)不符合契約的請(qǐng)求根本不會(huì)到達(dá)工具邏輯。這確保了工具接收到的輸入是“完結(jié)”的格式正確、類型匹配。Agent狀態(tài)契約Agent的長(zhǎng)期記憶和短期狀態(tài)也被要求定義為一個(gè)Pydantic模型。任何對(duì)狀態(tài)的讀寫都經(jīng)過模型的序列化和反序列化自動(dòng)過濾掉非法字段保證狀態(tài)結(jié)構(gòu)的完整性。當(dāng)狀態(tài)從一個(gè)會(huì)話持久化到另一個(gè)會(huì)話時(shí)其結(jié)構(gòu)是確定無(wú)疑的。消息流契約Agent與用戶、Agent與工具之間的消息如ChatMessage, ToolCallMessage, ToolResultMessage都有明確的類型定義。這保證了在復(fù)雜的多輪對(duì)話和工具調(diào)用流水線中每個(gè)環(huán)節(jié)處理的數(shù)據(jù)對(duì)象都是已知的、可靠的。這種強(qiáng)類型約束就像給每個(gè)工序的輸入輸出都配備了標(biāo)準(zhǔn)的“夾具”和“量具”不合格的零件根本無(wú)法流入下一站。3.2 統(tǒng)一的錯(cuò)誤處理與降級(jí)策略“完結(jié)”不是不允許出錯(cuò)而是要求錯(cuò)誤必須在當(dāng)前環(huán)節(jié)被妥善封裝和處理并以一種對(duì)下游友好的方式呈現(xiàn)。Agent Harness定義了一個(gè)分層的錯(cuò)誤體系ToolExecutionError工具執(zhí)行過程中發(fā)生的錯(cuò)誤如網(wǎng)絡(luò)超時(shí)、API限額用完。工具本身應(yīng)盡可能進(jìn)行重試等補(bǔ)救措施。如果最終失敗它必須拋出一個(gè)信息豐富的ToolExecutionError而不是原始的requests.exceptions.Timeout。AgentRuntimeErrorAgent核心邏輯運(yùn)行時(shí)的錯(cuò)誤如狀態(tài)機(jī)進(jìn)入非法狀態(tài)、決策邏輯矛盾。這部分錯(cuò)誤通常意味著Agent邏輯有Bug需要開發(fā)介入。HarnessFatalError基礎(chǔ)設(shè)施層面的嚴(yán)重錯(cuò)誤如配置加載失敗、關(guān)鍵資源無(wú)法初始化。這類錯(cuò)誤通常會(huì)導(dǎo)致整個(gè)Agent實(shí)例無(wú)法啟動(dòng)。關(guān)鍵在于Harness提供了一個(gè)全局的錯(cuò)誤處理與轉(zhuǎn)換中間件。任何未捕獲的異常都會(huì)被這個(gè)中間件捕獲并嘗試轉(zhuǎn)換為對(duì)用戶友好的消息或者觸發(fā)預(yù)定義的降級(jí)策略例如當(dāng)知識(shí)庫(kù)查詢失敗時(shí)自動(dòng)降級(jí)為僅使用LLM的內(nèi)部知識(shí)生成回答并記錄告警。這樣即使最底層的模塊出了錯(cuò)傳到用戶界面或上游調(diào)度系統(tǒng)的也是一個(gè)被“完結(jié)”處理過的、有明確后續(xù)行動(dòng)建議的結(jié)果而不是一串令人崩潰的堆棧跟蹤。3.3 可觀測(cè)性作為“完結(jié)”的檢驗(yàn)標(biāo)準(zhǔn)你怎么知道一個(gè)工序真的“完結(jié)”了在制造業(yè)靠質(zhì)檢。在軟件里靠可觀測(cè)性O(shè)bservability。Agent Harness內(nèi)置了強(qiáng)大的日志、指標(biāo)Metrics和追蹤Tracing能力這不是為了炫技而是為了給“自工程完結(jié)”提供驗(yàn)證手段。鏈路追蹤Tracing為每一次用戶請(qǐng)求、每一個(gè)工具調(diào)用、甚至每一次LLM交互生成唯一的追蹤ID。你可以清晰地看到一個(gè)請(qǐng)求的生命周期精確定位到是哪個(gè)“工序”慢了、失敗了或者輸出了異常數(shù)據(jù)。如果某個(gè)模塊聲稱自己處理完了但追蹤鏈在此斷掉或出現(xiàn)了非預(yù)期的跳轉(zhuǎn)那它就沒真正“完結(jié)”。結(jié)構(gòu)化日志日志不是簡(jiǎn)單的print而是帶有級(jí)別、模塊名、執(zhí)行ID、關(guān)鍵上下文的結(jié)構(gòu)化數(shù)據(jù)。例如工具模塊在返回ToolResponse時(shí)無(wú)論成功失敗都必須記錄一條包含execution_id、tool_name、duration和success字段的日志。這相當(dāng)于該工序的“加工記錄”可供后續(xù)審計(jì)和分析。健康檢查與就緒探針每個(gè)管理資源的模塊如數(shù)據(jù)庫(kù)連接池、外部服務(wù)客戶端都需要提供health_check()方法。Harness會(huì)定期調(diào)用或在關(guān)鍵操作前調(diào)用。如果健康檢查失敗該模塊會(huì)被標(biāo)記為“不健康”Harness可以阻止請(qǐng)求流入或切換到備用模塊。這確保了只有真正“就緒”即資源層面已完結(jié)的模塊才會(huì)被使用。通過這些可觀測(cè)性手段我們不僅能發(fā)現(xiàn)問題更能量化每個(gè)模塊“完結(jié)”的質(zhì)量比如工具調(diào)用的平均耗時(shí)、錯(cuò)誤率、狀態(tài)恢復(fù)的成功率等為持續(xù)改進(jìn)提供數(shù)據(jù)支撐。4. 實(shí)戰(zhàn)推演從“Bug流水線”到“完結(jié)流水線”讓我們通過一個(gè)更具體的、融合了多個(gè)熱詞的場(chǎng)景來(lái)看看貫徹“自工程完結(jié)”前后的巨大差異。假設(shè)我們正在開發(fā)一個(gè)“能碳管理AI Agent”它需要查詢數(shù)據(jù)庫(kù)、調(diào)用外部API進(jìn)行計(jì)算并生成報(bào)告。場(chǎng)景Agent收到用戶請(qǐng)求“計(jì)算我司上季度碳排放總量”?!癇ug流水線”模式舊做法Agent核心邏輯解析用戶意圖生成一個(gè)查詢對(duì)象{query: “碳排放總量”, period: “l(fā)ast_quarter”, company: “my_company”}調(diào)用“數(shù)據(jù)查詢工具”。數(shù)據(jù)查詢工具未完結(jié)接收查詢對(duì)象發(fā)現(xiàn)沒有company_id字段只有company名字。它嘗試用名字去查一個(gè)映射表但映射表連接失敗網(wǎng)絡(luò)抖動(dòng)。工具內(nèi)部捕獲了連接異常但只是記錄了一條“映射表連接失敗”的日志然后返回了None。Agent核心邏輯收到None它判斷為“查詢無(wú)結(jié)果”于是決定調(diào)用“估算模型工具”進(jìn)行估算。估算模型工具未完結(jié)它需要company_id和period作為輸入現(xiàn)在只有period。它嘗試使用一個(gè)默認(rèn)的company_id但這個(gè)默認(rèn)ID在模型里沒有對(duì)應(yīng)數(shù)據(jù)。模型計(jì)算過程出現(xiàn)除零錯(cuò)誤拋出一個(gè)原始的ZeroDivisionError。Agent核心邏輯沒有處理ZeroDivisionError的代碼異常向上拋出。最終結(jié)果用戶看到“內(nèi)部服務(wù)器錯(cuò)誤”。開發(fā)者排查需要從最頂層的錯(cuò)誤日志開始逆向追蹤經(jīng)過估算工具、Agent邏輯最后才發(fā)現(xiàn)根源是第一個(gè)工具里的映射表連接問題。排查鏈路長(zhǎng)責(zé)任不清?!巴杲Y(jié)流水線”模式Agent Harness加持下的新做法Agent核心邏輯解析意圖生成查詢對(duì)象。在調(diào)用工具前Harness的輸入驗(yàn)證中間件會(huì)根據(jù)“數(shù)據(jù)查詢工具”注冊(cè)的Schema自動(dòng)校驗(yàn)對(duì)象。發(fā)現(xiàn)缺少company_id但多了一個(gè)company它可能會(huì)嘗試補(bǔ)全或直接在此處返回一個(gè)驗(yàn)證錯(cuò)誤給用戶“請(qǐng)?zhí)峁┕綢D”。假設(shè)驗(yàn)證通過。數(shù)據(jù)查詢工具已完結(jié)映射表連接失敗。工具內(nèi)部進(jìn)行最多3次重試。重試均失敗后它不會(huì)返回None而是構(gòu)造一個(gè)明確的ToolResponse:success: falseerror: ToolExecutionError(codeDEPENDENCY_UNAVAILABLE, message公司名稱映射服務(wù)暫時(shí)不可用, details{retry_attempts: 3})data: null同時(shí)它通過Harness的日志系統(tǒng)記錄一條結(jié)構(gòu)化錯(cuò)誤日志包含錯(cuò)誤碼、重試次數(shù)和追蹤ID。Agent核心邏輯收到ToolResponse檢查success為false并讀取error.code。它配置了錯(cuò)誤處理策略對(duì)于DEPENDENCY_UNAVAILABLE錯(cuò)誤觸發(fā)降級(jí)策略——改為使用一個(gè)本地的、可能過時(shí)的靜態(tài)映射文件來(lái)獲取company_id。如果本地文件也沒有則在此處決定流程的終結(jié)向用戶返回一個(gè)友好的消息“暫時(shí)無(wú)法獲取精確的公司信息請(qǐng)稍后再試或直接提供公司ID”。流程在此明確結(jié)束不會(huì)進(jìn)入不可控的估算環(huán)節(jié)??捎^測(cè)性整個(gè)過程的追蹤鏈?zhǔn)峭暾?。運(yùn)維人員可以在儀表板上看到請(qǐng)求失敗在“數(shù)據(jù)查詢工具”錯(cuò)誤原因是依賴服務(wù)不可用并且看到了重試記錄。Agent核心邏輯的降級(jí)決策也被記錄。責(zé)任清晰定位迅速。對(duì)比之下高下立判。“完結(jié)流水線”模式下問題在最早可能的地方被識(shí)別、封裝、并給出了明確的后續(xù)路徑成功、失敗但友好提示、觸發(fā)降級(jí)。Bug沒有像擊鼓傳花一樣往下流而是在每個(gè)環(huán)節(jié)都被“消化”或“轉(zhuǎn)化”了。5. 開發(fā)者的思維轉(zhuǎn)變從“實(shí)現(xiàn)功能”到“完結(jié)工序”推行“自工程完結(jié)”最難的不是技術(shù)而是思維習(xí)慣的轉(zhuǎn)變。它要求我們以終為始從“工序輸出”的角度來(lái)設(shè)計(jì)每一個(gè)模塊。設(shè)計(jì)時(shí)自問我這個(gè)模塊/函數(shù)/類交付給調(diào)用方的“產(chǎn)品”是什么一個(gè)確定的數(shù)據(jù)結(jié)構(gòu)一個(gè)保證清理的資源句柄一個(gè)完整的業(yè)務(wù)流程結(jié)果調(diào)用方需要做什么樣的檢查才能使用它我能讓這個(gè)檢查變得更簡(jiǎn)單甚至不需要嗎實(shí)現(xiàn)時(shí)牢記異常不是用來(lái)拋的是用來(lái)處理的。處理不了所有異常那就處理你能處理的然后把剩下的包裝成調(diào)用方能夠理解和應(yīng)對(duì)的類型。資源不是用來(lái)申請(qǐng)的是用來(lái)管理生命周期的。誰(shuí)申請(qǐng)誰(shuí)就在其作用域內(nèi)負(fù)責(zé)釋放。測(cè)試時(shí)驗(yàn)證不僅要測(cè)試正常路徑更要測(cè)試邊界和異常路徑。你的模塊在輸入非法、依賴失效、資源不足的情況下輸出是否依然符合“完結(jié)”的契約它會(huì)不會(huì)把爛攤子丟出去評(píng)審時(shí)關(guān)注代碼評(píng)審時(shí)除了看邏輯是否正確要特別關(guān)注模塊的“接口契約”和“錯(cuò)誤處理”??纯从袥]有把本該自己處理的判斷丟給了調(diào)用方有沒有“偷偷”改變了某些全局狀態(tài)而沒有交代。在Agent Harness社區(qū)里我們經(jīng)?;ハ郣eview代碼一個(gè)常見的評(píng)論就是“這個(gè)地方返回null/undefined/異常調(diào)用方不好處理能不能在你的模塊里就給它一個(gè)確定的結(jié)果” 或者 “這個(gè)資源只在try塊里開了有沒有在finally塊或者用with語(yǔ)句確保關(guān)閉”這個(gè)過程一開始會(huì)有點(diǎn)痛苦感覺像是給自己戴上了“枷鎖”。但當(dāng)你習(xí)慣之后你會(huì)發(fā)現(xiàn)你寫的代碼模塊性更強(qiáng)依賴更清晰在復(fù)雜系統(tǒng)比如由多個(gè)AI Agent協(xié)作的場(chǎng)景中集成時(shí)調(diào)試和維護(hù)的難度會(huì)大大降低。你不再需要深入每一個(gè)下游模塊去理解它可能拋出的各種奇怪異常因?yàn)槟阈湃嗡唤o你的總是一個(gè)“完結(jié)”的、可預(yù)測(cè)的結(jié)果。6. 超越Agent通用軟件工程中的“完結(jié)”實(shí)踐雖然我們以AI Agent和Agent Harness為例但“自工程完結(jié)”的理念適用于幾乎所有軟件工程領(lǐng)域。微服務(wù)架構(gòu)每個(gè)微服務(wù)都應(yīng)該對(duì)其領(lǐng)域內(nèi)的業(yè)務(wù)邏輯和數(shù)據(jù)做到“完結(jié)”。它應(yīng)該通過API網(wǎng)關(guān)或服務(wù)網(wǎng)格處理好認(rèn)證、限流、熔斷對(duì)外提供穩(wěn)定的接口。一個(gè)服務(wù)內(nèi)部的數(shù)據(jù)存儲(chǔ)或緩存掛了應(yīng)該通過降級(jí)策略如返回緩存舊數(shù)據(jù)或默認(rèn)值來(lái)響應(yīng)而不是讓調(diào)用方看到“數(shù)據(jù)庫(kù)連接失敗”。前端開發(fā)一個(gè)UI組件應(yīng)該自己管理好它的加載狀態(tài)、錯(cuò)誤狀態(tài)。數(shù)據(jù)獲取失敗時(shí)它應(yīng)該在組件內(nèi)部顯示錯(cuò)誤提示而不是拋出一個(gè)異常讓整個(gè)頁(yè)面崩潰。它接收的props應(yīng)該有明確的PropTypes或TypeScript接口定義對(duì)不合理的輸入進(jìn)行早期警告或使用默認(rèn)值。數(shù)據(jù)處理管道ETL抽取、轉(zhuǎn)換、加載中的每一個(gè)步驟都應(yīng)該對(duì)數(shù)據(jù)的質(zhì)量負(fù)責(zé)。清洗步驟應(yīng)該處理缺失值、異常值轉(zhuǎn)換步驟應(yīng)該保證輸出格式的嚴(yán)格一致。一個(gè)步驟失敗整個(gè)管道應(yīng)該有檢查點(diǎn)Checkpoint和重試或死信隊(duì)列Dead Letter Queue機(jī)制而不是讓錯(cuò)誤數(shù)據(jù)污染下游。其核心思想是一致的通過強(qiáng)化模塊內(nèi)的內(nèi)聚性和對(duì)外的契約性降低模塊間的耦合度與認(rèn)知負(fù)擔(dān)從而構(gòu)建出更加健壯、可維護(hù)的系統(tǒng)。在當(dāng)今系統(tǒng)越來(lái)越復(fù)雜、依賴越來(lái)越多的環(huán)境下這種思維不再是“好習(xí)慣”而是“生存必需”?;氐介_頭的“修水管”視頻。那個(gè)程序員犯的錯(cuò)就是把“止住自家漏水”這個(gè)局部問題“解決”了卻沒有考慮這個(gè)操作在整個(gè)供水系統(tǒng)全局中是否“完結(jié)”。他關(guān)閉的閥門可能影響了主管道的壓力平衡把問題傳遞給了整個(gè)系統(tǒng)。我們的代碼也是如此。每一次圖省事的try-catch后直接throw每一次返回含義模糊的null每一次忘記關(guān)閉的連接都是在給系統(tǒng)的“下一道工序”——可能是另一個(gè)模塊可能是運(yùn)維同事也可能是最終用戶——埋下一顆顆定時(shí)炸彈。Agent Harness項(xiàng)目給我的最大啟示就是它把TPS這種經(jīng)過時(shí)間考驗(yàn)的、樸素的工程智慧用現(xiàn)代軟件框架的形式固化了下來(lái)。它逼著我們?nèi)ニ伎歼吔?、契約和狀態(tài)讓構(gòu)建可靠的AI Agent不再僅僅依賴于LLM的強(qiáng)大更依賴于扎實(shí)的、可預(yù)測(cè)的工程基礎(chǔ)設(shè)施。所以下次當(dāng)你寫下一行代碼時(shí)不妨問問自己我這個(gè)“工序”“完結(jié)”了嗎