第 1 课
装好 cactus-compute,跑通第一次工具调用并看懂返回结构
用 pip 安装 cactus-compute 4.1.0 并核对版本,关闭匿名用量统计;用 @cactus.tool 和 run() 跑通官方天气示例,看引擎和权重缓存在哪里;用 complete() 单步执行,读懂 function_calls、reasoning、confidence、peak_ram_mb,把执行结果传回模型;再看清三条边界:会多调、不闲聊、不支持中文。
课程目录第 1 / 3 课
学习位置仅保存在当前浏览器,有效期 180 天。
图文教材
本课对照的官方页面:Needle Python Docs 、GitHub 仓库 README 。测试环境:2026-11-12,Asia/Shanghai,Linux x86_64 云主机(无显卡),Python 3.13.5,
cactus-compute4.1.0(PyPI 发布于 2026-11-08)。下面所有「对照结果」都是这台机器的真实输出,只做了节选。
Cactus 是一套跑在手机、手表和普通电脑 CPU 上的小模型。它的 Python 包 cactus-compute 把三样东西装在一起:做工具调用和结构化抽取的 Needle 3、做语音转文字的 Whistle、做看图识字的 Mirage。引擎不到 2 MB,Needle 3 的权重约 35 MB,第一次用时从 Hugging Face 下载并缓存,之后完全在本机推理,不需要 API Key。本课先装好它,跑通第一次工具调用,再看懂它的返回结构和边界。
第 1 步:建一个虚拟环境并安装 cactus-compute
官方要求 Python 3.9 或更高。默认安装就包含文本、语音和视觉三部分的运行时,不会装 JAX。
mkdir -p ~/cactus-lab && cd ~/cactus-lab
python3 -m venv .venv
source .venv/bin/activate
pip install -U cactus-compute
对照结果(节选):
Collecting cactus-compute
Successfully installed Pillow-13.0.0 ... cactus-compute-4.1.0
... huggingface_hub-2.4.0 ...
确认版本:
python -c "import cactus; print(cactus.__version__)"
对照结果:
4.1.0
官方说明里提到,包会上报匿名用量计数(函数名、版本、系统,不含提示词和输出)。想关掉,在终端里先执行一次:
export CACTUS_TELEMETRY=0
第 2 步:跑通第一个工具调用
Needle 3 不聊天,它只做一件事:读懂一句话,决定该调哪个函数、参数填什么。新建 weather.py(代码来自官方 README):
import cactus
@cactus.tool
def get_weather(city: str):
"Get the current weather for a city."
return {"city": city, "temp_c": 27, "sky": "clear"}
with cactus.Cactus(tools=[get_weather]) as agent:
print(agent.run("What's the weather in Lagos?")["results"])
运行:
python weather.py
对照结果:
[{'city': 'Lagos', 'temp_c': 27, 'sky': 'clear'}]
@cactus.tool 会读函数签名得到参数类型,读文档字符串得到工具描述。run() 是完整的循环:模型选出调用,Cactus 替你执行 get_weather("Lagos"),再把结果交回模型,最后把执行结果放在 results 里返回。第一次运行会下载引擎和权重,测试机上整次耗时约 5 秒。下载的文件在缓存目录里:
du -sh ~/.cache/cactus/engine ~/.cache/cactus/models/*
对照结果(做完本系列三课之后):
1.9M /home/box/.cache/cactus/engine
21M /home/box/.cache/cactus/models/mirage
34M /home/box/.cache/cactus/models/needle3
17M /home/box/.cache/cactus/models/whistle
第 3 步:用 complete() 单步执行,看懂返回结构
run() 帮你执行了函数。想先检查模型要调什么、再决定执行不执行,就用 complete(),它每次只走一轮。新建 first_turn.py,工具定义来自官方 Python 文档:
import json
from typing import Literal
import cactus
@cactus.tool
def set_thermostat(temperature: int,
mode: Literal["heat", "cool", "auto"] = "auto"):
"""Set the thermostat.
Args:
temperature: target temperature in Celsius
mode: heating strategy to use
"""
return {"temperature": temperature, "mode": mode}
@cactus.tool(triggers=[r"\b(turn|switch|power|flip)\b.*\b(on|off)\b",
r"\btoggle\b"])
def control_device(device: str,
action: Literal["on", "off", "toggle"]):
"Switch or toggle any named smart-home device."
return {"device": device, "action": action}
agent = cactus.Cactus(tools=[set_thermostat, control_device])
r = agent.complete("toggle the garage door")
print("type:", r["type"])
for c in r["function_calls"]:
print("call:", c["name"], c["arguments"])
print("reasoning:", r["reasoning"])
print("confidence:", r["confidence"])
print("peak_ram_mb:", r["peak_ram_mb"])
out = control_device(**r["function_calls"][0]["arguments"])
r = agent.complete(json.dumps(out))
print("after result:", r["type"], r["function_calls"])
运行 python first_turn.py,对照结果:
type: call
call: control_device {'device': 'garage door', 'action': 'toggle'}
reasoning: 'toggle' -> action 'toggle', 'garage door' -> device 'garage door'.
confidence: 0.5182
peak_ram_mb: 114.0
after result: respond []
几个字段的意思:
function_calls:模型要调的函数和参数。参数由一套从你的类型定义编译出来的语法约束逐字生成,所以格式一定合法,action不可能出现on、off、toggle以外的值。reasoning:模型说明每个参数取自原句的哪一段。confidence:0 到 1 的置信度,下一课会用它决定「直接执行」还是「先问用户」。每次运行数值可能有零点零几的出入。peak_ram_mb:这一轮的内存峰值,测试机上约 114 MB。- 把函数执行结果用
json.dumps传回complete(),模型会判断还要不要继续调用。这里返回respond和空列表,表示这件事做完了。
triggers 是一组正则表达式。请求命中时,这一轮必须调用这个工具,适合用来兜住描述里写不全的说法,比如「toggle」。
第 4 步:看清它的边界:空调用、多余调用和语言
在 first_turn.py 末尾追加下面几行,再运行一次:
print("---")
for q in ["make it 21 and cool the room",
"write me a poem about the sea",
"把客厅的空调调到 21 度"]:
agent.reset()
r = agent.complete(q)
print(q)
for c in r["function_calls"]:
print(" ", c["name"], c["arguments"])
if not r["function_calls"]:
print(" (空调用)")
对照结果(--- 之后的部分):
make it 21 and cool the room
set_thermostat {'temperature': 21, 'mode': 'cool'}
control_device {'device': 'room', 'action': 'on'}
write me a poem about the sea
(空调用)
把客厅的空调调到 21 度
(空调用)
三条结果各说明一件事:
- 会多调:第一句正确调了
set_thermostat,但额外调了一次control_device(device='room', action='on')。从参数看,模型把「cool the room」里的 room 当成了一个要打开的设备。这是 1 亿参数级小模型的真实表现,下一课会讲怎么用工具设计和置信度把它拦住。 - 不会闲聊:跟工具无关的请求返回空列表。官方把这定为约定:没有工具能处理时就是空调用,不会生成任何自由文本,你的代码必须处理这种情况。
- 不懂中文:Needle 3 和 Whistle、Mirage 的官方语言都是英语、德语、法语、西班牙语、意大利语、荷兰语和波兰语。中文请求在测试机上返回空调用。面向中文用户的产品,可以先把指令翻成英文再交给它,或者用官方的微调流程训练自己的版本。
agent.reset() 会清空对话但保留工具。官方建议同一组工具只建一个 Cactus 实例反复用,不要每轮都新建。
常见坑
- 以为它会回答问题:Needle 3 只输出函数调用。要自然语言回复,得把工具结果交给你自己的模板或别的模型。
- 直接读取可选参数:可选参数在原句里没有依据时会被省略,
arguments里可能根本没有这个键。用.get()读。 - 装完 import 报错:多半是没激活虚拟环境。用
python -m pip install -U cactus-compute可以避免装到别的 Python 里。 - 从旧版
cactus-needle升级:官方 README 写明 4.x 统一用import cactus和cactus命令;Needle 2 留在cactus-compute<4和仓库的 legacy 分支。 - 下载慢或报 Hugging Face 限速:首次运行要从 Hugging Face 拉约 37 MB 文件。终端里出现「unauthenticated requests to the HF Hub」只是提示,不影响使用。
小结
这一课装好了 cactus-compute 4.1.0,用 run() 跑通了第一个工具调用,用 complete() 看懂了 function_calls、reasoning、confidence 等字段,也看到了它的三条边界:会多调、不闲聊、不支持中文。下一课学怎么把工具定义写对,用置信度决定执行还是确认,并用 extract() 从一句话里抽出结构化数据。
