跳到主要内容
AI教程

使用 Rizzo Flow 快速构建类型化本地 LLM 决策

了解如何安装 Rizzo Flow、运行其本地 llama.cpp 服务器,并获取类型化的布尔、选项、评分和数值决策。本教程涵盖 Playground、原生 API 与兼容 Jev 的 API、硬件选项、弃权、批处理、上下文限制、安全性以及实际性能方面的注意事项。

使用 Rizzo Flow 快速构建类型化本地 LLM 决策

什么是 Rizzo Flow?

Rizzo Flow 是一个开源、本地优先的系统,可将非结构化文本或 JSON 状态转换为带概率的类型化决策。它不要求语言模型逐个 token 生成散文或 JSON,而是在一次前向传递后读取模型对受限答案字母集合的概率。

这种设计支持以下决策:

  • 带有 true 概率的布尔答案。
  • 在命名选项中进行选择,并提供每个选项的概率。
  • 跨有序评分标准等级的评分。
  • 基于代表性锚点的数值估计。

Rizzo Flow 通过 llama.cpp 在你自己的硬件上运行。它支持 Apple Metal、NVIDIA CUDA、Vulkan、AMD ROCm、Intel SYCL 或 CPU 执行。它还提供兼容 Jev 的 HTTP 接口,让兼容应用可以面向本地 URL,而不是托管服务。

Rizzo Flow 项目徽标

Rizzo Flow 是一个独立项目。它复现的是 Jev 背后的接口模式,而不是 Jev 的专有架构或训练方式。除非你使用自己的代表性数据进行校准,否则其概率未经校准。

零 token 决策的工作原理

对于每个请求,Rizzo Flow 会将状态放在提示词开头,并对其处理一次。随后,问题从共享的状态缓存中分支出来。每个可能的答案都会映射到一个大写字母,系统只读取允许字母对应的 logits。

  1. 状态会被转换为文本,并预填充到模型的 KV 缓存中。
  2. 每个问题都会表示为一个受限的多项选择问题。
  3. 共享同一状态的问题会以微批次方式进行评估。
  4. 允许的答案 logits 会通过 softmax 转换为概率。
  5. Python 代码返回经过 schema 验证的布尔、选择、评分或数值数据。

整个过程没有解码循环、采样文本、输出解析或 JSON 修复。不过,生成零个 token 并不意味着计算量为零:状态和问题提示词仍然需要模型推理。

主要功能

  • 完全本地运行:模型推理在你的计算机上进行。
  • 类型化结果:应用接收结构化值,而不是生成的散文。
  • 概率分布:选择和评分结果会显示概率,而不只是 argmax 答案。
  • 四种原生基元:布尔、选择、评分和数值。
  • 可选的弃答:原生 API 可以报告证据不足、不确定性或超出范围的数值结果。
  • 共享状态批处理:同一请求中的多个问题会复用状态的 KV 缓存。
  • 兼容 Jev 的端点:现有客户端可以使用 /v1/systemone 和 /v1/models。
  • 长上下文模型:Spark-X2.5 原生支持最高 1,048,576 个 token 的上下文,但 Rizzo Flow 默认每个问题使用 8,192 个 token。
  • 本地工具:服务器包含 playground、交互式 OpenAPI 文档和 Snake 演示。

前置要求

安装 Rizzo Flow 前,请确保已准备好:

  • Python 3.11 或更高版本。
  • Git。
  • 用于依赖和环境管理的 uv。
  • 足够的磁盘空间来存放所选模型和运行时。

默认的 Spark-X2.5-4B Q8_0 模型下载大小约为 4.4 GB。运行时下载大小因平台而异:Mac 上约为 11 MB,CUDA 软件包约为 570 MB。

安装并启动服务器

克隆仓库,同步固定版本的依赖,下载默认运行时和模型,然后启动服务:

git clone https://github.com/Rizzo-AI-Academy/rizzo-flow
cd rizzo-flow
uv sync --locked
uv run rizzo download
uv run rizzo serve

下载命令会为当前计算机选择官方预构建的 llama.cpp 软件包,验证其 SHA-256 校验和,并下载 Spark-X2.5-4B Q8_0。中断的下载可以从停止的位置继续。

根据项目文档,模型加载大约需要十秒。服务准备就绪后,打开:

Rizzo Flow 本地决策 playground

playground 包含现成示例、问题构建器、两个 API 的原始 JSON 编辑器、概率条、计时详情以及等效的 cURL 命令。它不会发起外部调用,并且可以在英语和意大利语之间切换。

使用更小的模型

如需更快完成首次下载,请安装 1.7B 模型:

uv run rizzo download --size 1.7b
uv run rizzo serve --size 1.7b

1.7B Q8_0 文件约为 1.8 GB,运行速度大约快一倍,但 README 警告其准确性低得多。启用弃答功能时,它还往往会选择“证据不足”选项,因此请务必在自己的工作负载上仔细测试。

做出你的第一个决策

最快的 API 测试使用兼容 Jev 的 POST /v1/systemone 端点。以下请求用于判断一条支持消息是否表达了紧迫性:

curl http://127.0.0.1:8017/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "state": "救命!我的付款已经失败 3 天了。",
    "model": "rizzo-latest",
    "questions": {
      "is_urgent": {
        "type": "noul",
        "instructions": "这是否表达了紧迫性?"
      }
    }
  }'

noul 结果表示“是”的概率,取值为 0 到 1 之间的数字。即使请求使用 rizzo-latest 或便捷别名,响应仍会报告实际的本地模型标识符。

在一次请求中提出多个问题

Rizzo Flow 的设计目标是针对同一状态评估多个问题。将它们合并到一次请求中,可以让这些问题共享该状态的 KV 缓存:

curl http://127.0.0.1:8017/v1/systemone \
  -H 'Content-Type: application/json' \
  -d '{
    "state": "救命!我的付款已经失败 3 天了。",
    "model": "rizzo-latest",
    "questions": {
      "is_urgent": {
        "type": "noul",
        "instructions": "这是否表达了紧迫性?"
      },
      "department": {
        "type": "choice",
        "instructions": "哪个团队应该处理此问题?",
        "criteria": {
          "billing": "付款、开票、退款",
          "technical": "错误和服务中断",
          "sales": null
        }
      },
      "frustration": {
        "type": "score",
        "instructions": "客户有多沮丧?",
        "criteria": ["平静", "沮丧", "非常愤怒"]
      }
    }
  }'

响应包含 noul 的“是”概率、所有选择选项的概率,以及带有图例的概率加权分数。usage.output_tokens 的值始终为 0。

使用原生决策 API

原生 POST /v1/decisions 端点提供 Rizzo Flow 的完整功能集,包括数值问题和弃答功能。它支持以下四种问题类型:

  • boolean:返回类型化值和为真的概率。
  • choice:返回选定选项和完整的选项分布。
  • score:返回有序等级上的概率加权分数和归一化分数。
  • numeric:返回估计值、中位数、离散程度,以及低于或高于范围的概率。

根据锚点估计数值

数值问题定义递增的代表性锚点。以下示例要求模型读取报告的填充百分比:

curl http://127.0.0.1:8017/v1/decisions \
  -H 'Content-Type: application/json' \
  -d '{
    "state": {"measurement": 75, "unit": "percent"},
    "questions": {
      "fill": {
        "type": "numeric",
        "instructions": "读取报告的填充百分比。",
        "unit": "percent",
        "anchors": [
          {"value": 0, "description": "空"},
          {"value": 50, "description": "半满"},
          {"value": 75, "description": "四分之三满"},
          {"value": 100, "description": "完全充满"}
        ]
      }
    }
  }'

锚点是代表性数值,而不是统计区间。报告的均值始终位于最低和最高锚点之间,而分位数描述的是这些锚点上的离散概率分布。

理解弃答

原生问题默认允许弃答。Rizzo Flow 会添加一个内部的“证据不足”选项,而数值问题还会包含低于范围和高于范围的可能性。根据所选选项和策略,主要值可能为 null,状态可能报告为 insufficient_evidence、out_of_range 或 uncertain。

兼容 Jev 的格式不使用弃答。它的“是/否”结果恰好基于两个选项计算。如果你通过原生 API 使用较小的 1.7B 模型,请考虑按照项目建议将 allow_abstain 设置为 false,并验证其对你的数据的影响。

在没有服务器的情况下运行决策

对于脚本、测试或一次性评估,可以直接将请求文件传递给 CLI:

uv run rizzo decide examples/ticket.json

你也可以激活虚拟环境,从而省略 uv run 前缀:

source .venv/bin/activate
rizzo decide examples/ticket.json

在 PowerShell 中,使用以下命令激活:

.venv\Scripts\activate

选择模型、量化方式和设备

默认配置使用 Spark-X2.5-4B Q8_0。其他有文档说明的量化方式包括 Q4_K_M 和 BF16:

  • 4B Q8_0:约 4.4 GB,也是默认配置。
  • 4B Q4_K_M:约 2.6 GB。
  • 4B BF16:约 8.2 GB。
  • 1.7B Q8_0:约 1.8 GB。
  • 1.7B Q4_K_M:约 1.1 GB。
  • 1.7B BF16:约 3.4 GB。

启动服务器前,先检查运行时可见的设备:

uv run rizzo devices

然后可以明确选择设备系列:

uv run rizzo serve --device cuda
uv run rizzo serve --device vulkan
uv run rizzo serve --device metal
uv run rizzo serve --device cpu

指定的设备系列属于硬性要求,而不是提示,因此 Rizzo Flow 不会将明确指定的 GPU 系列静默降级为 CPU。其他运行时软件包可以单独下载:

uv run rizzo download --only runtime --runtime rocm
uv run rizzo download --only runtime --runtime sycl
uv run rizzo download --only runtime --runtime cpu

高级配置与实用技巧

将相关问题进行批处理

将关于同一状态的所有问题放在一个请求中。这是 Rizzo Flow 设计的核心:状态只预填充一次,问题后缀会以微批次进行评估。默认的问题微批次大小为 4,可通过 --batch-size 更改。

uv run rizzo serve --batch-size 8

更大的批次并不一定更好。请在目标机器上比较延迟和内存消耗。

谨慎增加上下文

尽管 Spark-X2.5 原生支持一百万 token 的上下文,但服务器默认每个问题使用 8,192 个 token。使用 --ctx 提高上限:

uv run rizzo serve --ctx 32768

KV 缓存会在启动时分配。对于 4B 模型,README 估计每个 token 约占 144 KiB,因此默认上限约需 1.4 GiB,32,000 个 token 时约需 4.8 GiB。超过配置上限的输入会被拒绝,而不是截断。超过约 60,000 个 token 后,还必须提高 schema.py 中仓库设置的 256 KB 状态上限。

保护兼容端点

启动服务器前设置 RIZZO_API_KEY,即可要求 Jev 兼容端点使用 Bearer 身份验证:

export RIZZO_API_KEY="replace-with-a-secret"
uv run rizzo serve

在 Windows PowerShell 中:

$env:RIZZO_API_KEY = "replace-with-a-secret"
uv run rizzo serve

身份验证失败会返回 HTTP 401。无效的请求数据可能返回 HTTP 422。

重定向兼容客户端

为托管版 TypeSafe API 设计的客户端,可以通过更改基础 URL 来连接本地服务:

export TYPESAFE_BASE_URL=http://127.0.0.1:8017

项目说明,这种环境变量配置面向官方 SDK,但目前尚未使用这些 SDK 进行测试。接口是兼容的,但底层本地模型并非 Jev。

正确理解置信度和概率

兼容 API 的置信度值描述的是选项分布的形态,并不是答案正确性的经验证概率。同样,模型原始概率也可能过度自信或存在其他校准偏差。

请在具有代表性的标注数据集上验证决策,并在必要时针对实际部署环境进行校准。服务器通过 --calibration 接受校准文件:

uv run rizzo serve --calibration fit.json

校准结果与拟合时所使用的模型文件、运行时、量化方式和硬件后端绑定。CUDA、Vulkan 和 Metal 的舍入方式可能不同,量化也可能改变返回的概率。

遵守答案槽位限制

每个候选项对应一个大写字母,因此每个问题最多有 26 个答案槽位。内部弃答选项和范围选项也会占用槽位。因此,选择题在不启用弃答时最多支持 26 个普通选项,启用弃答时最多支持 25 个。数值题可用的锚点更少,因为低于范围、高于范围以及可选的证据不足选项也会占用槽位。

使用自定义模型文件或 llama.cpp 构建

使用 --model 启动服务器并指定 GGUF 文件:

uv run rizzo serve --model /path/to/model.gguf

要使用自定义的 llama.cpp 安装,请将 RIZZO_LLAMA_DIR 指向包含 libllama 的目录。README 要求使用 llama.cpp 提交 161755f,因为绑定依赖该版本的头文件。

探索 Snake 演示

Rizzo Flow 实时控制 Snake 移动决策

本地 Snake 页面展示了如何使用类型化决策控制交互式应用。每次移动都会发送一个 POST /v1/decisions 请求,其中包含棋盘描述和列出合法移动的选择题。页面会显示答案概率、logits、计时信息和决策日志,且不会生成文本。

该演示还说明了一个重要的建模经验:输入表示方式很重要。README 报告称,与仅使用 ASCII 网格相比,4B 模型使用计算得到的逐步传感器数据时表现好得多。这些观察结果来自少量非正式游戏,不应视为基准测试。

运行检查

使用 GET /health 检查模型来源信息和文件哈希。请求与响应模式也可在 request.schema.json 和 response.schema.json 中找到。为了实现可复现部署,请固定模型、量化方式、运行时、后端、上下文配置和校准设置。

README 报告称,在 RTX 5060 Ti 上使用 Spark-X2.5-4B Q8_0 进行短决策时,耗时约 50 毫秒,但这取决于硬件和工作负载。同一份文档还指出,Apple Silicon、AMD、Intel、Linux NVIDIA 和 CPU 配置尚未全部经过同等程度的测试,因此请先在自己的机器上进行基准测试,再设定延迟预期。

结论

Rizzo Flow 提供了一个实用的本地界面,可将非结构化状态转换为带类型的概率决策,而无需生成文本。先从 Playground 开始,将相关问题合并到一个请求中;当需要数值估计或弃权时,使用原生 API。在投入生产前,请使用能够反映真实应用的数据,测试准确率、延迟、校准、量化以及后端行为。