Ollama 本地模型也能装 skill?用 Modelfile 搭一套可复用的调度骨架

📅 发布时间:2026/9/28 3:59:18
Ollama 本地模型也能装 skill?用 Modelfile 搭一套可复用的调度骨架
1. 为什么 ollama run 之后总觉得“少点东西”你大概率遇到过这个场景本地用 Ollama 拉了个 qwen2.5 或者 llama3ollama run进去聊得挺顺但一旦让它按固定格式输出、按固定步骤分析数据、或者调用某个外部脚本它就开始自由发挥。云端平台那种“导入一个 Skill 就能稳定干活”的体验在本地好像凭空消失了。先说清楚一件事Ollama 本身没有插件市场也没有ollama install skill这种命令。它的职责非常窄——下载和管理 GGUF 模型文件、把模型加载进内存或显存、暴露一个本地 HTTP 推理接口默认127.0.0.1:11434、通过 Modelfile 设置系统提示词和采样参数。它不扫描技能目录不识别用户意图也不会自动路由到某个工具。但“本地模型不能有 skill”是错的。Skill 的本质是一套标准化的业务流程、规则和输出模板它不需要嵌进模型权重里。真正缺的是那一层调度骨架谁来决定这次该用哪个 skill、谁来把 skill 文本和用户问题拼成完整 prompt、谁来接收模型返回并做后处理。这篇文章就围绕 Modelfile 和外部调度程序搭一套可复用的骨架让本地 Ollama 模型也能稳定挂载 skill 能力。适合已经在本地跑模型、想往工具调用和自动化工作流方向走的开发者。2. 前置准备Ollama 服务与 TaoToken 通道在写 Modelfile 之前先把两个基础件确认好。第一个是 Ollama 服务本身。安装完成后确认版本和服务状态ollama --version ollama list curl http://127.0.0.1:11434/api/tags如果curl能返回模型列表的 JSON说明本地推理服务已经就绪。后面所有调度程序都通过这个端口和模型通信不需要改动 Ollama 本体。第二个是统一 Key/API 通道。本地模型负责推理但调度程序里往往还要接其他 AI 工具——比如做意图路由时调一个更强的模型判断该选哪个 skill或者把本地结果交给云端模型做二次整理。这时候如果每个工具都单独配一套 Key维护成本会很高。我习惯用 TaoToken 把 Key 和 API 通道统一起来官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。它的作用是让你在调度程序里用同一套凭证访问不同模型本地 Ollama 和外部模型可以共存于同一条调度链路里。需要先拿到 API Key 的话去控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入文档可以参考 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面写了不同语言下的调用方式。如果你后面要做长期编码或 Agent 类任务可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。注意TaoToken 在这里的角色是统一通道不是替代 Ollama。本地推理仍然走127.0.0.1:11434只有需要外部模型参与调度时才走 TaoToken 的 API。3. 可复制的 Modelfile 骨架与 skill 注册配置这一节给两套东西一套是 Modelfile 骨架用来把单个 skill 固化进模型另一套是外部调度程序里的 skill 注册配置用来做多技能动态加载。两者可以组合使用。3.1 Modelfile 骨架把 skill 提示词打包成新模型Modelfile 的语法很简单核心就几个指令FROM指定基础模型SYSTEM写系统提示词PARAMETER调采样参数。下面是一个可直接复制的骨架把 skill 内容放在SYSTEM三引号里FROM qwen2.5:7b PARAMETER temperature 0.2 PARAMETER top_p 0.9 PARAMETER num_ctx 8192 SYSTEM 【Skill 名称】渠道留存分析 【触发条件】用户提供包含 dt、channel、register_cnt、retain_7d 字段的明细数据 【执行步骤】 1. 校验数据检查空值、负数异常行标注但不剔除 2. 计算 7 日留存率 retain_7d / register_cnt保留两位小数 3. 按渠道聚合总新增与平均留存率 4. 检测留存率环比下降超过 10% 的记录标记为异常波动 5. 只基于计算结果描述现象不推测外部原因 【输出格式】 一、数据概况 二、渠道指标汇总表 三、异常波动清单 四、客观结论 【禁止行为】 禁止编造输入中不存在的数据禁止输出模板以外的自由段落。 打包和运行ollama create retention-skill -f ./Modelfile ollama run retention-skill这套骨架的优点是零依赖命令行直接可用。缺点是 skill 永久占用系统提示词的 token技能一多就会互相干扰而且每次改 skill 都要重新ollama create。所以它只适合单一固定场景。3.2 skill 注册配置外部调度程序动态加载多技能场景要把调度层拆出来。做法是在本地建一个 skills 目录每个 skill 一个 Markdown 文件再用一个注册表描述触发关键词和文件路径。目录结构如下skills/ registry.json retention.md sql_review.md report_format.mdregistry.json是注册配置把 skill 名称、触发词、文件路径对应起来{ skills: [ { name: retention, keywords: [留存, 渠道, 7日], path: ./skills/retention.md, priority: 10 }, { name: sql_review, keywords: [SQL, 慢查询, 索引], path: ./skills/sql_review.md, priority: 8 } ] }调度程序读取这个注册表根据用户输入命中关键词加载对应 skill 文本再拼成完整 prompt 发给 Ollama。这样新增技能只需要加一个 md 文件和在 registry 里加一条记录不用重新打包模型。3.3 调度程序核心逻辑下面是一段可直接跑的 Python 调度骨架包含 skill 加载、关键词路由和 Ollama 调用import json import requests OLLAMA_URL http://127.0.0.1:11434/api/generate MODEL qwen2.5:7b def load_registry(path./skills/registry.json): with open(path, r, encodingutf-8) as f: return json.load(f) def load_skill(path): with open(path, r, encodingutf-8) as f: return f.read() def route_skill(query, registry): hits [] for item in registry[skills]: for kw in item[keywords]: if kw in query: hits.append(item) break if not hits: return None hits.sort(keylambda x: x[priority], reverseTrue) return hits[0] def build_prompt(skill_text, query): return f{skill_text}\n\n用户请求{query} def call_ollama(prompt): resp requests.post(OLLAMA_URL, json{ model: MODEL, prompt: prompt, stream: False, options: {temperature: 0.2} }, timeout120) resp.raise_for_status() return resp.json()[response] if __name__ __main__: registry load_registry() query 帮我分析这份渠道留存数据字段有 dt、channel、register_cnt、retain_7d skill route_skill(query, registry) if skill: skill_text load_skill(skill[path]) prompt build_prompt(skill_text, query) else: prompt query print(call_ollama(prompt))这段代码里Ollama 只负责推理skill 的选择和拼接全在调度层完成。如果你想让路由更聪明可以把关键词匹配换成向量检索或者调一次外部模型做意图分类——这时候就用 TaoToken 的统一通道在call_ollama旁边加一个走https://taotoken.net/api的分类函数即可Key 从环境变量读取不硬编码。4. 端到端验证一次完整的 skill 调用配置写完了得验证它真的能跑通。准备一份测试数据存成test_data.txtdt,channel,register_cnt,retain_7d 2024-06-01,A,1000,320 2024-06-01,B,800,200 2024-06-02,A,1100,300 2024-06-02,B,850,150 2024-06-03,A,1050,290 2024-06-03,B,900,120先验证 Modelfile 打包的模型ollama run retention-skill test_data.txt预期结果是模型按SYSTEM里定义的四个部分输出包含数据概况、渠道汇总表、异常波动清单和结论且不会出现“可能因为市场投放”这类无依据推测。再验证外部调度程序python scheduler.py如果路由命中retention程序会加载skills/retention.md并拼接 prompt。实测下来返回结果的结构稳定性和 Modelfile 版本接近但好处是你可以随时改retention.md而不用重新打包模型。验证成功的标志有三个输出包含模板要求的四个段落、异常波动清单里能正确标出 B 渠道 6 月 3 日留存率下降超过 10%、结论部分没有编造数据。如果你还想验证模型对话能力本身可以走 TaoToken 的模型对话入口做对比https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 把同样的 prompt 发给云端模型观察本地和云端在格式遵循上的差异方便调 skill 文本。5. 本篇常见错排查5.1 ollama create 报错 “no Modelfile found”原因通常是文件名大小写或路径不对。Modelfile 的 M 必须大写且要在命令里显式指定路径ls -l ./Modelfile ollama create retention-skill -f ./Modelfile如果文件在别的目录-f后面写相对或绝对路径都行但不要只写Modelfile而当前目录没有。5.2 调度程序连不上 11434先确认 Ollama 服务在跑curl -s http://127.0.0.1:11434/api/tags | head如果连接被拒绝说明服务没启动。Linux 下用systemctl status ollama看状态macOS 下确认菜单栏图标是否在。另外注意如果调度程序跑在容器里127.0.0.1指向的是容器自身要换成宿主机的可达地址。5.3 skill 命中错误或完全不命中关键词路由的典型问题是触发词太宽或太窄。比如“留存”这个词可能同时出现在多个 skill 的触发词里导致优先级高的总是被选中。排查方法是打印路由结果print(命中 skill:, skill[name] if skill else 无)如果发现总是命中同一个检查registry.json里各 skill 的priority和keywords是否有重叠。更稳的做法是给每个 skill 加一个exclude_keywords字段命中排除词时直接跳过。5.4 模型不按模板输出先确认 skill 文本里的输出格式是否足够具体。模糊的“输出报告”不如“按以下四段输出每段标题固定”有效。其次检查temperature高于 0.5 时格式遵循会明显下降建议设到 0.2 以下。如果还是跑偏把 skill 文本里的禁止行为写得更硬比如“禁止输出模板以外的任何段落”。5.5 外部模型调用返回 401如果调度程序里接了 TaoToken 做意图分类401 通常是 Key 没读到或环境变量名写错。确认echo $TAOTOKEN_API_KEY然后在代码里用os.environ.get(TAOTOKEN_API_KEY)读取不要写死在源码里。请求地址用https://taotoken.net/api不要带多余路径。6. 把调度层沉淀成可复用资产走到这里你应该已经有一套能跑的骨架了Modelfile 负责单技能固化registry 调度程序负责多技能动态加载Ollama 始终只做推理。这套结构最大的好处是 skill 和模型解耦——换基础模型只需要改FROM那一行加技能只需要往skills/目录扔文件。如果你后面要把这套东西接到更长的编码或 Agent 工作流里建议把调度程序里的模型调用统一走 TaoToken 通道Key 和 API 地址集中管理本地和云端模型用同一套调用方式。接入细节看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite Key 在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建。长期跑编码任务的话Coding Plan 的额度模型比按次调用更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个实用技巧skill 文本不要一次写太长把公共约束抽成一个base.md每个 skill 文件开头用一行include base.md引用调度程序加载时做一次文本替换。这样改公共规则只需要动一个文件所有 skill 同步生效。