化改造:私有化部署與協(xié)作路由實戰(zhàn))
把開源版 WorkBuddy 改造成企業(yè)版這件事起源于我們團隊內(nèi)部很多人的一個共識公共大模型工具用起來確實爽但一涉及公司內(nèi)部數(shù)據(jù)心里總是沒底。一次模型供應(yīng)商的接口調(diào)整引發(fā)了幾次誤調(diào)用管理層干脆叫停了非白名單工具的使用整個團隊的效率瞬間掉了一截。那陣子我正好在研究開源版的 WorkBuddy看它有沒有可能在內(nèi)部私有化跑起來。折騰了大概一個月我不僅把模型服務(wù)換了公司內(nèi)部的私有化部署版本還順手把同事這個原本只是聊天功能的東西做成了一套能真正派活、能觸達真人的協(xié)作入口。這篇文章就把我完整的改造鏈路、選型理由、踩過的坑和最終收益一次性講清楚。如果你所在的團隊也在做類似的嘗試比如打算把某個開源 AI 助手私有化或者想在公司內(nèi)部模型服務(wù)和團隊協(xié)作之間打通一條路那這篇文章應(yīng)該能讓你少走不少彎路。它涵蓋的內(nèi)容包括開源版 WorkBuddy 到底適不適合做改造基座、接公司模型時 API 之外的坑、同事 功能的真實設(shè)計邏輯以及企業(yè)落地繞不開的 SSO 和審計問題。我不是什么大廠架構(gòu)師只是一個啃過源碼、翻過文檔、也對著日志懷疑過人生的普通開發(fā)者寫出來的東西可能不夠理論化但一定都是從實際代碼和線上故障里得出的經(jīng)驗。1. 先搞清楚一個根本問題為什么是開源版 WorkBuddy而不是自研或者直接用 SaaS1.1 開源版原本擅長什么在決定改造之前我把 WorkBuddy 的源碼和文檔完整過了一遍。它的定位是本地優(yōu)先的 AI 工作臺核心能力集中在對話管理、上下文記憶、插件機制和知識庫掛載這幾塊。它的前端寫得很清爽對話界面、會話歷史、附件上傳這些基礎(chǔ)體驗都是現(xiàn)成的不需要我從零去畫界面。后端則走的是典型的模型不可知設(shè)計模型接口被抽象成一層 Provider這意味著我可以不碰業(yè)務(wù)代碼只改配置和新增適配器就能切換模型來源。這一點很關(guān)鍵。很多同類工具把模型適配寫死在代碼里換一個模型服務(wù)等于重寫一遍而 WorkBuddy 的設(shè)計天然允許你指定 Base URL、模型名稱、API Key 的讀取位置。底層數(shù)據(jù)存儲用的是本地文件和輕量數(shù)據(jù)庫的組合這讓我在改造數(shù)據(jù)模型、加入企業(yè)用戶字段的時候少了不少掣肘。另一點讓我下定決心的是它的許可證比較寬松允許內(nèi)部修改和二次分發(fā)省去了合規(guī)層面的周旋。坦白講WorkBuddy 的開源版本身也有一堆毛病。它的多用戶支持約等于沒有基本是一個人用一個實例的邏輯。權(quán)限體系、組織架構(gòu)、審計流水線這些企業(yè)級詞匯它一個都不沾邊。但正因為骨架足夠干凈改造起來反而比那種全家桶式軟件好操作。你給它一個單用戶的殼它還你一個靈活的底子剩下的就看你怎么填。1.2 我為什么不直接選 SaaS 產(chǎn)品市面上現(xiàn)成的企業(yè)級 AI 協(xié)作平臺其實不少界面漂亮、功能完善開箱即用穩(wěn)定性也有保障。但我當(dāng)時面臨一個實際限制公司內(nèi)部對數(shù)據(jù)出網(wǎng)有嚴格要求任何包含業(yè)務(wù)關(guān)鍵詞的對話記錄都不允許流向外部接口。這意味著所有經(jīng)過 SaaS 平臺的對話內(nèi)容都需要過一遍外部接口這在合規(guī)上直接不成立。即便供應(yīng)商承諾數(shù)據(jù)隔離合同條款寫得多漂亮也架不住內(nèi)部安全團隊那一關(guān)根本批不下來。另一層原因是模型成本不可控。SaaS 平臺通常按 token 計費而且對私有化部署的模型能力支持有限。我們內(nèi)部正好有一支團隊長期維護一套基于開源模型打造的推理服務(wù)與其繞道外部 API 再回來不如直接在內(nèi)網(wǎng)完成閉環(huán)。私有大模型雖然在某些任務(wù)上不如商業(yè) API 能力強但勝在可以針對團隊自己的使用習(xí)慣做微調(diào)而且每次調(diào)用都在內(nèi)網(wǎng)完成延遲和成本都穩(wěn)定得多。還有個容易被忽略的點協(xié)作閉環(huán)。SaaS 產(chǎn)品哪怕做一萬個功能也很難和公司內(nèi)部的 IM、郵件、會議室、任務(wù)系統(tǒng)完全打通。而 WorkBuddy 改造后我可以直接把它接進公司現(xiàn)有的通知體系A(chǔ)I 生成的結(jié)果可以一鍵轉(zhuǎn)給具體的人去確認這中間的價值遠大于一個純粹聊天的 AI 工具。1.3 我給自己定的三個改造目標整個項目立項的時候我沒有貪多只定了三條主線后來的開發(fā)工作幾乎全部圍繞這三條線展開。第一模型可控。讓 WorkBuddy 通過統(tǒng)一的適配層接入公司內(nèi)部署的模型服務(wù)所有對話記錄和上下文全部保留在內(nèi)網(wǎng)并且支持按模型維度做灰度切換。第二身份打通。接上公司統(tǒng)一的賬號認證體系讓員工可以直接用已有的企業(yè)賬號登錄不用再單獨注冊一套賬號密碼。這也是后續(xù)權(quán)限管理的基礎(chǔ)。第三協(xié)作閉環(huán)。在原版對話能力之上增加同事的能力——在對話里提到某個人系統(tǒng)要把這個人識別出來把相關(guān)任務(wù)或問題定向分配給 TA并通過 IM 和站內(nèi)通知的形式觸達對方。這樣 AI 助手就不再是孤島工具而是一個能連接真人的協(xié)作節(jié)點。這三個目標聽起來都不算復(fù)雜但每一項在真正落地時都比預(yù)期多花了不少時間。下面我按改造順序逐個拆解。2. 接公司模型這步坑全在 API 之外的細節(jié)里2.1 公司模型服務(wù)到底長什么樣說到接公司模型很多人的第一反應(yīng)是改一個 base_url把原來的 OpenAI 指向換成內(nèi)部地址。理論上是這樣但實際操作完全不是一回事。我們公司內(nèi)部的模型服務(wù)由另一個團隊維護他們的對外接口是一個基于內(nèi)網(wǎng)網(wǎng)關(guān)的 OpenAI 兼容協(xié)議。這意味著大部分網(wǎng)絡(luò)請求格式和 OpenAI 是相似的但鑒權(quán)方式完全不同公共 API 用的是 API Key而內(nèi)部網(wǎng)關(guān)走的是內(nèi)部統(tǒng)一認證服務(wù)簽發(fā)的臨時令牌令牌還有時效性過期后必須自動續(xù)簽。另外內(nèi)部模型服務(wù)的部署形態(tài)也和公共 API 不一樣。公共 API 通常在全球有多個節(jié)點延遲穩(wěn)定在幾十毫秒級別而內(nèi)部服務(wù)只有幾個 GPU 節(jié)點遇到高峰期會排隊有時還會因為模型換版本導(dǎo)致接口參數(shù)出現(xiàn)細微變動。這個差異直接影響了我后面對于超時、重試和隊列策略的設(shè)計。還有一個很容易忽略的點是模型列表的動態(tài)性。公共 API 的模型清單相對固定基本每個月加一兩個新模型。而內(nèi)部服務(wù)因為經(jīng)常做實驗性部署模型名可能一周就換一次甚至同名的模型后面對應(yīng)的實際底座也會變化。所以我不能把模型名寫死在配置里必須做一個可動態(tài)刷新的模型列表接口讓 WorkBuddy 每次讀取模型時都向網(wǎng)關(guān)拉取最新的可用清單。2.2 我在適配層里做的四件事為了讓 WorkBuddy 和內(nèi)部網(wǎng)關(guān)能夠穩(wěn)定協(xié)作我寫了一個獨立的模型適配服務(wù)放在 WorkBuddy 后端和公司模型網(wǎng)關(guān)之間。這個服務(wù)承擔(dān)了四件關(guān)鍵事情鑒權(quán)代理、協(xié)議歸一、流式轉(zhuǎn)發(fā)、限流熔斷。鑒權(quán)代理是最先要解決的。內(nèi)部網(wǎng)關(guān)要求每次請求都附帶有效的認證令牌而這塊令牌的獲取邏輯不在 WorkBuddy 范圍內(nèi)也不能落在前端。適配服務(wù)作為中間人負責(zé)定時刷新令牌池并在每次向上游轉(zhuǎn)發(fā)時自動注入頭部。對 WorkBuddy 側(cè)來說它只需要知道適配服務(wù)自己的地址和一套靜態(tài)內(nèi)部憑據(jù)就可以無感使用公司模型。這樣做還有一個額外好處令牌的刷新和失效完全對上層透明哪怕公司后續(xù)調(diào)整認證協(xié)議也不用手動去改 WorkBuddy 的配置。協(xié)議歸一其實做的是接口翻譯。盡管公司網(wǎng)關(guān)號稱兼容 OpenAI 格式但細節(jié)上總有出入。比如 OpenAI 版本對話參數(shù)里的 system 消息在新的模型版本里可能需要做特殊映射有些新模型對參數(shù)名極其敏感多傳一個廢棄字段直接報錯。適配服務(wù)負責(zé)把這些差異吞掉讓 WorkBuddy 面對的是一個穩(wěn)定的、字段標準化的模型接口。流式轉(zhuǎn)發(fā)則是一個技術(shù)上不復(fù)雜但小坑很多的地方。AI 模型回復(fù)走的是 SSE 流式協(xié)議用戶在界面看到字體逐個蹦出來的效果就是前端在不斷消費流式數(shù)據(jù)的結(jié)果。適配層需要原樣轉(zhuǎn)發(fā)這個流同時還要處理流異常中斷時的重連。如果適配層因為超時把流截斷了前端會表現(xiàn)為模型回了一半字突然停住這個現(xiàn)象非常勸退用戶。限流熔斷是我后來補上的。內(nèi)部 GPU 節(jié)點資源有限一個用戶的大段上下文請求可能會占住顯存很長時間多個請求同時打過去會導(dǎo)致排隊越來越嚴重。適配服務(wù)參考了網(wǎng)關(guān)的返回狀態(tài)一旦發(fā)現(xiàn)上游負載過高就主動放慢新請求的接入節(jié)奏同時對已經(jīng)進隊列的請求做超時保護避免讓用戶無限等待。2.3 從單用戶 API Key到按人按組授權(quán)原版 WorkBuddy 理論上只需要一個全局 API Key所有對話都從這個 Key 出發(fā)。但企業(yè)內(nèi)部使用場景不允許這種粗放模式不同部門的人對模型的使用權(quán)限是不同的有的部門可以用更強的模型跑長文本有的部門只能跑基礎(chǔ)模型還有人根本不應(yīng)該被允許使用某些功能。我在適配層里引入了一套簡單的授權(quán)策略。規(guī)則不復(fù)雜每個用戶在登錄后WorkBuddy 后端會拿到他的用戶信息和部門歸屬適配服務(wù)每轉(zhuǎn)發(fā)一次請求都會帶上這些身份屬性。網(wǎng)關(guān)側(cè)的規(guī)則引擎會判斷這個身份可以調(diào)哪些模型配額是多少超出的部分直接拒絕。這等于把權(quán)限控制前置到了模型調(diào)用這個環(huán)節(jié)而不是等對話記錄落庫以后再做過濾。這個改造的難點不在于代碼量而在于用戶身份要在請求鏈路里完整傳遞。WorkBuddy 原本的請求上下文里只有比較簡單的用戶標識我在改造時專門梳理了一遍請求鏈路確保從瀏覽器端到適配層再到網(wǎng)關(guān)用戶的身份屬性不會丟。丟了的話權(quán)限控制就會變成空話。我后來做了一個調(diào)試頁面專門展示每次模型請求對應(yīng)的用戶上下文和授權(quán)結(jié)果排查權(quán)限相關(guān)問題時非常有用。2.4 幾個必須配置好的參數(shù)跑通基本鏈路后我整理了一份適配層的推薦配置清單踩過坑之后再回看這幾個參數(shù)每一個都是血淚教訓(xùn)參數(shù)推薦值說明上游超時300s長上下文生成任務(wù)經(jīng)常超過 60s默認 30s 一定會斷流式空閑時間60s如果 60s 內(nèi)上游沒有任何流式數(shù)據(jù)返回觸發(fā)重連失敗重試次數(shù)2超過 2 次直接返回錯誤給用戶避免排隊雪崩令牌刷新提前量5min在令牌真正過期前 5 分鐘就刷新避免邊界競態(tài)隊列最大長度200超出后新請求直接拒絕返回當(dāng)前模型繁忙提示我第一次部署時就是沒把超時和重試這兩個參數(shù)調(diào)好結(jié)果用戶體驗非常糟糕問一個復(fù)雜問題生成到一半就斷了重新問一次又可能觸發(fā)兩次并發(fā)請求把 GPU 顯存直接擠爆。后來我把超時拉長同時把失敗重試改成指數(shù)退避策略狀況才明顯好轉(zhuǎn)。這些參數(shù)不會寫在 WorkBuddy 的默認配置里需要自己在適配層管理但它們的價值遠大于寫一百行業(yè)務(wù)代碼。3. 同事 不是聊天功能而是協(xié)作路由機制3.1 從所有事情都問 AI到AI 也能找人幫忙團隊剛開始使用 WorkBuddy 時大家都拿它當(dāng)增強版搜索引擎。比如讓 AI 總結(jié)會議紀要、續(xù)寫文案、查代碼邏輯基本是單人操作。但很快我就發(fā)現(xiàn)一個真實需求AI 生成的很多內(nèi)容需要人工確認或者跑完一步后必須由特定角色來接手。這時候用戶得復(fù)制一堆內(nèi)容切到 IM找到對應(yīng)的同事粘貼發(fā)送然后再切回來。整個流程非常割裂。于是AI 對話框里直接找出這個人就成了一個自然而然的產(chǎn)品需求。這里要說明我在規(guī)劃 同事 功能時并沒有打算做一個傳統(tǒng) IM 那樣的聊天提醒。我希望的是當(dāng)我在 WorkBuddy 對話框里輸入把這份需求的驗收標準整理好阿哲 幫忙確認系統(tǒng)能夠完成三件事識別出阿哲這個人把這段對話和上下文形成一個待辦事項通過公司內(nèi)部 IM 的機器人把通知發(fā)到阿哲那里阿哲點開通知后直接看到這條對話的完整上下文可以在 WorkBuddy 里回復(fù)并且回復(fù)內(nèi)容會作為同事協(xié)作記錄沉淀回同一個會話里。這套邏輯從產(chǎn)品角度叫協(xié)作閉環(huán)從實現(xiàn)角度其實就是一整套路由機制誰提到誰、誰確認哪個任務(wù)、回復(fù)去哪兒。整個功能并不存在什么深奧的技術(shù)難點但確實是 WorkBuddy 原生代碼里完全不存在的東西需要從零設(shè)計。3.2 解析層怎么把幫我 阿哲 看看這個變成結(jié)構(gòu)化數(shù)據(jù)市面上解析 的常規(guī)做法是正則匹配但這在中文場景下有個天然痛點同事的名字千奇百怪有的是三個字有的是兩個字有的是英文名加花名。直接靠正則去抓 開頭、直到空格或標點結(jié)束的字符串很容易把無關(guān)內(nèi)容截進來。我最后的實現(xiàn)思路是雙通道解析先讓 WorkBuddy 自己通過一個輕量模型做語義識別把對話中指名道姓的部分提取成列表然后用規(guī)則引擎做兜底校驗。為什么用雙通道因為我已經(jīng)有模型網(wǎng)關(guān)可用讓模型做一次實體抽取實在是很順手的事準確率遠比正則高。但模型抽取有概率抽取出不存在的人名或者把非 場景的張總王哥這類稱呼也抓進來這時候就需要規(guī)則引擎去比對用戶目錄過濾掉無效實體。放在實際流程里是這樣的用戶發(fā)送一條消息后WorkBuddy 后端先去用戶中心拉一次組織架構(gòu)快照然后用快照做正則初篩找出所有疑似 的片段接著把整個消息文本和候選名單交給模型讓模型確認哪些是真實指向某個同事的并給出候選人的部門信息用來消歧。最后把確認過的候選人集合返回前端前端彈一個確認 誰的下拉框用戶點選確認后消息才正式發(fā)送。這個人工確認步驟一開始被產(chǎn)品經(jīng)理吐槽繁瑣但實際上它解決了大問題同一家公司如果存在兩個同名阿哲系統(tǒng)直接列出部門和頭像讓用戶選擇避免發(fā)錯人的尷尬。上線以來因為這個確認步驟引起的誤發(fā)消息數(shù)是零。3.3 路由層任務(wù)如何派發(fā)、通知怎么送達解析層只負責(zé)認出人真正讓 同事 生效的是路由層。當(dāng)一條消息確認 了某個人之后WorkBuddy 后端會執(zhí)行以下幾件事在會話對象下創(chuàng)建一個協(xié)作待辦記錄字段包括發(fā)起人、接收人、原消息、會話鏈接、狀態(tài)、截止時間然后通過內(nèi)部 IM 機器人的 webhook 向接收人發(fā)送一條消息消息卡片里附帶該會話的鏈接和簡要上下文同時把這條協(xié)作記錄寫入對應(yīng)會話的協(xié)作時間線方便后續(xù)追溯。這里面相對棘手的是通知渠道。公司內(nèi)部 IM 的消息機器人接口通常比較穩(wěn)定但發(fā)送頻率有限制瞬時發(fā)太多會被限流。我在路由層里加了一個進程內(nèi)隊列所有協(xié)作通知先進隊列再按 5 條每秒的節(jié)奏勻速發(fā)送避免觸發(fā)限流。通知失敗不要緊失敗的消息會重試三次最終仍然失敗就轉(zhuǎn)成站內(nèi)信待辦用戶登錄 WorkBuddy 后會看到你有未處理協(xié)作請求的橫幅。還有一個細節(jié)是通知內(nèi)容的隱私性。機器人推送的消息卡片不能包含完整對話明文否則任何路過的人瞄一眼屏幕都可能看到業(yè)務(wù)內(nèi)容。我最終的實現(xiàn)方式是卡片里只包含一段摘要發(fā)起人、會話主題、 你的那句話再附上點擊查看詳情。完整內(nèi)容必須通過 WorkBuddy 的會話打開查看同時校驗接收人是否有權(quán)限進入該會話。這個設(shè)計兼顧了通知的即時性和內(nèi)容的安全性。3.4 權(quán)限與邊界能 誰誰能看這段對話同事 功能有三個權(quán)限邊界如果不設(shè)清楚功能上線后一定會出亂子。第一個邊界是可見性。并非所有員工都能出現(xiàn)在 候選列表里。我的規(guī)則是只有當(dāng)前會話的參與人、組織架構(gòu)上屬于同一部門或協(xié)作項目組的成員才會出現(xiàn)在候選名單避免用戶隨手 到完全不相關(guān)的人。這個規(guī)則不是硬限制管理員可以在后臺調(diào)整但默認配置偏向保守。第二個邊界是會話權(quán)限。原版 WorkBuddy 的會話本質(zhì)上是私有數(shù)據(jù)只有創(chuàng)建者能看到。改造后我引入了會話成員的顯式授權(quán)一個會話可以被發(fā)起人添加一個或多個協(xié)作者協(xié)作者有權(quán)查看會話歷史、在會話內(nèi)回復(fù)、在對話里追加 其他成員。非成員即使拿到會話鏈接也會看到您無權(quán)訪問該會話的提示頁。這個邏輯聽起來簡單但原版代碼里并沒有會話成員表我需要額外建表并修改前端的路由守衛(wèi)工作量比想象中多。第三個邊界是操作審計。用戶發(fā)出的每一個協(xié)作指令都會被記錄誰在什么時候 了誰接收人做了什么處理會話最終有沒有閉環(huán)。這些記錄一方面滿足合規(guī)要求另一方面也方便復(fù)盤哪類問題總在同事間反復(fù)流轉(zhuǎn)但沒解決對后續(xù)優(yōu)化團隊協(xié)作流程很有價值。審計記錄不外發(fā)只在 WorkBuddy 管理后臺可見只有管理員賬號能夠查詢。4. 企業(yè)落地繞不開的三座大山SSO 登錄、審計日志、部署運維4.1 SSO 集成把 WorkBuddy 的登錄框整個換掉原版 WorkBuddy 的登錄模塊非常簡單就是注冊手機號或郵箱然后輸密碼。企業(yè)內(nèi)部部署顯然不能用這套邏輯否則賬號體系就是第二個孤島。我的做法是把登錄入口改成公司統(tǒng)一門戶的 OIDC 認證流程WorkBuddy 后端不再自己驗證密碼而是把所有登錄請求都重定向到公司認證中心用戶輸完企業(yè)賬號密碼后認證中心回調(diào) WorkBuddy 的回調(diào)地址攜帶認證碼后端再通過這個認證碼換取用戶身份和部門信息。這個改造的難點在于 WorkBuddy 原本的會話管理是自研的一套 Session 機制它不會自動理解 OIDC 的用戶身份。我需要在登錄成功之后做一次身份映射把認證中心返回的企業(yè)用戶 ID 映射到 WorkBuddy 內(nèi)部的用戶 ID如果是第一次登錄自動創(chuàng)建內(nèi)部用戶并標記來源為 SSO。整個過程用到了原版代碼里幾乎沒碰過的 User 數(shù)據(jù)表還要處理用戶離職后賬號如何停用這種邊界場景。這里有個很小但極其容易踩的坑OIDC 回調(diào)地址的協(xié)議和端口必須和實際部署時的完全一致。我在測試環(huán)境用的是 HTTP 8080 端口回調(diào)地址寫的是 http://xxx 結(jié)果上了生產(chǎn)環(huán)境配了 HTTPS 443我忘了改回調(diào)地址導(dǎo)致用戶每次登錄都報回調(diào)地址不合法。當(dāng)時排查了幾個小時最后才發(fā)現(xiàn)是回調(diào)地址配置沒跟著環(huán)境同步。后來我把這個配置從代碼里抽出來放到環(huán)境變量里再也沒出過問題。4.2 審計日志在夠用和過度記錄之間找到平衡企業(yè)環(huán)境做這種 AI 工具審計日志是躲不開的。但審計日志怎么做很有講究。假設(shè)把所有對話明文都記錄下來一旦數(shù)據(jù)庫被拖走那危害不亞于對話內(nèi)容直接泄露到外部。假設(shè)什么都不記出了事想回溯又無從下手。我最后選擇的策略是分級記錄用戶身份、會話 ID、時間戳、調(diào)用的模型類型、對話消息量、本次請求的返回狀態(tài)全部記錄在審計表中但對話的完整明文不落庫或者只在會話自身的存儲里保留并且這個存儲也做了加密。如果確實需要對某條對話做具體內(nèi)容追溯可以通過一個管理員專屬的安全復(fù)盤接口單獨拉取指定會話的內(nèi)容并且拉取行為本身也會生成一條審計事件。這樣做的好處是日常審計日志的敏感度降到最低就算日志被別人讀到了也不會直接泄露對話內(nèi)容而真正需要內(nèi)容級追蹤的場景仍然有手段并且全程留痕。審計日志的保留周期我建議至少一年。內(nèi)部合規(guī)檢查時這件事被反復(fù)確認過。同時日志表要按時間加索引否則數(shù)據(jù)量一上來查詢一個季度內(nèi)的操作記錄會慢到讓人懷疑人生。4.3 部署形態(tài)一套 Docker Compose 加反向代理整個系統(tǒng)最后以一套 Docker Compose 的形式跑在公司內(nèi)網(wǎng)的一臺服務(wù)器上。服務(wù)包括 WorkBuddy 后端、適配服務(wù)、前端靜態(tài)資源、數(shù)據(jù)庫。對外用 Nginx 做反向代理統(tǒng)一端口終止 HTTPS同時配置 WebSocket 支持因為 WorkBuddy 前端和后臺之間有實時通信通道。部署上最需要注意的一個點是 WorkBuddy 的前端構(gòu)建產(chǎn)物里經(jīng)常會內(nèi)置連接地址。我一開始沒留意這個直接拿默認配置構(gòu)建結(jié)果前端頁面請求的是 localhost后端服務(wù)明明在另一臺機器上頁面就是連不上。解決辦法是把后端地址配置為環(huán)境變量注入構(gòu)建時用相對路徑或者運行時配置覆蓋。這類問題在本地開發(fā)時永遠不可能暴露人家連的就是本機一切正常但一上服務(wù)器就把配置分離的問題暴露無遺。備份策略也值得一提。數(shù)據(jù)庫每天自動備份會話存儲目錄則通過定時任務(wù)同步到內(nèi)部存儲服務(wù)器?;謴?fù)流程我實際演練過一次從備份恢復(fù)到服務(wù)可用大概花了十幾分鐘這個恢復(fù)時長在可接受范圍內(nèi)。更重要的收獲是演練過程中我發(fā)現(xiàn)備份腳本有一個隱患它沒有保留最近三份版本而是直接覆蓋那樣如果某天的備份是壞數(shù)據(jù)就會把之前的也沖掉。改掉之后每次備份都帶時間戳保留最近三十份。5. 上線 30 天踩坑實錄從能用到好用的距離5.1 流式輸出斷連前端等了 20 秒才開始顯示第一個字上線初期最影響用戶信心的問題是流式輸出不穩(wěn)定。用戶問一個問題界面要等很久才開始顯示回復(fù)內(nèi)容有時干脆一個字都不出過一會兒提示網(wǎng)絡(luò)錯誤。我沿著鏈路排查最終鎖定的根因是反向代理的緩沖機制。Nginx 默認會緩沖后端返回的數(shù)據(jù)要攢到一定量才發(fā)給瀏覽器這對 SSE 流式響應(yīng)非常致命。前端看到的現(xiàn)象就是首字延遲極高甚至超時。解決方案是給對應(yīng)的 location 加上關(guān)閉緩沖的配置同時把 HTTP 版本設(shè)為 1.1允許流式響應(yīng)逐塊傳遞。這個改動只有一個參數(shù)但效果立竿見影流式輸出的首字延遲從十幾秒降到一秒以內(nèi)。排查這個問題的過程本身也很值得分享。我先是在瀏覽器網(wǎng)絡(luò)面板看到響應(yīng)處于 pending 狀態(tài)以為是后端處理慢于是去查后端日志發(fā)現(xiàn)響應(yīng)早就開始發(fā)送了說明數(shù)據(jù)是從服務(wù)器出來后在某條鏈路被卡住了。再加了緊跟反代層的日志才發(fā)現(xiàn)是 Nginx 緩沖掉了。所以碰到流式問題不要一上來就懷疑模型先從前端網(wǎng)絡(luò)請求面板出發(fā)逐步往后端、反代、上游方向排查。5.2 同名消歧兩個阿哲引發(fā)的 22 分鐘錯亂有次上線后我收到一個真實反饋用戶發(fā)了一條 阿哲 那個客戶方案你再看下過了幾分鐘一個完全不是目標同事的人回復(fù)了這不是我負責(zé)的客戶吧。原來公司真有兩個阿哲一個在銷售部一個在技術(shù)部。用戶選擇下拉框的時候手滑沒在意選成了技術(shù)部那個鬧出個不大不小的烏龍。解決方案分為兩檔。一檔是在下拉框里同時展示部門和頭像強制用戶看到足夠的上下文再選另一檔是為協(xié)同頻繁的同事之間增加最近協(xié)作的人是否排序把解析結(jié)果里頻率最高的候選人放到第一位。這兩點拼在一起誤選率降到了幾乎為零。我也意識到任何涉及人與人之間觸達的功能模糊匹配必須讓用戶顯式確認任何自動化都不能完全替代人的判斷。代碼再聰明一旦點錯就涉及尷尬和信任問題。5.3 重試風(fēng)暴一次模型過載引發(fā)的連鎖故障某個下午內(nèi)部模型服務(wù)因為另外一個大團隊的批處理任務(wù)導(dǎo)致推理響應(yīng)變慢。WorkBuddy 這邊的用戶發(fā)起新請求后遲遲收不到結(jié)果客戶端自動重試邏輯被觸發(fā)。重試是好事可我不小心把重試范圍設(shè)得過大而且重試間隔是固定幾秒用戶一多重試請求像洪水一樣涌向適配層進一步加重了上游的負擔(dān)。結(jié)果就是模型服務(wù)徹底過載新請求全部被拒頁面到處報錯。這是典型的重試放大故障。我把重試策略徹底重寫了一遍首次重試延遲 1 秒之后每次乘以 2最大畫在 30 秒同一個用戶的同一會話并發(fā)請求數(shù)上限為 1多余的直接提示上一條還沒完成適配層看到上游返回 503 類錯誤時不再轉(zhuǎn)發(fā)新請求而是直接返回友好錯誤并記錄到面板中。調(diào)整之后哪怕上游再出問題整個系統(tǒng)也只是變慢而不是崩潰。運營維護 AI 服務(wù)重試策略的重要性真的不輸給模型本身的選型。5.4 改了配置沒生效瀏覽器緩存和鏡像緩存一起坑人開發(fā)完新版本后我給測試環(huán)境部署了一版帶有新的模型列表和 功能配置的鏡像。測試反饋說仍然看到舊的模型名也沒有 候選框。我檢查后端服務(wù)、環(huán)境變量、數(shù)據(jù)庫配置全都沒有問題。最后才注意到最新構(gòu)建的 Docker 鏡像里Nginx 鏡像層把之前構(gòu)建過的舊靜態(tài)資源層原樣保留了瀏覽器那邊又緩存了更早的 JavaScript 文件。兩重緩存疊加導(dǎo)致我看到的永遠是新代碼測試看到的永遠是舊代碼。處理方式是雙管齊下構(gòu)建鏡像時清理全局緩存層靜態(tài)資源文件名加哈希指紋并配置 Nginx 對帶哈希的資源做長效緩存、對 HTML 入口做 no-cache。瀏覽器端也通過發(fā)版時換個靜態(tài)資源目錄路徑強制刷新。這些改動本身不值錢但如果沒有遇到過這種明明對卻沒用的假象新手很難想到問題出在緩存上。6. 改造之后的運營情況以及我還想繼續(xù)動刀的方向6.1 團隊真實使用情況上線到現(xiàn)在一個多月團隊里的活躍使用人數(shù)大概穩(wěn)定在部門人數(shù)的六成左右日均對話量維持在幾百輪。主動 同事 的協(xié)作消息量雖然只占全部消息的一成但每一輪都確實替代了一次 IM 上的消息搬運。用戶反饋里最受歡迎的功能一個是對話中可以直接生成初稿然后 對應(yīng)人去確認另一個是所有協(xié)作記錄都能在會話里找到不用再去聊天記錄里翻。這些反饋讓我確定當(dāng)初定下的模型可控、身份打通、協(xié)作閉環(huán)三個目標方向是對了。不過在普及率上還有很多問題。不少人把它當(dāng)作別人的 AI 工具覺得要登錄內(nèi)網(wǎng)才能訪問麻煩。我下一步計劃把 WorkBuddy 的入口接到公司門戶首頁去減少用戶尋找成本。另一個反饋是希望手機上也能方便使用目前 Web 端只在電腦瀏覽器上做了適配手機瀏覽器打開體驗一般。這個只能等后續(xù)安排專門做移動端適配優(yōu)先級不算最高。6.2 現(xiàn)有邊界它能做什么和不做什么我得說清楚這套企業(yè)版 WorkBuddy 目前的邊界。它不是一個全能協(xié)作平臺做不到像專業(yè)項目管理工具那樣完整地記錄人、事、時間的復(fù)雜依賴關(guān)系。同事 功能的核心是把 AI 產(chǎn)出轉(zhuǎn)交給真人而不是替代任務(wù)系統(tǒng)。它也不支持復(fù)雜的審批流比如需要主管確認后才能把結(jié)果發(fā)送到群里這類流程目前需要外部系統(tǒng)配合。模型能力方面內(nèi)部模型在某些通用常識類任務(wù)上依然是短板團隊使用時會明顯感覺到回答質(zhì)量在部分場景不如商業(yè) API。好在我當(dāng)時設(shè)計了模型適配層這條橋是搭好的如果后續(xù)內(nèi)部部署了更強的模型或者安全策略允許對接外部模型做兜底只需要調(diào)整路由策略就能實現(xiàn)切換不需要動業(yè)務(wù)代碼。這就是當(dāng)時堅持把模型適配獨立出來的遠期價值。6.3 下一步三個明確的方向接下來我打算動刀的地方有三個。第一個是給會話增加智能標簽讓系統(tǒng)自動學(xué)習(xí)團隊經(jīng)常討論的問題類型比如需求評審、Bug 復(fù)現(xiàn)、方案設(shè)計并基于這些標簽優(yōu)化對話和協(xié)作通知的分組展示。第二個是支持更細粒度的模型權(quán)限目前只是按部門和角色配置大概的可用模型范圍下一步希望做到某個具體用戶或某個具體會話可以綁定指定模型進一步滿足不同業(yè)務(wù)場景的需求。第三個是把協(xié)作閉環(huán)做深不僅要能 人還希望一個協(xié)作事項被處理后系統(tǒng)能自動整理處理結(jié)論并回填到原始會話這樣整個過程形成一個可追溯、可復(fù)盤的工作流。這些方向每一個都會花不少時間而且都需要持續(xù)收集用戶反饋來校準。好在我已經(jīng)把基礎(chǔ)設(shè)施和代碼結(jié)構(gòu)打得夠穩(wěn)后續(xù)不管做哪個方向都不用擔(dān)心以前的功能崩掉。做這種企業(yè)內(nèi)部落地的項目酒香也怕巷子深基礎(chǔ)設(shè)施穩(wěn)、反饋快、迭代小步快跑比憋一個大版本更重要。