大模型網(wǎng)關(guān)架構(gòu)設(shè)計(jì)與自動(dòng)化編程Agent接入實(shí)踐)
1. 企業(yè)大模型網(wǎng)關(guān)到底解決什么問題1.1 從一個(gè)真實(shí)困境說起去年下半年我所在的團(tuán)隊(duì)同時(shí)接入了三家大模型供應(yīng)商的API。起初大家覺得沒什么無非是多寫幾個(gè)HTTP請(qǐng)求的事。但兩個(gè)月后問題集中爆發(fā)了財(cái)務(wù)部門拿著賬單來問為什么這個(gè)月API費(fèi)用漲了三倍安全團(tuán)隊(duì)發(fā)現(xiàn)有幾個(gè)業(yè)務(wù)線的請(qǐng)求把內(nèi)部數(shù)據(jù)發(fā)到了外部接口運(yùn)維同事抱怨說某家供應(yīng)商限流之后整個(gè)客服系統(tǒng)直接掛了而開發(fā)同學(xué)最頭疼的是——每接一個(gè)新業(yè)務(wù)就要重新寫一遍鑒權(quán)、重試、日志、計(jì)費(fèi)邏輯。這就是沒有網(wǎng)關(guān)的典型癥狀。大模型網(wǎng)關(guān)本質(zhì)上是一個(gè)位于業(yè)務(wù)應(yīng)用和模型服務(wù)之間的中間層它把模型調(diào)用這件事從“每個(gè)業(yè)務(wù)自己搞定”變成“統(tǒng)一入口、統(tǒng)一治理”。你可以把它理解成公司前臺(tái)所有訪客請(qǐng)求先到前臺(tái)登記、驗(yàn)證身份、分配去向而不是讓每個(gè)人直接闖進(jìn)辦公區(qū)。1.2 網(wǎng)關(guān)的核心能力清單一個(gè)能落地的大模型網(wǎng)關(guān)至少要覆蓋以下幾塊能力缺一個(gè)都會(huì)在后期變成坑統(tǒng)一接入層屏蔽不同供應(yīng)商的API差異業(yè)務(wù)側(cè)只面對(duì)一套接口規(guī)范。OpenAI、Anthropic、國(guó)內(nèi)各家模型的請(qǐng)求格式、鑒權(quán)方式、流式返回協(xié)議都不一樣網(wǎng)關(guān)要做協(xié)議轉(zhuǎn)換。密鑰與權(quán)限管理API Key不能散落在各個(gè)業(yè)務(wù)代碼里。網(wǎng)關(guān)集中托管密鑰業(yè)務(wù)側(cè)用內(nèi)部Token調(diào)用網(wǎng)關(guān)再替換成真實(shí)密鑰轉(zhuǎn)發(fā)。配額與限流按業(yè)務(wù)線、按用戶、按模型維度設(shè)置QPS和Token配額防止某個(gè)業(yè)務(wù)把額度吃光影響其他人??捎^測(cè)性每次調(diào)用的耗時(shí)、Token消耗、成功率、錯(cuò)誤碼都要記錄這是后續(xù)優(yōu)化和計(jì)費(fèi)的依據(jù)。路由與降級(jí)主模型超時(shí)或限流時(shí)自動(dòng)切換到備用模型保證業(yè)務(wù)連續(xù)性。內(nèi)容安全請(qǐng)求和響應(yīng)都要過一遍敏感內(nèi)容檢測(cè)這是企業(yè)場(chǎng)景的硬要求。注意很多團(tuán)隊(duì)一開始只做了“統(tǒng)一接入”就上線了結(jié)果三個(gè)月后發(fā)現(xiàn)沒有配額管理一個(gè)測(cè)試腳本跑了一晚上燒掉幾千塊。網(wǎng)關(guān)的能力要一次性規(guī)劃分階段實(shí)現(xiàn)但不能漏項(xiàng)。1.3 為什么現(xiàn)在必須做這件事過去一年Agent和自動(dòng)化編程的爆發(fā)讓模型調(diào)用量從“偶爾幾次”變成了“持續(xù)高頻”。一個(gè)自動(dòng)化編程Agent在一次任務(wù)中可能發(fā)起幾十次模型調(diào)用一個(gè)客服Agent每天處理上萬(wàn)輪對(duì)話。這種量級(jí)下沒有網(wǎng)關(guān)的裸調(diào)用模式根本撐不住。而且企業(yè)采購(gòu)模型服務(wù)時(shí)往往簽了年度框架協(xié)議需要精確的用量統(tǒng)計(jì)來分?jǐn)偝杀具@些都不是業(yè)務(wù)代碼能順手解決的。2. 網(wǎng)關(guān)架構(gòu)設(shè)計(jì)與技術(shù)選型2.1 整體架構(gòu)分層我在實(shí)際項(xiàng)目中采用的架構(gòu)分為四層從下往上依次是接入層負(fù)責(zé)接收業(yè)務(wù)請(qǐng)求做初步的協(xié)議解析和身份驗(yàn)證。這一層用Nginx或者云廠商的負(fù)載均衡即可不需要太復(fù)雜。網(wǎng)關(guān)核心層是重點(diǎn)包含路由引擎、配額管理器、密鑰管理器、協(xié)議轉(zhuǎn)換器、內(nèi)容過濾器五個(gè)模塊。這一層我用Go語(yǔ)言實(shí)現(xiàn)原因是Go的并發(fā)模型適合處理大量短連接請(qǐng)求而且部署簡(jiǎn)單一個(gè)二進(jìn)制文件扔上去就能跑。適配層針對(duì)每家模型供應(yīng)商寫一個(gè)Adapter把統(tǒng)一的內(nèi)部請(qǐng)求格式轉(zhuǎn)換成各家特有的格式。比如OpenAI用messages數(shù)組某些國(guó)內(nèi)模型用prompt字符串Adapter負(fù)責(zé)抹平這些差異。觀測(cè)層收集所有調(diào)用指標(biāo)寫入時(shí)序數(shù)據(jù)庫(kù)同時(shí)把詳細(xì)日志落到對(duì)象存儲(chǔ)。這一層用Prometheus加Grafana做監(jiān)控面板日志用Loki查詢。2.2 為什么選自研而不是用開源方案市面上確實(shí)有一些開源網(wǎng)關(guān)項(xiàng)目但我在評(píng)估后選擇了自研核心層。原因有三個(gè)第一開源方案通常只覆蓋了基礎(chǔ)轉(zhuǎn)發(fā)配額管理和計(jì)費(fèi)邏輯需要大量二次開發(fā)第二企業(yè)內(nèi)部的權(quán)限體系和組織架構(gòu)差異很大硬套開源方案的模型反而更麻煩第三模型供應(yīng)商的API變更頻繁自研可以快速響應(yīng)。當(dāng)然這不是說所有東西都要從零寫。協(xié)議轉(zhuǎn)換、日志采集、監(jiān)控告警這些通用能力能復(fù)用開源組件就復(fù)用。我的原則是核心治理邏輯自研通用基礎(chǔ)設(shè)施復(fù)用。2.3 關(guān)鍵設(shè)計(jì)決策與取舍同步還是異步網(wǎng)關(guān)對(duì)業(yè)務(wù)側(cè)暴露同步接口但內(nèi)部對(duì)模型調(diào)用采用異步加回調(diào)的方式。這樣做的好處是網(wǎng)關(guān)不會(huì)因?yàn)槟硞€(gè)慢請(qǐng)求被阻塞可以同時(shí)處理更多請(qǐng)求。代價(jià)是業(yè)務(wù)側(cè)需要支持異步回調(diào)模式對(duì)于簡(jiǎn)單的問答場(chǎng)景可能顯得重。流式返回怎么處理大模型輸出通常是流式的網(wǎng)關(guān)必須支持SSEServer-Sent Events透?jìng)?。我的做法是在網(wǎng)關(guān)層做流式解析每個(gè)chunk都過一遍內(nèi)容過濾然后再轉(zhuǎn)發(fā)給業(yè)務(wù)側(cè)。這會(huì)增加一點(diǎn)延遲但安全合規(guī)的要求不能妥協(xié)。多租戶隔離不同業(yè)務(wù)線共用網(wǎng)關(guān)但配額、密鑰、日志必須隔離。我在數(shù)據(jù)模型上用tenant_id做分區(qū)鍵所有查詢和寫入都帶這個(gè)條件避免數(shù)據(jù)串門。3. 自動(dòng)化編程Agent的接入實(shí)踐3.1 Agent與傳統(tǒng)調(diào)用的本質(zhì)區(qū)別傳統(tǒng)業(yè)務(wù)調(diào)模型基本是“一問一答”模式用戶發(fā)一個(gè)問題模型返回一個(gè)答案結(jié)束。但Agent不一樣它有一個(gè)循環(huán)執(zhí)行的過程思考、調(diào)用工具、觀察結(jié)果、再思考、再調(diào)用直到任務(wù)完成。這意味著Agent對(duì)網(wǎng)關(guān)提出了幾個(gè)新要求會(huì)話保持同一個(gè)任務(wù)的多輪調(diào)用需要關(guān)聯(lián)起來方便追蹤和計(jì)費(fèi)。工具調(diào)用透?jìng)鰽gent會(huì)觸發(fā)函數(shù)調(diào)用Function Calling網(wǎng)關(guān)需要正確透?jìng)鬟@些結(jié)構(gòu)化數(shù)據(jù)。長(zhǎng)超時(shí)支持一個(gè)復(fù)雜任務(wù)可能跑幾分鐘網(wǎng)關(guān)的超時(shí)設(shè)置要相應(yīng)放寬。并發(fā)控制Agent可能同時(shí)發(fā)起多個(gè)子任務(wù)網(wǎng)關(guān)要能識(shí)別并合理分配配額。3.2 CLI工具與網(wǎng)關(guān)的配合自動(dòng)化編程場(chǎng)景下CLI工具是Agent的重要入口。比如Codex CLI、各種代碼生成命令行工具它們背后都是調(diào)用模型API。我的做法是在CLI和模型之間插入網(wǎng)關(guān)CLI配置的API地址指向網(wǎng)關(guān)網(wǎng)關(guān)再轉(zhuǎn)發(fā)到真實(shí)模型。這樣做的好處很明顯CLI工具不需要知道真實(shí)API Key也不需要處理限流重試這些都由網(wǎng)關(guān)兜底。而且所有CLI的調(diào)用記錄都統(tǒng)一在網(wǎng)關(guān)側(cè)方便統(tǒng)計(jì)哪個(gè)團(tuán)隊(duì)、哪個(gè)項(xiàng)目用了多少額度。配置示例以常見的OpenAI兼容CLI為例# CLI配置文件中的設(shè)置 export OPENAI_BASE_URLhttps://gateway.internal.company.com/v1 export OPENAI_API_KEY內(nèi)部簽發(fā)的TokenCLI工具會(huì)以為自己直連了模型服務(wù)實(shí)際上所有請(qǐng)求都經(jīng)過了網(wǎng)關(guān)的治理。3.3 Agent并發(fā)扛壓的實(shí)戰(zhàn)經(jīng)驗(yàn)Agent場(chǎng)景下并發(fā)問題特別突出。我遇到過的情況是一個(gè)自動(dòng)化代碼審查Agent在高峰期同時(shí)處理20個(gè)倉(cāng)庫(kù)的PR每個(gè)PR觸發(fā)3到5次模型調(diào)用瞬間就是上百個(gè)并發(fā)請(qǐng)求。如果網(wǎng)關(guān)沒有做好并發(fā)控制要么被供應(yīng)商限流要么自己先崩了。我的解決方案是兩級(jí)隊(duì)列第一級(jí)在網(wǎng)關(guān)入口做請(qǐng)求排隊(duì)超過閾值的請(qǐng)求進(jìn)入等待隊(duì)列而不是直接拒絕第二級(jí)在供應(yīng)商Adapter做并發(fā)信號(hào)量控制確保對(duì)每家供應(yīng)商的并發(fā)數(shù)不超過其限制。等待隊(duì)列的長(zhǎng)度和超時(shí)時(shí)間根據(jù)業(yè)務(wù)重要性分級(jí)配置核心業(yè)務(wù)隊(duì)列長(zhǎng)一些非核心業(yè)務(wù)短一些。實(shí)操心得隊(duì)列不是越長(zhǎng)越好。我一開始把隊(duì)列設(shè)得很大結(jié)果請(qǐng)求堆積導(dǎo)致整體延遲飆升用戶體驗(yàn)反而更差。后來改成“短隊(duì)列快速失敗客戶端重試”的模式整體成功率反而更高。4. 從零搭建網(wǎng)關(guān)的核心步驟4.1 環(huán)境準(zhǔn)備與基礎(chǔ)框架先確定技術(shù)棧。我的選擇是Go 1.21以上版本配合Gin框架做HTTP服務(wù)Redis做配額計(jì)數(shù)和分布式鎖PostgreSQL存配置和日志元數(shù)據(jù)。監(jiān)控用Prometheus客戶端庫(kù)埋點(diǎn)。項(xiàng)目結(jié)構(gòu)大致如下gateway/ cmd/ # 啟動(dòng)入口 internal/ adapter/ # 各供應(yīng)商適配器 router/ # 路由引擎 quota/ # 配額管理 filter/ # 內(nèi)容過濾 observer/ # 觀測(cè)埋點(diǎn) config/ # 配置文件 deploy/ # 部署腳本4.2 協(xié)議轉(zhuǎn)換層的實(shí)現(xiàn)要點(diǎn)協(xié)議轉(zhuǎn)換是網(wǎng)關(guān)最核心也最繁瑣的部分。以O(shè)penAI格式為內(nèi)部標(biāo)準(zhǔn)其他供應(yīng)商的請(qǐng)求都要轉(zhuǎn)成這個(gè)格式。關(guān)鍵字段的映射關(guān)系需要仔細(xì)處理內(nèi)部字段OpenAI某國(guó)內(nèi)模型A某國(guó)內(nèi)模型Bmessagesmessagesprompt拼接messagesmax_tokensmax_tokensmax_lengthmax_new_tokenstemperaturetemperaturetemperaturetop_p配合streamstreamstreamstream轉(zhuǎn)換時(shí)要注意有些供應(yīng)商不支持system角色需要把system消息合并到第一條user消息里有些供應(yīng)商的流式返回格式不同需要重新組裝SSE事件。4.3 配額管理的具體實(shí)現(xiàn)配額管理用Redis的滑動(dòng)窗口算法。每個(gè)租戶在每個(gè)時(shí)間窗口內(nèi)的調(diào)用次數(shù)和Token消耗都記錄在Redis的有序集合里每次請(qǐng)求前檢查是否超限。# 偽代碼示意配額檢查邏輯 def check_quota(tenant_id, model, estimated_tokens): key fquota:{tenant_id}:{model}:{current_window()} current redis.zcard(key) if current tenant_limit[tenant_id][model]: return False redis.zadd(key, {request_id: now()}) redis.expire(key, window_size * 2) return TrueToken消耗的統(tǒng)計(jì)要等響應(yīng)完成后才能精確計(jì)算所以采用“預(yù)扣實(shí)扣”的方式請(qǐng)求前按預(yù)估Token預(yù)扣響應(yīng)后按實(shí)際用量調(diào)整。4.4 內(nèi)容過濾的落地方式內(nèi)容過濾分兩個(gè)方向請(qǐng)求過濾和響應(yīng)過濾。請(qǐng)求過濾檢查用戶輸入是否包含敏感信息響應(yīng)過濾檢查模型輸出是否合規(guī)。我用的方案是關(guān)鍵詞匹配加正則表達(dá)式對(duì)于更復(fù)雜的場(chǎng)景可以接入專門的內(nèi)容安全服務(wù)。過濾規(guī)則要可配置不同業(yè)務(wù)線可以有不同的嚴(yán)格程度。比如面向內(nèi)部開發(fā)者的代碼生成場(chǎng)景可以寬松一些面向外部用戶的產(chǎn)品則要嚴(yán)格。5. 常見問題與排查技巧5.1 典型問題速查表問題現(xiàn)象可能原因排查方向解決方案請(qǐng)求超時(shí)但模型側(cè)正常網(wǎng)關(guān)到模型的連接池耗盡檢查連接池配置和當(dāng)前連接數(shù)增大連接池或優(yōu)化連接復(fù)用流式返回中斷SSE解析遇到特殊字符抓包看原始數(shù)據(jù)修復(fù)解析邏輯增加容錯(cuò)配額計(jì)數(shù)不準(zhǔn)Redis時(shí)鐘漂移或并發(fā)競(jìng)爭(zhēng)對(duì)比Redis和日志數(shù)據(jù)用Lua腳本保證原子性某供應(yīng)商頻繁限流并發(fā)控制未生效檢查信號(hào)量實(shí)現(xiàn)修復(fù)并發(fā)控制邏輯日志丟失異步寫入緩沖區(qū)滿檢查日志隊(duì)列長(zhǎng)度增大緩沖區(qū)或改同步寫入5.2 踩過的坑與避坑指南坑一密鑰輪換導(dǎo)致服務(wù)中斷。有一次供應(yīng)商要求輪換API Key我直接在配置里替換后重啟網(wǎng)關(guān)結(jié)果因?yàn)榕渲眉虞d順序問題新Key還沒生效舊Key已經(jīng)失效導(dǎo)致幾分鐘的服務(wù)中斷。后來改成熱更新機(jī)制新Key先加載驗(yàn)證通過后再切換??佣魇巾憫?yīng)的Token統(tǒng)計(jì)偏差。流式返回時(shí)每個(gè)chunk的Token數(shù)不好精確計(jì)算我一開始用字符數(shù)除以4估算結(jié)果和供應(yīng)商賬單對(duì)不上。后來改成在網(wǎng)關(guān)側(cè)用Tokenizer精確計(jì)算雖然增加了一點(diǎn)CPU開銷但數(shù)據(jù)準(zhǔn)確了。坑三Agent的會(huì)話關(guān)聯(lián)丟失。Agent的多輪調(diào)用如果沒有正確傳遞會(huì)話ID網(wǎng)關(guān)就無法把它們關(guān)聯(lián)起來導(dǎo)致計(jì)費(fèi)分散、追蹤困難。解決方案是在請(qǐng)求頭里強(qiáng)制要求攜帶X-Session-Id網(wǎng)關(guān)側(cè)做校驗(yàn)和補(bǔ)全。提示網(wǎng)關(guān)上線前一定要做壓力測(cè)試特別是流式場(chǎng)景下的并發(fā)測(cè)試。我用Locust模擬了500并發(fā)流式請(qǐng)求發(fā)現(xiàn)了連接池和內(nèi)存泄漏兩個(gè)問題都是在測(cè)試階段解決的。5.3 監(jiān)控告警的關(guān)鍵指標(biāo)網(wǎng)關(guān)必須監(jiān)控的指標(biāo)包括請(qǐng)求量、成功率、P95延遲、Token消耗速率、配額使用率、各供應(yīng)商的錯(cuò)誤率。告警閾值要根據(jù)業(yè)務(wù)特點(diǎn)設(shè)置比如成功率低于99%告警某供應(yīng)商錯(cuò)誤率連續(xù)5分鐘超過5%觸發(fā)降級(jí)。6. 自動(dòng)化編程場(chǎng)景的擴(kuò)展思考6.1 Agent Skill與網(wǎng)關(guān)的結(jié)合現(xiàn)在很多Agent框架支持Skill機(jī)制每個(gè)Skill是一段可復(fù)用的能力。網(wǎng)關(guān)可以作為Skill的底層支撐比如一個(gè)“代碼生成”Skill背后就是調(diào)用網(wǎng)關(guān)的模型接口。這樣Skill的開發(fā)者不需要關(guān)心模型選型和密鑰管理專注于業(yè)務(wù)邏輯。6.2 多Agent協(xié)作下的網(wǎng)關(guān)角色當(dāng)多個(gè)Agent協(xié)作完成一個(gè)任務(wù)時(shí)網(wǎng)關(guān)不僅是模型調(diào)用的通道還可以承擔(dān)協(xié)調(diào)者的角色。比如限制整個(gè)任務(wù)的總Token預(yù)算當(dāng)預(yù)算快用完時(shí)通知Agent調(diào)整策略。這需要在網(wǎng)關(guān)側(cè)增加任務(wù)級(jí)別的配額管理。6.3 未來可能的演進(jìn)方向網(wǎng)關(guān)下一步可以往智能化路由方向發(fā)展根據(jù)請(qǐng)求的內(nèi)容特征自動(dòng)選擇最合適的模型。比如代碼生成用擅長(zhǎng)代碼的模型文案創(chuàng)作用擅長(zhǎng)文字的模型簡(jiǎn)單問答用便宜的小模型。這需要積累足夠的調(diào)用數(shù)據(jù)來訓(xùn)練路由策略。我在實(shí)際項(xiàng)目中的體會(huì)是大模型網(wǎng)關(guān)這件事技術(shù)難度不是最高的難的是把治理邏輯想清楚。哪些該管、哪些不該管、管到什么程度這些決策比寫代碼更重要。一開始不要追求大而全先把統(tǒng)一接入和配額管理做扎實(shí)后面再逐步疊加內(nèi)容過濾、智能路由這些能力。每加一個(gè)功能都要問自己這個(gè)功能解決的是什么真實(shí)問題不解決會(huì)怎樣。想不清楚的就先不做等有了實(shí)際需求再加。