Instructions to use whcl412/LycheeAI-coder-2b-II-pro-f16 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use whcl412/LycheeAI-coder-2b-II-pro-f16 with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("whcl412/LycheeAI-coder-2b-II-pro-f16") prompt = "Write a story about Einstein" messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, add_generation_prompt=True ) text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Pi
How to use whcl412/LycheeAI-coder-2b-II-pro-f16 with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "whcl412/LycheeAI-coder-2b-II-pro-f16"
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "whcl412/LycheeAI-coder-2b-II-pro-f16" } ] } } }Run Pi
# Start Pi in your project directory: pi
- MLX LM
How to use whcl412/LycheeAI-coder-2b-II-pro-f16 with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Interactive chat REPL mlx_lm.chat --model "whcl412/LycheeAI-coder-2b-II-pro-f16"
Run an OpenAI-compatible server
# Install MLX LM uv tool install mlx-lm # Start the server mlx_lm.server --model "whcl412/LycheeAI-coder-2b-II-pro-f16" # Calling the OpenAI-compatible server with curl curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "whcl412/LycheeAI-coder-2b-II-pro-f16", "messages": [ {"role": "user", "content": "Hello"} ] }' - Hermes Agent
How to use whcl412/LycheeAI-coder-2b-II-pro-f16 with Hermes Agent:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "whcl412/LycheeAI-coder-2b-II-pro-f16"
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default whcl412/LycheeAI-coder-2b-II-pro-f16
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use whcl412/LycheeAI-coder-2b-II-pro-f16 with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "whcl412/LycheeAI-coder-2b-II-pro-f16"
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "whcl412/LycheeAI-coder-2b-II-pro-f16" \ --custom-provider-id mlx-lm \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
LycheeAI-coder-2b-II-pro · MLX 4bit
v7 —— 用「精选 1000 条 × 1 epoch」替代「全量 5341 条 × 0.22 epoch」, 验收 26/27 通过(v6.1 是 25/27)。
基于 MiniCPM5-2B 微调的轻量编程助手 · Apache 2.0 2B 参数,主打本地工具调用
这是个人开发者的项目,2B 参数注定它只是个"本地小助手"。 它比 1B 版更能干,但请别用 GPT / Claude / DeepSeek 的标准要求它。
它最大的用处是:跑在你自己的机器上,不用联网,能调工具,响应飞快。
本仓库是 MLX 4bit 量化版本,1.4 GB,Apple Silicon 上开箱即用。
四个版本,按需要选:
| 版本 | 体积 | 用在什么场景 |
|---|---|---|
| MLX 4bit(本仓库) | 1.42 GB | Mac 上日常跑,最省内存、最快 |
| MLX 无损 bf16 | 5.03 GB | 要质量基准 / 二次量化 / 继续微调 |
| -f16 | 5.03 GB | 同上(同一份权重,方便 transformers 用户找) |
| GGUF | 1.45 / 2.50 / 4.69 GB | Ollama、llama.cpp |
MLX 无损版(bf16)是给"要在它身上再加工"的人用的:4bit 是有损的,不能作为二次量化或续训的基础。 它和
-f16仓库是同一份权重(字节相同),两个名字只是为了让 MLX 用户和 transformers 用户都能找到。 格式是标准 safetensors,MLX 和 transformers 都能直接加载。
中文
这一版做了什么
上一版(1b-II)最大的短板是工具调用完全不会——训练数据里一条工具样本都没有。这一版专门补上了这块:
| 能力 | 1b-II | 2b-II-pro |
|---|---|---|
| 工具调用格式 | ❌ 全错、字段编造 | ✅ 裸 JSON,格式稳定 |
| 单步调用 | ❌ | ✅ |
| 多步调用 | ❌ | ✅ |
| 拒绝不该做的事 | ⚠️ 时好时坏 | ✅ |
| 结果解读 | ❌ | ✅ |
基座换了:从 MiniCPM5-1B 换成 MiniCPM5-2B(面壁智能,Apache 2.0,512K 上下文)。
快速开始
MLX(Apple Silicon,本仓库)
pip install mlx-lm
# 直接加载(4bit,1.4 GB)
python -m mlx_lm.generate \
--model whcl412/LycheeAI-coder-2b-II-pro \
--prompt "帮我写个快速排序"
# 起个 OpenAI 兼容的服务
python -m mlx_lm.server --model whcl412/LycheeAI-coder-2b-II-pro --port 8080
要在 MLX 上用无损版(二次量化、继续微调、要质量基准),把模型名换成
whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16即可(5.03 GB,MLX 直接加载)。 手工拼 prompt 时记得套 chat template 并传enable_thinking=False,否则会复读乱码。
transformers
本仓库是 MLX 4bit 格式,transformers 不能直接加载。
需要 HF safetensors 请用无损版 → whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16(或等价的 -f16)。
Ollama(GGUF)
已发布 → whcl412/LycheeAI-coder-2b-II-pro-GGUF
三档可选:q4_k_m(1.45 GiB)/ q8_0(2.50 GiB,推荐)/ f16(4.69 GiB 无损)。
仓库里带 Modelfile,下载后 ollama create 即可用。国内可用 ModelScope 镜像。
⚠️ 注意:Ollama 的自动 tool_calls 解析对这个小模型不可靠——它是按「直接输出 JSON」训练的,不带运行时期望的包裹标签。生产环境请从 content 里自己解析 JSON,详见 GGUF 仓库 README。
工具调用
输出格式
它输出的是裸 JSON(OpenAI function calling 风格),不带任何包裹:
{"name": "get_weather", "arguments": {"city": "北京"}}
没有 ```json 代码块围栏、没有** <tool_call> 标签、没有**"好的我来调用工具"这种旁白。
单步调用
system: 你是 LycheeAI-coder-2b-II-pro。可以使用工具来帮助用户。
可用工具:
- get_weather(city: string): 查询指定城市的天气
user: 北京今天天气怎么样?
输出:
{"name": "get_weather", "arguments": {"city": "北京"}}
多步调用(有依赖时只输出第一个)
问"北京今天适合出门吗",它需要先查天气再判断,此时只输出第一步:
{"name": "get_weather", "arguments": {"city": "北京"}}
你把工具结果喂回去,它再继续。宿主需要循环驱动:解析 → 执行 → 喂回结果 → 再解析。
完全独立的调用会并列输出
同时查北京和上海,它会一次给两行:
{"name": "get_weather", "arguments": {"city": "北京"}}
{"name": "get_weather", "arguments": {"city": "上海"}}
该拒绝时会明确拒绝
不是所有请求都调工具。超出能力或不合规的,它会说明原因并给替代方案,不会硬编一个工具调用糊弄过去。
宿主接入注意
格式不是通用的
这一点很重要:不同 agent 框架的工具调用格式不一样。
| 框架/模型 | 格式 |
|---|---|
| OpenAI / 大多数本地框架 | 裸 JSON {"name":..., "arguments":{...}} |
| Anthropic / Claude Tools | XML 标签 <function_calls><invoke name="..."> |
| 部分国产模型 | <tool_call> 自定义标签包 JSON |
| MCP | 传输层协议,内部通常还是 JSON-RPC |
它默认输出裸 JSON。如果你的宿主需要别的格式,在 system 里明确告诉它,它会按你说的来。
什么时候给工具,什么时候别给
它训练时见过四类场景,行为是分开的:
| 类别 | 例子 | 它的行为 |
|---|---|---|
| 工具能算出确定结果 | 789*123、1000 美元换人民币 |
调工具 |
| 能力/伦理不允许 | 订机票、破解 WiFi、伪造证明 | 明确拒绝 + 给替代方案 |
| 工具帮不上忙 | 什么是递归、写二分查找 | 直接回答 |
| 宿主没有工具 | 纯聊天软件里的"帮我搜一下" | 说明没有这个能力,不硬调 |
最后一行很关键:如果你的宿主是纯聊天(微信、客服机器人),不要给它任何工具定义,它会自然地说"我这边没有联网能力",而不是吐一坨宿主接不住的 JSON。
如果你给它 calculate 却不希望它算简单加减,可以在 system 里说明。默认行为是"算术优先用工具"。
训练信息
| 项 | 值 |
|---|---|
| 基座 | openbmb/MiniCPM5-2B |
| 方法 | LoRA(rank 8,dropout 0,scale 20,16 层) |
| 数据量 | 980 条(精选,v7) |
| 轮次 | 1 epoch(490 步) |
| 学习率 | 5e-5(adam) |
| batch / seq | 2 / 2048 |
| 框架 | MLX-LM 0.31.3 |
| 本版本量化 | 4bit affine(group_size 64,4.501 bits/weight) |
数据构成(v6 系列)
v6 是在 v5 基础上做的定向修复,共四批:
| 批次 | 条数 | 解决什么 |
|---|---|---|
| v5 基础 | 4,526 | v6.1 起点(v5 + OOD 初版) |
| v6.1 | 4,526 | 修 v6 的 ⑥⑨ 退步项,OOD 补强 |
| v6.2 | 4,778 | 补算术泛化(K6,252 条程序化生成) |
| v6.3 | 5,093 | 补能力边界 + 结果收尾(K7,315 条) |
| v6.4 | 5,341 | 补运算类型均衡(K8,248 条) |
发布的是 v6.1(不是 v6.4)。原因见下面"关于迭代"。
三类行为严格区分
这是训练中最容易搞混的地方:
| 类别 | 例子 | 正确行为 |
|---|---|---|
| 工具调用 | 789*123 |
调 calculate |
| 真拒绝(能力/伦理不允许) | 订机票、破解 WiFi | 说不做 + 给替代方案 |
| 无需工具 | 什么是递归、写二分查找 | 直接回答,不提工具 |
混为一谈会导致模型把正常提问也当成要拒绝的事——踩过这个坑。
能力边界
能做:
- 写代码、调 bug、解释技术概念
- 单步 / 多步工具调用(裸 JSON)
- 拒绝能力范围外或不合规的请求
- 解读工具返回结果
- 中文 / 英文 / 粤语
不擅长:
- 复杂多步推理(2B 容量限制)
- 超长上下文(虽然基座支持 512K,但实际长文表现没测)
- 无工具时的算术(会算错,请给
calculate——见下面"已知问题") - 自称身份的 OOD 场景(见下面"已知问题")
⚠️ 已知问题
1. 无工具时会心算,而且算错
这是当前最实际的短板。实测:
1234 + 5678 = 2000 (正确 6912)
35 ** 2 = 70 (正确 1225)
13.5 × 4 = 7.8 (正确 54)
199 × 257 = 40043 (正确 51143)
根因(做过对照实验确认):模型是按运算类型决定调不调工具的,而这跟训练数据里各类型的样本量完全对应。
| 运算类型 | 训练样本量 | 行为 |
|---|---|---|
乘法 * |
432 | ✅ 调工具 |
加法 + |
86 | ❌ 心算 |
幂 ** |
61 | ❌ 心算 |
| 小数 | 61 | ❌ 心算且算错 |
减法 - |
49 | ❌ 心算 |
不是"字符串记忆"(见过 38% vs 没见过 50%,差不多),也不是"难度"(1234+5678 也在心算)。
规避方法:**只要涉及算术,就在 system 里明确要求"任何计算都必须用 calculate"**:
需要任何数值计算时,一律调用 calculate,不要自己心算。
这条指令实测有效。v6.4 试图用数据修正(K8,按运算类型均衡),但引入了更严重的副作用(工具调用率从 60%+ 崩到 12%),所以发布了 v6.1 而没有发布 v6.4。
2. 身份问答在「宿主提供的通用 system」下会出错
如果你的 system prompt 是这样(很多 agent 框架的默认写法):
你是一个AI助手,可以使用工具来帮助用户。
可用工具:
- get_weather(city: str): 查询指定城市的天气
...
需要调用工具时,请直接输出 JSON:{"name": "工具名", "arguments": {...}}
那么问"你叫什么名字"时,它可能乱答(实测出现过"我叫 Qwen")。
但如果 system 是它训练时的格式,就完全正常:
你是 LycheeAI-coder-2b-II-pro,由 MiniCPM5-2B 通过 LoRA 微调而来的编程助手。
→ 答"我叫 LycheeAI-coder-2b-II-pro,由 MiniCPM5-2B 微调而来的编程助手。"
原因:训练数据的身份样本里,system 全部是上面第二种。模型没见过"通用 system + 工具列表 + 问身份"这个组合,没有训练信号。
规避方法:接入时把身份写进 system。比如:
你是 LycheeAI-coder-2b-II-pro。可以使用工具来帮助用户。
可用工具:
...
3. 两种 system 结构的一致性
训练数据混了两种结构:工具定义放 system / 放 user。实测两种都能用,但放 system 更稳(这也是主流 agent 框架的做法)。
4. 工具结果有两种结构,宿主需要知道
模型见过两种上下文,两种都认:
结构 A —— 有明确的调用轮(主流 agent 框架的样子):
user: 789*123 等于多少
assistant: {"name": "calculate", "arguments": {"expression": "789*123"}}
user: [工具调用结果] {"name": "calculate", "result": {"value": 97047}}
assistant: 789 × 123 = **97047**。
结构 B —— 宿主已代为执行,直接把结果给模型(67 条早期样本用这个):
user: 跑一下这段代码
[工具调用结果] {"exit_code": 0, "stdout": "45\n"}
assistant: 执行成功,输出 `45`。
区别:结构 B 的结果**不带 name**,因为上下文里本来就没有调用轮,工具身份从 system 的工具列表和问题本身就能推断。
建议:正常接入用结构 A(多轮循环)。只有在你已经知道该调什么、只想让模型解读结果时,才用结构 B。
v7 做了什么:用「精选」代替「全量」
起因:一个 500 步的硬约束
新版训练被限制在 500 步以内。而 步数 = ceil(条数 / batch_size),batch=2 → 最多 1000 条。
原来的数据集有 5,341 条,两条路:
| 方案 | 效果 |
|---|---|
| 5,341 条 × 250 步 | 每条样本只被见到 0.22 次(22% epoch) |
| 980 条 × 490 步 | 每条都被完整学到 1 次(100% epoch)✅ |
为什么第二条更好——不只是"学得够",而是行为惯性按样本条数加权:
原数据集里 51.4% 是单轮纯文本(普通聊天),它们把梯度拉向"纯聊天";而"多轮完整链路"(调用→拿结果→收尾,宿主真实交互形态)只占 10.7%。在 22% epoch 下,每条多轮样本平均只贡献 0.22 次更新——根本学不到。
重新配比:按行为重要性,而不是按原始比例
| 类别 | 原数据集 | v7 精选 | 说明 |
|---|---|---|---|
| 多轮链路(调用→结果→收尾) | 569 | 201(20.5%) | 最值钱,宿主真实路径 |
| 工具在 user + 调用 | 780 | 243(24.8%) | 占原始 66%,结构不能丢 |
| 有工具但不调 | 819 | 216(22.0%) | 压住"手痒调工具"的反面样本 |
| 工具在 system + 调用 | 397 | 179(18.3%) | 主结构 |
| 单轮纯文本 | 2,746 | 130(13.3%) | 从 51.4% 砍到 13.3% |
算术问题的真正解法:两头夹
之前的分析已经找到根因——乘法独占 50.5% 的样本,所以模型学到的是"乘法 → 调工具"这个特例,而不是"算术 → 调工具"这个通则。
v6.4 试过只做"补"(补 248 条稀缺类型),结果配比反而更差,还把工具调用率从 60%+ 砸到 12%。
v7 用两头夹:
一头补稀缺:K9 生成 90 条,按稀缺度加权(小数加法最缺 → 补最多)
一头削过剩:对基线里超量的乘法样本下采样,删掉 48 条
结果——乘法占比 **50.5% → 20.0%**,七个运算类型的最热/最冷差距从 30.5 倍降到 2.4 倍:
| 运算类型 | v6.1 | v7 |
|---|---|---|
| 乘法 | 244(50.5%) | 81(20.0%) |
| 幂 | 44(9.1%) | 66(16.3%) |
| 除法 | 53(11.0%) | 62(15.3%) |
| 加法 | 71(14.7%) | 54(13.3%) |
| 小数乘法 | 31(6.4%) | 47(11.6%) |
| 减法 | 31(6.4%) | 42(10.4%) |
| 小数加法 | 8(1.7%) | 35(8.6%) |
直接效果:v7 修好了两个长期存在的验收失败项——①b 第二道算术题、⑧b 多轮追问。
修掉一个源数据缺陷:工具名幻觉
质检时发现 9 处自相矛盾的样本:system 里只定义了 calculate,模型却去调 read_file / get_weather。
根因在数据生成脚本:「多轮追问」那批轨迹里有些是查天气、读文件,但 system 被写死只定义 calculate。**这等于在教模型"调用没定义的工具"**。
修法:先扫轨迹实际调用了哪些工具,只定义那些。
但修完发现新问题:「见过 ≥3 种工具」的样本从 56 条掉到 0 条——模型失去了「在多个候选里挑对的」训练信号。于是又加了干扰工具(2/3 的样本掺入 1~3 个无关工具,但不掺
calculate),恢复到 24 条。教训:修一个问题很容易带出另一个。每次修改后必须重新做全量质检。
一个影响深远的方法论修正:稳定采样
第一次尝试修验收 ⑥ 时,我调高了某类配额、砍了另一类,结果 26/1 变成 23/4——修一个坏三个。
查数据才发现真因:改配额会让 40% 的样本被重洗(两版只有 585 条共有)。等于每做一次实验就换了个数据集,任何结论都不可归因。这正是 v6 系列"修一个坏一个"的机制。
修法:把采样改成按内容哈希排序,改配额变成"单调扩展"——原来选中的样本仍然全部在,只多补差额。
验证:只改一类配额(260→220),**重叠率 99.9%**。
验收结果
| 版本 | 验收 | 说明 |
|---|---|---|
| v6.1(上一版) | 25 / 2 | ❌ ①b 第二道算术、❌ ⑧b 多轮追问 |
| v7(本版) | 26 / 1 | ✅ ①b、⑧b 全修好;❌ ⑥ 工具放 user |
26/1 已经过复现验证:同一份数据独立重跑一次训练,结果完全一致(含失败项相同)。
为什么没继续修 ⑥
⑥ 的考点是"工具定义放在 user 消息里"(而不是 system)。v7 在这一项退化成心算。
后续试了两个方向:
- v7b:调高 user 结构配额 + 砍多轮样本 → 23/4,修一个坏三个
- v7c:不动配额,纯追加 90 条该考点样本 → 22/5,工具调用倾向被整体稀释
三次尝试指向同一个结论:
在 1000 条预算内,任何"定向补强"都会挤掉其他能力。26/1 已经是这个预算下的最优解。
⑥ 属于兼容性场景,不是核心能力。如果你的宿主把工具定义放在 user 消息里,请在 system 中补一句身份/工具说明(见上面「已知问题 3」),或改用把工具定义放 system 的标准写法。
五轮迭代的完整复盘
| 版本 | 验收 | 结果 |
|---|---|---|
| v6 | 24 / 3 | ⑥⑨ 坏,①b 好 |
| v6.1 | 25 / 2 | ✅ 曾发布 |
| v6.2 | 24 / 3 | 修好 ①b,但 ⑧a ⑨g 退步 |
| v6.3 | 25 / 2 | 修好 ⑧a ⑨g,①b 又退;逐项与 v6.1 相同(等于白跑) |
| v6.4 | 25/2 + ④坏 | 调用率崩到 12% |
| v7 | 26 / 1 | ✅ 本版发布 |
| v7b | 23 / 4 | 尝试修 ⑥ 失败 |
| v7c | 22 / 5 | 尝试修 ⑥ 失败(另一方向) |
核心教训:v6 系列每轮都是「发现问题 → 补 5% 数据 → 全量重训」,把整个能力分布重洗一遍,结果修一个坏一个。补丁数据的效力取决于它占新数据集的比例,不是它的绝对条数——v6.4 补了 248 条去对冲 244 条乘法基线,占比只有 4.6%,压不住已有惯性。
v7 换了方法:改配比,不改增量。不往大数据集里塞补丁,而是重新精选整个数据集。
许可
Apache 2.0,跟基座 MiniCPM5-2B 一致。
训练数据、脚本都在项目里,欢迎 issue 交流。
English
What's new
The biggest gap in the previous release (1b-II) was that it couldn't call tools at all — the training data contained zero tool-use samples. This release is built specifically to fix that:
| Capability | 1b-II | 2b-II-pro |
|---|---|---|
| Tool-call format | ❌ malformed, invented fields | ✅ bare JSON, stable |
| Single-step calls | ❌ | ✅ |
| Multi-step calls | ❌ | ✅ |
| Refusing what it shouldn't do | ⚠️ inconsistent | ✅ |
| Interpreting tool results | ❌ | ✅ |
New base model: switched from MiniCPM5-1B to MiniCPM5-2B (OpenBMB, Apache 2.0, 512K context).
This repo is the MLX 4-bit quantized build, 1.4 GB, ready to run on Apple Silicon.
Four builds, pick by use case:
| Build | Size | Use case |
|---|---|---|
| MLX 4-bit (this repo) | 1.42 GB | Everyday use on a Mac — fastest, smallest footprint |
| MLX lossless bf16 | 5.03 GB | Quality baseline / re-quantization / further fine-tuning |
| -f16 | 5.03 GB | Same as above (identical weights, named for transformers users) |
| GGUF | 1.45 / 2.50 / 4.69 GB | Ollama, llama.cpp |
The MLX lossless (bf16) build is for anyone who needs to build on top of this model — 4-bit is lossy and unsuitable as a base for re-quantization or continued training. It holds byte-identical weights to the
-f16repo; the two names exist so both MLX and transformers users can find it. It's a standard safetensors checkpoint — loadable by MLX and transformers alike.
Quick start
MLX (Apple Silicon, this repo)
pip install mlx-lm
python -m mlx_lm.generate \
--model whcl412/LycheeAI-coder-2b-II-pro \
--prompt "write a quicksort"
python -m mlx_lm.server --model whcl412/LycheeAI-coder-2b-II-pro --port 8080
transformers
This repo is in MLX 4-bit format and cannot be loaded by transformers.
Use the lossless build for HF safetensors → whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16 (or the equivalent -f16).
Ollama (GGUF)
Published → whcl412/LycheeAI-coder-2b-II-pro-GGUF
Three variants: q4_k_m (1.45 GiB) / q8_0 (2.50 GiB, recommended) / f16 (4.69 GiB, lossless).
The repo ships a Modelfile — download, then ollama create.
⚠️ Note: Ollama's automatic tool_calls parsing is unreliable for this small model — it was trained to emit bare JSON, without the wrapper tags runtimes expect. For production, parse the JSON from content yourself. See the GGUF repo README.
Tool calling
Output format
It emits bare JSON (OpenAI function-calling style), with no wrapper at all:
{"name": "get_weather", "arguments": {"city": "Beijing"}}
No ```json fences, **no** <tool_call> tags, no "Sure, let me call the tool" narration.
Multi-step calls (only the first step is emitted when dependent)
For "Is it a good day to go out in Beijing?", it needs to check the weather first. In that case it emits only the first step:
{"name": "get_weather", "arguments": {"city": "Beijing"}}
Feed the tool result back and it continues. Your host must drive the loop: parse → execute → feed result back → parse again.
Independent calls are emitted in parallel
{"name": "get_weather", "arguments": {"city": "Beijing"}}
{"name": "get_weather", "arguments": {"city": "Shanghai"}}
It refuses clearly when it should
Not every request becomes a tool call. When something is out of scope or disallowed, it says so and offers alternatives instead of fabricating a tool call.
When to give it tools, and when not to
| Category | Example | Behavior |
|---|---|---|
| Tool gives a definite answer | 789*123, 1000 USD to CNY |
calls the tool |
| Out of scope / disallowed | booking flights, cracking WiFi | refuses + offers alternatives |
| Tool doesn't help | "what is recursion", "write binary search" | answers directly |
| Host has no tools | "search for me" in a plain chat app | says it can't, doesn't emit JSON |
The last row matters: if your host is a plain chat app, give it no tool definitions at all. It will say "I have no web access" instead of emitting JSON your host can't handle.
Training
| Item | Value |
|---|---|
| Base | openbmb/MiniCPM5-2B |
| Method | LoRA (rank 8, dropout 0, scale 20, 16 layers) |
| Data | 5,341 samples |
| Epochs | 1 (2,671 steps) |
| LR | 5e-5 (adam) |
| batch / seq | 2 / 2048 |
| Framework | MLX-LM 0.31.3 |
| Quantization | 4-bit affine (group_size 64) |
⚠️ Known issues
1. It does mental arithmetic without tools — and gets it wrong
1234 + 5678 = 2000 (should be 6912)
35 ** 2 = 70 (should be 1225)
13.5 × 4 = 7.8 (should be 54)
199 × 257 = 40043 (should be 51143)
Root cause (confirmed by controlled experiment): the model decides based on operation type, and this maps exactly onto per-type sample counts in the training data — multiplication 432 samples → calls the tool; addition 86 / power 61 / subtraction 49 → does it in its head.
It is not string memorization (38% vs 50%, no real difference), and not difficulty (1234+5678 also gets mental math).
Workaround: state it explicitly in the system prompt:
需要任何数值计算时,一律调用 calculate,不要自己心算。
2. Identity questions fail under a generic system prompt
If your system prompt is the generic "You are an AI assistant with tools..." style, asking "what's your name" may produce nonsense (observed: "I'm Qwen").
Put the identity in the system prompt:
You are LycheeAI-coder-2b-II-pro. You have access to tools.
Cause: every identity sample in the training data used the specific system prompt above; the model never saw the generic-tools-plus-identity combination.
3. Tool results come in two shapes — your host should know which it's sending
Both are understood:
Shape A — an explicit call turn (what mainstream agent frameworks do):
user: 789*123 等于多少
assistant: {"name": "calculate", "arguments": {"expression": "789*123"}}
user: [工具调用结果] {"name": "calculate", "result": {"value": 97047}}
assistant: 789 × 123 = **97047**。
Shape B — the host already executed it and hands the result straight over (used by 67 early samples):
user: 跑一下这段代码
[工具调用结果] {"exit_code": 0, "stdout": "45\n"}
assistant: 执行成功,输出 `45`。
Difference: shape B has no name in the result, because there is no call turn in context — the tool identity is inferable from the system tool list and the question itself.
Recommendation: use shape A for normal integration (multi-turn loop). Use shape B only when you already know which tool to run and just want the model to interpret the result.
4. Why v6.1 and not v6.4
Five iterations were trained. v6.2 fixed one item and broke two; v6.3 was item-for-item identical to v6.1; v6.4 collapsed the tool-call rate to 12%. v6.1 is the only version with a real improvement over v6 (25/2 vs 24/3 on a 27-item eval).
Lesson: each round patched ~5% of the data and retrained on everything, reshuffling the whole behavior distribution. The next version will validate on small-scale runs first.
License
Apache 2.0, same as the MiniCPM5-2B base.
LycheeAI —— 小的,但能干活。
LycheeAI — small, but it gets the job done.
- Downloads last month
- 1,018
Quantized