
上個月凌晨兩點我被生產環(huán)境的告警電話叫醒。一個面向內部運營團隊的客服智能體突然對超過三成的用戶請求沉默——不是模型沒推理而是消息根本沒能送達到那個Agent實例。排查了一小時發(fā)現是路由層配置里一個很不起眼的超時參數被改小了Agent側明明已經處理完回復卻因為超時被網關當作失敗丟棄。那一刻我意識到當Agent從一個孤立的聊天機器人變成一群需要互相協(xié)作、由不同團隊維護、跑在不同環(huán)境里的分布式單元時怎么把請求可靠地送到正確的Agent手里這件事已經比怎么訓練一個更好的提示詞重要得多。Agent-Reach這個項目就是在這種背景下被我認真做起來的。Agent-Reach是一個智能體觸達網關解決的核心問題是當你有一套或很多套Agent服務時外部請求應該如何穩(wěn)定、可路由、可觀測地分發(fā)到正確的Agent實例上并把結果可靠地返回。它不負責Agent的推理邏輯只負責觸達這件事——誰該被觸達、以什么協(xié)議觸達、觸達失敗怎么辦、整個鏈路有沒有被完整記錄。標題里的Reach其實是雙關一個意思是覆蓋即任何類型的Agent都能接入另一個意思是觸達即請求真正落到Agent手里并拿到響應。這篇文章我會從最初的設計動機講起把協(xié)議選型、路由策略、可觀測性建設以及上線后被真實流量教育過的幾個坑全部攤開說。1. 從一次凌晨的生產事故說起Agent觸達為什么會失效1.1 事故復盤問題不在模型而在連接那天的故障表象是客服Agent不回復但翻到Gateway的日志就發(fā)現所有請求其實都成功推給了Agent側的消息隊列Agent也確實消費并產出了回復。問題出在回程鏈路上Agent通過HTTP回調把結果送回網關因為回調超時閾值被某位同事從5秒收緊到3秒導致大量晚到的回復被網關直接丟棄。用戶看到的自然就是石沉大海。這件事讓我重新審視了團隊里所有Agent的集成方式。當時每個Agent都是各自為政有的是通過WebSocket長連維持一個常駐會話有的只暴露REST API由調用方同步等待有的是異步任務型提交后靠輪詢拿結果。調用方要同時處理三種協(xié)議、兩套鑒權體系加上每個Agent的超時策略還不一樣——線下跑通很容易線上一起抖動就各種花式超時、重放、丟消息。當時我們自嘲說這不是在調Agent是在調一套手工拼裝的分布式消息系統(tǒng)。1.2 Reach的雙關覆蓋面和響應力的失衡這件事背后其實是一個更普遍的問題單Agent時代你只需要關注人機對話鏈路多Agent協(xié)作時代流程被切碎成了大量跨服務調用任何一個跳點都可能成為信號盲區(qū)。我做Agent-Reach時首先想清楚的就是網關不應該是一個聰明的路由器而應該是一個保證送達的中間人——它關心的不是Agent怎么思考而是Agent值不值得被信任地調用。所以Reach被我拆成兩個指標來定義覆蓋率Coverage系統(tǒng)里有哪幾種Agent接入方式網關都能不能接住觸達成功率Reachability請求從入網到出網端到端的成功比例。故障發(fā)生后我復盤出的結論是覆蓋率再高只要觸達成功率不是99.9%以上企業(yè)場景里就沒人敢用。這也是Agent-Reach后續(xù)一切設計的原點和驗收標準。2. Agent-Reach要解決的核心問題把觸達能力從業(yè)務代碼里抽出來2.1 拆解前的集成痛點如果你稍微調研一圈企業(yè)內部Agent落地情況會發(fā)現一個普遍規(guī)律業(yè)務系統(tǒng)直接調用Agent API時代碼里藏著大量和智能體無關的臟活。一個典型的調用場景大概是這樣的def call_agent(agent_name, payload): # 誰知道這個Agent用的是http還是ws先查配置 endpoint registry.get_endpoint(agent_name) if endpoint.protocol http: resp requests.post(endpoint.url, jsonpayload, timeout3) elif endpoint.protocol ws: # 又得維護一個WebSocket連接池還得處理重連... # 調用完之后還要手動打日志、手動重試、手動判斷是不是消息重復...這種代碼最大的問題不是難看而是每個業(yè)務團隊都在重復實現同樣的觸達邏輯而且實現得都不完整。有人忘了重試有人重試導致Agent側重復執(zhí)行了危險操作有人把超時設成10秒讓用戶干等有人壓根沒接入鏈路追蹤。一個Agent要在N個業(yè)務系統(tǒng)里被調用就有N個版本的殘次品集成層。2.2 網關模式的決定為什么不是SDK也不是全托管平臺當時擺在我面前有三條路做一套標準SDK讓所有業(yè)務方通過SDK調用Agent做一個完全托管的Agent編排平臺把Agent注冊、調度、編排全做進去做一個輕量觸達網關只負責接入、路由、分發(fā)、可靠性。我最終選擇3有一個很現實的原因團隊Agent的技術棧各異、部署形態(tài)各異有的在Kubernetes里跑有的還在舊虛擬機環(huán)境有的甚至是以腳本形式周期執(zhí)行的。SDK需要改動每個業(yè)務方的代碼推廣成本極高全托管平臺看著美但要把存量Agent全部改造遷移基本等于做一個新項目。網關則是在所有人中間站一個公共節(jié)點業(yè)務方只需要把請求發(fā)給網關網關負責搞定背后的一切對存量的侵入最小。注意這里說的網關不是API Gateway那種HTTP反向代理它本質上是一個帶狀態(tài)的消息路由層。API Gateway關注請求轉發(fā)、鑒權、限流Agent觸達網關還必須關注異步確認、會話狀態(tài)、重試策略、Agent存活感知——這些才是讓Agent真的能被業(yè)務信任的關鍵。2.3 目錄式的Agent注冊讓每個智能體都有名字Agent-Reach做起來的第一件事不是畫架構圖而是設計Agent注冊目錄。我給Agent定了幾個最小元數據注冊上去才能被路由agent_id: code-review-agent display_name: 代碼評審Agent version: 2.4.1 protocol: v1.hybrid endpoints: - type: task_submit url: https://svc.internal/v2/async/task - type: status_query url: https://svc.internal/v2/async/status transport: - websocket - http-callback capabilities: - review_pull_request - detect_security_vulnerability - suggest_refactoring routing_tags: team: platform-engineering model_profile: gpt-4o-mini latency_sla: medium這套注冊表的價值后來被驗證得很充分它讓路由不再靠寫死的if-else而是變成對注冊數據的查詢。新Agent接入不必改網關代碼只需要提交一份這樣的注冊信息。這直接推高了覆蓋率——第一個月就有十一個不同類型的Agent接入其中三個完全沒有經過我手自己看著文檔就注冊成功了。3. 協(xié)議與消息模型讓不同技術棧的Agent能說上話3.1 統(tǒng)一Envelope的設計取舍Agent之間的通信最煩的一點是不同Agent的說法不一樣。有的Agent把用戶問題和上下文一起放在body里的message字段有的把上下文放到meta里還有的接受JSON純字符串。如果網關不做統(tǒng)一封裝那么路由、重試、追蹤全都無從談起——你連哪個字段代表這次請求的唯一ID都找不出來。Agent-Reach定義了一套輕量的Envelope協(xié)議所有進出網關的消息都被包一層統(tǒng)一的信封{ envelope: { api_version: 1.0, trace_id: c4a2f1e7-9d1b-4a2e-8c5a-1f9e3b2d7c01, message_id: msg_01HZ9KZ5T2M8VQ4B, agent_id: code-review-agent, session_id: sess_8f61, timestamp: 2025-06-12T14:23:1108:00, timeout_hint: 30 }, payload: {} }有幾個點值得多說一句trace_id是每次用戶請求鏈路全局唯一的所有Agent和網關日志都必須帶它message_id是網關發(fā)的Agent測回執(zhí)時要帶上它保證哪些消息已經被確認可以對齊timeout_hint是網關建議Agent在多少秒內完成避免不同Agent默認響應時間不一致。要說明的是這個設計不是為了規(guī)定Agent內部怎么寫代碼而是給觸達過程找一個公共坐標系。Agent收到Envelope后可以把payload解析成自己的內部結構但回執(zhí)和日志必須遵循Envelope否則網關只管送到后續(xù)有沒有處理就抓瞎了。3.2 多通道適配WebSocket、消息隊列、HTTP回調協(xié)議統(tǒng)一之后真正麻煩的是通道適配。現實世界里Agent可觸達的方式五花八門。Agent-Reach里我做了四個適配器適配器之間用同一個內部事件總線連接這樣新通道可以只實現統(tǒng)一接口就接進來。通道類型適用場景關鍵問題HTTP同步調用Agent處理快、結果即時返回超時設置、重試安全性HTTP異步回調長耗時Agent先受理后回傳回調地址暴露、回調鑒權WebSocket長連接常駐內存、實時對話型Agent連接保活、消息Fragment消息隊列如Redis Stream、Kafka高吞吐任務分發(fā)消費確認、消息堆積這么多適配器里最容易翻車的是異步回調。因為Agent完成任務后要主動把結果推給網關這要求Agent配置網關的回調地址。生產環(huán)境里我曾經遇到Agent側把回調地址里一個斜杠寫錯結果所有完成的請求都靜默丟失。后來我在網關上做了一個補償查詢設計如果一個message_id超過預期時間仍未收到回調網關會主動向Agent的status_query端點發(fā)起一次狀態(tài)查詢。這個設計相當于給單向回調裝了一個反向巡檢把很多靜默失敗變成了可發(fā)現、可恢復的失敗。3.3 超時與重試分布式系統(tǒng)里唯一確定的事在分布式環(huán)境里幽靈消息和重復投遞是繞不開的。我把超時和重試策略分成兩層傳輸層網關到Agent的HTTP/WS傳輸短超時如5秒失敗后做有限次數重試業(yè)務層Agent從受理到產出結果的整體時長長超時如30秒到2分鐘超時后發(fā)起狀態(tài)查詢而不是盲目重發(fā)。重試最危險的是非冪等Agent。我曾經遇到一個生成圖片的Agent因為網關重試機制設置不當同一條生成請求被連續(xù)執(zhí)行了三次給用戶賬戶扣了三次費用。事后我在注冊表里加了idempotent: true/false字段——冪等的Agent允許自動重試非冪等的Agent在超時后只能置為待人工確認狀態(tài)由調用方決定是否重發(fā)。這個小改動直接杜絕了后續(xù)的惡性重復扣費問題。4. 路由分發(fā)與負載調度從輪詢走向按需觸達4.1 基于標簽的路由能力描述比模型名稱更可靠做路由的時候我第一個念頭是按模型名路由比如所有GPT-4o的請求都去A組。后來發(fā)現這是個誤區(qū)業(yè)務方真正關心的是這個請求有沒有代碼審查能力而不是它背地里是哪個模型。同一個能力可能由不同模型實現同一個模型也在不斷換代按模型名路由會讓客戶端邏輯跟著模型升級一起改非常脆弱。Agent-Reach的做法是標簽路由。注冊Agent時聲明capabilities和routing_tags調用方只描述自己需要什么能力網關從注冊目錄里篩選滿足條件的Agent集合candidates [ agent for agent in registry.all() if review_pull_request in agent.capabilities and agent.routing_tags.get(team) platform-engineering ]能力完全一樣但由不同團隊維護的Agent之間再用權重分配流量——這為同一個能力有多個實現留了一手后面灰度測試新Agent時特別好用。4.2 優(yōu)先級與成本水位讓1%的貴模型干最關鍵的事Agent-Reach上線后的一個意外收獲是它順手解決了成本控制問題。不同Agent背后模型的成本可能差出幾十倍如果一視同仁輪詢財務報表會非常難看。我在路由規(guī)則里加了cost_profile標簽并給請求設置了成本閾值。舉個例子代碼評審Agent有兩個版本一個用旗艦模型一個用輕量模型。如果一個Pull Request只是改了幾行注釋網關會直接路由給輕量版Agent只有涉及核心邏輯變更、具備一定復雜度信號時才走高成本通道。實測下來高成本模型調用量下降了約38%但用戶對評審質量的滿意度幾乎沒降——這說明大部分場景其實不需要火力全開路由層的成本感知能力被嚴重低估了。4.3 會話親和性別讓用戶覺得對面的Agent失憶多Agent系統(tǒng)的另一個隱形坑是會話狀態(tài)。客服場景里用戶和Agent聊了十輪狀態(tài)都保存在A實例內存里下一輪請求網關隨機路由到B實例B對之前聊了什么一頭霧水用戶會覺得Agent突然變傻了。Agent-Reach的默認策略是會話親和性同一個session_id的連續(xù)請求優(yōu)先路由到上次處理它的Agent實例除非該實例不健康或負載超過閾值。親和性需要網關側維護一份會話與實例的映射緩存還要給映射設置TTL防止長期霸占。這個設計沒什么技術含量但對于用戶體驗的改善立竿見影——很多用戶根本說不清哪句話讓AI不夠聰明其實只是狀態(tài)丟了。不過要注意親和性不能無腦開啟。有些Agent本身是無狀態(tài)的強制親和反而會影響負載均衡。所以我把親和性做成Agent級別可配置有狀態(tài)Agent默認親和無狀態(tài)Agent直接輪詢。5. 可觀測性建設你沒法調試一個看不見的Agent5.1 Trace ID貫穿從用戶請求到Agent回復的整條鏈路Agent系統(tǒng)調試之所以難是因為鏈路跨越太多個進程用戶HTTP請求先到網關網關推送消息隊列Agent消費Agent再調用外部模型API最后回調網關再返回給用戶。任意一跳慢了或丟了都很難定位。Agent-Reach把鏈路追蹤作為基礎設施來建設而不是事后加個日志。網關在入口就生成trace_id通過Envelope傳給Agent。每個適配器、每個路由決策、每次重試都必須把trace_id打進結構化日志和指標里。接入了日志平臺后查一個問題只需要輸入一個ID整條鏈路里所有跳點的時間消耗、狀態(tài)碼、重試次數全部浮現出來。舉個真實案例有一次用戶反饋Agent回答特別慢我輸入trace_id查鏈路發(fā)現耗時大頭根本不在模型而在一個外部知識庫API的DNS解析上——因為Agent所在的舊環(huán)境DNS緩存不斷失效。如果沒有鏈路追蹤這種跨系統(tǒng)的性能問題幾乎不可能靠猜定位。5.2 觸達率與覆蓋率度量兩個容易被混淆的指標接入了可觀測性之后Agent-Reach在監(jiān)控面板上展示四個核心指標。我強烈建議任何做Agent網關的團隊至少先盯住這四個指標含義健康標準觸達成功率請求成功送達Agent并拿到回執(zhí)的比例≥99.9%路由覆蓋率有匹配Agent的請求數 / 總請求數≥100%沒有就屬于配置缺陷平均觸達延遲從入網到Agent回執(zhí)確認的耗時P95視Agent類型而定重試觸發(fā)率需要網關重試的消息占比≤5%這里我要特別說下觸達成功率和覆蓋率的關系。覆蓋率低的時候觸達成功率再高也沒用因為你放棄了一部分請求覆蓋率100%但觸達成功率99%同樣糟糕因為每100個請求就有1個靜默丟失。必須兩個一起看才算是Agent觸達質量的完整畫面。5.3 離線依賴治理模型服務宕機時網關該做什么Agent網關本質上是個中介它最怕的不是自己掛而是上游模型服務掛了之后下游所有請求堆積、超時、雪崩。我在Agent-Reach里做了一個Agent健康度探測模塊定期向各Agent發(fā)送輕量ping一個不消耗模型調用的存活探測如果Agent連續(xù)N次無響應就在注冊目錄里把它標記為unhealthy路由時自動摘除。摘除后請求不會發(fā)到一個已經沒有生還希望的Agent上而是進入故障降級策略——比如直接返回該能力暫不可用的友好提示或者路由到備用的降級Agent。這個設計和負載均衡里的健康檢查是一個道理但在Agent場景下更要謹慎因為很多Agent的存活并不能代表它的模型依賴沒有故障。后來我還加了一個更細的依賴探針Agent可以上報自己依賴的上游比如指定模型API網關根據模型API的公開狀態(tài)自動調整Agent的負載系數——模型不穩(wěn)定時自動降低給該Agent的流量。6. 上線后的實戰(zhàn)糾偏我被真實流量教育過的幾個設計6.1 取消萬能Agent意圖分流比我想象的更復雜Agent-Reach起初一直想把整個客服功能收歸到一個萬能Agent里以為能力集中管理路由邏輯就能簡化。但真實流量上來后發(fā)現越是面向各種問題的萬能Agent越容易在邊界場景里表現平庸——一會兒要處理退款糾紛一會兒要回答產品配置一會兒又要做情感安撫單個提示詞工程根本罩不住。后來我把萬能Agent拆成三個垂直Agent路由層增加一道很薄的意圖分類步驟根據用戶請求的語義粗粒度分到對應的垂直Agent。這道意圖分流不一定非要用大模型做用一組精確關鍵詞模型、甚至一個輕量分類器就能跑得很好。實測下來整體回復準確率上升了約12%重度Agent的負載還下降了。拆Agent聽起來像產品決策實際上沒有網關的路由能力支撐根本不敢拆——因為拆開之后怎樣把用戶請求準確送過去就成了第一道坎。6.2 消息積壓與背壓控制異步回調模式上線后遇到過一個小時級的抖動某Agent因為上游模型API變慢消費速度跟不上消息隊列積壓了幾萬條任務而網關還在源源不斷往里推。如果沒有背壓控制積壓只會越來越深最后老任務全部超時新任務也被卡住。Agent-Reach里加的方案是網關和Agent之間有一個信用額度機制每個Agent實例維護一個當前未完成任務數的指標當未完成任務數超過閾值網關就不再向該Agent分發(fā)新任務而是進入排隊狀態(tài)。這其實就是分布式系統(tǒng)里最常見的流控思路但放在Agent場景里特別容易忽略——因為大家都覺得Agent會自己消化任務忽略了Agent背后的模型服務同樣有吞吐上限。6.3 灰度發(fā)布讓新Agent先接10%的流量Agent升級比普通服務升級更讓人緊張因為模型行為是概率性的你沒法保證新版本在所有輸入上都比舊版本好。Agent-ReACH幫我做了一件很有價值的事支持同一Agent ID下注冊多個版本路由時按權重分配流量?;叶炔呗跃唧w是新版本Agent以version: 2.5.0注冊初始權重10%網關自動把10%的能力請求路由到新版本其余90%繼續(xù)走舊版本觀察關鍵指標觸達成功率、平均延遲、用戶投訴率、上下文切換時長指標穩(wěn)定后提升權重到30%、50%最后100%時把舊版本摘除。這個機制成本很低但對Agent迭代的信心提升很大。以前每次升級Agent團隊都提心吊膽現在新版本上線就是一次普通的灰度發(fā)布。6.4 兼容性策略給Agent升級留出雙重注冊窗口最后一個坑是關于版本過渡的。Agent內部依賴的模型或數據格式變了之后新舊Agent可能無法處理彼此的請求。如果網關的注冊目錄里舊版本一摘除所有在途消息和持久化會話馬上就會出兼容性問題。Agent-Reach最后保留了一個很實用的小設計新舊版本共存窗口。摘除舊版本前網關會把舊版本標記為draining狀態(tài)不再分配新請求但允許已經發(fā)給它的在途請求繼續(xù)處理和回執(zhí)。只有等舊版本的在途任務數降為0才真正從目錄里移除。這個優(yōu)雅下線窗口的時長可配置通常設置15到30分鐘足夠讓異步任務清空又不至于讓舊版本一直占用資源。最后再分享一點個人體會吧。Agent-Reach這個項目做下來我最大的感受是大家聊Agent時都喜歡盯著模型、提示詞、RAG這些聰明的部分但真正讓Agent在業(yè)務環(huán)境里跑得穩(wěn)、跑得久的恰恰是一堆看起來不太聰明的工程活——消息不丟、超時合理、路由可預期、出了問題查得到。這些活本來不該每個團隊都重新發(fā)明一遍網關類基礎設施的價值就在這里。如果你也正在做多Agent應用我建議不要急著寫業(yè)務邏輯先問問自己請求到Agent之間的這段路是不是可靠、可路由、可追蹤的如果答案是否定的那你可能也需要一個屬于自己的Reach。