:從零搭建企業(yè)級Agent基礎(chǔ)設(shè)施的關(guān)鍵技術(shù))
1. 為什么Agent Infra突然成了“隱秘戰(zhàn)場”這兩年大模型本身的熱度已經(jīng)在肉眼可見地下滑圈內(nèi)人都在說“模型層卷到頭了”真正還在悶聲布局、搶人搶資源的地方其實是Agent的基礎(chǔ)設(shè)施層也就是Agent Infra。這個領(lǐng)域不太有聲量因為面向開發(fā)者而不是大眾消費者但巨頭們在這個賽道的布局密度遠(yuǎn)比我預(yù)想的高得多。我在某公司做后端架構(gòu)的工作去年開始陸續(xù)接觸Agent相關(guān)項目。剛開始我和大多數(shù)人一樣覺得Agent不就是調(diào)幾個API、套個Prompt模板、讓模型走幾步工具調(diào)用嘛實在沒什么技術(shù)含量。直到我們自己做了一個跨平臺系統(tǒng)的Agent化改造我才真正意識到Agent做到真正穩(wěn)定、可控、可運維、可觀測的時候就變成了一套全新的基礎(chǔ)設(shè)施工程而這套工程里面每塊都是硬骨頭。巨頭們在爭什么表面上是搶大模型API的調(diào)用入口實際上是在搶Agent運行時的控制權(quán)。誰掌握了Agent運行時的編排、記憶流轉(zhuǎn)、工具調(diào)用鏈路誰就掌握了未來AI應(yīng)用中最高價值的那一層抽象。你想想看大模型本身更像一個發(fā)動機(jī)而Agent Infra是底盤、剎車、儀表盤和懸掛系統(tǒng)發(fā)動機(jī)再好沒有底盤也跑不起來。真正開過車的人都知道底盤調(diào)校的復(fù)雜度遠(yuǎn)遠(yuǎn)超過發(fā)動機(jī)本身。對普通工程師來說這反而是個好機(jī)會。巨頭和巨頭之間的競爭實際上把整個賽道的工具鏈、開源方案、行業(yè)標(biāo)準(zhǔn)都催熟了我們不用自己踩一遍所有的坑不少基礎(chǔ)設(shè)施級的方案已經(jīng)有開源實現(xiàn)。作為工程師入場的方式很簡單把手頭現(xiàn)有的業(yè)務(wù)系統(tǒng)往Agent方向改造一遍理解其中哪一塊兒最痛、最缺基建那就是我們的入場券。2. Agent Infra到底包含哪些內(nèi)容先說結(jié)論Agent Infra不是某一個具體組件而是圍繞“Agent的運行、記憶、工具、評估、安全”建立起來的一整套支撐體系。我把它拆成五個核心面方便大家理解。2.1 運行時與執(zhí)行引擎Agent運行時是整個體系的骨架。它負(fù)責(zé)調(diào)度大模型調(diào)用、工具調(diào)用、用戶指令之間的循環(huán)核心要解決的是“讓Agent在多次推理與動作之間不迷路”。我們用的方案是事件驅(qū)動架構(gòu)。模型每推理完一輪會輸出一個意圖然后運行時把這個意圖解析成可執(zhí)行的任務(wù)步驟再觸發(fā)對應(yīng)的工具服務(wù)。這套設(shè)計的核心難點在狀態(tài)管理Agent執(zhí)行一個有五步甚至十步的任務(wù)時中途哪一步掛了、哪一步超時了、哪一步返回的數(shù)據(jù)格式不符合預(yù)期運行時都要能感知到并做出補(bǔ)償。實際做下來最崩潰的就是時序問題。模型生成工具調(diào)用參數(shù)是毫秒級的事情但工具真正返回結(jié)果可能需要幾秒甚至更久。如果運行時只是簡單同步等待整個Agent就卡死了。我們最后用異步事件流來解決問題模型先把調(diào)用意圖丟進(jìn)隊列工具服務(wù)執(zhí)行完把結(jié)果重新注入上下文運行時再決定下一步動作。這套機(jī)制本身不復(fù)雜但穩(wěn)定性要求極高消息丟失、重復(fù)投遞、結(jié)果亂序都可能導(dǎo)致Agent整個推理鏈條亂掉。2.2 記憶與狀態(tài)管理記憶是Agent區(qū)別于普通對話機(jī)器人的核心能力。但記憶在工程上的實現(xiàn)遠(yuǎn)比想象中坑多。我們一開始天真地用Redis把所有對話記錄存下來每次請求直接塞進(jìn)Prompt里后來發(fā)現(xiàn)上下文塞爆是遲早的事而且塞進(jìn)去的內(nèi)容大部分是無效噪音模型反而不容易抓住重點。后來我們把記憶拆成短期記憶和長期記憶短期記憶是當(dāng)前任務(wù)內(nèi)的會話上下文長期記憶則按用戶維度存儲并增加一個摘要和檢索的過程。長期記憶的落地方式參考了向量檢索的思路先把歷史交互記錄做向量化然后根據(jù)當(dāng)前任務(wù)需求做語義檢索選出最相關(guān)的幾條作為參考上下文。這聽起來很順理成章但實際操作中記憶寫入的觸發(fā)時機(jī)、摘要的更新策略、記憶的過期清理全都要手寫代碼控制。很多Agent項目之所以越跑越“笨”就是因為記憶里堆了大量過時、矛盾甚至錯誤的信息模型再怎么聰明也會被帶偏。2.3 上下文工程上下文工程是我個人認(rèn)為Agent Infra里最微妙的一塊。它不是簡單地把背景資料塞給模型而是要在“給多少信息、給什么粒度、以什么順序給”之間做取舍。我們內(nèi)部封裝了一個上下文管理服務(wù)每次模型推理前動態(tài)組裝機(jī)器學(xué)習(xí)到的上下文用戶畫像、歷史記憶摘要、當(dāng)前任務(wù)狀態(tài)、工具調(diào)用結(jié)果、系統(tǒng)提示詞每一部分都有單獨的優(yōu)先級和拼接策略。關(guān)鍵的經(jīng)驗是上下文必須在任務(wù)推進(jìn)中持續(xù)壓縮和重寫而不是一次性固定下來。比如一個多輪工具調(diào)用的任務(wù)每一步執(zhí)行完之后原始返回值不一定還需要原樣放在上下文里這時候可以做一個結(jié)構(gòu)化抽取把關(guān)鍵字段提煉出來替換掉冗長的原始數(shù)據(jù)。這塊沒有標(biāo)準(zhǔn)答案完全靠實驗和經(jīng)驗積累。但有一點是確定的上下文設(shè)計決定了Agent能力的上限。模型本身的能力再強(qiáng)上下文給糊了照樣輸出垃圾內(nèi)容。2.4 可觀測性與評估體系A(chǔ)gent系統(tǒng)的調(diào)試難度比傳統(tǒng)微服務(wù)高一個數(shù)量級核心原因是不確定性。同樣的輸入模型每次的輸出都不完全一樣工具調(diào)用鏈條也可能走不同分支所以傳統(tǒng)按日志關(guān)鍵字檢索定位問題的方式幾乎失靈。我們的做法是引入Tracing機(jī)制為每一次Agent執(zhí)行生成唯一的Trace ID貫穿從接收指令到模型推理到工具調(diào)用再到最終回復(fù)的全鏈路。每一步都記錄入?yún)ⅰ⒊鰠?、耗時、token消耗并且額外記錄了模型當(dāng)時選擇的“思路過程”這樣才能在出問題的時候還原決策路徑。評估體系是更頭疼的事情。普通接口測試是斷言“返回的JSON是否包含某某字段”Agent評估沒法這么干你得判斷“這個回答是否完成用戶意圖”“工具調(diào)用步驟是否合理”“有沒有產(chǎn)生幻覺信息”。我們最后做了三層評估規(guī)則層攔截明顯失誤語義層用大模型打分人工抽檢層定期復(fù)盤。是的你沒看錯用大模型評估大模型這是當(dāng)前沒有人能完全繞開的路子但必須作為兜底而不是唯一依賴。2.5 安全與護(hù)欄體系A(chǔ)gent比傳統(tǒng)API多了“行動力”所以安全要求完全不同。傳統(tǒng)接口最多是數(shù)據(jù)泄露Agent可以調(diào)用工具可能觸發(fā)刪除操作、寫數(shù)據(jù)、發(fā)消息等高危行為。我們的做法是把工具調(diào)用全部納入審批和流控高風(fēng)險操作必須二次確認(rèn)低風(fēng)險操作也要走頻控和配額限制。每次工具調(diào)用前會有一個獨立的校驗器對參數(shù)做白名單檢查和類型校驗避免模型生成惡意或者錯誤參數(shù)直接打到下游系統(tǒng)。另外Prompt注入是Agent特有的大坑用戶可能故意在輸入里塞“忽略前面的指令”這時候不能光靠模型自覺還得靠輸入側(cè)過濾和指令權(quán)限隔離來兜底。3. 巨頭之間的競爭邏輯和我們工程師有什么關(guān)系巨頭在Agent Infra的布局看著離我們很遠(yuǎn)實際上直接影響著我們手里的技術(shù)選型和職業(yè)方向。我理解他們的布局主要有三層邏輯。第一層是模型入口之爭。大模型本身越來越像標(biāo)準(zhǔn)化商品API價格一路走低純靠模型賺錢越來越難。真正有黏性的是Agent平臺開發(fā)者在平臺上配置工具、構(gòu)建Agent、沉淀工作流用習(xí)慣了就很難遷移。這就像早年間手機(jī)廠商搶生態(tài)入口一樣最終鎖定的是開發(fā)者習(xí)慣和生態(tài)壁壘。第二層是數(shù)據(jù)回流。Agent運行過程中產(chǎn)生海量的調(diào)用鏈數(shù)據(jù)、用戶偏好數(shù)據(jù)、任務(wù)成功失敗數(shù)據(jù)這些數(shù)據(jù)對模型迭代和產(chǎn)品優(yōu)化價值巨大?;A(chǔ)設(shè)施層做得越好越能收集到高質(zhì)量的數(shù)據(jù)飛輪。第三層是標(biāo)準(zhǔn)制定權(quán)。開源框架的接口規(guī)范、工具調(diào)用協(xié)議、記憶存儲格式誰參與得多、貢獻(xiàn)得多誰的話語權(quán)就大。畢竟大家都不希望自己的技術(shù)棧綁在某一家廠商上但也不希望自己在標(biāo)準(zhǔn)制定中缺席這就是很多巨頭一邊推自家閉源產(chǎn)品一邊積極往開源項目里提交代碼的原因。工程師能從中得到什么兩條非?,F(xiàn)實的紅利一是工具鏈紅利。巨頭打架開源項目受益我們內(nèi)部很多方案現(xiàn)在不需要從零手寫了。生態(tài)越成熟Agent開發(fā)門檻越低懂底層原理的人就越值錢。二是差異化紅利。巨頭做通用基礎(chǔ)設(shè)施但落地到具體行業(yè)、具體業(yè)務(wù)還是需要有人懂業(yè)務(wù)、懂系統(tǒng)集成。這就是工程師的機(jī)會把通用Agent能力翻譯成某個垂直領(lǐng)域里真正可用的方案。通用基礎(chǔ)設(shè)施和垂直落地之間存在一個巨大的空白層屬于兩頭都不太愿意深度扎進(jìn)去的地方但它恰恰是工程師施展空間最大的區(qū)域。4. 實操過程從零搭一個最小可用的Agent運行框架理論說再多不如直接上手跑一套。下面是我們內(nèi)部做的第一個Agent基礎(chǔ)設(shè)施原型的過程不算多高級但足夠你理解這套體系的運行邏輯。4.1 整體架構(gòu)設(shè)計我們規(guī)劃了一個極簡的Agent Infra骨架Agent運行時負(fù)責(zé)任務(wù)拆解、狀態(tài)流轉(zhuǎn)、模型調(diào)用編排工具注冊中心維護(hù)所有可供Agent調(diào)用的外部能力接口記憶服務(wù)管理短期記憶和長期記憶的讀寫與檢索評估服務(wù)對Agent每次執(zhí)行結(jié)果做自動評估Trace追蹤記錄完整調(diào)用鏈技術(shù)棧選擇上我們用了一個比較成熟的異步任務(wù)隊列框架做事件流轉(zhuǎn)模型層接了多種大模型API統(tǒng)一走一套協(xié)議適配層這樣切換不同的模型提供方時不會改動上層編排邏輯。4.2 核心代碼骨架運行時最關(guān)鍵的是Event Loop機(jī)制直接決定Agent的并發(fā)處理能力。我把核心骨架簡化了一下。# runtime_loop.py import asyncio from enum import Enum class AgentState(Enum): IDLE 0 PARSING 1 PLANNING 2 EXECUTING 3 FINISHED 4 FAILED 5 class AgentRuntime: def __init__(self, memory_client, tool_registry): self.state AgentState.IDLE self.memory memory_client self.tools tool_registry self.event_queue asyncio.Queue() self.trace_id None async def submit_task(self, user_input, trace_id): self.trace_id trace_id await self.event_queue.put((parse, user_input)) async def start(self): while True: event_type, payload await self.event_queue.get() if event_type parse: await self.handle_parse(payload) elif event_type plan: await self.handle_plan(payload) elif event_type execute: await self.handle_execute(payload) elif event_type finalize: await self.handle_finalize(payload) break async def handle_parse(self, user_input): # 調(diào)用模型解析用戶意圖 intent await self.model_parse(user_input) await self.event_queue.put((plan, intent)) self.state AgentState.PLANNING async def handle_plan(self, intent): # 根據(jù)意圖決定是否需要調(diào)用工具 if self.tools.is_tool_call_required(intent): tool_name, params await self.model_decide_tool(intent) await self.event_queue.put((execute, (tool_name, params))) else: await self.event_queue.put((finalize, intent)) async def handle_execute(self, tool_task): tool_name, params tool_task # 異步執(zhí)行工具調(diào)用 result await self.tools.invoke(tool_name, params) # 將結(jié)果寫回記憶上下文 await self.memory.update_current_context(self.trace_id, tool_name, result) await self.event_queue.put((finalize, result)) async def handle_finalize(self, final_payload): # 生成最終回復(fù) answer await self.model_generate(final_payload) self.state AgentState.FINISHED return answer這套骨架邏輯上很直觀但實際生產(chǎn)化要補(bǔ)的東西非常多。每個異步任務(wù)要做超時控制隊列里的事件要做優(yōu)先級區(qū)分工具調(diào)用要做并發(fā)限制模型解析失敗要有重試和降級策略。我們第一次上線時沒考慮工具調(diào)用超時結(jié)果某個外部接口響應(yīng)卡了幾分鐘直接把整個事件循環(huán)堵死所有Agent全部掛起。后來才加上超時和熔斷邏輯# tool_invocation.py import asyncio import functools async def invoke_with_timeout(tool_func, timeout_seconds10): try: return await asyncio.wait_for( tool_func(), timeouttimeout_seconds ) except asyncio.TimeoutError: # 返回一個明確的超時標(biāo)記而不是拋出異常 return { status: timeout, partial_result: None, retryable: True }4.3 記憶模塊的實現(xiàn)要點記憶模塊的細(xì)節(jié)決定Agent的“智商持續(xù)在線程度”。我們用了兩層存儲短期記憶放Redis長期記憶放向量數(shù)據(jù)庫。短期記憶以Trace ID為Key保存當(dāng)前任務(wù)所有的交互記錄、中間狀態(tài)和工具返回結(jié)果。長期記憶只在任務(wù)結(jié)束時進(jìn)行一次沉淀把本次任務(wù)的摘要、關(guān)鍵決策、用戶偏好抽出來向量化后寫入。# memory.py class AgentMemory: def __init__(self, redis_client, vector_client, summarizer): self.redis redis_client self.vector vector_client self.summarizer summarizer async def save_short_term(self, trace_id, role, content): await self.redis.rpush(fagent:{trace_id}:ctx, f{role}:{content}) async def load_relevant_long_term(self, user_id, query, top_k5): query_vector self.vector.embed(query) results await self.vector.search( indexagent_memory, query_vectorquery_vector, top_ktop_k, filter{user_id: user_id} ) return results async def finalize_memory(self, trace_id, user_id): # 拉取整條短期記憶 raw_ctx await self.redis.lrange(fagent:{trace_id}:ctx, 0, -1) # 調(diào)用摘要模型提煉長期記憶 summary await self.summarizer.summarize(raw_ctx) await self.vector.upsert( indexagent_memory, vectors[{ id: trace_id, vector: self.vector.embed(summary), metadata: { user_id: user_id, summary: summary, timestamp: datetime.now().isoformat() } }] ) # 清理短期記憶 await self.redis.delete(fagent:{trace_id}:ctx)實操下來最大的反饋是長期記憶的寫入時機(jī)非常關(guān)鍵不是所有任務(wù)都值得沉淀。如果一筆交易只查了一個訂單狀態(tài)這也要寫入長期記憶用不了多久向量庫里全是噪音。我們后來加了業(yè)務(wù)規(guī)則過濾只有模型判斷對用戶有長期價值的交互才觸發(fā)沉淀。4.4 工具調(diào)用協(xié)議設(shè)計工具調(diào)用的協(xié)議設(shè)計同樣是Agent Infra的核心環(huán)節(jié)。大模型沒有“天然知道你的系統(tǒng)接口怎么調(diào)用”的能力需要我們把工具能力描述清楚。我們?yōu)槊總€工具維護(hù)了一份JSON Schema格式的聲明包括功能描述、參數(shù)類型、枚舉值、示例和必要的調(diào)用約束。{ tool_name: query_order_status, description: 根據(jù)訂單號查詢訂單當(dāng)前狀態(tài)和物流信息, parameters: { type: object, properties: { order_id: { type: string, description: 訂單號通常為字母和數(shù)字組合 }, include_logistics: { type: boolean, description: 是否返回物流軌跡詳情, default: false } }, required: [order_id] }, constraints: { rate_limit: 10, timeout_ms: 3000, requires_confirmation: false } }這個描述文件的質(zhì)量直接決定模型工具選擇的準(zhǔn)確率。早期我們寫著“查詢訂單狀態(tài)”五個字就完事模型經(jīng)常把物流查詢和訂單查詢混在一起參數(shù)也不知道該傳什么。后來仿照寫API文檔的標(biāo)準(zhǔn)把每個字段的枚舉范圍、邊界情況、錯誤碼都補(bǔ)全工具選擇的準(zhǔn)確率肉眼可見地上來了。不要省這個功夫工具描述寫不好Agent腿腳就不利索。5. 常見問題與排查技巧實錄Agent Infra的排查比傳統(tǒng)系統(tǒng)難在“不確定性”上。同一個問題可能模型抽風(fēng)、上下文污染、工具返回異常、記憶檢索偏差四者都會導(dǎo)致類似的外部表現(xiàn)。我們整理了內(nèi)部高頻踩坑清單放在這里給大家當(dāng)速查表。5.1 上下文污染的典型表現(xiàn)與規(guī)避典型表現(xiàn)是Agent越到后面越“丟三落四”比如在前面的工具調(diào)用中已經(jīng)拿到訂單金額后面生成總結(jié)時卻硬說沒獲取到。排查后發(fā)現(xiàn)是上下文太長早期信息被截斷或者重要性權(quán)重太低模型直接忽略。規(guī)避手段有幾種我按有效性排序動態(tài)壓縮每輪工具返回結(jié)果到達(dá)時立刻用一次輕量模型調(diào)用壓縮成關(guān)鍵字段摘要分段控制上下文按時間線分塊模型可以根據(jù)需要回溯指定段落而不是全部平鋪關(guān)鍵信息置頂把用戶核心訴求、任務(wù)目標(biāo)、關(guān)鍵約束始終放在上下文最靠前的位置5.2 模型工具選擇錯亂模型有時會選錯工具比如查詢天氣的Agent突然去調(diào)用了訂機(jī)票的工具。排查發(fā)現(xiàn)是工具描述里存在模糊相似語義讓模型產(chǎn)生了歧義。解決思路是工具描述之間做語義隔離。每個工具描述要寫清楚邊界這個工具管什么、不管什么、和另一個工具的區(qū)別在哪。同時善用枚舉值約束把參數(shù)范圍卡死減少模型自由發(fā)揮的空間。5.3 Trace日志缺少關(guān)鍵決策原因早期我們的Trace只記錄“模型調(diào)用了哪個工具”沒有記錄“為什么調(diào)用這個工具”。結(jié)果出問題時完全無法還原模型的決策依據(jù)只能瞎猜。后續(xù)我們在Trace里增加了“思路過程”字段每個關(guān)鍵決策點都把模型當(dāng)時的理由和候選列表記錄下來。代價是多消耗一些Token但排查效率提升非常明顯。對于高復(fù)雜度任務(wù)這部分投入完全值得。5.4 記憶檢索結(jié)果漂移向量檢索有個現(xiàn)象輸入問題稍微改幾個詞檢索出來的記憶片段就可能完全不一樣導(dǎo)致Agent有時候想起這個、有時候想起那個行為不穩(wěn)定。我們的應(yīng)對方法是檢索后加一個重排序?qū)影押蜻x記憶片段先全部取回來再用模型統(tǒng)一打分排序只留下最相關(guān)的幾條。這讓記憶引入的波動性大幅下降。5.5 成本失控Agent一個任務(wù)可能調(diào)用十幾次模型接口token消耗比普通對話高一個量級如果沒做控制月底賬單會很嚇人。我們做了三個維度的成本管控單任務(wù)Token上限、緩存優(yōu)先機(jī)制和模型分級。簡單任務(wù)走小參數(shù)模型復(fù)雜任務(wù)才啟用大參數(shù)模型。另外工具調(diào)用結(jié)果如果可以直接復(fù)用就不重復(fù)請求模型生成。6. 我的幾點實操體會與后續(xù)可以擴(kuò)展的方向踩了這么多坑之后我個人的核心體會是Agent Infra的本質(zhì)是把“模型不確定性”和“工程確定性”之間的沖突調(diào)和好。模型天生不確定基礎(chǔ)設(shè)施的職責(zé)就是在這種不確定上建一層可控的殼讓上層業(yè)務(wù)不至于被模型的隨機(jī)性拖垮。另一個深刻的感受是Agent Infra當(dāng)前沒有標(biāo)準(zhǔn)答案每個團(tuán)隊都在用自己的方式摸索。巨頭們的方案不一定適合中小團(tuán)隊開源社區(qū)的方案也不一定貼合你的業(yè)務(wù)場景。最好的做法是理解原理、跑通最小實現(xiàn)然后結(jié)合自己業(yè)務(wù)的真實痛點去疊加能力。對我們這個跨平臺系統(tǒng)項目來說Agent Infra從零到一跑通之后后續(xù)有幾個方向值得深入多Agent協(xié)作調(diào)度把單Agent擴(kuò)展成多Agent協(xié)同工作的形態(tài)需要一套更完善的任務(wù)編排和消息傳遞機(jī)制Agent評估數(shù)據(jù)回注把線上評估結(jié)果沉淀成數(shù)據(jù)集反哺模型微調(diào)形成數(shù)據(jù)閉環(huán)人機(jī)協(xié)同審批流高風(fēng)險操作引入人工確認(rèn)節(jié)點這需要在運行時和業(yè)務(wù)系統(tǒng)之間做更深的集成最后分享一個小技巧剛接觸Agent Infra的工程師別急著追新框架、新名詞先把“一條Agent請求從進(jìn)來到出去完整經(jīng)過哪些環(huán)節(jié)、每個環(huán)節(jié)可能出什么問題”畫清楚。這個基礎(chǔ)打牢了后面不管是換框架還是自研都不會慌。