戰(zhàn):Ollama+Python調(diào)度+工具調(diào)用完整指南)
最近不少朋友問我怎么在本地搭一個(gè)真正能干活的 AI Agent而不是只會(huì)聊天的對話框。我自己摸索了一段時(shí)間最后選定的方案是Ollama 跑本地模型再寫一套輕量的調(diào)度邏輯把模型變成一個(gè)能查資料、能調(diào)工具、能按流程執(zhí)行任務(wù)的智能代理。整套下來不花一分錢 API 費(fèi)用數(shù)據(jù)全在自己機(jī)器上隱私和可控性都拿捏得很穩(wěn)。這篇文章就把我完整踩過的坑、選型的邏輯、以及可以直接照抄的搭建流程全部寫出來適合有一定編程基礎(chǔ)、想擺脫云 API 依賴的開發(fā)者參考。1. 搭一個(gè)本地 AI Agent 前先想清楚三件事1.1 Agent 到底是什么和聊天機(jī)器人有什么區(qū)別先說個(gè)基礎(chǔ)認(rèn)知問題。很多人把能連續(xù)對話誤當(dāng)成 Agent其實(shí)完全不是一回事。普通的聊天機(jī)器人是你說一句我回一句它沒有目標(biāo)也不會(huì)主動(dòng)調(diào)用外部工具。而 AI Agent 的核心是給定一個(gè)目標(biāo)它能自己規(guī)劃步驟、調(diào)用工具、分析結(jié)果、調(diào)整策略直到把任務(wù)完成。舉個(gè)生活化的例子。你讓聊天機(jī)器人幫我查一下明天的天氣并規(guī)劃出行它只會(huì)回復(fù)一段建議。但一個(gè)合格的 Agent 會(huì)這樣做先調(diào)用天氣 API 獲取數(shù)據(jù)判斷是否需要帶傘再調(diào)用地圖 API 規(guī)劃路線最后把結(jié)果匯總成一段可讀的答復(fù)。每一步都是模型在思考但每一步的執(zhí)行都依賴外部工具。所以搭建 Agent 的關(guān)鍵不只是選一個(gè)大模型而是要把模型推理和工具執(zhí)行這兩條線拼接起來。Ollama 解決的是推理這一半另一半需要我們自己用代碼搭一個(gè)調(diào)度器這也是這篇文章里最核心的內(nèi)容。1.2 為什么選 Ollama 而不是云 API你可以用任何模型服務(wù)來搭 Agent但我最終選擇 Ollama原因有三個(gè)。第一是成本。我做的項(xiàng)目里有大量測試和反復(fù)調(diào)優(yōu)的場景每天可能要發(fā)幾百次請求。如果用云 API這些請求都是真金白銀。Ollama 跑在自己的 GPU 或 CPU 上沒有調(diào)用次數(shù)限制隨便刷。對于個(gè)人開發(fā)者、學(xué)習(xí)項(xiàng)目、原型驗(yàn)證來說免費(fèi)這個(gè)優(yōu)勢是壓倒性的。第二是隱私。很多企業(yè)場景或者個(gè)人敏感數(shù)據(jù)根本不適合發(fā)到云端。Ollama 的所有推理都在本地完成模型文件、中間結(jié)果、日志全部留在自己手里。這一點(diǎn)在做私有化知識(shí)庫、內(nèi)部文檔問答、代碼輔助工具時(shí)特別重要。第三是可控性。云 API 的版本更新、模型下架、限流策略都不由你控制。Ollama 裝好之后模型文件就在你的磁盤里你可以隨意切換版本、調(diào)整參數(shù)、做微調(diào)沒有任何外部約束。當(dāng)然Ollama 也有代價(jià)。你沒有云端那么強(qiáng)的算力所以跑不了超大參數(shù)模型模型效果也不一定比頂級(jí)商業(yè) API 好。但對我這種方式是先用起來、跑通流程的開發(fā)階段來說Ollama 是性價(jià)比最高的起點(diǎn)。1.3 整體方案本地模型 調(diào)度腳本 工具接口我先說一下最終架構(gòu)讓你心里有個(gè)地圖。用戶輸入 - 主調(diào)度循環(huán) - Ollama 本地模型 - 輸出意圖判斷 ↓ 需要工具 解析出工具名和參數(shù) - 執(zhí)行函數(shù) - 返回結(jié)果給模型 - 繼續(xù)推理 ↓ 不需要工具 直接輸出最終回答整個(gè)系統(tǒng)分成三層模型層Ollama 加載本地開源模型負(fù)責(zé)理解意圖、生成文本、決定是否調(diào)用工具。調(diào)度層一段 Python 腳本負(fù)責(zé)循環(huán)調(diào)用模型、解析輸出、維護(hù)多輪對話記憶、判斷任務(wù)是否結(jié)束。工具層一組普通的 Python 函數(shù)比如查詢天氣、讀寫文件、計(jì)算表達(dá)式、搜索本地文檔。每個(gè)函數(shù)就對應(yīng) Agent 的一只手。這個(gè)架構(gòu)看起來簡單但很實(shí)用。好處是每一層都能獨(dú)立替換今天用 Ollama 跑一個(gè) 7B 模型明天換更大的模型調(diào)度層完全不用改工具層加一個(gè)函數(shù)Agent 就多一項(xiàng)能力。下面我按這個(gè)框架逐步拆解搭建過程。2. 環(huán)境搭建裝好 Ollama 并選對模型2.1 安裝 Ollama3 分鐘跑起第一個(gè)模型Ollama 的安裝是我見過的最省心的 AI 工具之一。在 macOS 和 Windows 上直接去官網(wǎng)下載安裝包雙擊安裝即可。在 Linux 上執(zhí)行一句腳本就能裝好curl -fsSL https://ollama.com/install.sh | sh裝好之后驗(yàn)證是否成功ollama --version然后拉取一個(gè)模型試試。我用的主力模型是 Qwen2.5 7B Instruct這個(gè)模型中文效果好、工具調(diào)用能力也不錯(cuò)體積適中適合大多數(shù)人的硬件。拉取命令ollama pull qwen2.5:7b第一次拉取會(huì)下載幾個(gè) GB 的文件等它跑完然后啟動(dòng)一個(gè)簡單對話ollama run qwen2.5:7b 你好做個(gè)自我介紹如果能看到正常回復(fù)就說明 Ollama 已經(jīng)跑通了。這里有個(gè)值得多說一句的點(diǎn)Ollama 安裝完成后會(huì)默認(rèn)在后臺(tái)啟動(dòng)一個(gè)本地服務(wù)監(jiān)聽11434端口。也就是說你用命令行能對話其實(shí)底層就是通過 HTTP API 在調(diào)用后面我們用 Python 寫 Agent 調(diào)度器也是走這個(gè)端口。2.2 模型量化與顯存需求怎么算很多新手卡在這一步模型下載好了但跑起來特別慢或者直接報(bào)內(nèi)存不足。這涉及模型量化和硬件配置的關(guān)系。模型文件有很多種精度格式最常見的是 FP16 和量化版。一個(gè) 7B 參數(shù)的模型FP16 格式大約占用 14GB 顯存很多人的顯卡根本裝不下。量化指的是把模型里的浮點(diǎn)數(shù)精度降低比如從 16 位降到 4 位模型體積直接縮到四分之一效果損失卻控制在可接受范圍內(nèi)。Ollama 上默認(rèn)拉取的模型都自帶量化版本你不需要手動(dòng)處理。以 Qwen2.5 7B 為例量化格式模型文件大小運(yùn)行所需內(nèi)存估算推薦硬件q4_K_M約 4.7GB6-8GB8GB 顯存或 16GB 內(nèi)存q8_0約 8.2GB10-12GB12GB 顯存或 32GB 內(nèi)存FP16約 15GB16GB24GB 顯存或 64GB 內(nèi)存我自己的機(jī)器是 8GB 顯存的顯卡跑 q4_K_M 版本很流暢生成速度大約每秒 40 個(gè) token。如果你沒有獨(dú)立顯卡只有 CPU 和 16GB 內(nèi)存也能跑就是速度慢一些大概每秒幾個(gè) token做實(shí)驗(yàn)夠用但聊天的體驗(yàn)會(huì)打折扣。注意如果你的機(jī)器內(nèi)存或顯存小于模型所需Ollama 會(huì)優(yōu)先保證模型加載然后擠壓其他程序的內(nèi)存空間結(jié)果就是電腦卡死。所以選模型之前先看一眼自己的硬件再?zèng)Q定用哪個(gè)量化版本。2.3 調(diào)好關(guān)鍵運(yùn)行參數(shù)Ollama 模型的運(yùn)行參數(shù)可以通過命令行設(shè)置也可以直接在啟動(dòng)時(shí)寫好。這些參數(shù)對 Agent 的穩(wěn)定性影響非常大我逐個(gè)說。溫度temperature控制生成隨機(jī)性。0 表示每次輸出最確定的答案1 表示最大隨機(jī)。做 Agent 任務(wù)我強(qiáng)烈建議把溫度調(diào)到 0 到 0.3 之間。因?yàn)?Agent 需要穩(wěn)定地輸出工具調(diào)用指令溫度太高它就會(huì)開始自由發(fā)揮格式一亂調(diào)度器就解析不了。上下文長度num_ctx這個(gè)決定模型能記住多少前面的對話。默認(rèn)值通常是 2048 或 4096。做 Agent 任務(wù)時(shí)因?yàn)橐ぞ叻祷氐拈L文本、多輪對話歷史上下文很容易被打滿。建議調(diào)大比如 8192 或 16384但注意這會(huì)增加顯存消耗。重復(fù)懲罰repeat_penalty防止模型重復(fù)同樣的句子。默認(rèn)值 1.1 就行不用動(dòng)。top_p核采樣參數(shù)默認(rèn) 0.9 附近一般不需要調(diào)。如果發(fā)現(xiàn)輸出質(zhì)量不穩(wěn)定可以降到 0.8。在實(shí)際項(xiàng)目中我通常把這些參數(shù)寫進(jìn)啟動(dòng)腳本不手動(dòng)改全局配置。比如ollama run qwen2.5:7b --temperature 0.1 --num_ctx 8192這里要注意--num_ctx如果設(shè)得比模型默認(rèn)值大會(huì)顯著增加顯存占用。比如 7B 模型 q4 量化本身只需 5GB但把上下文提到 16K 之后可能需要再多 3-4GB。根據(jù)你的顯存情況量力而行。3. 手把手讓 Agent 學(xué)會(huì)干活3.1 用 API 跟本地模型對話命令行只是用來測試的要搭建 Agent真正的核心是調(diào)用 Ollama 的 HTTP API。它提供了一個(gè)很簡潔的接口直接發(fā) POST 請求就行。先看一個(gè)簡單的對話請求curl http://localhost:11434/api/chat -d { model: qwen2.5:7b, messages: [ {role: user, content: 1 1 等于幾} ], stream: false }響應(yīng)的 JSON 里有個(gè)message.content字段就是模型的回答。用 Python 調(diào)用更直觀import requests def chat_with_model(messages, temperature0.2): response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: messages, stream: False, options: { temperature: temperature } } ) return response.json()[message][content]注意stream參數(shù)。默認(rèn)情況下模型是逐字輸出流式返回的對 Agent 這種機(jī)器對機(jī)器的場景來說流式反而增加解析復(fù)雜度所以直接設(shè)成false一次性拿完整結(jié)果簡單省事。3.2 讓模型輸出工具調(diào)用指令A(yù)gent 的關(guān)鍵一步是讓模型決定要不要調(diào)工具以及調(diào)哪個(gè)工具、傳什么參數(shù)。這本質(zhì)上是一個(gè)提示詞工程問題。我的做法是在系統(tǒng)提示詞里給模型一個(gè)明確的行為規(guī)則。下面是我真實(shí)用過的一套提示詞模板你是任務(wù)規(guī)劃助手。你有一個(gè)可以使用的工具列表 - weather(city: string): 查詢城市天氣 - calculator(expression: string): 計(jì)算數(shù)學(xué)表達(dá)式 - current_time(): 獲取當(dāng)前時(shí)間 規(guī)則 1. 如果用戶問題需要工具才能回答必須輸出嚴(yán)格的 JSON格式為 {tool: 工具名, params: {參數(shù)名: 參數(shù)值}, thought: 簡短推理說明} 2. 如果不需要工具直接輸出純文本回答。 3. 判斷要準(zhǔn)確不能把簡單問題復(fù)雜化。然后把這段提示詞和用戶消息一起發(fā)給模型。模型在推理時(shí)要么輸出普通文本要么輸出 JSON 格式的工具調(diào)用指令。調(diào)度器拿到輸出后先用正則表達(dá)式提取 JSON再根據(jù)tool字段匹配對應(yīng)的 Python 函數(shù)完成調(diào)用。這里有一個(gè)很重要的經(jīng)驗(yàn)?zāi)P洼敵?JSON 時(shí)經(jīng)常帶一些額外的文字比如好的我來查一下天氣JSON 如下{...}。如果你是直接json.loads整個(gè)字符串一定會(huì)報(bào)錯(cuò)。所以必須用正則把大括號(hào)里的內(nèi)容提取出來再去解析。import re import json def extract_json(raw_text): pattern r\{[^{}]*\} match re.search(pattern, raw_text, re.DOTALL) if match: return json.loads(match.group(0)) return None注冊工具函數(shù)的時(shí)候用字典映射def weather(city): # 這里可以接真實(shí)天氣 API演示時(shí)返回模擬數(shù)據(jù) return f{city}今天晴氣溫 22-28 度 def calculator(expression): return str(eval(expression)) def current_time(): from datetime import datetime return datetime.now().strftime(%Y-%m-%d %H:%M) TOOLS { weather: weather, calculator: calculator, current_time: current_time, }新增工具的時(shí)候只需要寫一個(gè)普通 Python 函數(shù)然后在系統(tǒng)提示詞的工具列表里加上描述再把這個(gè)函數(shù)注冊進(jìn)TOOLS字典Agent 就能用了。3.3 搭建工具調(diào)度主循環(huán)核心調(diào)度邏輯其實(shí)就是一個(gè) while 循環(huán)把消息發(fā)給模型解析輸出如果輸出是工具調(diào)用指令就執(zhí)行工具把結(jié)果加進(jìn)消息歷史再發(fā)給模型如果輸出是普通回答就結(jié)束并把回答展示給用戶。直接看我寫的一個(gè)完整骨架def run_agent(user_input): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_input}, ] max_iterations 5 # 防止死循環(huán) for _ in range(max_iterations): response_text chat_with_model(messages) tool_call extract_json(response_text) if tool_call and tool in tool_call: tool_name tool_call[tool] params tool_call.get(params, {}) if tool_name in TOOLS: # 執(zhí)行工具 result TOOLS[tool_name](**params) # 把工具結(jié)果追加到對話歷史 messages.append({role: assistant, content: response_text}) messages.append({role: tool, content: str(result)}) continue else: return f錯(cuò)誤未知工具 {tool_name} else: # 沒有工具調(diào)用這就是最終回答 return response_text return 達(dá)到最大循環(huán)次數(shù)任務(wù)未完成這段代碼有幾個(gè)值得注意的點(diǎn)。第一為什么工具結(jié)果要作為消息的一部分發(fā)回去而不是直接在系統(tǒng)外部拼接因?yàn)槟P托枰吹焦ぞ叻祷氐臄?shù)據(jù)才能決定下一步是繼續(xù)調(diào)用工具還是生成最終回答。這本質(zhì)上是一個(gè)多輪推理的過程。如果你把工具結(jié)果存在變量里下一輪請求時(shí)模型根本不知道你查到了什么。第二max_iterations上限很重要。新手經(jīng)常踩這個(gè)坑模型在循環(huán)里反復(fù)調(diào)用同一個(gè)工具或者調(diào)用一個(gè)不存在工具導(dǎo)致無限循環(huán)程序卡死。設(shè)置一個(gè)上限最好打印日志這樣你能看到模型每一步在想什么、要調(diào)什么方便調(diào)試。第三消息歷史不能無限增長。每次工具返回結(jié)果都會(huì)追加兩條消息時(shí)間一長上下文會(huì)被撐爆。所以在實(shí)戰(zhàn)版本里我會(huì)加一個(gè)裁剪邏輯只保留最近 N 輪的消息丟棄更早的歷史。3.4 用 Open WebUI 給 Agent 一個(gè)可視界面調(diào)度腳本寫完之后你其實(shí)已經(jīng)擁有了一個(gè)能跑的 Agent 核心。但純命令行交互不太友好我建議加一層可視界面這里推薦開源項(xiàng)目 Open WebUI。Open WebUI 是一個(gè)很成熟的本地模型聊天界面支持與 Ollama 對接還能額外掛載知識(shí)庫、自定義工具。安裝最簡單的方式是用 Dockerdocker run -d \ -p 3000:8080 \ -v open-webui:/app/backend/data \ --name open-webui \ --restart always \ ghcr.io/open-webui/open-webui:main啟動(dòng)后瀏覽器訪問http://localhost:3000注冊一個(gè)本地賬號(hào)然后在設(shè)置里把 Ollama 地址填成http://host.docker.internal:11434就能在網(wǎng)頁里和模型聊天了。不過要提醒的是Open WebUI 本身默認(rèn)走的是聊天邏輯不是我們上面寫的調(diào)度邏輯。如果你想在網(wǎng)頁上體驗(yàn)完整的工具調(diào)用 Agent需要再花點(diǎn)功夫把自定義工具注冊進(jìn) Open WebUI或者直接把調(diào)度腳本封裝成一個(gè)輕量 API再用一個(gè)簡單前端頁面去調(diào)用。我個(gè)人的建議是先靠腳本驗(yàn)證 Agent 邏輯確認(rèn)穩(wěn)定后再去折騰界面不要一開始就陷入前端的泥潭。4. 優(yōu)化、擴(kuò)展與安全細(xì)節(jié)4.1 提示詞怎樣寫Agent 才穩(wěn)定提示詞對 Agent 的穩(wěn)定性影響怎么說都不為過。同樣的模型提示詞寫得模糊它就會(huì)頻繁誤判、亂調(diào)工具寫清楚了它能連續(xù)處理非常復(fù)雜的任務(wù)。我總結(jié)幾個(gè)實(shí)戰(zhàn)原則。第一工具描述必須具體到輸入?yún)?shù)是什么、返回值是什么、什么場景下使用。比如天氣工具不要只寫查詢天氣要寫輸入城市名返回該城市當(dāng)日天氣狀況適合用戶詢問天氣時(shí)使用。模型是靠描述來匹配意圖的描述越具體準(zhǔn)確率越高。第二必須在提示詞里說清楚什么時(shí)候不要用工具。如果不加這一條模型可能看到任何問題都先輸出工具指令。加上如果用戶只是閑聊、問看法、做總結(jié)請不要調(diào)用工具之后誤調(diào)用率會(huì)大幅下降。第三給模型一些示例。如果某個(gè)工具容易誤匹配可以在提示詞里加一個(gè)示例比如用戶問明天需要帶傘嗎你應(yīng)該調(diào)用 weather 工具查詢城市天氣再根據(jù)結(jié)果回答。模型對示例的模仿能力非常強(qiáng)給一個(gè)例子勝過一百句規(guī)則。注意每次修改提示詞后建議跑一遍同一批測試用例對比輸出變化。我自己的經(jīng)驗(yàn)是做一個(gè)簡單的回歸測試腳本把 10 個(gè)典型問題跑一遍檢查工具調(diào)用是否正確避免改好一處、弄壞三處。4.2 跑得更快顯存、上下文和并發(fā)調(diào)優(yōu)本地模型最難的就是性能。我把性能問題分為三個(gè)維度分別說。顯存層面最有效的做法是先把顯存占用降下來。優(yōu)先選擇量化程度高的模型版本比如 q4_K_M。如果還嫌不夠換一個(gè)更小的模型比如把 7B 換成 3B 甚至 1.8B。模型小了速度會(huì)快但推理質(zhì)量也會(huì)下降需要自己權(quán)衡。上下文層面上下文長度直接決定模型能吃多少內(nèi)容。很多 Agent 任務(wù)其實(shí)不需要非常長的上下文尤其是工具結(jié)果要精簡。我在設(shè)計(jì)工具返回值時(shí)會(huì)刻意讓工具返回簡短結(jié)果而不是一股腦輸出一大段。比如天氣工具返回晴22-28 度而不是返回一整篇天氣報(bào)告。這不僅省上下文還能減少模型的理解負(fù)擔(dān)。并發(fā)層面Ollama 默認(rèn)是串行處理請求的一個(gè)模型同時(shí)只能服務(wù)一個(gè)請求。如果有多個(gè)人要同時(shí)用最簡單的方式是多加載幾個(gè)模型實(shí)例但顯存成本翻倍。實(shí)戰(zhàn)里我更推薦加一層請求隊(duì)列讓 Agent 系統(tǒng)排隊(duì)處理任務(wù)而不是同時(shí)強(qiáng)推多個(gè)請求導(dǎo)致內(nèi)存爆炸。另外可以開啟OLLAMA_NUM_PARALLEL環(huán)境變量允許一個(gè)模型并發(fā)處理多個(gè)請求。這個(gè)配置會(huì)顯著提高吞吐量但會(huì)同步增加顯存和內(nèi)存消耗適合顯存充足的情況。4.3 接入外部工具與私有數(shù)據(jù)Agent 的價(jià)值上限取決于你給它接了多少外掛。我建議優(yōu)先接這三類。第一類是 API 型工具。比如查天氣、查物流、翻譯、搜索網(wǎng)頁。實(shí)現(xiàn)方式就是寫一個(gè)函數(shù)內(nèi)部用requests請求外部接口返回 JSON 結(jié)果。注意要設(shè)置請求超時(shí)避免模型等一個(gè)遲遲不響應(yīng)的接口導(dǎo)致整個(gè)調(diào)度卡住。第二類是本地文件工具。比如讓 Agent 讀取、搜索、總結(jié)某個(gè)目錄下的文檔。做法是封裝一個(gè)read_file(path)和search_files(keyword)函數(shù)讓模型可以自己翻文件、提取信息。我在做一個(gè)內(nèi)部知識(shí)庫助手時(shí)就是靠這種工具讓 Agent 直接查詢本地文檔效果比預(yù)訓(xùn)練知識(shí)更準(zhǔn)。第三類是代碼執(zhí)行工具。讓 Agent 寫 Python 代碼、執(zhí)行代碼、返回運(yùn)行結(jié)果。這個(gè)能力非常強(qiáng)大但風(fēng)險(xiǎn)也最高因?yàn)槟P蜕傻拇a不一定安全可能執(zhí)行破壞性命令。我的建議是只在沙箱環(huán)境里開放這個(gè)工具比如 Docker 容器內(nèi)別在宿主機(jī)上裸跑。私有數(shù)據(jù)這塊除了上面的文件工具還可以用向量數(shù)據(jù)庫做語義檢索。把文檔切成小塊用本地 embedding 模型轉(zhuǎn)成向量存進(jìn)向量庫然后給 Agent 一個(gè)search_vectors(query)工具它就能基于本地資料回答問題。整個(gè)鏈路全部在本地?cái)?shù)據(jù)不出機(jī)器。用戶問我們項(xiàng)目的部署文檔里數(shù)據(jù)庫連接配置怎么寫 Agent 判斷需要查資料 - 調(diào)用 search_vectors(數(shù)據(jù)庫連接配置) 工具返回相關(guān)文檔片段 - Agent 總結(jié)片段 - 輸出最終回答這個(gè)架構(gòu)擴(kuò)展性很強(qiáng)后續(xù)想把 Agent 從助手升級(jí)成自動(dòng)執(zhí)行任務(wù)的角色只需要繼續(xù)加工具比如加定時(shí)任務(wù)工具、郵件發(fā)送工具、數(shù)據(jù)報(bào)表工具它就能完成更加復(fù)雜的組合操作。5. 常見問題速查搭建中一個(gè)坑一個(gè)坑踩過來這套系統(tǒng)我前前后后改了三版踩過不少坑。把最典型的幾個(gè)問題整理成一個(gè)速查表你大概率也會(huì)遇到。癥狀可能原因解決辦法模型啟動(dòng)后一直顯示loading模型文件過大或顯存不足Ollama 在反復(fù)交換內(nèi)存換更小量化模型關(guān)閉其他占用顯存的程序加--num_ctx縮小上下文Agent 亂調(diào)用工具明明問題很簡單卻查了天氣提示詞里沒有不需要工具時(shí)直接回答的規(guī)則在系統(tǒng)提示詞中補(bǔ)充否定規(guī)則并加示例模型輸出的 JSON 解析失敗模型在 JSON 前后加了自然語言描述用正則提取大括號(hào)內(nèi)容再解析 JSON不要直接json.loads對話輪數(shù)多了之后模型開始失憶上下文長度被打滿最早的歷史被丟棄加大num_ctx或精簡工具返回內(nèi)容限制消息歷史條數(shù)請求報(bào)connection refusedOllama 服務(wù)沒啟動(dòng)或者端口被占用執(zhí)行ollama serve啟動(dòng)服務(wù)查看 11434 端口占用情況第一次調(diào)用模型特別慢模型正在加載進(jìn)內(nèi)存Ollama 有冷啟動(dòng)延遲等待幾秒或者在服務(wù)啟動(dòng)前用預(yù)熱腳本發(fā)一個(gè)空請求提前加載模型工具函數(shù)報(bào)錯(cuò)說參數(shù)不匹配模型傳了錯(cuò)誤的參數(shù)名或參數(shù)個(gè)數(shù)給工具函數(shù)自定義一個(gè) dict 描述參數(shù)格式并在提示詞里寫清楚5.1 Ollama 服務(wù)起不來怎么辦這個(gè)坑我踩得最狠。有一次我把模型參數(shù)調(diào)大之后運(yùn)行ollama run直接報(bào)錯(cuò)服務(wù)一直起不來查了半天發(fā)現(xiàn)是顯存不足導(dǎo)致 Ollama 進(jìn)程崩潰。解決辦法是檢查一下nvidia-smi看顯存占用再確認(rèn)模型文件的量化格式和顯存是否匹配。如果確認(rèn)是顯存問題最穩(wěn)妥的方法是換小模型。當(dāng)時(shí)我把 7B 模型換成 3B一切恢復(fù)正常。又或者在啟動(dòng)前釋放一下顯存關(guān)掉其他占用顯存的進(jìn)程。另一個(gè)常見原因是端口被其他程序占用。Ollama 默認(rèn)監(jiān)聽11434如果你機(jī)器上恰好有其他服務(wù)占了同一個(gè)端口服務(wù)就會(huì)啟動(dòng)失敗。排查方法lsof -i :11434如果有結(jié)果說明端口被占用要么停掉那個(gè)程序要么修改 Ollama 的端口配置。5.2 模型一直處于上下文加載狀態(tài)對話半天沒響應(yīng)很多人遇到過第一次發(fā)消息后模型要卡很久才有反應(yīng)。這是因?yàn)?Ollama 第一次調(diào)用時(shí)需要把整個(gè)模型文件從磁盤加載到內(nèi)存這個(gè)過程取決于模型大小和磁盤速度。7B 模型大概需要幾十秒。解決辦法有兩個(gè)。第一設(shè)置環(huán)境變量OLLAMA_KEEP_ALIVE1h表示模型加載后在一小時(shí)內(nèi)保持駐留內(nèi)存避免每輪對話都重新加載。這個(gè)配置非常有效我設(shè)完之后第二輪對話的響應(yīng)時(shí)間從幾十秒降到了即時(shí)。第二做一個(gè)預(yù)熱調(diào)用。在 Agent 系統(tǒng)啟動(dòng)的時(shí)候主動(dòng)給模型發(fā)一個(gè)簡單請求讓它提前加載完成。這樣用戶真正使用時(shí)不會(huì)感受到冷啟動(dòng)的延遲。5.3 工具調(diào)用輸出格式不穩(wěn)定同一個(gè)問題這次能用下次不能用這種情況通常不是模型本身的錯(cuò)而是提示詞寫得不夠嚴(yán)格。模型在輸出 JSON 時(shí)有時(shí)候會(huì)用單引號(hào)有時(shí)候會(huì)多一個(gè)逗號(hào)有時(shí)候用中文冒號(hào)。這些都會(huì)導(dǎo)致解析失敗。除了用正則提取之外我還有一個(gè)糾錯(cuò)策略如果 JSON 解析失敗就把錯(cuò)誤信息和原始輸出反饋給模型讓模型重新輸出一遍。相當(dāng)于給模型一次自我修正的機(jī)會(huì)。代碼如下if tool_call is None: messages.append({role: user, content: 你的輸出包含無效的 JSON請重新輸出只輸出 JSON不要有其他文字。}) continue實(shí)測下來這個(gè)糾錯(cuò)策略能讓成功率從 80% 提升到 95% 以上。偶爾出錯(cuò)沒關(guān)系關(guān)鍵是系統(tǒng)能自我修正而不是一錯(cuò)到底。5.4 API 請求耗時(shí)太長有沒有辦法優(yōu)化如果你的工具環(huán)節(jié)涉及外部網(wǎng)絡(luò)請求比如天氣 API、搜索 API這些請求的耗時(shí)可能比模型推理還長。我建議給每個(gè)外部工具設(shè)置 10 秒超時(shí)并且在工具層做結(jié)果緩存。同一個(gè)城市查天氣短時(shí)間內(nèi)不會(huì)有變化可以直接返回上次的結(jié)果大幅減少重復(fù)請求。另外Ollama 的num_predict參數(shù)也可以限制模型單次生成的最大 token 數(shù)。Agent 場景下模型很多輸出都是簡短的 JSON 指令把這個(gè)值設(shè)成 512 就能滿足需求不用讓它生成到默認(rèn)的 2048。限制 token 數(shù)可以明顯提升響應(yīng)速度。options: { temperature: 0.2, num_predict: 512 }最后還有一個(gè)調(diào)優(yōu)思路如果任務(wù)簡單用一個(gè)小模型做判斷用一個(gè)大模型做總結(jié)。比如意圖識(shí)別階段直接用 3B 模型判斷是否要調(diào)工具速度快了好幾倍等真正需要生成長回答的時(shí)候再切換到 7B 模型。這個(gè)雙模型架構(gòu)雖然復(fù)雜一點(diǎn)但效果和成本的平衡非常好。我個(gè)人在這套系統(tǒng)上花的時(shí)間大概有一半是在調(diào)提示詞和做異常兜底而不是在寫核心調(diào)度代碼。這也印證了一個(gè)觀點(diǎn)Agent 能不能穩(wěn)定干活差的不是模型的智力而是你給它的規(guī)則、工具和容錯(cuò)機(jī)制。當(dāng)你把提示詞寫清楚、把工具接口設(shè)計(jì)好、把異常處理補(bǔ)齊之后Ollama 跑出來的 Agent 完全可以勝任很多日常自動(dòng)化任務(wù)。最后分享一個(gè)小技巧每次給 Agent 新增工具之前先在提示詞里手動(dòng)描述這個(gè)工具的輸入輸出格式跑通一個(gè)任務(wù)之后再把工具注冊到代碼里。這樣增量式開發(fā)排查問題會(huì)輕松很多。