境準(zhǔn)備到模型接入避坑指南)
1. 為什么這次必須把 openJiuwen 在本地跑起來先交代一下背景。之前我在內(nèi)網(wǎng)環(huán)境里臨時(shí)用過一個(gè)在線版本的知識問答工具體驗(yàn)其實(shí)還行但那個(gè)服務(wù)托管在外部數(shù)據(jù)要經(jīng)過別人家的服務(wù)器很多內(nèi)部資料根本不敢傳上去。后來同事推薦了 openJiuwen說是完全開源、可以本地部署的項(xiàng)目我當(dāng)時(shí)手頭正好有一臺空閑的辦公主機(jī)就想著周末把它裝好讓團(tuán)隊(duì)在局域網(wǎng)里直接用。第一次嘗試很狼狽照著官網(wǎng)文檔一步步點(diǎn)折騰了大半天服務(wù)倒是起來了但頁面上所有請求都在轉(zhuǎn)圈日志各種報(bào)錯最后只能刪掉重來。這個(gè)周末我把它重新?lián)炱饋頁Q了思路沒有再盲信官網(wǎng)給的“快速開始”而是從版本、依賴、模型服務(wù)、配置這幾個(gè)維度重新拆了一遍前后花了大約六個(gè)小時(shí)終于把 openJiuwen 穩(wěn)穩(wěn)地跑在了本地。這篇文章就把整個(gè)過程里踩過的坑、繞開的彎路和最終的有效路徑完整寫下來。先說一句openJiuwen 是什么。它本質(zhì)上是一個(gè)開源的知識庫問答平臺可以把你手頭的文檔、筆記、網(wǎng)頁內(nèi)容導(dǎo)入進(jìn)去借助本地部署的大模型來做檢索和問答。和直接調(diào)用在線 API 不同本地部署意味著所有數(shù)據(jù)都留在自己的機(jī)器上適合企業(yè)內(nèi)部資料整理、個(gè)人知識庫搭建、離線環(huán)境下的文檔問答這類場景。如果你正打算部署 openJiuwen或者你剛在官網(wǎng)文檔里被繞暈了這篇文章應(yīng)該能幫你省掉至少一整天的排查時(shí)間。我把整個(gè)流程拆成了幾個(gè)部分官網(wǎng)信息的甄別、基礎(chǔ)環(huán)境的準(zhǔn)備、本地模型服務(wù)的搭建、openJiuwen 本體的安裝、運(yùn)行時(shí)的常見問題以及部署之后的一些實(shí)際體驗(yàn)。2. 官網(wǎng)文檔上的三處“信息雷”2.1 穩(wěn)定版和開發(fā)版的分支陷阱openJiuwen 官網(wǎng)的文檔入口其實(shí)做得很好看首頁明確寫著“穩(wěn)定版”“開發(fā)版”兩個(gè)文檔切換按鈕但很多人在官網(wǎng)看文檔的時(shí)候根本不會注意自己處于哪個(gè)分支。我第一次就是直接打開了默認(rèn)的開發(fā)版文檔里面寫了很多新特性的安裝方式還引用了尚未合并到正式版本的配置文件字段。這就出了一個(gè)很典型的問題開發(fā)版的部署步驟要求的環(huán)境變量、依賴版本跟穩(wěn)定版并不一致。等我按照開發(fā)版文檔裝完以后發(fā)現(xiàn)項(xiàng)目代碼里根本沒有對應(yīng)的配置文件和數(shù)據(jù)庫遷移腳本啟動當(dāng)然是失敗。后來我才注意到頁面右上角的版本切換切回“穩(wěn)定版”之后很多最初對不上的東西才對上了。同樣的問題也會出現(xiàn)在 GitHub 倉庫的 README 上。如果你打開的是默認(rèn)分支 main看到的可能是最新開發(fā)狀態(tài)而 release 分支或 tag 才是當(dāng)前穩(wěn)定發(fā)行版。建議你在開始之前先確認(rèn)自己要用的是哪個(gè)版本然后同時(shí)鎖定官網(wǎng)的文檔版本和代碼倉庫的 tag不要讓文檔和代碼各說各話。2.2 依賴清單里的隱性前提官網(wǎng)的“環(huán)境要求”頁面寫得很簡單說什么 Python 3.8 以上、Node.js 14 以上、再加一個(gè)數(shù)據(jù)庫就行。這看起來不算復(fù)雜但實(shí)際上這只是“跑起來”的最低要求不是“穩(wěn)定運(yùn)行”的真實(shí)條件。我一開始就按照最低要求來結(jié)果發(fā)現(xiàn)openJiuwen 的檢索服務(wù)需要用到 Redis 做緩存和任務(wù)隊(duì)列但環(huán)境要求里只有在“高級部署”頁面才提到前端構(gòu)建時(shí)用到了較新的 Node 特性Node 14 根本編不過去報(bào)錯信息還很模糊只提示一個(gè)語法錯誤數(shù)據(jù)庫方面雖然支持 SQLite 快速體驗(yàn)但只要并發(fā)稍微高一點(diǎn)SQLite 就頻繁鎖庫日志里全是數(shù)據(jù)庫 locked。如果你只是想在本機(jī)跑通 demo那 SQLite 簡陋配置沒問題。但要在局域網(wǎng)里給幾個(gè)人正常用建議從一開始就把 Redis、PostgreSQL 或者是 MySQL 準(zhǔn)備到位后面會少很多麻煩。我在最后給出的部署建議里會把每一步該裝什么列清楚。2.3 下載包和 git 倉庫的文件不一致官網(wǎng)提供 zip 包下載也提供了 git 克隆入口。我這次第一次用的是從官網(wǎng)下載的 release 壓縮包但解壓后發(fā)現(xiàn)幾個(gè)后端模塊目錄是空的里面只有占位說明文件。比較奇怪的是同樣的版本通過 git clone 拉下來文件是完整的。這種事情在開源項(xiàng)目里不算罕見發(fā)布流程里漏了子模塊或者沒跑完整構(gòu)建壓縮包生成得倉促。但對我們部署者來說浪費(fèi)的時(shí)間是實(shí)打?qū)嵉?。所以我的建議是盡量用 git 標(biāo)簽方式拉代碼不要直接下載壓縮包。比如git clone --depth 1 --branch v1.2.1 https://github.com/openjiuwen/openjiuwen.git這樣至少能保證文件完整以后升級的時(shí)候也好切分支。2.4 官方示例配置不能直接復(fù)制官網(wǎng)給了很多 .env.example 示例文件但如果你直接cp .env.example .env然后就啟動大概率會卡在某個(gè)環(huán)節(jié)。官方示例里很多值填的是占位符比如LLM_API_BASEhttp://localhost:11434/v1看起來沒問題但實(shí)際模型服務(wù)的路徑、鑒權(quán)方式會因?yàn)槟P秃蠖瞬煌煌A硗馐纠锏臄?shù)據(jù)庫連接字符串用的是 Docker 內(nèi)網(wǎng)地址本地直接跑后端進(jìn)程時(shí)這個(gè)地址是沒有意義的。正確做法是先理解示例里每個(gè)配置項(xiàng)的含義再根據(jù)自己的實(shí)際環(huán)境改。不要怕麻煩把環(huán)境變量都過一遍尤其注意端口、路徑、密鑰這幾類。3. 部署前的地基硬件、系統(tǒng)、Python 環(huán)境的搭建順序3.1 硬件怎么選才不虧openJiuwen 本體其實(shí)不吃資源真正吃資源的是本地大模型推理。實(shí)踐下來我建議按模型規(guī)模來決定機(jī)器配置模型規(guī)模參數(shù)量最低內(nèi)存推薦顯存適用場景小模型1.5B~3B8G4G簡單問答、文本分類中模型7B~8B16G8G知識庫檢索問答、摘要大模型13B~14B32G16G多文檔長文本推理我這次用的是 7B 量級的量化模型配的是 16G 內(nèi)存 8G 顯存的機(jī)器跑 openJiuwen 的問答功能基本夠用。文檔檢索時(shí)的響應(yīng)時(shí)間在 5 到 15 秒之間屬于可以接受的范圍。如果機(jī)器內(nèi)存太小建議先別碰 7B 以上的模型老老實(shí)實(shí)先用小模型驗(yàn)證流程。3.2 用 virtualenv 隔離環(huán)境避免系統(tǒng) Python 被搞亂很多部署教程都直接讓你pip install然后在系統(tǒng) Python 里安裝一堆依賴。這樣做短期沒問題但一旦你之后要裝別的 Python 項(xiàng)目版本沖突和系統(tǒng)污染會非常惡心。建議一開始就建一個(gè)獨(dú)立的虛擬環(huán)境。打開終端先裝好 python3-venv 和 pipsudo apt update sudo apt install -y python3-venv python3-pip git build-essential然后創(chuàng)建虛擬環(huán)境mkdir -p /opt/openjiuwen cd /opt/openjiuwen python3 -m venv venv source venv/bin/activate之后再安裝任何 Python 依賴都在這個(gè)虛擬環(huán)境里操作退出環(huán)境就用deactivate。這一步看起來多花了五分鐘后續(xù)能幫你擋掉大量版本沖突問題。3.3 提前部署 Postgres 和 Redis如果只是本機(jī)測試用 SQLite 當(dāng)然省事但是 openJiuwen 在初始化知識庫索引、批量導(dǎo)入文檔的時(shí)候會頻繁讀寫數(shù)據(jù)庫。SQLite 的并發(fā)寫能力很弱一旦導(dǎo)入任務(wù)和其他查詢同時(shí)發(fā)生幾乎必現(xiàn)鎖庫。實(shí)測中我遇到過多次database is locked后來換成 PostgreSQL 就沒有再出現(xiàn)。如果你不熟悉 PostgreSQL可以用 Docker 快速起一個(gè)docker run -d --name openjiuwen-pg \ -e POSTGRES_USERopenjiuwen \ -e POSTGRES_PASSWORDopenjiuwen_pass \ -e POSTGRES_DBopenjiuwen \ -p 5432:5432 \ postgres:14Redis 更簡單docker run -d --name openjiuwen-redis \ -p 6379:6379 \ redis:7這里有一點(diǎn)要提醒如果公司網(wǎng)絡(luò)環(huán)境不允許直接拉 Docker Hub 鏡像提前確認(rèn)一下內(nèi)網(wǎng)有沒有鏡像倉庫別到了最后一步才傻眼。如果 Docker 也不能用可以裝原生的 PostgreSQL 和 Redis只是排障的難度會高一些。4. 本地模型服務(wù)準(zhǔn)備沒有模型openJiuwen 就是個(gè)空殼openJiuwen 本身不內(nèi)置大模型它只是一個(gè)平臺需要對接模型推理服務(wù)。你可以選擇對接線上 API但既然目標(biāo)是本地部署大部分人的選擇自然是本地推理引擎。我這次用的是 Ollama 配合 7B 量化模型流程方便資源占用也比較友好。4.1 用 Ollama 還是其他推理方案關(guān)于推理后端的選擇我在部署前簡單列過幾個(gè)方案方案安裝難度顯存要求適合程度Ollama低單機(jī)最友好低個(gè)人和中小團(tuán)隊(duì)首選vLLM中需要較高配置高高并發(fā)生產(chǎn)環(huán)境llama.cpp中需要自己編譯靈活純 CPU 場景對于大多數(shù)人來說Ollama 是性價(jià)比最高的選擇。它支持 OpenAPI 兼容接口openJiuwen 直接通過 HTTP 調(diào)用就行不需要額外寫適配代碼。安裝也就一條命令curl -fsSL https://ollama.com/install.sh | sh4.2 模型拉取失敗的應(yīng)對方式裝著裝著一個(gè)常見問題就來了模型下載到一半失敗比如網(wǎng)絡(luò)中斷、磁盤空間不足、進(jìn)度條卡住不動。我第一次拉 7B 模型的時(shí)候在 87% 的地方卡了十幾分鐘最后直接報(bào)錯退出。這里有幾個(gè)實(shí)用的處理方式第一使用環(huán)境變量指定模型存儲目錄避免默認(rèn)位置空間不夠export OLLAMA_MODELS/data/ollama-models ollama pull qwen2.5:7b-instruct-q4_K_M第二如果下載經(jīng)常中斷可以分多個(gè)終端觀察日志或者直接用ollama list查看已下載部分。Ollama 對斷點(diǎn)續(xù)傳的支持不太好重試也是一種辦法。比較粗暴但有效的方式是刪除殘留的 manifest 和 blob 文件之后重新拉。第三模型下載需要占用網(wǎng)卡如果你的機(jī)器上有大量其他流量很可能下載會很慢。盡量選擇網(wǎng)絡(luò)比較空閑的時(shí)間段。4.3 模型接口和 openJiuwen 的對接模型服務(wù)跑起來以后要確認(rèn)一下接口地址是否可以被 openJiuwen 訪問。默認(rèn)情況下 Ollama 只監(jiān)聽 127.0.0.1如果你要在一臺機(jī)器上部署 openJiuwen 和 Ollama那沒問題但如果你想讓局域網(wǎng)里其他機(jī)器也通過 openJiuwen 訪問模型服務(wù)就需要開放監(jiān)聽地址。修改/etc/systemd/system/ollama.service中的啟動參數(shù)或者直接運(yùn)行時(shí)指定OLLAMA_HOST0.0.0.0 ollama serve然后調(diào)用一下接口驗(yàn)證是否可用curl http://localhost:11434/v1/chat/completions \ -H Content-Type: application/json \ -d {model:qwen2.5:7b-instruct-q4_K_M,messages:[{role:user,content:你好}]}如果返回了正常的 JSON說明模型服務(wù)正常。openJiuwen 配置里的LLM_API_BASE就填這個(gè)地址LLM_API_KEY可以填任意非空字符串因?yàn)?Ollama 不檢查 API Key。5. openJiuwen 安裝的完整流程從 clone 到界面亮起來5.1 獲取代碼并鎖定版本這一步是整個(gè)部署里最簡單但也最容易埋雷的。我強(qiáng)烈建議使用 git clone 而不是下載壓縮包。代碼如下cd /opt/openjiuwen git clone --depth 1 --branch stable https://github.com/openjiuwen/openjiuwen.git app cd app如果你不知道有哪些穩(wěn)定分支可以先不指定分支拉取然后用git tag列出所有版本挑一個(gè)看起來比較新的穩(wěn)定版本。5.2 后端依賴安裝與配置進(jìn)入項(xiàng)目目錄后確認(rèn)虛擬環(huán)境依然處于激活狀態(tài)然后安裝后端依賴pip install --upgrade pip pip install -r requirements.txt這里有一個(gè)小技巧如果requirements.txt比較大安裝過程很慢可以考慮用國內(nèi)鏡像源加速pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple接下來復(fù)制環(huán)境變量模板cp .env.example .env修改.env中這幾個(gè)核心配置項(xiàng)DB_ENGINEpostgresql DB_HOST127.0.0.1 DB_PORT5432 DB_USERopenjiuwen DB_PASSWORDopenjiuwen_pass DB_NAMEopenjiuwen REDIS_HOST127.0.0.1 REDIS_PORT6379 LLM_PROVIDERollama LLM_API_BASEhttp://127.0.0.1:11434/v1 LLM_API_KEYsk-local LLM_MODELqwen2.5:7b-instruct-q4_K_M5.3 數(shù)據(jù)庫遷移與初始數(shù)據(jù)依賴裝好、配置寫好后就要初始化數(shù)據(jù)庫結(jié)構(gòu)。大多數(shù) Python Web 項(xiàng)目會提供管理命令openJiuwen 也是一樣flask db upgrade python manage.py init_role python manage.py create_admin --email adminexample.com --password yourpassword如果你急著測試也可以先用 SQLite 模式跳過 PostgreSQL 的配置但前面提到過別在高并發(fā)場景下用 SQLite 硬撐。5.4 前端構(gòu)建與服務(wù)啟動openJiuwen 的前端是獨(dú)立構(gòu)建的如果你從源碼啟動需要先編譯靜態(tài)資源cd frontend npm install npm run build cd ..前端構(gòu)建完成后后端有兩種啟動方式。測試時(shí)直接跑開發(fā)服務(wù)器python manage.py runserver --host 0.0.0.0 --port 8000真實(shí)使用時(shí)建議用 gunicorngunicorn -w 4 -b 0.0.0.0:8000 app:create_app()打開瀏覽器訪問http://localhost:8000用剛才創(chuàng)建的賬號登錄openJiuwen 的主界面就應(yīng)該能看到了。到這一步整個(gè)本地部署的核心流程就算跑通了。6. 啟動和運(yùn)行中繞不開的典型問題6.1 我實(shí)際遇到過的報(bào)錯和處理方式這一節(jié)我把問題和處理方式單獨(dú)拿出來說因?yàn)檫@些問題非常典型幾乎每個(gè)部署 openJiuwen 的人都會碰到至少一兩個(gè)癥狀原因處理方法啟動后訪問 502gunicorn 沒起來或端口被占用先看日志再確認(rèn)port配置殺掉占用進(jìn)程登錄后無限跳轉(zhuǎn)SECRET_KEY 為空或跨域配置錯誤在.env里生成隨機(jī)的 SECRET_KEY導(dǎo)入文檔時(shí)轉(zhuǎn)圈Redis 沒啟動或 worker 沒起來確認(rèn) Redis 進(jìn)程啟動 celery worker問答返回空內(nèi)容模型名填錯或模型沒下載完ollama list檢查模型ollama pull補(bǔ)齊上傳文件超時(shí)Nginx 上傳大小限制配置client_max_body_size或直接用開發(fā)服務(wù)器測試調(diào)用模型接口報(bào) 401服務(wù)端配置的 API Key 與請求頭不匹配檢查.env里的 LLM_API_KEY6.2 白屏問題的排查鏈前端頁面白屏是我最初遇到最頭疼的問題看起來啥也沒顯示但后端日志又沒報(bào)錯。排查思路是這樣的先打開瀏覽器開發(fā)者工具看控制臺的報(bào)錯。如果是加載 JS 資源 404說明collectstatic沒執(zhí)行或者靜態(tài)目錄配置不對如果是跨域報(bào)錯檢查后端的CORS_ALLOWED_ORIGINS是否包含了你訪問的域名和端口如果控制臺沒有報(bào)錯但頁面空白可能是前端構(gòu)建產(chǎn)物為空重新執(zhí)行npm run build確認(rèn)dist目錄里有內(nèi)容。6.3 日志怎么讀才有用前端交互出現(xiàn)問題大多數(shù)時(shí)候信息藏在后端日志里。啟動 gunicorn 時(shí)加上--access-logfile - --error-logfile -可以把請求日志打到終端gunicorn -w 4 -b 0.0.0.0:8000 --access-logfile - --error-logfile - app:create_app()日志中如果出現(xiàn)Traceback直接定位最后一個(gè)異常信息如果是EOFError、connection reset大概率是反向代理配置問題。如果日志正常但功能異常再看 openJiuwen 自己的應(yīng)用日志一般會輸出在每個(gè)模塊自己的目錄下。6.4 Docker 方式部署時(shí)的注意點(diǎn)很多人會自然考慮用 docker compose 做一鍵部署。之前的失敗也試過這種方式但沒有成功原因大多卡在模型服務(wù)如何與容器通信的問題上。容器里的 openJiuwen 訪問宿主機(jī)上的 Ollama地址不能寫localhost要寫host.docker.internal:11434或者在啟動容器時(shí)加--networkhost。用 Docker 部署確實(shí)能省下環(huán)境配置的功夫但排查容器的網(wǎng)絡(luò)、數(shù)據(jù)卷掛載和日志要繞不少路。如果你是第一次部署我更建議直接在宿主機(jī)上跑等流程徹底走通了再考慮容器化。7. 部署成功之后我實(shí)際是怎么用它的7.1 把團(tuán)隊(duì)文檔變成可檢索的知識庫服務(wù)跑起來之后的用途才是我真正關(guān)心的。我主要把 openJiuwen 用在了內(nèi)部資料的整理上。以前我們團(tuán)隊(duì)的幾十個(gè)文檔散落在不同的網(wǎng)盤、本地目錄里想找一個(gè)細(xì)節(jié)經(jīng)常要翻半天?,F(xiàn)在統(tǒng)一導(dǎo)入到 openJiuwen 里再用本地模型做檢索增強(qiáng)問答同事直接問“去年第三季度的項(xiàng)目驗(yàn)收報(bào)告里提到的那幾個(gè)問題有哪些”就能拿到準(zhǔn)確答案。這一步的意義在于文檔不是存起來就完事還得讓人能找到、能復(fù)用。openJiuwen 在這個(gè)過程中扮演的角色就是連接文檔和大模型的中間層它負(fù)責(zé)切分文檔、建立索引、召回片段然后把片段交給模型生成回答。本地部署后這些內(nèi)容都不會出內(nèi)網(wǎng)安全邊界清晰很多。7.2 運(yùn)行一周后我給自己的三個(gè)提醒第一備份要提前做。openJiuwen 的數(shù)據(jù)分別在數(shù)據(jù)庫和向量索引目錄里我吃過一次備份不完整的虧恢復(fù)之后發(fā)現(xiàn)歷史導(dǎo)入的文檔全丟了?,F(xiàn)在我會定期把 Postgres 的 dump 和向量索引目錄整個(gè)打包備份放到專門的備份盤。第二模型不是越大越好。我一開始覺得 7B 不夠想上 14B 的模型結(jié)果顯存扛不住問答響應(yīng)直接變成半分鐘以上體驗(yàn)反而更差。后來把模型量化等級調(diào)低控制上下文長度響應(yīng)速度立刻上了個(gè)臺階。如果你也不確定該用哪檔模型可以先從 4bit 量化的小模型測起再逐步往上調(diào)整。第三升級要克制。 openJiuwen 更新頻率并不算特別高但每次更新如果動了數(shù)據(jù)庫結(jié)構(gòu)升級前最好先在另一臺機(jī)器上測試。盲升級導(dǎo)致數(shù)據(jù)遷移失敗、服務(wù)起不來的案例在我認(rèn)識的開源項(xiàng)目用戶里已經(jīng)見了好幾個(gè)。7.3 如果要重新來一遍我會怎么做如果再讓我從零部署一次 openJiuwen我的快捷鍵是先花二十分鐘讀官網(wǎng)的穩(wěn)定版文檔和項(xiàng)目的 issues 列表把版本、數(shù)據(jù)庫、模型后端這三個(gè)關(guān)鍵決定先想清楚再去碰代碼。不要急著git clone也不要直接pip install。部署這種項(xiàng)目真正的成本從來不是執(zhí)行命令的時(shí)刻而是排錯和返工的精力消耗。下載模型、配置接口、初始化數(shù)據(jù)庫、驗(yàn)證問答鏈路每一步都有各自的坑但只要把順序理清踩坑一次之后就能形成自己的穩(wěn)定流程。這篇文章寫下來也是希望后來者能少走幾段我走過的彎路。