議與Nacos實戰(zhàn):構(gòu)建多Agent協(xié)作互通層)
1. 互通層為什么單機跑通的 Agent一上線就失聯(lián)先說個背景。上一期我們把單個 Agent 的構(gòu)建、記憶管理和工具調(diào)用都盤了一遍很多朋友照著做完之后本地測試一切正常結(jié)果一放到多進程、多服務(wù)的環(huán)境里就出問題A 服務(wù)的 Agent 要調(diào)用 B 服務(wù)的 Agent兩邊明明都在跑卻互相找不到或者說找到了但調(diào)不通。這個問題幾乎出現(xiàn)在每一個從單體走向分布式的 Agent 項目里本質(zhì)就是互通層沒做。AgentScope Java 在這塊的設(shè)計思路很直接用 A2A 協(xié)議把 Agent 的能力暴露成標(biāo)準(zhǔn)服務(wù)再用 Nacos 做這些服務(wù)的注冊與發(fā)現(xiàn)讓 Agent 之間像調(diào)用普通微服務(wù)一樣互相協(xié)作。很多剛開始接觸這個概念的同學(xué)會問我已經(jīng)有 HTTP 接口了為什么還要搞 A2A直接調(diào)接口不就行了答案是如果你只是調(diào)用別人的寫死接口那叫系統(tǒng)集成不叫 Agent 協(xié)作。A2A 解決的是動態(tài)發(fā)現(xiàn)能力、動態(tài)編排任務(wù)、跨實現(xiàn)框架通信這幾件事。你的 Agent 可能用的是 AgentScope Java對方的 Agent 可能跑在別的語言框架上你們之間沒有約定好 AgentCard、Task 狀態(tài)機、Message 結(jié)構(gòu)就只能靠人肉對齊參數(shù)改一版崩一版。這篇文章就圍繞互通層展開重點講三件事A2A 協(xié)議的核心機制、Nacos 接線的完整鏈路、以及我在實測中踩過的坑。內(nèi)容是基于 AgentScope Java 當(dāng)前主線的 A2A 模塊經(jīng)驗部分示例代碼做了簡化類名和包路徑以你實際引入的版本為準(zhǔn)但思路和排查路徑是通用的。2. 把 Agent 說出去A2A 協(xié)議與 AgentCard 的核心機制2.1 A2A 不是消息隊列是一套卡口明確的遠(yuǎn)程調(diào)用協(xié)議很多人第一次看 A2A 文檔會誤以為它是類似 MQ 的異步消息系統(tǒng)其實不對。A2AAgent2Agent是一個基于 JSON-RPC 的同步/異步混合協(xié)議核心特征是每個 Agent 暴露一組標(biāo)準(zhǔn)端點如message/send、task/get、task/cancel所有請求響應(yīng)都走 HTTP POST JSON。一次任務(wù)Task有完整的生命周期狀態(tài)submitted、working、input-required、completed、canceled、failed。消息內(nèi)容被拆成 Part也就是多模態(tài)內(nèi)容片段可以同時包含文本、URL、結(jié)構(gòu)化數(shù)據(jù)。舉個例子。假設(shè)你有一個訂單售后處理 Agent另一個團隊用別的框架寫了一個倉儲庫存 Agent。兩邊要協(xié)作A2A 的做法是售后 Agent 收到用戶訴求后需要查庫存于是它構(gòu)造一個 Task把用戶的訴求描述、相關(guān)訂單 ID、需要查詢的商品清單作為 Message 發(fā)出去通過 A2A 端點發(fā)給庫存 Agent。庫存 Agent 處理完成后返回一個completed狀態(tài)的任務(wù)帶上庫存結(jié)果 Part。整個過程是標(biāo)準(zhǔn)化的兩邊都不需要關(guān)心對方的內(nèi)部實現(xiàn)。這種設(shè)計的好處在于協(xié)議卡在邊界上邊界以內(nèi)的實現(xiàn)隨便你折騰。這和微服務(wù)之間走 REST 是同一個邏輯只不過 A2A 把Agent 之間的對話抽象成了可以編排、可恢復(fù)、帶狀態(tài)機的任務(wù)而不是簡單的請求響應(yīng)。2.2 AgentCard 里該放什么不該放什么A2A 協(xié)議里有一個經(jīng)常被一筆帶過但實際上很關(guān)鍵的組件AgentCard。它本質(zhì)是一個 JSON 描述文件通常放在服務(wù)的/.well-known/agent.json路徑下用于告訴調(diào)用方這個 Agent 是誰、能干什么、怎么調(diào)。我在項目里維護過三個 Agent 的卡片第一版把能干什么寫得特別詳細(xì)幾乎把每個工具函數(shù)的參數(shù)都列進去了結(jié)果維護成本極高改一個字段就要同步更新卡片。后來我把卡片收斂成下面這個結(jié)構(gòu){ name: order-after-sale-agent, description: 處理訂單售后訴求可查詢訂單狀態(tài)、發(fā)起退款、生成工單, url: https://agent-gateway.example.com/a2b, version: 1.0.0, capabilities: { functionCalling: true, streaming: false, multiModal: false }, skills: [ { name: query_order, description: 根據(jù)訂單號查詢訂單當(dāng)前狀態(tài), parameters: { type: object, properties: { orderId: { type: string } }, required: [orderId] } } ] }一個核心教訓(xùn)是AgentCard 是發(fā)現(xiàn)用的不是文檔用的。它要讓遠(yuǎn)程 Agent 在幾毫秒內(nèi)判斷你能不能滿足我的訴求、我該不該把任務(wù)發(fā)給你所以description和skills里的摘要信息一定要寫清楚邊界。比如你的 Agent 只能處理國內(nèi)訂單就在 description 里直接寫prompt僅支持國內(nèi)訂單海外訂單請轉(zhuǎn)人工省得調(diào)用方把任務(wù)打過來再被打回去來回浪費一次遠(yuǎn)程調(diào)用。另一個容易忽略的點是url字段要和實際部署環(huán)境對齊。本地聯(lián)調(diào)時可以是 localhost 地址一旦接到 Nacos 上這個 url 就要改成網(wǎng)關(guān)或服務(wù)實例的對外地址。否則就會出現(xiàn)Nacos 里能看到服務(wù)Agent 也選擇了實例但發(fā)請求時發(fā)現(xiàn) AgentCard 里的 url 是別人舊環(huán)境的地址直接 404。3. Nacos 接線Agent 服務(wù)如何被動態(tài)發(fā)現(xiàn)3.1 服務(wù)注冊與發(fā)現(xiàn)的完整鏈路Nacos 在整套接線里扮演的角色是服務(wù)注冊中心 配置中心。Agent 服務(wù)啟動后會把自身的 IP、端口、健康狀態(tài)、元數(shù)據(jù)注冊到 Nacos調(diào)用方不再需要寫死對端地址而是通過服務(wù)名去 Nacos 拉取實例列表從中選一個健康實例發(fā)起 A2A 調(diào)用。接線鏈路可以拆成五步Agent 服務(wù)啟動讀取配置向 Nacos 注冊自身實例信息。Nacos 注冊中心維護服務(wù)名到實例列表的映射并通過心跳機制感知實例存活狀態(tài)。調(diào)用方從 Nacos 查詢目標(biāo)服務(wù)名拿到一個或多個健康實例。調(diào)用方構(gòu)造 A2A 請求向目標(biāo)實例的 A2A 端點發(fā)送 HTTP POST。目標(biāo) Agent 處理請求返回結(jié)果任務(wù)狀態(tài)流轉(zhuǎn)更新。這個鏈路看著簡單但實際接線時容易被三個細(xì)節(jié)卡住服務(wù)名命名規(guī)范、實例元數(shù)據(jù)設(shè)計、健康檢查配置。先講服務(wù)名。我建議按照業(yè)務(wù)域-角色-用途的格式命名比如agent-order-service、agent-logistics-service不要用agent1、agent2這種沒有任何語義的名字。因為在多 Agent 協(xié)作場景里服務(wù)名就是 Agent 的電話號碼別人通過名字找到你名字起得清晰能省很多溝通成本。再講元數(shù)據(jù)。Nacos 注冊實例時可以攜帶自定義 metadata這是一個很容易被浪費掉的字段。你可以把 AgentCard 的關(guān)鍵信息直接塞進 metadata比如agentName、version、capabilities。這樣調(diào)用方在拉取實例列表時不需要先發(fā)一次 HTTP 請求去讀 AgentCard直接通過元數(shù)據(jù)就能做簡單過濾。這在高頻調(diào)用場景下對性能和穩(wěn)定性都有幫助。健康檢查配置同樣值得上心。默認(rèn)的心跳機制依賴服務(wù)實例主動上報如果 Agent 的 A2A 端點所在端口和 Nacos 健康檢查端口配置不一致會出現(xiàn)服務(wù)注冊成功但始終不健康的詭異情況。我在調(diào)試中就遇到過這個問題客戶端拉到的實例一直標(biāo)記為 unhealthy排查了半天才發(fā)現(xiàn)是spring.cloud.nacos.discovery.port和實際暴露 A2A 端點的端口沒對齊。3.2 metadata 設(shè)計讓調(diào)用方一眼看懂 Agent 能力接線的難點往往不是注冊上去而是讓對方快速知道該不該調(diào)用你。我推薦在 Nacos 實例元數(shù)據(jù)里固定維護以下幾項metadata 字段示例值作用agentNameorder-after-sale-agentAgent 的唯一標(biāo)識服務(wù)名前綴version1.0.0能力版本用于灰度兼容owneraftersale-team團隊負(fù)責(zé)人方便排查capabilitiesfunctionCalling,streaming能力標(biāo)簽用于快速過濾heartbeatInterval5000心跳間隔幫助調(diào)用方預(yù)判故障這里有一個經(jīng)驗metadata 里的能力和 AgentCard 里的能力必須保持同步否則調(diào)用方根據(jù) metadata 判斷你支持 streaming結(jié)果發(fā)來流式請求發(fā)現(xiàn)不支持任務(wù)直接失敗。我目前的處理方式是做一個啟動時的配置校驗把 metadata 和本地 AgentCard 做一次比對不一致就打印告警日志寧可啟動失敗也不要帶病上線。這個設(shè)計還有一個額外收益當(dāng)你想做 Agent 分組或灰度時metadata 直接作為路由維度。比如新版本 Agent 上線只注冊 10% 的實例metadata 標(biāo)version2.0.0-canary調(diào)用方通過 Nacos 的權(quán)重策略就能把部分任務(wù)灰度到新版本上不需要改動任何 A2A 調(diào)用代碼。4. 端到端接線實操AgentScope Java Spring Boot Nacos4.1 環(huán)境準(zhǔn)備與依賴配置這一部分我直接給一份可以照抄的依賴清單。項目的基本框架是 Spring Boot 3.x AgentScope Java 當(dāng)前主線版本 Nacos 服務(wù)端 2.x。dependency groupIdcom.alibaba.agentscope/groupId artifactIdagentscope-agent/artifactId version${agentscope.version}/version /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId version${alibaba.cloud.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependencyNacos 服務(wù)端我建議直接用 Docker 起一個做開發(fā)驗證docker run --name nacos-server -p 8848:8848 -p 9848:9848 \ -e MODEstandalone \ nacos/nacos-server:v2.3.0然后配置 application.yml注意namespace和group一定要提前定好。不同環(huán)境的 Nacos 命名空間必須隔離不然測試環(huán)境的 Agent 和生產(chǎn)環(huán)境的 Agent 互相串線整個協(xié)作就亂了。spring: application: name: order-after-sale-agent cloud: nacos: discovery: server-addr: 127.0.0.1:8848 namespace: dev group: AGENT_GROUP register-enabled: true4.2 實現(xiàn)一個業(yè)務(wù) Agent 并暴露為 A2A 端點這里用一個訂單查詢 Agent做例子。在 AgentScope Java 里Agent 核心邏輯是重寫消息處理方法把輸入 Message 解析成業(yè)務(wù)需求執(zhí)行對應(yīng)的工具函數(shù)再封裝成輸出 Message 返回。Component public class OrderQueryAgent extends AgentBase { private final OrderService orderService; public OrderQueryAgent(OrderService orderService) { this.orderService orderService; } Override protected Message invoke(Message input) { // 解析入?yún)⑦@里簡化成只處理文本文本內(nèi)容 String text input.getTextContent(); if (text null || text.isBlank()) { return Message.textMessage( 參數(shù)不完整需要提供訂單號, order-query-agent ); } // 調(diào)用業(yè)務(wù)服務(wù)查詢訂單 OrderInfo info orderService.queryByOrderId(text.trim()); if (info null) { return Message.textMessage(訂單不存在: text, order-query-agent); } // 把查詢結(jié)果封裝為結(jié)構(gòu)化Part返回 return Message.textMessage( 訂單狀態(tài): info.getStatus() ,金額: info.getAmount(), order-query-agent ); } }有了 AgentBean之后最核心的一步是把它暴露成 A2A 遠(yuǎn)程端點。AgentScope Java 默認(rèn)提供的服務(wù)端適配模塊可以幫我們省去大量協(xié)議樣板代碼你只需要把 Agent 實例掛載到路由上框架會自動完成協(xié)議轉(zhuǎn)換。RestController RequestMapping(/a2b) public class A2AController { private final A2AServerManager a2aServerManager; public A2AController(A2AServerManager a2aServerManager) { this.a2aServerManager a2aServerManager; } PostMapping(/message/send) public Task sendMessage(RequestBody IncomingMessage message) { return a2aServerManager.sendMessage(message); } PostMapping(/task/get) public Task getTask(RequestBody TaskQuery query) { return a2aServerManager.getTask(query.getTaskId()); } GetMapping(/.well-known/agent.json) public AgentCard getAgentCard() { return a2aServerManager.generateAgentCard(); } }需要注意不同版本的 AgentScope Java 在端點路徑上可能略微不同但/.well-known/agent.json和message/send是協(xié)議層的約定盡量保持一致降低對接成本。如果你是自己手寫 Controller建議先別急著做復(fù)雜邏輯直接把 AgentCard 和消息收發(fā)跑通再逐步加功能。4.3 編寫客戶端通過 Nacos 找到 Agent 并發(fā)起任務(wù)服務(wù)端暴露好之后客戶端的關(guān)鍵是從 Nacos 拿實例列表然后選中一個實例發(fā)起 A2A 調(diào)用。這一步的選實例邏輯很要命。最簡單可靠的策略是先過濾健康實例再按權(quán)重隨機選擇一個。Component public class AgentServiceInvoker { private final NamingService namingService; public AgentServiceInvoker(NamingService namingService) { this.namingService namingService; } public String invokeAgent(String serviceName, String messageText) throws Exception { // 1. 從 Nacos 拉取健康實例 ListInstance instances namingService.selectInstances(serviceName, true); if (instances null || instances.isEmpty()) { throw new RuntimeException(沒有可用Agent實例: serviceName); } // 2. 簡單的隨機選擇生產(chǎn)環(huán)境可以換成帶權(quán)負(fù)載 Instance instance instances.get(ThreadLocalRandom.current().nextInt(instances.size())); String url http:// instance.getIp() : instance.getPort() /a2b; // 3. 構(gòu)造 A2A 請求體 MapString, Object body new HashMap(); body.put(taskId, UUID.randomUUID().toString()); body.put(localAgentId, user-service); body.put(remoteAgentId, serviceName); body.put(message, Map.of( role, user, content, messageText )); // 4. 發(fā)起 JSON-RPC 調(diào)用 RestTemplate restTemplate new RestTemplate(); ResponseEntityMap resp restTemplate.postForEntity(url /message/send, body, Map.class); return resp.getBody().toString(); } }這段代碼最核心的價值不在邏輯而在于它揭示了 Agent 協(xié)作和普通 RPC 調(diào)用的關(guān)系。A2A 調(diào)用本質(zhì)上就是一次 HTTP 請求不要把它想得過于魔幻。只是它的請求體和響應(yīng)體要符合協(xié)議規(guī)范任務(wù)狀態(tài)要按協(xié)議狀態(tài)機走。4.4 運行驗證與調(diào)用鏈分析接線完成后別急著上線先把整條鏈路的日志打全。我習(xí)慣在三個位置打日志Nacos 注冊成功、收到入站消息、發(fā)送出站消息。每個日志都要帶上taskId和serviceName這樣出問題的時候能通過同一個 taskId 把一條調(diào)用鏈串起來。一個簡單的壓測驗證# 先看 AgentCard 是否可訪問 curl http://localhost:8080/a2b/.well-known/agent.json # 再發(fā)一條消息 curl -X POST http://localhost:8080/a2b/message/send \ -H Content-Type: application/json \ -d {taskId:demo-test-001,localAgentId:caller,remoteAgentId:order-after-sale-agent,message:{role:user,content:查詢訂單 OG123456}}如果返回的內(nèi)容里包含正常的訂單狀態(tài)信息且 Nacos 控制臺上能看到order-after-sale-agent的實例處于健康狀態(tài)那么這條接線就基本跑通了。5. 踩坑實錄接線過程中最折磨人的五個問題5.1 AgentCard 404 或地址錯亂癥狀調(diào)用方在 Nacos 中拿到了 Agent 的實例地址但訪問/.well-known/agent.json時返回 404或者拿到的是一個過期環(huán)境的地址。根因我把 AgentCard 的url字段和 Nacos 元數(shù)據(jù)里的uri字段都配成了內(nèi)網(wǎng)地址而調(diào)用方在外網(wǎng)環(huán)境。內(nèi)網(wǎng)地址它當(dāng)然訪問不到。處理方式統(tǒng)一用一個網(wǎng)關(guān)域名作為 Agent 的對外地址。比如如果你的 Agent 服務(wù)在網(wǎng)關(guān)后面AgentCard 的url就配成網(wǎng)關(guān)對外的域名加路徑Nacos 注冊的 ip/port 則保留服務(wù)實例實際的內(nèi)網(wǎng)地址。兩者各司其職Nacos 里的地址用于 VPC 內(nèi)服務(wù)發(fā)現(xiàn)AgentCard 里的 url 用于跨網(wǎng)或跨域標(biāo)識。5.2 Nacos 心跳丟失服務(wù)列表時有時無癥狀服務(wù)啟動后Nacos 控制臺能看到實例但過幾分鐘就變成了不健康再過一會兒直接消失然后又重新出現(xiàn)。根因這個坑大概率出在端口配置和網(wǎng)絡(luò)隔離上。Nacos 2.x 的 gRPC 端口是主端口加 1000 偏移8848 對應(yīng) 9848如果防火墻只開了 8848 而沒開 9848心跳上報會間歇性失敗。處理方式把 8848 和 9848 都放通確認(rèn)服務(wù)器安全組規(guī)則別漏。還有一個容易忽略的點Agent 服務(wù)所在的容器如果做了端口映射spring.cloud.nacos.discovery.ip和port要顯式配置成宿主機可達的地址否則注冊的是容器內(nèi)網(wǎng) IP別的服務(wù)根本路由不過去。5.3 Message 序列化不一致中文亂碼或結(jié)構(gòu)丟失癥狀調(diào)用方發(fā)了個結(jié)構(gòu)化的消息對端 Agent 收到的content是奇怪的亂碼或者嵌套的 Map 結(jié)構(gòu)丟失了。根因兩邊用的 HTTP 客戶端/服務(wù)端對 JSON 編解碼的默認(rèn)行為不一致。比如 Jackson 的默認(rèn)配置在某些版本會對未知字段報錯或者對MapString, Object的 value 類型推斷錯誤。處理方式給 HTTP 調(diào)用統(tǒng)一設(shè)置produces和consumes為application/json;charsetUTF-8同時避免在 Message 里塞過于復(fù)雜的嵌套泛型。A2A 協(xié)議的 Message 本質(zhì)是 Part 列表盡量用扁平結(jié)構(gòu)如content字符串 metadata簡單鍵值對復(fù)雜對象放到artifact里用文件或內(nèi)容地址引用。5.4 Task 狀態(tài)機沒同步好癥狀調(diào)用方發(fā)出任務(wù)后一直輪詢task/get返回的狀態(tài)始終是working但 Agent 那邊其實已經(jīng)處理完了。根因?qū)崿F(xiàn) A2A 服務(wù)端時Task 狀態(tài)沒有同步更新。處理方式任務(wù)處理結(jié)束后不要只返回業(yè)務(wù)結(jié)果一定要把 Task 狀態(tài)顯式置為completed或failed。這尤其容易發(fā)生在異步處理場景里。如果是異步任務(wù)建議單獨維護一個 Task 存儲處理線程完成后更新狀態(tài)再開放查詢接口。不要試圖用線程內(nèi)的局部變量去驅(qū)動狀態(tài)查詢跨請求的臨時狀態(tài)丟失是埋雷高發(fā)區(qū)。5.5 安全與頻控癥狀A(yù)gent 端點暴露到公網(wǎng)后被掃描器刷了一波請求Nacos 服務(wù)列表里出現(xiàn)一堆陌生的服務(wù)名。處理方式A2A 端點不要裸奔。至少加一層簡單的鑒權(quán)邏輯比如Authorization頭校驗或agent-id白名單。同時配合 Nacos 的鑒權(quán)能力控制注冊權(quán)限。頻控方面可以引入 sentinel 結(jié)合 Nacos 做限流配置動態(tài)下發(fā)這個我在后面的擴展章節(jié)細(xì)說。6. 互通層的延伸配置中心、動態(tài)刷新與多環(huán)境路由6.1 Nacos 配置中心承載 Agent 配置的動態(tài)更新Agent 的很多狀態(tài)性配置比如系統(tǒng)提示詞、工具啟停開關(guān)、模型參數(shù)都適合放在 Nacos 配置中心里做動態(tài)下發(fā)。典型場景某個 Agent 的系統(tǒng)提示詞寫得不夠好想調(diào)整策略不用重新發(fā)布服務(wù)直接在 Nacos 里改配置Agent 通過監(jiān)聽配置變更自動加載新提示詞。實現(xiàn)思路是引入 Nacos 配置監(jiān)聽器。AgentScope Java 的 Agent 對象在運行時讀取配置我們需要把配置讀取這一步做成可以動態(tài)刷新的模式。Component public class DynamicPromptUpdater { private final AgentBase agent; private final ConfigService configService; public DynamicPromptUpdater(AgentBase agent, ConfigService configService) { this.agent agent; this.configService configService; } PostConstruct public void registerListener() throws NacosException { configService.addListener(agent-prompt, AGENT_GROUP, new Listener() { Override public Executor getExecutor() { return Executors.newSingleThreadExecutor(); } Override public void receiveConfigInfo(String configInfo) { // 動態(tài)更新Agent的系統(tǒng)提示詞 agent.updateSystemPrompt(configInfo); } }); } }需要注意的是動態(tài)更新雖然方便但對 Agent 這種會話狀態(tài)敏感的組件要謹(jǐn)慎。如果一個長任務(wù)正在執(zhí)行中你在中途改了系統(tǒng)提示詞可能導(dǎo)致任務(wù)行為不一致。我的建議是只對非關(guān)鍵狀態(tài)做熱更新比如模型溫度、超時時間、工具開關(guān)而系統(tǒng)提示詞盡量走灰度發(fā)布。6.2 多環(huán)境路由與灰度發(fā)布前面提到 metadata 里的 version 字段配上 Nacos 后可以做很靈活的路由。比如你有 5 個訂單查詢 Agent 實例其中 1 個是 v2.0 新版本你想讓 10% 請求打到新版其他打到 v1.0。Nacos 支持設(shè)置實例權(quán)重按權(quán)重分配流量。spring: cloud: nacos: discovery: metadata: version: v2.0 weight: 1另一個思路是根據(jù) Agent 卡片的 capabilities 做路由。例如某實例聲明自己支持多模態(tài)調(diào)用方拿到實例列表后先按capabilities過濾只保留帶multiModal的實例再做二次分發(fā)。這種先過濾再選擇的模式比單純隨機選擇能顯著降低無效調(diào)用。6.3 限流配置的下發(fā)與熔斷最后提一下限流。Agent 端點暴露后最怕的不是業(yè)務(wù)問題而是被大量無效請求打掛。我在前面的項目里嘗試過把 sentinel 和 Nacos 結(jié)合起來限流規(guī)則統(tǒng)一存在 Nacos 配置中心sentinel 客戶端監(jiān)聽配置變更動態(tài)調(diào)整每個 Agent 端點的 QPS 閾值。這個組合解決了一個很實際的痛點你的 Agent 是供多個業(yè)務(wù)方調(diào)用的每個業(yè)務(wù)方的優(yōu)先級不同。給內(nèi)部核心業(yè)務(wù)配額高一些給外部試用的配額低一些這個配額比不是一錘定音而是能通過 Nacos 實時調(diào)整。限流規(guī)則有一個routeId的概念可以按調(diào)用方維度做精細(xì)化控制。在寫這套配置的時候啟動時的限流規(guī)則不要寫在代碼里直接寫在 Nacos 上否則改一個閾值又得重新部署。我自己遇到過的坑是限流規(guī)則沒放在 Nacos 里結(jié)果壓測時 QPS 超了只能臨時改代碼重新上線耽誤了半小時。從那以后我所有的流控規(guī)則都走配置中心。7. 寫在最后互通層的核心理念做了這么多期的實戰(zhàn)走到互通層這一期我心里最有感觸的一點是不要讓 Agent 之間的協(xié)作像人與人之間的私聊而要讓它們像服務(wù)與服務(wù)之間的 API 調(diào)用。私聊沒有標(biāo)準(zhǔn)、沒有契約、沒有狀態(tài)回溯而 A2A Nacos 的組合恰恰把這些補上了。如果你也正在做多 Agent 系統(tǒng)我的建議是先把 AgentCard 寫好寫清楚邊界再把 Nacos 的 metadata 設(shè)計好別浪費這個免費的能力透傳入口最后把任務(wù)狀態(tài)機跑順日志打全。這三件事做到位互通層的基本盤就穩(wěn)了。