作中的觸達(dá)編排與治理實(shí)踐)
1. 為什么我做了 Agent-Reach多智能體協(xié)作里最容易被低估的“觸達(dá)”問(wèn)題先說(shuō)個(gè)真實(shí)場(chǎng)景。上個(gè)月我們內(nèi)部搞了一次多智能體聯(lián)調(diào)三個(gè) Agent 分別負(fù)責(zé)查庫(kù)存、生成報(bào)價(jià)、執(zhí)行訂單。單拿出來(lái)每一個(gè)都跑得好好的結(jié)果串起來(lái)之后第一個(gè) Agent 在對(duì)話歷史里翻到了三個(gè)月前一條已經(jīng)失效的折扣信息第二個(gè) Agent 拿著這個(gè)信息直接算錯(cuò)了價(jià)格第三個(gè) Agent 還想當(dāng)然地調(diào)了一個(gè)已經(jīng)被下線的接口。排查到最后問(wèn)題根本不在模型能力而是在 Agent 之間“怎么找到對(duì)方、怎么傳上下文、怎么控制調(diào)用邊界”這一整條鏈路。那段時(shí)間我每晚都在想同一個(gè)問(wèn)題能不能把“觸達(dá)”這件事做成一種可配置、可觀測(cè)、可管控的通用能力這就是 Agent-Reach 的起點(diǎn)。Agent-Reach 不是一個(gè)模型也不是某個(gè)具體的 Agent 應(yīng)用而是一套面向多智能體協(xié)作場(chǎng)景的觸達(dá)編排方案。它要管的事情很具體當(dāng)一個(gè) Agent 需要調(diào)用另一個(gè) Agent 的能力時(shí)消息該走哪條路由、上下文該帶多少、能調(diào)用哪些工具、不能越過(guò)哪些邊界、失敗了怎么重試、整條調(diào)用鏈怎么追蹤。簡(jiǎn)單說(shuō)單 Agent 的能力決定它“聰明不聰明”Agent-Reach 決定一群 Agent 在一起時(shí)“找不找得到人、傳不傳得對(duì)話、控不控得住邊界”。這篇文章主要聊三部分Agent-Reach 的設(shè)計(jì)思路、核心代碼實(shí)現(xiàn)、我在實(shí)際部署和聯(lián)調(diào)中踩過(guò)的坑。適合三類人看正在搭多 Agent 平臺(tái)的架構(gòu)師、做 AI 自動(dòng)化流程的開(kāi)發(fā)者以及因?yàn)?Agent 互相亂調(diào)而頭疼的運(yùn)維同學(xué)。如果你只是剛接觸多智能體也沒(méi)關(guān)系我會(huì)把里面的關(guān)鍵概念都用大白話拆開(kāi)講。1.1 一次差點(diǎn)讓項(xiàng)目翻車的聯(lián)調(diào)事故先把這個(gè)事故完整還原一下因?yàn)樗钦麄€(gè) Agent-Reach 的起源。我們當(dāng)時(shí)的架構(gòu)很簡(jiǎn)單用戶輸入先進(jìn)主 AgentRouter主 Agent 根據(jù)語(yǔ)義把請(qǐng)求分發(fā)給子 Agent子 Agent 再調(diào)用各自的工具??雌饋?lái)沒(méi)什么問(wèn)題實(shí)際跑起來(lái)全是洞。第一個(gè)洞是“調(diào)用風(fēng)暴”。某個(gè)子 Agent 在處理退款請(qǐng)求時(shí)因?yàn)槟貌坏阶銐虻挠脩粜畔⑦B續(xù)向主 Agent 發(fā)起了三次回傳請(qǐng)求主 Agent 又把它當(dāng)成新任務(wù)重新分發(fā)結(jié)果同一筆退款在幾分鐘內(nèi)被重復(fù)處理了兩遍。第二個(gè)洞是“上下文越界”。子 Agent 之間傳消息時(shí)默認(rèn)把整段對(duì)話歷史都帶上一個(gè)負(fù)責(zé)寫文案的 Agent 被塞進(jìn)了大量數(shù)據(jù)庫(kù)查詢?nèi)罩据敵鲑|(zhì)量明顯下降還白白浪費(fèi)了幾萬(wàn) token。第三個(gè)洞是“幽靈接口調(diào)用”。有 Agent 基于訓(xùn)練語(yǔ)料中的舊接口格式生成調(diào)用請(qǐng)求但真實(shí)環(huán)境里那個(gè)接口早就換路徑了失敗后它不報(bào)錯(cuò)反而嘗試了三種參數(shù)變體把日志刷得亂七八糟。這三個(gè)洞其實(shí)指向同一個(gè)根因我們沒(méi)有對(duì) Agent 之間的觸達(dá)行為做任何約束。每個(gè) Agent 都像一臺(tái)可以隨意踩油門的車但沒(méi)有車道、沒(méi)有紅綠燈、沒(méi)有限速牌。Agent-Reach 要補(bǔ)的就是基礎(chǔ)設(shè)施這一層。1.2 Agent-Reach 到底解決什么四種“觸達(dá)”我把多智能體協(xié)作里的核心問(wèn)題歸納成四種觸達(dá)Agent-Reach 的所有設(shè)計(jì)都是圍繞這四種觸達(dá)展開(kāi)的。觸達(dá)類型核心問(wèn)題失控時(shí)的典型表現(xiàn)路由觸達(dá)一個(gè) Agent 怎么找到有能力處理該請(qǐng)求的另一個(gè) Agent請(qǐng)求被錯(cuò)誤轉(zhuǎn)發(fā)、重復(fù)分發(fā)、找不到處理者上下文觸達(dá)消息在傳遞時(shí)應(yīng)該攜帶哪些有效信息上下文爆炸、無(wú)效信息污染、token 超限資源觸達(dá)Agent 能調(diào)用哪些工具、接口、數(shù)據(jù)庫(kù)調(diào)用已下線接口、越權(quán)訪問(wèn)、副作用不可控權(quán)限觸達(dá)不同 Agent 之間允許什么粒度的互相調(diào)用子 Agent 反向觸發(fā)高危操作、繞過(guò)審批鏈四種觸達(dá)不是平級(jí)的。資源觸達(dá)和權(quán)限觸達(dá)是底座路由觸達(dá)和上下文觸達(dá)是表現(xiàn)層。Agent-Reach 的做法是把這四類規(guī)則統(tǒng)一描述、統(tǒng)一執(zhí)行、統(tǒng)一觀測(cè)而不是像以前那樣散落在每個(gè) Agent 的 prompt 里碰運(yùn)氣。1.3 什么場(chǎng)景需要它什么人適合讀誠(chéng)實(shí)說(shuō)如果你的項(xiàng)目只有一個(gè) Agent、只調(diào)幾個(gè)外部 API那 Agent-Reach 是過(guò)度設(shè)計(jì)你直接寫個(gè)工具函數(shù)就行。但一旦你的系統(tǒng)開(kāi)始出現(xiàn)這些信號(hào)就該考慮它了主 Agent 的 prompt 越來(lái)越臃腫、子 Agent 之間互相調(diào)用沒(méi)有規(guī)律可循、一次用戶請(qǐng)求會(huì)觸發(fā)十幾跳內(nèi)部消息、出了問(wèn)題只能靠翻日志猜鏈路。具體來(lái)說(shuō)Agent-Reach 比較適合這幾類場(chǎng)景企業(yè)內(nèi)部的多 Agent 自動(dòng)化中臺(tái)、客服和銷售場(chǎng)景的 Agent 協(xié)作流、代碼生成與執(zhí)行沙箱需要有嚴(yán)格的資源邊界、以及由多個(gè)垂直領(lǐng)域 Agent 組成的內(nèi)容生產(chǎn)系統(tǒng)。讀這篇文章的人我假設(shè)你至少有一個(gè)跑通的多 Agent demo或者正在設(shè)計(jì)這樣的系統(tǒng)不然后面代碼部分的體感會(huì)弱一些。2. Agent-Reach 的架構(gòu)設(shè)計(jì)把觸達(dá)控制做成平臺(tái)能力Agent-Reach 在設(shè)計(jì)上堅(jiān)持一個(gè)原則觸達(dá)控制不應(yīng)該是每個(gè) Agent 自己的“自覺(jué)”而應(yīng)該下沉成平臺(tái)層的強(qiáng)制約束。好比一個(gè)公司的跨部門協(xié)作不能指望著每個(gè)員工都主動(dòng)守規(guī)矩得有統(tǒng)一的 OA 流程、權(quán)限系統(tǒng)和審計(jì)日志。所以 Agent-Reach 沒(méi)有做成 SDK 讓你在每個(gè) Agent 里手動(dòng)埋點(diǎn)而是做成了一個(gè)代理層所有 Agent 之間的通信都從它這里經(jīng)過(guò)。這樣做有兩個(gè)直接好處第一新接入一個(gè) Agent 不需要改它的內(nèi)部邏輯只需要聲明它“能干什么、不能干什么”第二所有觸達(dá)行為都會(huì)留下結(jié)構(gòu)化日志排查問(wèn)題的時(shí)候不用再靠猜。2.1 六個(gè)層級(jí)的分工Agent-Reach 的內(nèi)部結(jié)構(gòu)從下往上分成六層每一層只解決一種觸達(dá)問(wèn)題。接入層負(fù)責(zé)把不同類型的 AgentOpenAI 接口、自研模型、純規(guī)則腳本統(tǒng)一成標(biāo)準(zhǔn)的消息格式屏蔽底層差異。編排層負(fù)責(zé)處理外部進(jìn)來(lái)的用戶請(qǐng)求決定要不要拆分成子任務(wù)、按什么順序分發(fā)這是整個(gè)系統(tǒng)的入口。路由層根據(jù)路由表把消息送到目標(biāo) Agent路由表支持精確匹配、模糊匹配和兜底策略。觸達(dá)協(xié)議層這是 Agent-Reach 比較核心的一層。它定義了消息中允許攜帶什么元信息、上下文的裁剪規(guī)則、同步還是異步、以及超時(shí)和重試預(yù)算。策略層負(fù)責(zé)加載權(quán)限和資源邊界規(guī)則比如某個(gè) Agent 不允許調(diào)用支付接口某些外部工具只能在特定時(shí)間段訪問(wèn)??捎^測(cè)層把每一次觸達(dá)包括成功、失敗、被攔截記錄為一條 reach trace包含鏈路 ID、跳數(shù)、耗時(shí)、調(diào)用來(lái)源和目標(biāo)。這么分層的好處是你想改某一個(gè)維度的規(guī)則時(shí)不用動(dòng)其他層。比如要調(diào)整上下文裁剪策略只需要改觸達(dá)協(xié)議層的配置路由和權(quán)限完全不受影響。2.2 路由與覆蓋范圍白名單不是限制是安全網(wǎng)很多人剛接觸 Agent-Reach 時(shí)對(duì)“白名單路由”這個(gè)設(shè)計(jì)有點(diǎn)抵觸覺(jué)得把 Agent 之間的調(diào)用限制死了會(huì)影響靈活性。我的觀點(diǎn)恰恰相反在多智能體系統(tǒng)里自由度越高出事故的概率越大白名單看起來(lái)是限制其實(shí)是安全網(wǎng)。Agent-Reach 路由層不搞“Agent 自己決定找誰(shuí)”的分布式自主調(diào)用而是統(tǒng)一維護(hù)一張路由表。路由表里有三種條目精確路由task_type 完全匹配時(shí)走指定 Agent、模糊路由用關(guān)鍵詞權(quán)重匹配比如“退款”命中客服 Agent 的概率設(shè)為 0.9、拒絕路由明確禁止某些 task_type 出現(xiàn)在某些 Agent 之間比如“刪除數(shù)據(jù)庫(kù)”這種任務(wù)禁止從低權(quán)限 Agent 轉(zhuǎn)發(fā)。我見(jiàn)過(guò)很多失敗的多智能體項(xiàng)目問(wèn)題都出在“讓 Agent 自己找伙伴”。模型在開(kāi)放環(huán)境下做工具選擇已經(jīng)不夠穩(wěn)定再讓它做跨 Agent 路由選擇錯(cuò)誤率會(huì)疊加。所以 Agent-Reach 把路由決策從模型手里拿回來(lái)交給確定性的規(guī)則引擎。模型負(fù)責(zé)理解語(yǔ)義、提取參數(shù)Agent-Reach 負(fù)責(zé)決定把參數(shù)交給誰(shuí)。這和微服務(wù)架構(gòu)里的 API 網(wǎng)關(guān)是一個(gè)道理如果每個(gè)服務(wù)都自己去服務(wù)發(fā)現(xiàn)、自己決定調(diào)用誰(shuí)那系統(tǒng)離雪崩就不遠(yuǎn)了。2.3 觸達(dá)半徑上下文不是傳得越多越好上下文觸達(dá)是 Agent-Reach 里我個(gè)人認(rèn)為最有價(jià)值的設(shè)計(jì)。大多數(shù)多智能體系統(tǒng)傳上下文的方式是“整段復(fù)制”A 把用戶原始輸入 自己的思考 工具返回結(jié)果 歷史消息全部塞給 B。結(jié)果就是 B 收到的信息里至少有一半是噪聲模型要在噪聲里找關(guān)鍵信息既慢又容易出錯(cuò)。Agent-Reach 引入了一個(gè)叫“觸達(dá)半徑”的概念。每個(gè) Agent 在注冊(cè)時(shí)聲明自己的上下文偏好處理這個(gè)類型的任務(wù)最多需要多少 token、需要什么類型的上下文用戶摘要、工具返回、歷史決策、哪些信息明確不需要。當(dāng) A 要向 B 傳消息時(shí)觸達(dá)協(xié)議層會(huì)根據(jù) B 的偏好對(duì)上下文做一次裁剪和摘要而不是直接轉(zhuǎn)發(fā)原文。我舉一個(gè)具體例子。用戶要求“把上周的銷售數(shù)據(jù)整理成周報(bào)并發(fā)送給管理層”主 Agent 手里有整整 5 萬(wàn) token 的原始數(shù)據(jù)。如果直接傳給寫周報(bào)的 Agent成本高不說(shuō)模型還容易抓錯(cuò)重點(diǎn)。Agent-Reach 的做法是先讓一個(gè)輕量的聚合組件把數(shù)據(jù)加工成“核心結(jié)論 關(guān)鍵圖表 異常標(biāo)注”再把這份摘要傳給寫周報(bào)的 Agenttoken 直接降到 3000質(zhì)量反而更穩(wěn)定。后續(xù)第四節(jié)我會(huì)給出具體的實(shí)現(xiàn)代碼。3. 核心代碼實(shí)現(xiàn)路由策略、退避重試與上下文裁剪理論講得再多不如直接看代碼。Agent-Reach 的核心實(shí)現(xiàn)不算復(fù)雜它的聰明之處在于把策略與執(zhí)行分離。我用 Python 寫了最小的可運(yùn)行版本大家可以直接照著搭。3.1 路由策略配置用 DSL 表達(dá)觸達(dá)規(guī)則Agent-Reach 使用 YAML 作為策略描述語(yǔ)言原因很簡(jiǎn)單可讀性好非技術(shù)人員也能維護(hù)。下面是系統(tǒng)里一份典型的配置routes: - name: refund_flow match: type: keyword_weight keywords: [退款, 退貨, refund] min_score: 0.6 target: agent:refund_service fallback: agent:human_handoff allowed_hops: 3 - name: report_flow match: type: exact task_type: generate_weekly_report target: agent:report_writer required_context: template: summary max_tokens: 4096 include_fields: [conclusion, key_metrics, anomalies] reach_policy: default_timeout_ms: 5000 retry_budget: 3 backoff_base_ms: 200 max_parallel_calls: 8 blocklist: - from: agent:report_writer to: agent:payment_executor reason: 寫周報(bào)的 Agent 不允許觸發(fā)支付操作路由配置里有個(gè)關(guān)鍵的allowed_hops字段這是防循環(huán)觸達(dá)的核心。每次消息經(jīng)過(guò)一個(gè) Agent跳數(shù)就加一超過(guò)上限直接丟棄并告警。required_context則聲明了上下文觸達(dá)的要求目標(biāo) Agent 不需要原始數(shù)據(jù)只需要summary模板、最多 4096 token、只要結(jié)論和異常字段。這部分的設(shè)計(jì)思路是把“該找誰(shuí)”和“該帶什么”都變成可聲明的配置而不是寫死在代碼里。這樣即便系統(tǒng)里有幾十個(gè) Agent運(yùn)維同學(xué)也能在開(kāi)會(huì)時(shí)打開(kāi) YAML 直接討論某個(gè)流程該不該加白名單而不是去翻代碼。3.2 路由執(zhí)行器匹配、轉(zhuǎn)發(fā)與退避重試配置是靜態(tài)的真正干活的是路由執(zhí)行器。核心代碼如下import asyncio import random import yaml from dataclasses import dataclass from typing import Any, Optional dataclass class ReachContext: trace_id: str source: str target: str hops: int payload: dict policy: dict class ReachRouter: def __init__(self, config_path: str): with open(config_path, r, encodingutf-8) as f: self.config yaml.safe_load(f) self.routes {r[name]: r for r in self.config[routes]} self.policy self.config[reach_policy] def match_route(self, task_type: str, keywords: list[str]) - Optional[dict]: for route in self.routes.values(): matcher route[match] if matcher[type] exact: if task_type matcher[task_type]: return route elif matcher[type] keyword_weight: score sum(1 for kw in matcher[keywords] if kw in keywords) score score / len(matcher[keywords]) if score matcher.get(min_score, 0.5): return route return None async def forward(self, ctx: ReachContext, agent_client: Any) - dict: if ctx.hops ctx.policy[max_hops]: raise RuntimeError(freach hops exceeded: {ctx.trace_id}) max_retry ctx.policy[retry_budget] base_backoff ctx.policy[backoff_base_ms] for attempt in range(max_retry 1): try: result await agent_client.invoke(ctx.payload) ctx.payload[result] result return ctx.payload except Exception as e: if attempt max_retry: raise delay base_backoff * (2 ** attempt) random.uniform(0, 50) await asyncio.sleep(delay / 1000) return {}這里有兩個(gè)值得說(shuō)明的設(shè)計(jì)點(diǎn)。第一重試不是無(wú)限重試retry_budget等于 3 時(shí)總共最多嘗試 4 次而且退避時(shí)間按指數(shù)增長(zhǎng)200ms、400ms、800ms 再加一點(diǎn)隨機(jī)抖動(dòng)。隨機(jī)抖動(dòng)是為了避免多個(gè) Agent 同時(shí)失敗后在同一個(gè)時(shí)間點(diǎn)集體重試把系統(tǒng)打出一個(gè)新的峰值。第二重試只覆蓋“調(diào)用 Agent 過(guò)程”的異常如果上一次調(diào)用已經(jīng)成功執(zhí)行但響應(yīng)丟失重試可能會(huì)造成重復(fù)操作。這個(gè)問(wèn)題我在第五節(jié)會(huì)專門講怎么用冪等鍵處理。3.3 上下文裁剪器按觸達(dá)半徑壓縮信息再來(lái)看上下文裁剪器的實(shí)現(xiàn)。很多剛接觸 Agent-Reach 的讀者想當(dāng)然地以為“裁剪就是把文本截?cái)唷逼鋵?shí)不是最有效的裁剪是按照接收方 Agent 的偏好做結(jié)構(gòu)化抽取。class ContextTrimmer: def __init__(self, token_estimatorNone): if token_estimator is None: self.token_estimator lambda text: len(text) // 3 else: self.token_estimator token_estimator def trim(self, raw_context: dict, route: dict) - dict: required route.get(required_context, {}) template required.get(template, full) max_tokens required.get(max_tokens, 4096) include_fields required.get(include_fields, []) if template summary: # 只提取關(guān)鍵字段priority 字段在前 trimmed {} for field in include_fields: if field in raw_context: trimmed[field] raw_context[field] # 如果還是超出預(yù)算則對(duì)最長(zhǎng)的字段做遞歸摘要 while self._estimate_tokens(trimmed) max_tokens: longest_key max(trimmed, keylambda k: self._estimate_tokens(trimmed[k])) trimmed[longest_key] self._summarize(trimmed[longest_key]) trimmed[_trim_note] auto-trimmed by Agent-Reach return trimmed # full 模板原樣傳出但會(huì)記錄警告 if self._estimate_tokens(raw_context) max_tokens: return { **raw_context, _trim_warning: fcontext exceeds {max_tokens} tokens, } return raw_context def _estimate_tokens(self, data): if isinstance(data, str): return self.token_estimator(data) return sum(self._estimate_tokens(v) for v in data.values()) def _summarize(self, text: str) - str: # 生產(chǎn)環(huán)境可以接 LLM 或抽取式摘要 if len(text) 500: return text return text[:500] ...[truncated by Agent-Reach]_summarize里我故意留了簡(jiǎn)化的截?cái)噙壿媽?shí)際生產(chǎn)環(huán)境可以替換成 LLM 摘要調(diào)用。不過(guò)要注意摘要本身也是有成本的如果每一次消息傳遞都調(diào)一次 LLM 做摘要整體延遲和費(fèi)用都會(huì)上去。建議的做法是對(duì)高頻觸達(dá)的 Agent預(yù)先把原始數(shù)據(jù)處理成多級(jí)摘要緩存按需取用不同精度的版本。3.4 可觀測(cè)性reach trace 怎么記才有效最后是觸達(dá)鏈路的可觀測(cè)性。Agent-Reach 里每一條消息都會(huì)生成一個(gè)結(jié)構(gòu)化的 trace 日志字段如下{ reach_trace_id: rt_8f2a91c, source_agent: main_router, target_agent: refund_service, route_name: refund_flow, hops: 2, context_tokens_before: 48200, context_tokens_after: 3800, decision: allowed, duration_ms: 1240, retry_count: 1 }這個(gè)日志的價(jià)值在于它能直接回答“一個(gè)問(wèn)題卡在哪一跳”。比如你看到duration_ms在某條路由上突然飆升同時(shí)retry_count從 0 變成 3基本可以斷定目標(biāo) Agent 出現(xiàn)了性能問(wèn)題。又比如context_tokens_before和after的差距很小說(shuō)明觸達(dá)協(xié)議層沒(méi)有正確裁剪需要檢查路由配置里的required_context是否被目標(biāo) Agent 正確聲明。4. 從零到一一次完整的 Agent-Reach 聯(lián)調(diào)實(shí)錄理論代碼都有了接下來(lái)走一遍實(shí)際聯(lián)調(diào)過(guò)程。我盡量按真實(shí)操作的順序來(lái)包括配置、啟動(dòng)、壓測(cè)、調(diào)參方便你照著復(fù)現(xiàn)。4.1 最小拓?fù)渑c角色劃分這次聯(lián)調(diào)我用了三個(gè) Agent 組成的最小系統(tǒng)場(chǎng)景是“用戶申請(qǐng)退款系統(tǒng)判斷是否符合政策符合則退款不符合則轉(zhuǎn)人工”。main_router主路由 Agent接收用戶輸入負(fù)責(zé)語(yǔ)義識(shí)別與任務(wù)分發(fā)。refund_checker退款校驗(yàn) Agent查詢訂單狀態(tài)、退款政策輸出是否可退以及退款金額。payment_executor支付執(zhí)行 Agent負(fù)責(zé)實(shí)際發(fā)起退款轉(zhuǎn)賬屬于高風(fēng)險(xiǎn) Agent必須嚴(yán)格保護(hù)。按照第五節(jié)的權(quán)限原則payment_executor只允許被refund_checker在特定條件下調(diào)用主路由不能直接觸達(dá)它。這個(gè)限制寫在路由配置里即便模型胡說(shuō)八道想跨級(jí)調(diào)用Agent-Reach 也會(huì)在策略層攔下來(lái)。4.2 完整配置與啟動(dòng)流程routes: - name: parse_refund_request match: type: keyword_weight keywords: [退款, 退錢, refund] min_score: 0.7 target: agent:refund_checker allowed_hops: 3 - name: execute_refund match: type: exact task_type: refund_approved target: agent:payment_executor allowed_hops: 2 # 必須具備 refund_checker 的簽名才允許執(zhí)行 required_claims: - role: refund_checker action: approve_refund reach_policy: max_hops: 5 default_timeout_ms: 3000 retry_budget: 2 backoff_base_ms: 150 blocklist: - from: agent:main_router to: agent:payment_executor reason: 主路由不允許直接觸達(dá)支付執(zhí)行器啟動(dòng)流程很簡(jiǎn)單先啟動(dòng)三個(gè) Agent 服務(wù)再啟動(dòng) Agent-Reach 代理層然后把三個(gè) Agent 的注冊(cè)信息名稱、能力描述、上下文偏好、回調(diào)地址寫進(jìn) Agent-Reach 的服務(wù)目錄。聯(lián)調(diào)時(shí)所有的消息都不直接走 Agent 之間的點(diǎn)對(duì)點(diǎn)連接而是統(tǒng)一發(fā)給 Agent-Reach 的入口由它做路由分發(fā)。這樣一個(gè)小時(shí)后整個(gè)系統(tǒng)的觸達(dá)路徑就全部處于可觀測(cè)狀態(tài)了。4.3 壓測(cè)與參數(shù)調(diào)整記錄我做了兩輪壓測(cè)第一輪用默認(rèn)參數(shù)第二輪根據(jù)結(jié)果調(diào)參。測(cè)試場(chǎng)景是模擬 1000 個(gè)退款請(qǐng)求觀察三類指標(biāo)正確路由率、平均端到端耗時(shí)、越權(quán)攔截次數(shù)。參數(shù)版本正確路由率P95 耗時(shí)被攔截的越權(quán)調(diào)用上下文裁剪后平均 token默認(rèn)超時(shí) 5s重試 392.4%4120ms374200調(diào)參后超時(shí) 3s重試 297.8%1860ms413850第一輪正確路由率不高的原因很有代表性refund_checker在處理部分請(qǐng)求時(shí)響應(yīng)超過(guò)了 3 秒觸發(fā)了重試但重試后的重復(fù)請(qǐng)求又把隊(duì)列堵住形成連鎖延遲。把超時(shí)從 5 秒降到 3 秒、重試預(yù)算從 3 降到 2 之后反而因?yàn)橄到y(tǒng)更早地放棄了“慢請(qǐng)求”整體吞吐反而上去了。越權(quán)攔截次數(shù)從 37 升到 41看起來(lái)變差了其實(shí)不是。調(diào)參前主路由有更多機(jī)會(huì)直接觸達(dá)支付執(zhí)行器部分請(qǐng)求因?yàn)槁酚苫靵y在早期就失敗了壓根輪不到策略層攔截。調(diào)參后路由更準(zhǔn)確真正到達(dá)支付環(huán)節(jié)的請(qǐng)求更多被攔截的絕對(duì)次數(shù)自然上升。這里要看的指標(biāo)是“攔截率”實(shí)際上從 3.7% 降到了 4.1% 的假象背后高風(fēng)險(xiǎn)操作總數(shù)從 1000 次降至 980 次攔截率實(shí)際是上升的。所以排查問(wèn)題時(shí)一定要結(jié)合總量看比例不能只看絕對(duì)值。5. 線上踩過(guò)的坑常見(jiàn)問(wèn)題與排查技巧說(shuō)到底Agent-Reach 不是裝了就能一勞永逸。上線兩個(gè)月我這邊遇到過(guò)不少奇怪的問(wèn)題挑幾個(gè)典型的說(shuō)說(shuō)順帶整理成速查表方便你遇到同類問(wèn)題時(shí)快速定位。5.1 Agent 循環(huán)調(diào)用與幽靈消息這是多 Agent 系統(tǒng)里最經(jīng)典的事故?,F(xiàn)象是某條消息在幾個(gè) Agent 之間來(lái)回傳遞日志里全是“轉(zhuǎn)發(fā)成功”但沒(méi)有任何一個(gè) Agent 真正在做有效輸出直到allowed_hops耗盡才被攔截。我遇到的具體場(chǎng)景是主路由把一個(gè)退款請(qǐng)求轉(zhuǎn)給refund_checkerrefund_checker覺(jué)得信息不足把消息回傳給主路由補(bǔ)充請(qǐng)求主路由又沒(méi)有把消息標(biāo)記為“來(lái)自子 Agent 的回傳”而是當(dāng)成全新任務(wù)重新分發(fā)于是又轉(zhuǎn)回refund_checker形成死循環(huán)。解決辦法有兩層第一層是嚴(yán)格規(guī)范消息類型回傳消息必須帶parent_trace_id和callback_typerequest_more_info路由層遇到這類消息只做信息合并不再走分發(fā)邏輯第二層是保留allowed_hops限制讓系統(tǒng)在失控時(shí)能夠兜底熔斷。5.2 上下文越過(guò)觸達(dá)半徑模型開(kāi)始“拼接幻覺(jué)”有一次我發(fā)現(xiàn)負(fù)責(zé)生成日?qǐng)?bào)的 Agent 在輸出里混進(jìn)了一條不該出現(xiàn)的舊訂單數(shù)據(jù)。追查 trace 后發(fā)現(xiàn)主路由把整段用戶對(duì)話歷史原樣傳給了日?qǐng)?bào) Agent其中包含三個(gè)月前的一次訂單查詢記錄日?qǐng)?bào) Agent 把這個(gè)歷史信息當(dāng)成了本周數(shù)據(jù)直接寫進(jìn)了總結(jié)。這不是模型的問(wèn)題是上下文觸達(dá)失控。解決方式是嚴(yán)格執(zhí)行required_context模板并且在模板里聲明include_fields白名單沒(méi)有出現(xiàn)在白名單里的字段一律丟棄。同時(shí)建議在消息里加一個(gè)_trim_note標(biāo)記我在代碼里就是這么做的讓下游 Agent 知道自己收到的是裁剪后的摘要而不是完整原始數(shù)據(jù)。這個(gè)標(biāo)記雖然不起眼但能顯著減少模型把摘要當(dāng)成完整數(shù)據(jù)的概率。5.3 權(quán)限邊界失效的三種典型路徑第一種是配置覆蓋。后加的路由規(guī)則把payment_executor的required_claims漏掉了等于開(kāi)了個(gè)后門。我的建議是每次配置變更都跑一遍靜態(tài)校驗(yàn)?zāi)_本檢查每個(gè)高風(fēng)險(xiǎn) Agent 是否仍然有完整的權(quán)限聲明。第二種是錯(cuò)誤地在 Agent 的 prompt 里寫“你可以調(diào)用任何工具”模型在生成函數(shù)調(diào)用時(shí)繞過(guò)了 Agent-Reach 的 API直接直連底層 HTTP 接口。這個(gè)問(wèn)題得靠網(wǎng)絡(luò)層封禁解決Agent-Reach 的策略層管不到繞過(guò)它的流量。第三種是回傳消息里攜帶了額外的工具調(diào)用意圖目標(biāo) Agent 在解析時(shí)把這個(gè)意圖當(dāng)成用戶指令執(zhí)行了。解決方案是解析消息時(shí)只能讀取payload里白名單字段其他字段一律忽略。5.4 排查工具箱一頁(yè)紙速查表癥狀可能原因排查步驟修復(fù)方案請(qǐng)求在多個(gè) Agent 之間反復(fù)橫跳回傳消息被當(dāng)成新任務(wù)分發(fā)查 reach trace 的 hops 和 route_name規(guī)范 callback_type分發(fā)表里增加回傳攔截下游 Agent 輸出包含無(wú)關(guān)歷史信息上下文未按模板裁剪對(duì)比 context_tokens_before 和 after配置 required_context 白名單某條路由耗時(shí)突然變長(zhǎng)目標(biāo) Agent 響應(yīng)慢觸發(fā)多次重試看 retry_count 與 duration_ms縮短超時(shí)時(shí)間降低重試預(yù)算高風(fēng)險(xiǎn) Agent 被異常調(diào)用配置缺失或直連繞過(guò)查攔截日志和網(wǎng)絡(luò)訪問(wèn)日志補(bǔ) claims網(wǎng)絡(luò)層加白名單 ACL重復(fù)執(zhí)行業(yè)務(wù)操作首次調(diào)用成功但響應(yīng)丟失重試導(dǎo)致重復(fù)檢查業(yè)務(wù)側(cè)對(duì)賬日志引入冪等鍵Agent-Reach 重試時(shí)攜帶相同冪等 ID老實(shí)說(shuō)Agent-Reach 這套方案并不驚艷它的價(jià)值在于把很多人默認(rèn)“靠模型自覺(jué)”的事情變成了一套強(qiáng)制機(jī)制。我實(shí)際部署中體會(huì)最深的有三點(diǎn)第一路由決策一定要從模型手里拿回來(lái)模型理解語(yǔ)義就夠了別讓它做網(wǎng)絡(luò)拓?fù)鋵用娴倪x擇第二上下文裁剪的收益比想象中還要大不僅省錢還直接提升了下游 Agent 的準(zhǔn)確率第三可觀測(cè)性是一切排障的前提沒(méi)有 reach trace上面這些問(wèn)題每一個(gè)都要靠瞎猜浪費(fèi)大半天。另外還有一個(gè)非常實(shí)用的建議如果你剛開(kāi)始搭不要一上來(lái)就追求全自動(dòng)的 Agent 自主協(xié)作先把路由、權(quán)限、上下文模板這些確定性部分搞扎實(shí)再逐步放開(kāi)自由度。這個(gè)順序走下來(lái)系統(tǒng)穩(wěn)定性會(huì)肉眼可見(jiàn)地提升。