codecamp

Ollama 决策(Decision)

前面几篇讲的都是"让模型生成内容"。这一篇不一样:Decision 不生成文章,它只做判断——这段文本该归到哪个标签、是不是一个投诉、紧急程度打几分。输出不是一段话,而是一组概率。

这类任务如果硬塞给通用对话模型,你会花很大力气写提示词、解析文本、处理它偶尔多加的一句"我认为……"。Decision(官方内部代号 System One)把这一步抽成专门接口,返回的是能直接进业务代码的结构化概率。

要求 Ollama v0.35.0 或更高;模型下载:ollama pull nimble。版本基线 v0.40.12,官方 docs.ollama.com/capabilities/decision。

一、决策模型有哪几个

模型 能读图 说明
nimble 否 纯文本判断,最快的一档
tev1 否 文本判断
clef 是 可判图,需 v0.35.1+
clef-flash 是 可判图的轻量版

本地调用不需要 API Key,也不消耗云端额度。这是 Decision 相比通用对话模型最实用的一点——把它放在自己的机器上跑,分类任务一分钱不花。

二、终端里最快的一次判断

ollama run 支持直接提问。把待判断的文本用管道喂进去(或者写成文件重定向),用 --ask 提出每个问题:

## 是/否问题:把工单内容作为"状态"喂给模型
ollama run nimble --ask "Is a refund being requested?" < ticket.txt

不只是是/否。加 --choice 可以让它在候选标签里选,加 --level 可以让它按等级打分:

## 在多个标签中选一个,每个标签带一句说明
ollama run nimble --ask "Which team should handle this?" \
  --choice billing="Payments and refunds" \
  --choice technical="Software problems" < ticket.txt

## 按从低到高的等级打分,--level 的顺序即等级顺序
ollama run nimble --ask "How urgent is this?" \
  --level "Routine" \
  --level "Soon" \
  --level "Immediate" < ticket.txt

一次可以问好几个问题,把 --ask 连着写就行:

## 一次请求解决三个判断,比调三次模型省时间
ollama run nimble --ask "Is a refund being requested?" \
  --ask "Which team should handle this?" \
  --choice billing="Payments and refunds" \
  --choice technical="Software problems" \
  --ask "How urgent is this?" \
  --level "Routine" \
  --level "Soon" \
  --level "Immediate" < ticket.txt

输出很紧凑,每行一个答案,后面跟着概率:

Is a refund being requested?    yes      97%
Which team should handle this?  billing  91%
How urgent is this?             Soon     64%  1.2 / 2

最后那个 1.2 / 2 就是打分结果——不是"选了 1.2 分",而是三个等级概率的加权平均,下面第五节会展开。

两个容易忽略的细节:

  • state 可以喂 JSON 文件,然后在问题里用反引号引用字段名,例如 --ask 'Is a refund being requested in `body`?' < ticket.json。这样只判断某个字段,不必把整份 JSON 都交给模型。
  • 判图用 clef:ollama run clef ./receipt.png --ask "Is this a restaurant receipt?"。

想看完整返回而不仅是那一行摘要,加 --format json。

另外,不带 --ask 直接 ollama run nimble 会进入交互模式:你的第一条消息被当作 state,之后每一行都是针对这个 state 的问题,可以一次粘贴一堆;/clear 清空重来。带上 --ask 则相反——每条消息都是新的 state,问题沿用 --ask 里定义的。

三、用 HTTP 接口:/v1/systemone

写进业务系统要调接口。决策端点是 /v1/systemone(注意不是 /api/*,这也是它与其他能力不同的地方),请求体两块:state 是待判断内容,questions 是问题定义。

## 单选:在三个标签里选最合适的一个
curl http://localhost:11434/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "nimble",
    "state": "Our checkout has returned 500 errors since 9am.",
    "questions": {
      "label": {
        "type": "choice",
        "instructions": "Which label fits this ticket?",
        "criteria": {
          "billing": "Payments and refunds",
          "bug": "Software errors",
          "account": "Login and account access"
        }
      }
    }
  }'

返回体里没有一句自然语言,全是数字:

{
  "model": "nimble",
  "answers": {
    "label": {
      "type": "choice",
      "choice": "bug",
      "probabilities": { "billing": 0.0125, "bug": 0.9781, "account": 0.0093 },
      "confidence": 0.8906
    }
  },
  "usage": { "input_tokens": 174, "output_tokens": 1 }
}

questions 是个对象,键名由你自己定(上面叫 label),返回的 answers 用同样的键名对应。所以可以在一次请求里塞进多个互不相关的问题:

## 两个问题共用一个 state:一个是/否 + 一个打分
curl http://localhost:11434/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "nimble",
    "state": { "ticket": "I was charged twice. Please refund the extra payment." },
    "questions": {
      "refund": {
        "type": "noul",
        "instructions": "Is the customer requesting a refund?",
        "criteria": {
          "false": "No refund is requested",
          "true": "The customer requests a refund"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgently does this ticket need a response?",
        "criteria": [
          "Routine: no time pressure",
          "Soon: a customer is inconvenienced",
          "Immediate: a critical service is unavailable"
        ]
      }
    }
  }'

这个例子返回退款概率 0.9989、紧急度 0.8308(0–2 分制)。结果会随模型和配置波动,别拿单次结果当绝对标准。

四、三种问题类型

type 用途 criteria 形式 返回什么
choice 多选一分类 2–26 个具名选项,每个带说明 最可能的选项 + 每个选项的概率
noul 是/否判断 分别为 "false" / "true" 写的描述 true 的概率,0–1
score 按量级打分 2–10 条描述,从低到高排列 概率加权平均分

score 的算法值得单独说清楚,因为它常被误解。等级是从 0 开始编号的。以三档紧急度为例:

score = 0 × P(Routine) + 1 × P(Soon) + 2 × P(Immediate)

所以"Soon 概率 64%"对应的是 0×0.06 + 1×0.64 + 2×0.30 = 1.24,约 1.2 分。排序方向写错是最常见的坑——criteria 数组必须从最低等级排到最高等级,否则分数会完全反掉。

choice 和 score 还会返回 confidence(0–1),表示模型对某个答案的倾向有多强。它高不代表答对,只代表分布够集中。做阈值判断时建议看 probabilities 分布本身:比如最高概率低于 0.6 就转人工复核,这比看 confidence 更稳。

五、与工具调用(Tool Calling)的区别

这两个能力容易被混在一起,其实分工很清楚:

Decision Tool Calling
模型输出 预定义选项的概率 / 分数 要调用的函数名与参数
是否生成文本 不生成 可生成解释文本
谁来执行 无后续动作 你的代码执行函数,再把结果回传
典型场景 分类、路由、优先级打分、是否转人工 查天气、查数据库、下工单

一句话概括:工具调用是"让模型决定做什么动作",Decision 是"让模型给出一个判断结果"。做 Agent 编排时两者常配合——先用 Decision 低成本判出意图分类,再交给对应的处理链,只有需要外部数据时才进 tool calling。省掉的是"让通用模型自由发挥"带来的不稳定和 token 消耗。

六、判图:Clef 系列

clef 与 clef-flash 能在判断时读图。图片放在 images 数组里,必须是 base64 编码的 PNG / JPEG / WebP——不接受 URL,也不接受 data URL:

## 判图:state 仍然必填,作为判断的上下文
curl http://localhost:11434/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "clef-flash",
    "state": "A user took this screenshot and wants to know what it shows.",
    "images": ["<base64-encoded image>"],
    "questions": {
      "has_ollama": {
        "type": "noul",
        "instructions": "Does this image contain Ollama?"
      },
      "app": {
        "type": "choice",
        "instructions": "Which application is shown in this screenshot?",
        "criteria": {
          "vscode": "Visual Studio Code",
          "terminal": "A terminal emulator",
          "browser": "A web browser",
          "other": "A different application"
        }
      }
    }
  }'

两个要点:图片被所有问题共享,不能给每个问题单独指定;state 仍然必填,哪怕只是写一句"这是一张截图"。少了 state 请求会失败。

返回里 noul 类型直接给概率值("noul": 0.959),choice 仍然给分布。

七、走云端

clef 也能在 Ollama 云端跑,需要付费方案或已购买用量额度。两种用法:

## 方式一:模型名加 :cloud,经本地 Ollama 转发(需 v0.40.5+ 且已登录 ollama.com)
curl http://localhost:11434/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "clef:cloud",
    "state": "Our checkout has returned 500 errors since 9am.",
    "questions": {
      "urgent": {
        "type": "noul",
        "instructions": "Does this ticket need attention within the hour?"
      }
    }
  }'

## 方式二:直连云端,带 API Key
curl https://ollama.com/v1/systemone \
  -H "Authorization: Bearer $OLLAMA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "clef",
    "state": "Our checkout has returned 500 errors since 9am.",
    "questions": {
      "urgent": {
        "type": "noul",
        "instructions": "Does this ticket need attention within the hour?"
      }
    }
  }'

云端的硬限制要提前算好:

  • 单次请求最多 64 个问题,问题名只能含 ASCII 字母、数字、_、.、-,最长 100 字符
  • 最多 4 张图,每张 ≤4 MiB 且 ≤1600 万像素,合计 ≤8 MiB
  • 输入受模型 65,536 token 上下文窗口限制,超长直接拒绝
  • 按输入 token 计费(输出 token 极少,因为不生成文本)

本地跑不花钱,云端按量计费且便于免去模型下载。如果只是做分类,建议先本地跑 nimble,只有需要判图又不想下载模型时才走云端。

想继续了解图片输入的通用用法(对话式描述而非判断),看 3.5 Ollama 视觉(Vision);需要把判断结果接进工具箱时看 3.8 Ollama 工具调用(Tool calling)。

Ollama 结构化输出
Ollama 视觉(Vision)
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

Ollama Cloud

Ollama CLI 命令行参考

Ollama Modelfile 参考

Ollama 上下文长度

Ollama 导入自定义模型

Ollama 常见问题

Ollama 硬件支持

Ollama 故障排查

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }