每天一个开源项目#68 Needle:14MB 工具调用小模型装进口袋设备
Trending rank: #17 | 快照日期: 2026-08-13 | Stars: 4,494(API 补充核验) | Forks: 317 | 主语言: Python | License: MIT(仓库 LICENSE;pyproject/PyPI 标注 Apache-2.0,需按分发形态核对)
📋 项目概览
| 项目 | 内容 |
|---|---|
| 项目名 | cactus-compute/needle(Needle 2) |
| 一句话 | 一个面向工具调用、设备控制和结构化抽取的 45M 参数小模型,README 宣称可打包为 14MB 单文件引擎,在约 28MB RAM 内完成会话。 |
| Stars | 4,494(2026-08-13 API 补充核验;Trending 快照未保留今日新增 Star) |
| Forks | 317 |
| 语言 | Python 为主;补充统计:Python 151,678 bytes、JavaScript 20,802、CSS 13,162、HTML 5,857 |
| License | GitHub API/仓库 LICENSE: MIT;pyproject 与 PyPI: Apache-2.0,存在版本面差异 |
| 版本 | 源码 __version__ / pyproject: 2.0.0;引擎下载版本: 2.0.1;Git tag / PyPI 最新: 2.0.2 |
| 适合关注的人 | 做端侧 AI、智能硬件、工具调用 Agent、本地结构化抽取、低内存自动化的工程团队 |
今天热榜里排第一的是 diagram-design,它很实用,但本质是 Agent diagram skill 与静态 HTML/SVG 模板;macro、paperclip、orca 属于工作台/Agent IDE;semantica、orca、Kronos、agency-agents 已经在历史报告中作为主角或明确旧主题出现。综合创新性、技术深度与实用边界,本期选择排名第 17 的 cactus-compute/needle:它试图把"工具调用小模型"从云端 API 拉到手机、穿戴、智能家居、机器人这类 tiny devices 上。
🔥 为什么值得关注
过去一年,Agent 工具调用的主流路径是:把工具 schema 发给大模型,让云端模型输出 JSON,再由应用执行。这条路开发体验很好,但在端侧场景里会遇到三个硬约束:网络不可用或不稳定、延迟和成本不可控、隐私数据不适合离开设备。Needle 的价值,不是再做一个聊天模型,而是把模型能力收敛到一个窄但高价值的操作:输入自然语言,输出可执行的结构化工具调用。
README 给出的核心卖点非常激进:45M 参数、14MB 单文件、约 28MB RAM、256 token 滑动窗口、工具 schema 约束解码、置信度门控、工具检索只放入 Top 5 工具。这些设计共同指向一个判断:端侧 Agent 未必需要通用闲聊模型,很多设备只需要"可靠地把一句话转成一个有限动作"。比如"把卧室空调调到 21 度并设成制冷",真正重要的是工具名、参数、边界条件和低置信度时不要误执行。
需要强调的是,本文把 README 性能数字作为维护者声明,而不是独立复现实验。实际核验中,我完成了源码浅克隆、版本面检查、schema 生成逻辑检查、CLI parser 审计与无模型下载的确定性探针;没有下载 Hugging Face 权重/引擎,也没有复现 14MB、28MB RAM、吞吐等模型运行指标。因此本文重点放在架构拆解、接口边界和工程成熟度,而不是替它背书性能承诺。
🏗️ 核心特性
1. 工具调用即产品边界:没有自由文本兜底
Needle 的 README 明确写道:它"solves every problem as a function call"。这很重要,因为端侧自动化最怕模型输出一段看似合理但不可执行的解释。Needle 的返回结构是 JSON 对象,核心字段包括 type、function_calls、reasoning、confidence、prefill_tps、decode_tps 等。
典型 Python 用法如下:
python
import needle
@needle.tool
def get_weather(city: str):
"Get the current weather for a city."
return {"city": city, "temp_c": 27, "sky": "clear"}
agent = needle.Needle(tools=[get_weather])
print(agent.run("what's it like in Lagos right now?")["results"])
源码中 Needle.run() 的循环非常直接:先调用 complete() 得到模型输出,如果 type == "call" 且存在 function_calls,就按工具名在本地函数表里查找并执行;执行结果再通过下一轮 complete(json.dumps(results)) 回灌给模型,直到没有新的工具调用或达到 max_steps。
text
用户 query
-> Needle.complete()
-> JSON: function_calls[]
-> 本地函数表查找工具
-> 执行工具并收集 results
-> results 回灌给模型
-> 最终 response 附带 results
这不是 LangChain 式的复杂 Agent planner,而是一个窄协议:模型只负责"选工具 + 填参数 + 给置信度"。这种窄化正是它能进入端侧场景的关键。
2. Schema 不是提示词,而是解码约束的一部分
README 里反复强调"byte-level grammar compiled from your schemas constrains every token"。从 Python 层看,needle.agent.tools.build_schema() 会把函数签名、类型标注、Google-style docstring、Literal、Field 约束和 Pydantic model 转成 JSON schema 形态。
可声明的参数边界包括:
| 约束类型 | Needle Field 支持 | 工程意义 |
|---|---|---|
| 枚举 | enum / Literal |
模型只能在合法动作模式中选择 |
| 数值范围 | ge/le/gt/lt |
防止温度、金额、音量越界 |
| 字符串格式 | pattern / format |
约束账号、设备 ID、日期等格式 |
| 长度与数组 | minLength/maxLength/minItems/maxItems |
限制输出体积与参数合法性 |
| 必填/可选 | 默认值、Optional、required |
缺证据的字段可省略,不强行猜测 |
我用源码中的 schema 构造逻辑做了无模型探针,验证 Literal["heat", "cool", "auto"] 会进入 enum,Field(gt=0, le=10000) 会映射到 exclusiveMinimum / maximum,正则 pattern 和 maxLength 也能写入 schema。模型是否总能给出正确语义仍需要权重实测,但"约束被编译进调用边界"这件事在 Python 层是可核验的。
3. 置信度门控:宁可升级,不要误执行
README 对 confidence 的定义是两路信号的最小值:一个 calibrated post-hoc head 评估完整 prompt 与刚产生的 call,另一个来自调用 token 的解码概率。应用侧可以设置阈值:高于阈值就执行,低于阈值就追问、升级到大模型或交给人处理。
这对端侧设备特别重要。一个手机联系人提取错了,最多是体验问题;一个智能门锁、支付、机器人动作如果误执行,风险会明显放大。Needle 的产品哲学更像一个"动作分类器 + 参数抽取器",而不是通用助手:
| 情况 | 推荐处理 |
|---|---|
confidence >= 阈值 |
本地执行工具 |
confidence < 阈值 |
追问或升级到云端大模型 |
| off-topic | 返回空调用 [],不生成自由文本 |
| 参数证据不足 | 省略可选字段,而不是猜测 |
4. 工具检索:大工具目录只把 Top 5 放进上下文
README 说:声明工具数量不超过 5 个时直接渲染;超过 5 个时,初始化阶段用内置 contrastive head 对每个工具 schema 做 embedding,每轮 query 也做 embedding,只把得分最高的 5 个工具放进上下文并据此重建 grammar。未入选工具"不可达",不是概率较低。
这解决了一个实际问题:端侧模型上下文非常短,不能像云端大模型一样塞进几十上百个工具定义。Needle 选择把工具目录变成检索问题,再把生成问题限制在 Top 5 的闭集里。
text
工具目录 schemas
-> schema fingerprint
-> 内置 embedding / tool_index_path 持久化
-> query embedding
-> Top 5 工具进入上下文
-> 语法约束只允许这 5 个工具
5. LoRA 微调与单文件导出
Needle 不只提供推理 wrapper,也提供数据生成、LoRA 微调和 .cact 导出流程。README 中的流程是:JSONL 样本 → 可选合成扩充 → LoRA 微调 → merge adapter → quantize/export → 用同一个 engine 加载 tuned .cact。
CLI parser 源码中可核验的子命令包括:
| 子命令 | 关键参数 | 用途 |
|---|---|---|
needle generate-data |
--tools、--augment、--num-samples、--output、--model |
生成或扩充工具调用训练数据 |
needle finetune |
jsonl_path、--epochs、--batch-size、--lora-rank、--lora-alpha、--generate |
在冻结基座上做 LoRA 微调 |
needle build |
checkpoint、--lora、--out、--bits {2,4}、--upload |
合并并导出 .cact |
needle playground |
--weights、--host、--port |
本地浏览器调试工具 schema 与 query |
needle run |
--checkpoint、--query、--tools、--max-len、--no-constrained |
从 checkpoint 做工具调用生成 |
注意:当前 CLI 使用 add_help=False,实际运行 subcommand --help 会报 unrecognized arguments;因此本文没有把 needle playground --help 这类命令写成可用示例,而是基于 parser 源码核验参数名。
🔬 技术架构深度解析
3 层产品边界:Python 包、JAX 训练代码、原生引擎
Needle 仓库不是一个纯 Python 推理模型。它更像三层组合:
text
┌─────────────────────────────────────────────────────────┐
│ Python API / CLI │
│ - @needle.tool / Field / extract │
│ - Needle.run / complete / reset │
│ - generate-data / finetune / build / playground │
└───────────────────────┬─────────────────────────────────┘
│ ctypes
┌───────────────────────▼─────────────────────────────────┐
│ Native engine: libneedle.{dylib,so,dll} │
│ - 首次从 Hugging Face 下载并缓存 │
│ - 由 ENGINE_VERSION = 2.0.1 选择平台 wheel │
│ - Python 层暴露 needle_init / complete / reset / load │
└───────────────────────┬─────────────────────────────────┘
│ .cact weights / baked engine
┌───────────────────────▼─────────────────────────────────┐
│ Needle 2 model / Simple Attention Network │
│ - 45M 参数、工具调用/抽取专用 │
│ - README 宣称 14MB 单文件、约 28MB RAM │
│ - 工具 schema 语法约束 + 置信度门控 │
└─────────────────────────────────────────────────────────┘
这意味着:仓库源码能审计 Python API、JAX 架构、量化导出与训练流程;但实际高性能运行依赖外部分发的 native library 和权重文件。本文没有下载并执行这些二进制,因此对端到端性能保持边界声明。
Simple Attention Network:为小模型重新分配参数预算
needle/model/architecture.py 中的 PRESETS 暴露了两个配置:base 与 needle。其中 needle 配置为 d_model=768、num_heads=12、num_kv_heads=6、num_layers=27、engram_layers=(2, 15);base 为 d_model=512、num_heads=8、num_kv_heads=4、num_layers=27。
它的块结构不是标准 Transformer FFN,而是把多个机制组合起来:
text
Token embedding
-> RoPE + GQA Attention
-> ZCRMSNorm + gated residual
-> HadamardMLP(固定 Walsh-Hadamard 变换替代传统大 FFN)
-> Multi-lane Hyper-Connections
-> Engram KV memory 注入(指定层触发)
-> final norm / logits / tool-call decode
几个值得拆的点:
- HadamardMLP :源码里
_walsh_matrix()构造 Walsh-Hadamard 矩阵,HadamardMLP用d1 * z @ H -> silu(d2 * z) @ H -> d3替代传统两层大 FFN。固定正交变换减少可训练矩阵规模,把参数更多留给注意力、门控和记忆。 - GQA attention :
num_heads与num_kv_heads分离,needlepreset 是 12 query heads / 6 KV heads,降低 KV 投影与缓存成本。 - Engram memory :源码中
engram_indices()使用 hashed n-gram table,Engram通过 key/value projection 把历史 token 的 n-gram 信息注入特定层。README 称其为 engram key-value memory。 - Multi-lane Hyper-Connections :
mhc_lanes=4,每层通过phi_pre/phi_post/phi_res与 Sinkhorn 归一化在 lane 之间路由残差,类似把单一残差流扩成多通道状态。 - KV window :decode 代码支持
kv_window,并允许 sink 位置保留,README 宣称工具被 pinned as KV sinks,形成 256-token sliding window 的有界内存策略。
量化:不是"全模型简单 2-bit",而是 CQ 量化管线
needle/model/quantize.py 里可以看到 Cactus Quants 的核心痕迹:cq_quantize() 先按 group reshape,经过 Hadamard 旋转,归一化到 unit vector,再用 codebook 做 nearest quantization,最后反旋转还原;cq_model_bytes() 还会把量化值、group scale 和未量化参数分开估算大小。
text
权重张量
-> 按 group_size 分组
-> Walsh-Hadamard rotation
-> norm / unit direction
-> codebook nearest quantization
-> dequant / inverse rotation
-> .cact export
这比"直接 int2 量化"更复杂,也解释了为什么 README 会说 CQ2-bit。严格地说,源码层能确认 CQ 量化工具链存在,并支持 --bits {2,4};但实际 14MB 体积、质量曲线和端侧 RAM 占用需要配套权重与引擎验证。
结构化调用流水线
Needle 的工程流水线可以画成下面这样:
text
开发者声明工具
├─ Python function + type hints
├─ docstring Args
├─ Field constraints
└─ Pydantic model
↓
JSON schema / tool schemas
↓
Needle init
├─ tools_json
├─ optional system facts
├─ optional tool_index_path
└─ native lib needle_init
↓
用户自然语言 query
↓
模型受 grammar 约束解码
↓
{type, function_calls, reasoning, confidence}
↓
应用按置信度执行 / 追问 / 升级
源码规模与测试足迹
本次浅克隆 commit 为 a30627fbc9a0d795e3a86d207e125f9aadbe838a,提交时间 2026-08-13T08:25:49+01:00,最后提交说明为"enhance support for PEP 604 unions"。排除 .git 后的文件规模如下:
| 指标 | 数值 |
|---|---|
| 文件数(排除 .git) | 40 |
| Python 文件 | 23 |
| 代码文件(py/js/css/html/sh) | 26 |
| 生产代码行 | 4,374 |
| 测试文件 | 8 |
| 测试代码行 | 636 |
| README 行数 | 270 |
| 主要生产目录 | needle/agent、needle/model、needle/playground |
无模型下载探针通过了 schema 生成检查:函数签名、Literal enum、Field 数值边界、正则与长度限制均能进入 schema。完整 pytest 未运行成功,因为系统 Python 环境未安装 pytest;我没有临时安装依赖以避免影响定时任务环境,也没有下载模型权重做端到端推理。
README 性能数字的证据等级
| 声明 | 来源 | 本文处理 |
|---|---|---|
| 45M 参数 | README | 作为维护者声明引用 |
| 14MB 单文件 binary | README | 未独立复现,需下载权重/引擎验证 |
| 约 28MB RAM 会话 | README | 未独立复现,硬件/平台相关 |
| prefill 4300 tps / decode 850 tps 示例 | README 返回 JSON 示例 | 不作为通用 benchmark,只说明返回字段形态 |
| 5x 到 70x 小于若干小模型 | README | 需要同硬件、同任务、同指标对齐后才可比较 |
| schema grammar 约束 | README + Python schema 源码 | Python 层可核验,模型解码仍需权重实测 |
📖 README 核心内容摘要
README 的叙事非常清晰:Needle 不是"更小的 ChatGPT",而是"一个可本地运行的工具调用基础模型"。它把结构化抽取、函数调用、设备控制看成同一类任务:只要你把目标结构声明成一个工具,模型就输出这个工具的参数。
核心 API 分 5 种:
- Simple :用
@needle.tool装饰 Python 函数,签名与 docstring 自动生成 schema,run()自动执行工具循环。 - Medium :用
Literal、默认值、Google-styleArgs:提升参数描述和枚举约束。 - Advanced :用
needle.Field添加范围、pattern、长度、数组等强约束。 - Extraction :用 Pydantic model 声明结构,
needle.extract(text, Schema)返回 typed object。 - Manual schema:直接传入 JSON schema,适合跨语言或非 Python 工具注册。
README 还强调 system turn 只承载"事实"而非"指令":例如 date、locale、device、battery、network、location、user、assistant。这样相对时间"tomorrow at 7"只有在 date: 存在时才会解析成绝对时间。这个设计很适合端侧自动化,因为设备状态是事实上下文,不应该变成可注入的 prompt 指令面。
安装与运行方面,README 给出最小安装:
sh
pip install cactus-needle
Python 包在首次使用时会通过 huggingface_hub 下载平台对应的 native library,并缓存到 ~/.cache/cactus-needle/<ENGINE_VERSION>/。源码中平台标签覆盖 macOS arm64/x86_64、Windows amd64/arm64、Linux manylinux2014/musllinux aarch64/x86_64。
🚀 快速上手
安装
sh
pip install cactus-needle
最小工具调用示例
python
import needle
@needle.tool
def set_lights(room: str, brightness: int):
"""Set light brightness.
Args:
room: room name
brightness: brightness from 0 to 100
"""
return {"room": room, "brightness": brightness, "ok": True}
agent = needle.Needle(tools=[set_lights])
response = agent.run("dim the living room to 30")
print(response["results"])
带约束的参数声明
python
from typing import Annotated, Literal
import needle
@needle.tool
def set_thermostat(
temperature: Annotated[int, needle.Field(ge=16, le=30)],
mode: Literal["heat", "cool", "auto"] = "auto",
):
"Set the thermostat."
return {"temperature": temperature, "mode": mode}
agent = needle.Needle(tools=[set_thermostat])
print(agent.run("make it 21 and cool the room"))
结构化抽取
python
from pydantic import BaseModel
import needle
class Invoice(BaseModel):
vendor: str
total: float
due_date: str
invoice = needle.extract(
"Invoice from Acme Corp, $1,200.00, due 2026-09-01",
Invoice,
)
print(invoice.vendor, invoice.total)
本地 Playground
sh
needle playground
源码 parser 显示该命令支持 --weights、--host、--port。如果传入 tuned .cact,可用 needle playground --weights my.cact 启动;默认 host 为 127.0.0.1、port 为 7860。
微调与导出边界
sh
needle generate-data --tools my_tools.json --num-samples 500 --output data.jsonl
needle finetune data.jsonl --epochs 3 --lora-rank 16 --lora-alpha 32
needle build checkpoints/needle2.pkl --lora checkpoints/needle_lora.pkl --out my_needle.cact --bits 2
这些参数均来自当前源码 parser。实际训练和导出会涉及 JAX、checkpoint、可能的 OpenRouter API Key 与 Hugging Face 下载,不属于本文的无模型探针范围。
📊 增长速度与社区热度
增长速度评估
Trending 快照只保留了完整热榜顺序和 rank 1 的富化信息,没有保留 cactus-compute/needle 的"今日新增 Stars"。因此不能把 API 补充值反推成日增长,也不能编造"today's stars"。本期只能给出两个保守指标:
| 指标 | 数值 | 说明 |
|---|---|---|
| Trending 排名 | #17 / 17 | 虽在榜尾,但技术含量高,适合作为非榜首精选 |
| Stars | 4,494 | 2026-08-13 API 补充核验值 |
| Forks | 317 | API 补充核验值 |
| Open issues/PRs 合并计数 | 27 | GitHub open_issues_count 同时包含 issues 与 PRs,未进一步拆分 |
| 创建时间 | 2026-02-24 | 约半年内达到 4.5K Stars,生命周期热度较高 |
| 最近推送 | 2026-08-13 | 与 Trending 当日同日仍活跃 |
| 最新 tag / PyPI | v2.0.2 / 2.0.2 | 发布面比源码内版本号更靠前 |
如果按创建时间到快照日粗略折算,约 170 天获得 4,494 Stars,生命周期平均约 26 Stars/天。这个数值只能作为长期基线,不能代表今日增长速度。
社区热度与成熟度
| 维度 | 观察 |
|---|---|
| 维护活跃度 | 当日有提交,最近提交修复 PEP 604 union schema 支持并补测试 |
| 发布成熟度 | Git tag 已到 v2.0.2,PyPI 也显示 2.0.2;但源码 __version__ 仍是 2.0.0,引擎版本为 2.0.1,存在版本面不同步 |
| 测试足迹 | 8 个测试文件、约 636 行测试;覆盖工具 schema、渲染、生成、推理、微调、构建等模块,但本文未跑完整依赖测试 |
| 二进制边界 | 运行依赖 Hugging Face 分发的 libneedle,源码仓库不等于完整可审计引擎实现 |
| License 风险 | 仓库 LICENSE 与 GitHub API 为 MIT;pyproject/PyPI 为 Apache-2.0。集成前应确认代码、包、权重、引擎各自条款 |
今日 Trending 完整榜单
以下榜单顺序来自 2026-08-13 预抓取快照;Stars/Forks/License 为后续 API 补充核验或快照保留值,未保留今日新增 Star。
| Rank | Repository | 说明 | Stars | Forks | Language | License |
|---|---|---|---|---|---|---|
| 1 | cathrynlavery/diagram-design | Claude Code/Codex/Pi 的编辑级图表 skill | 11,984 | 747 | HTML | MIT |
| 2 | macro-inc/macro | 邮件、聊天、文档、任务、Agent、CRM 统一工作台 | 2,207 | 251 | Rust | AGPL-3.0 |
| 3 | semantica-agi/semantica | Graph-native AI context / governance infrastructure | 5,965 | 641 | Python | MIT |
| 4 | stablyai/orca | 多 Agent 并行工作 ADE | 44,339 | 3,087 | TypeScript | MIT |
| 5 | msitarzewski/agency-agents | AI agency prompt/agent collection | 144,848 | 23,444 | Shell | MIT |
| 6 | shiyu-coder/Kronos | 金融市场 foundation model | 37,015 | 6,155 | Python | MIT |
| 7 | NanmiCoder/MediaCrawler | 多平台内容与评论爬虫 | 62,186 | 12,157 | Python | 未明确 |
| 8 | hugohe3/ppt-master | AI 生成原生 PowerPoint deck | 46,118 | 3,747 | Python | MIT |
| 9 | infiniflow/ragflow | RAG + Agent context engine | 87,723 | 10,330 | Go | Apache-2.0 |
| 10 | paperclipai/paperclip | 工作场景 Agent 管理应用 | 77,901 | 14,302 | TypeScript | MIT |
| 11 | NVIDIA-NeMo/Switchyard | Rust LLM traffic proxy / protocol router | 963 | 97 | Rust | Apache-2.0 |
| 12 | ZuodaoTech/everyone-can-use-english | 人人都能用英语 | 36,311 | 5,037 | TypeScript | GPL-3.0 |
| 13 | smicallef/spiderfoot | OSINT 与攻击面映射自动化 | 20,441 | 3,307 | Python | MIT |
| 14 | localsend/localsend | 跨平台 AirDrop 替代品 | 88,039 | 4,885 | Dart | Apache-2.0 |
| 15 | Lightricks/LTX-2 | LTX-2 音视频生成模型推理与 LoRA 训练包 | 8,783 | 1,400 | Python | 未明确 |
| 16 | embabel/embabel-agent | JVM/Kotlin Agent framework | 4,264 | 413 | Kotlin | Apache-2.0 |
| 17 | cactus-compute/needle | 14MB 工具调用小模型与端侧推理包 | 4,494 | 317 | Python | MIT/API;包元数据 Apache-2.0 |
🎯 适用场景
| 场景 | 为什么 Needle 适合 | 需要注意的边界 |
|---|---|---|
| 智能家居语音控制 | 工具集合有限,动作参数可 schema 化,本地低延迟更重要 | 高风险动作必须设置置信度阈值和二次确认 |
| 手机端个人自动化 | 联系人、日历、提醒、设置等动作适合结构化调用 | 需要严格权限隔离,Needle 本身不是 OS sandbox |
| 穿戴设备 | 内存预算小、任务窄、离线可用价值高 | 需实测目标芯片上的 RAM、耗电和延迟 |
| 机器人简单指令 | 自然语言转有限动作参数,低置信度可升级 | 物理世界执行前应有安全策略层 |
| 本地结构化抽取 | Pydantic/schema 形式天然适配票据、短信、通知抽取 | 复杂长文档受上下文窗口限制 |
| 大模型前置过滤 | 低风险本地处理,高风险或低置信度再发云端 | 需要设计升级策略和审计日志 |
| 嵌入式工具目录 | 工具检索让大目录只暴露 Top 5,降低上下文压力 | 工具召回错误会让正确工具"不可达" |
💡 总结
Needle 最值得关注的地方,不是 Star 数,也不是"14MB"这个标题党式数字,而是它把 Agent 工程里的一个关键环节单独抽出来做极致优化:本地、低内存、强约束地把自然语言变成工具调用。这条路线和云端大模型 Agent 并不冲突,反而更像一个端侧执行层:简单、确定、低风险的动作在本地完成;复杂、模糊、低置信度的问题再升级。
从源码看,它已经不只是 README 概念:Python API、schema 构造、Field 约束、JAX 架构、CQ 量化、LoRA 微调、Playground、CLI parser 都存在。但它也有清晰边界:原生引擎和权重由 Hugging Face 分发,仓库 LICENSE 与包元数据存在差异,版本面也不完全同步,README 性能数字需要独立复现。对于工程团队,我会把它视为一个值得试验的"端侧工具调用专用模型",而不是可以直接替换云端 Agent 的完整平台。