
我最早聽到“Agent-Reach”這個詞是在一次內部例會。當時我們組的智能體系統(tǒng)連續(xù)三周出現(xiàn)“工具調用了但沒生效”的工單大家各抒幾見最后負責人拋了一句話“別把問題當模型問題先算算你的Agent到底能觸達到哪一步?!蔽液髞戆堰@句話當成了項目起點。所謂觸達不只是“能調用一個工具接口”這么簡單。它至少包含三層工具能不能被發(fā)現(xiàn)、上下文能不能支撐到調用那一輪、調用之后的返回信息能不能被正確接住。我當時的系統(tǒng)三層都在出問題工具注冊表里有兩百多個接口實際每次任務卻只掃描到十幾個任務做到第8輪上下文窗口就快滿了后面的工具調用還在往里面塞資料有幾個外部服務返回的成功狀態(tài)碼是200但數(shù)據字段被截斷智能體照樣把殘缺信息當作最終結果交給用戶。這些問題單獨看都能解釋成偶發(fā)故障但放到同一張時間線里就看到一個共性Agent能到達的能力范圍和任務實際需要的范圍之間存在一條很寬的空隙。摸清并填平這條空隙就是Agent-Reach這個項目要做的事。它不是什么酷炫的新模型也不是一個通用平臺而是一套被我拼出來的內部工具能力注冊表、觸達軌跡記錄、上下文壓縮、失敗回退編排外加一堆圍繞它們寫的巡檢腳本。這套東西解決的是三類人的問題正在為多智能體任務連續(xù)性頭疼的工程師想把工具調用失敗率壓下一個數(shù)量級的平臺組以及懷疑自己選型有問題、但還沒定位到具體環(huán)節(jié)的架構師。1. 先定義問題“觸達”到底指什么智能體為什么夠不到目標1.1 三個反復被提到的“夠不到”現(xiàn)場先舉三個很有代表性的場景。第一個場景是“工具路由遺漏”。系統(tǒng)里注冊了221個工具但任務來到時Agent只見過頂層菜單里那十幾個。原因是我們只做了工具名前綴匹配沒有按任務意圖維護索引。結果是業(yè)務同學用自然語言提問時智能體選了一個表面上相關、但數(shù)據完全不支持需求的工具。第二個場景是“上下文遮蔽”。任務進行到第8輪需要調用“客戶應付賬款匯總”工具但前置對話已經把上下文窗口擠得只剩1500個token。模型拿到工具返回的是一大張表格塞回去時先被截斷了后半部分最終它只盯著前半部分寫出結論。這個錯誤很隱蔽因為系統(tǒng)日志顯示“調用成功”沒人看到截斷發(fā)生在上下文壓縮環(huán)節(jié)。第三個場景是“半截返回信息被當作最終答案”。外部接口返回200但明細字段被上游限流策略裁剪成“暫無”。Agent不區(qū)分異常值和特殊業(yè)務狀態(tài)碼直接告訴用戶該客戶無應收款項。后來我們查接口文檔才發(fā)現(xiàn)“暫無”和“金額為零”在業(yè)務上是完全兩回事。這三個場景的共同點是它們都不在模型推理層面而在Agent和外界的連接層。推理再強拿不到對的數(shù)據結果也是錯的。1.2 把“觸達”拆成三類可度量的范圍我后來和團隊復盤時把觸達拆成三類范圍分別對應不同性質的故障觸達維度定義典型失敗信號可以觀察的指標工具觸達目標工具能否被正確發(fā)現(xiàn)并調用路由遺漏、參數(shù)錯配、權限報錯工具發(fā)現(xiàn)率、調用成功率、參數(shù)校驗失敗率數(shù)據觸達任務所需上下文能否完整保留并送達截斷、召回碎片、字段缺失上下文保留率、字段完整率、召回命中率流程觸達多步任務中每一步產物能否正確傳到下一步任務中斷、重復調用、死循環(huán)任務完成率、平均步數(shù)、步驟間失敗率拿一個退款流程舉例工具觸達指Agent能不能找到“退款創(chuàng)建”接口數(shù)據觸達指它能不能拿到訂單號、金額、用戶身份并且這些字段沒被打折流程觸達指創(chuàng)建退款之后下一步的“通知用戶渠道”能不能拿到上一步的操作結果。前者不行就換路徑中者不行就得修上下文后者不行就是狀態(tài)同步的問題。1.3 為什么不能只把鍋甩給模型能力很多團隊遇到這類問題第一反應是換更大的模型或者加更多few-shot示例。我的實際體會是模型的能力邊界確實存在但大多數(shù)事故發(fā)生在“模型能看到什么”這一層。你給模型一張殘缺的地圖它再聰明也只能規(guī)劃一條殘缺的路線。這也是“Agent-Reach”這個名字的由來。我不關心模型本身有多大我只關心在具體任務里它到底能觸達哪些工具、哪些數(shù)據、哪些流程節(jié)點。把這些東西變成顯式的、可配置、可觀測的規(guī)則比單純升級模型參數(shù)更穩(wěn)、更快。2. 能力目錄把“能到達什么”變成一張可以查的路由表Agent-Reach 的地基不是一個大模型而是一張能力目錄。它不像傳統(tǒng)API網關那樣只存接口路徑和鑒權方式還要存工具之間的前置關系、依賴順序、上下文成本以及它能服務哪些業(yè)務意圖。2.1 注冊工具時不止寫接口地址還要寫“觸達條件”我們早期注冊工具只寫三樣名字、接口地址、參數(shù)schema。結果就是工具之間沒有先后關系Agent只能靠模型猜。加入Agent-Reach后每條工具多了一組字段用來寫明“什么情況下這個工具能碰、碰之前必須完成什么”。- id: order.create display: 創(chuàng)建訂單 input_schema: customer_id: string sku_id: string quantity: integer prerequisites: - condition: stock.query_result.available input.quantity required_tool: stock.query - condition: auth.has_role(user, buyer) affects: - cart.release - payment.request reach_score: 0.92這段配置解決了一個特別實際的問題工具的選擇順序。原來Agent經常不看庫存就下單現(xiàn)在“創(chuàng)建訂單”這條工具的前置條件里明確寫了依賴“庫存查詢”路由層會先把庫存查詢工具放進候選列表等返回結果滿足條件之后再把“創(chuàng)建訂單”暴露給模型。也就是說這不再是提示詞層面的建議而是流程層面的約束。2.2 用可達性分數(shù)排序而不是把所有工具都堆給模型很多工作流引擎的做法是把候選工具按字母序或注冊時間排個序全塞進上下文。但工具數(shù)量一上百模型的選擇準確率會明顯下降而且token開銷爆炸。我們改用了一個簡單的啟發(fā)式得分叫“可達性分數(shù)”reach_score 0.4 * 歷史調用成功率 0.3 * 權限匹配度 0.3 * 任務意圖相關度。這三個子分都是離線算好、在線查表的不需要實時跑模型。權限匹配度來自用戶角色和工具roles字段的重疊程度任務意圖相關度則來自我們在能力目錄里給工具標注的業(yè)務意圖標簽。排序后只把前5個工具放進候選列表模型在這5個里面做選擇準確率反而比之前幾十個工具一起給要高不少。這個思路不復雜但它意味著你不能再偷懶每個新工具上線時都必須維護成功率和意圖標簽。否則分數(shù)會失真排序也就失去了意義。2.3 動態(tài)可見性不同角色看到的能力目錄不一樣能力目錄同時承擔了權限收斂的作用。同一個工具不同角色看到的優(yōu)先級不同。比如客服人員能看見“查看客戶聯(lián)系人”和“查詢訂單狀態(tài)”但看不到“發(fā)送營銷短信”運營人員能看到“創(chuàng)建優(yōu)惠券”但看不到“刪除優(yōu)惠券”。routes: customer_service: - crm.contact.read - order.status.read operations: - coupon.create - crm.contact.read finance_ops: - order.create - invoice.send曾經踩過一個坑我們讓Agent在內部問答里能查應收款明細結果普通客服通過追問也能觸發(fā)這個工具一口氣把全量客戶余額信息拉了出來。后來把這層角色和工具的映射搬進路由表每次任務開始前先解析用戶身份再決定哪些工具可以出現(xiàn)在候選列表里。這比在提示詞里寫“不要訪問無關數(shù)據”可靠得多。3. 觸達軌跡記錄每次訪問讓失敗可以被復現(xiàn)能力目錄解決的是“應該能到哪”但系統(tǒng)里還有很多“實際到不了”的意外情況。為了把這些意外變成可分析的數(shù)據Agent-Reach有一套統(tǒng)一的觸達軌跡記錄每次工具調用都會生成一條結構化的入賬。3.1 一次任務的一串腳印觸達事件的結構我定義了一個很簡單的JSON結構核心字段就十幾個盡量不引入額外復雜度。{ trace_id: tr_8f2a11b3, task_id: task_refund_2093, agent_id: assistant-negotiation, step: 4, intent: resolve_refund, tool_selected: refund.create, tool_returned: { status: ok, body: refund_id: RF-4432 }, context: { remaining_tokens: 3120, compressed: false }, reach_grade: pass }叫“觸達軌跡”而不是“調用日志”是因為我們關注的不只是“調用了沒”還包括調用前后上下文狀態(tài)。字段里的remaining_tokens會告訴我們這次調用發(fā)生的時候模型上下文還剩下多少余量compressed字段則記錄是不是剛剛做過壓縮。這兩個字段幫我們定位了很多“看似調用成功實則結果不可用”的問題。3.2 失敗歸因怎么判斷是工具壞了、權限不夠還是上下文斷了觸達軌跡收集了一段時間后我們發(fā)現(xiàn)失敗類型其實可以歸成幾類每一類的處理方式完全不同。做個表放在一起最直觀失敗信號大概率根因建議處理路徑返回權限錯誤用戶角色和工具roles不匹配走權限申請或切換可替代工具調用超時/網絡錯誤外部系統(tǒng)不穩(wěn)定指數(shù)退避重試超過2次人工介入返回字段為空/缺字段上游限流或接口裁剪檢查字段完整率補充查詢邏輯上下文剩余token不足前序步驟塞了太多內容觸發(fā)上下文壓縮或改走子任務工具不在候選列表路由意圖標簽缺失回到能力目錄補標簽而不是硬塞給模型有了這張對應表每天巡檢就變成了一道判斷題而不是翻日志大海撈針。3.3 我在記錄過程中踩的坑別把敏感字段寫進軌跡觸達軌跡上線第一天我們就出過一次問題。當時為了排查一個訂單查詢問題把用戶郵箱和手機號直接打進了事件字段。雖然只在內部日志里但內部日志同樣要守最小化原則。后來我在寫入函數(shù)里加了一條白名單規(guī)則凡是匹配身份證、手機號、郵箱、地址等模式的字段一律替換成掩碼形式。{ input: { customer_id: cus_****, phone: 138****1234, note: *** } }這個改動對排查效率影響不大因為trace_id還能去原始工單系統(tǒng)里找回完整數(shù)據但安全合規(guī)的壓力小了很多。如果你想在自己的項目里復用這套東西敏感字段掩碼這一步千萬不要省略內部日志不是一個可以隨便存全量個人信息的地方。4. 把觸達范圍撐大的三條腿上下文壓縮、就近路由、失敗回退能力目錄和觸達軌跡是基礎設施真正讓任務完成率變高的是三個運行時機制上下文壓縮、就近路由、失敗回退。這三個機制對應三類常見問題token不夠用、路由選不準、下游調用不穩(wěn)。4.1 上下文壓縮把不需要的細節(jié)折疊但保留“可回查”線索任務循環(huán)一長上下文一定會膨脹。我們的策略不是無腦截斷而是分塊壓縮。對于已經完成步驟的完整工具返回值保留摘要并把原始事件的關鍵位置寫到摘要下面。這樣模型在需要細節(jié)時可以通過摘要里的trace_id反查完整數(shù)據。舉一個實際例子前序工具返回 stock.query: {sku: P-102, stock: 3220, warehouse: [SH-1, BJ-2], eta: 2025-04-10} 壓縮后入上下文 庫存查詢已完成可用庫存尚充足具體倉儲明細見 tr_82fe91。 如果后續(xù)步驟需要確認某個倉庫是否有貨再調用 stock.query 帶上次的trace_id。這個“可回查”的壓縮方式比單純把數(shù)據刪掉要穩(wěn)得多。它既省了token又沒有真正丟失信息因為你給模型留了一個主動拉取詳情的鉤子。實際運行中我們經常在最終匯總階段看到模型回頭追問某個倉庫的具體地址而這種追問完全不會占用前序步驟的上下文額度。4.2 就近路由先過濾再排序最后讓模型做選擇很多Agent框架喜歡把所有工具都塞給模型讓模型自己決定??晒ぞ咭欢嗄P途拖衲阍谝粋€貨架很混亂的超市找醬油效率并不高。Agent-Reach 的做法是先把候選列表過濾到一個很小的集合再讓模型做最終選擇。def pick_tool(intent, candidate_tools, user_context): # 1. 權限過濾 accessible [t for t in candidate_tools if t.has_access(user_context)] if not accessible: return None # 2. 按可達性分數(shù)排序 accessible.sort(keylambda t: t.reach_score, reverseTrue) # 3. 只留top5給模型決策 shortlist accessible[:5] selected model.choose_tool(shortlist, intent) return selected這個邏輯很簡單但效果很明顯工具誤選率下降最明顯的是第10輪以后的任務。因為越到后面上下文越雜給模型的候選越少反而越不容易被無關工具干擾。4.3 失敗回退不是所有失敗都需要讓用戶重新輸入早期看到工具失敗就直接把錯誤拋給用戶這是最差的體驗。Agent-Reach 把失敗分成可重試和不可重試兩類??芍卦嚨陌凑詹呗宰詣犹幚聿豢芍卦嚨牟呸D到人工。我們預設了一個回退優(yōu)先級按順序嘗試權限失敗先檢查是否有替代工具沒有則引導用戶走身份授權入口。超時或5xx錯誤指數(shù)退避重試一次間隔從2秒開始最多三次。參數(shù)不滿足前置條件回到最近一個可滿足條件的前置工具重新執(zhí)行一次。上下文不足觸發(fā)上下文壓縮再重新發(fā)起調用。這個回退不是無限循環(huán)。每次回退都會寫入觸達軌跡的同一根trace_id如果連續(xù)兩次回退仍失敗就生成一條告警并移交人工。讓智能體自己掙扎一會兒不等于讓它無限掙扎。4.4 這套機制上線后我們迭代前后的對比為了直觀我貼一張當時記錄的數(shù)據指標改造前改造后一個季度多步任務完成率68%91%工具誤選率22%6%上下文截斷導致的結果錯誤每周約15起每周約2起用戶主動反饋“答案不對”每周12次每周4次數(shù)據可能有一點幸存者偏差因為優(yōu)化過程中同時也在換更穩(wěn)的模型版本但我比較確定的是路由和回退機制貢獻了其中大半的提升。因為它們解決的并不是偶發(fā)的推理幻覺而是結構性的夠不到問題。5. 三個讓我重新調整設計的實戰(zhàn)案例理論說得再好終歸要落到業(yè)務里。這里分享三個真實案例每個都逼著我改了Agent-Reach的某個設計。5.1 案例一客戶信息查詢功能始終讀不到擴展字段我們的CRM里有200多個自定義字段但智能體查詢客戶信息時只讀到客戶名稱、電話、地址這些基礎字段銷售團隊自定義的“跟進階段”“意向等級”始終為空。排查觸達軌跡后發(fā)現(xiàn)agent確實調用了crm.contact.read但返回體里根本沒帶擴展字段。原因不在Agent而在能力目錄注冊時這個工具只給了固定的字段schema沒把動態(tài)自定義字段的說明寫進去。后來我們在工具定義里增加了field_metadata每次調用前按需加載字段描述并把用戶高頻使用的自定義字段名做了同義詞映射比如“意向等級”同時匹配intent_level、lead_level、buying_phase。改完之后查詢返回字段完整率從47%提升到了96%。這個案例給我的教訓是工具觸達不只是找到接口還要讓工具描述跟業(yè)務語言對得上。5.2 案例二知識庫問答把引用的關鍵前提弄丟了知識庫問答是最隱蔽的一類故障。用戶問“應收賬款的賬期是多少”Agent從向量庫里召回三段內容其中一段說“本政策僅適用于預付客戶”但另兩段沒有這個限定條件。結果Agent把適用范圍擴大成了所有客戶。它不是不會讀而是召回的語境里關鍵的限定句被放在了一段它沒引用的文檔里。解決方式是給知識庫召回階段增加“前提保留”策略如果一條知識被標記為“限定性條款”向量召回時必須把它的上級標題和適用范圍一起帶進上下文。這相當于在做數(shù)據觸達時把上下文當成一張關系網而不是三塊獨立碎片。后來我們還做了一個更細的規(guī)則限定性條款所在的文本塊在壓縮時不允許被摘要掉必須原樣保留。5.3 案例三先查庫存還是先建預訂單順序選反了電商場景里Agent需要根據客戶要求創(chuàng)建預訂單。第一次上線時它經常跳過庫存校驗直接建單最后發(fā)現(xiàn)倉庫無貨整條流程回退客戶體驗很差。根因很簡單工具定義里沒有前置關系。后來我在能力目錄里給“創(chuàng)建預訂單”加了前置條件要求必須存在庫存查詢結果且可用量大于需求量。路由層會先把庫存查詢工具放進候選列表等條件滿足后再讓“創(chuàng)建預訂單”出現(xiàn)。這個改動只花了一個下午但把該場景的任務失敗率從15%降到了2%。這個案例解釋了為什么能力目錄必須是運行時的路由約束而不只是開發(fā)期的注釋文檔。5.4 這些案例背后的共同點三個案例分別對應了工具觸達、數(shù)據觸達、流程觸達。它們看起來是三類不同問題但修復手段都在同一個地方Agent-Reach的能力目錄和觸達軌跡。把“工具能不能被找到”“上下文能不能保留到位”“流程能不能按順序走”這三種能力集中管理而不是散落在每個Agent的提示詞里才是這套方案的真正價值。每次出問題我們幾乎都能在一張路由表或者一類觸達事件里定位到原因改完配置以后所有Agent都會自動生效而不需要一個一個去改提示詞。6. 生產環(huán)境里真正難纏的邊界問題權限、限流、上下文預算上線三個月后功能基本穩(wěn)定真正讓人頭疼的東西變成了一些邊界和成本問題。這一節(jié)寫三個最值得注意的。6.1 觸達范圍不等于授權范圍外部系統(tǒng)權限必須按用戶角色傳遞Agent-Reach 很容易讓人產生一個誤解既然智能體什么都能調那它就代表最高權限。這是非常危險的。如果路由表只按系統(tǒng)賬號鑒權而不把發(fā)起任務的用戶身份傳下去可能出現(xiàn)用戶A通過智能體查到了用戶B的訂單數(shù)據。我們的做法是在每次任務開始前生成一個權限上下文綁定發(fā)起人的角色和資源范圍。所有外部工具調用都必須攜帶這個上下文服務端再校驗一次工具級權限??偨Y成一句話觸達能力可以很寬但用戶的觸達邊界必須很窄。6.2 外部系統(tǒng)的限流閾值必須寫進能力目錄很多外部系統(tǒng)都有調用頻率限制但Agent發(fā)起調用是不認人的。同一批客服同時在線每人發(fā)一次查詢外部接口很容易瞬間被打爆。后來我們在能力目錄里給每個外部工具增加了一個元數(shù)據字段rate_limit同時接入到一個簡單的令牌桶服務。當某個工具的實時調用量達到閾值的80%時路由表會自動調低該工具的reach_score把請求優(yōu)先分給其它可承載的通道。這個設計不算多高級但它真正把“外部系統(tǒng)穩(wěn)定性”變成了智能體決策的一個因子而不是等著下游報警。以前我們總以為智能體只會被模型坑后來發(fā)現(xiàn)它也會被上游接口的脆弱性坑。把限流閾值納入路由決策以后這種坑至少從偶發(fā)變成了可控。6.3 上下文預算管理寧可少給也要保證最后幾步能走壓縮做久了我們意識到它不能解決所有問題因為總有些上下文是壓不掉的核心信息。所以Agent-Reach 里的任務在啟動時會做一個非常簡單的預算分配。比如給整個任務分配的上下文上限是12000個token前8輪最多用6000保留4000給工具返回和最終生成剩下2000作應急。這樣設計的原因是我們發(fā)現(xiàn)很多任務失敗發(fā)生在第9輪到第12輪原因不是前幾步信息不夠而是最后幾步沒有上下文空間了導致模型拿不到最終要匯總的數(shù)據。與其讓前幾步都詳盡發(fā)揮到極限不如把預算拆好保證流程收尾時有足夠的余量。這有點像跑長跑最累的不是起步而是終點前那一段。每次壓縮模塊被觸發(fā)時我們也會在觸達軌跡里打一個compressed標記用來統(tǒng)計哪些任務類型最容易出現(xiàn)預算緊張后續(xù)再針對性做精簡。6.4 升級Agent-Reach之后運維側的日常巡檢最后說下日常運維。我建議每天晚上跑一次觸達軌跡掃描任務把當天滿足以下條件的軌跡挑出來reach_grade不是pass或者帶有truncated標記任務步數(shù)超過15步但完成狀態(tài)是失敗同一種工具在同一時間段內連續(xù)出現(xiàn)3次失敗。把這些軌跡導出成一份簡短的報告第二天早上花十五分鐘掃一遍。這一步看起來不起眼但它是整個Agent-Reach體系里邊際收益最高的一環(huán)。因為系統(tǒng)不會自己變好只有你天天盯著觸達軌跡才能在前一天的問題還沒擴散成用戶投訴之前堵住它。多智能體項目的復雜度恰恰就藏在這些看似瑣碎的邊界細節(jié)里。