Text Generation
MLX
Safetensors
llama
code
minicpm
chinese
conversational
tool-calling
quantization
4-bit precision
Instructions to use whcl412/LycheeAI-coder-2b-II-pro 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 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") 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 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"
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" } ] } } }Run Pi
# Start Pi in your project directory: pi
- MLX LM
How to use whcl412/LycheeAI-coder-2b-II-pro 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"
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" # 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", "messages": [ {"role": "user", "content": "Hello"} ] }' - Hermes Agent
How to use whcl412/LycheeAI-coder-2b-II-pro 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"
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
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use whcl412/LycheeAI-coder-2b-II-pro 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"
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" \ --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"
|
Download README.md from whcl412/LycheeAI-coder-2b-II-pro: direct link, hf CLI and curl.
- Browser
- Download file 26.5 kB
-
https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro/resolve/main/README.md
- Command line
-
hf download hf://whcl412/LycheeAI-coder-2b-II-pro/README.md
-
curl -L -o README.md https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro/resolve/main/README.md
26.5 kB
| license: apache-2.0 | |
| tags: | |
| - code | |
| - minicpm | |
| - chinese | |
| - conversational | |
| - tool-calling | |
| - mlx | |
| - quantization | |
| base_model: openbmb/MiniCPM5-2B | |
| pipeline_tag: text-generation | |
| library_name: mlx | |
| # LycheeAI-coder-2b-II-pro · MLX 4bit | |
| > **v7** —— 用「精选 1000 条 × 1 epoch」替代「全量 5341 条 × 0.22 epoch」, | |
| > 验收 26/27 通过(v6.1 是 25/27)。 | |
| [中文](#中文) | [English](#english) | |
| > 基于 [MiniCPM5-2B](https://huggingface.co/openbmb/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](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16) | 5.03 GB | **要质量基准 / 二次量化 / 继续微调** | | |
| | [-f16](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-f16) | 5.03 GB | 同上(**同一份权重**,方便 transformers 用户找) | | |
| | [GGUF](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-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,本仓库) | |
| ```bash | |
| 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`](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16)(或等价的 [`-f16`](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-f16))。 | |
| ### Ollama(GGUF) | |
| 已发布 → [`whcl412/LycheeAI-coder-2b-II-pro-GGUF`](https://huggingface.co/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 镜像](https://modelscope.cn/models/whcl412/LycheeAI-coder-2b-II-pro-GGUF)。 | |
| ⚠️ 注意:Ollama 的**自动 `tool_calls` 解析对这个小模型不可靠**——它是按「直接输出 JSON」训练的,不带运行时期望的包裹标签。生产环境请从 `content` 里自己解析 JSON,详见 GGUF 仓库 README。 | |
| --- | |
| ## 工具调用 | |
| ### 输出格式 | |
| 它输出的是**裸 JSON**(OpenAI function calling 风格),不带任何包裹: | |
| ```json | |
| {"name": "get_weather", "arguments": {"city": "北京"}} | |
| ``` | |
| **没有** ```json 代码块围栏、**没有** `<tool_call>` 标签、**没有**"好的我来调用工具"这种旁白。 | |
| ### 单步调用 | |
| ``` | |
| system: 你是 LycheeAI-coder-2b-II-pro。可以使用工具来帮助用户。 | |
| 可用工具: | |
| - get_weather(city: string): 查询指定城市的天气 | |
| user: 北京今天天气怎么样? | |
| ``` | |
| 输出: | |
| ```json | |
| {"name": "get_weather", "arguments": {"city": "北京"}} | |
| ``` | |
| ### 多步调用(有依赖时只输出第一个) | |
| 问"北京今天适合出门吗",它需要先查天气再判断,此时**只输出第一步**: | |
| ```json | |
| {"name": "get_weather", "arguments": {"city": "北京"}} | |
| ``` | |
| 你把工具结果喂回去,它再继续。**宿主需要循环驱动**:解析 → 执行 → 喂回结果 → 再解析。 | |
| ### 完全独立的调用会并列输出 | |
| 同时查北京和上海,它会一次给两行: | |
| ```json | |
| {"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](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16) | 5.03 GB | **Quality baseline / re-quantization / further fine-tuning** | | |
| | [-f16](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-f16) | 5.03 GB | Same as above (**identical weights**, named for transformers users) | | |
| | [GGUF](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-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 `-f16` repo; 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) | |
| ```bash | |
| 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`](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-MLX-bf16) (or the equivalent [`-f16`](https://huggingface.co/whcl412/LycheeAI-coder-2b-II-pro-f16)). | |
| ### Ollama (GGUF) | |
| Published → [`whcl412/LycheeAI-coder-2b-II-pro-GGUF`](https://huggingface.co/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: | |
| ```json | |
| {"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**: | |
| ```json | |
| {"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 | |
| ```json | |
| {"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. | |
| --- | |
| <p align="center"> | |
| <sub>LycheeAI —— 小的,但能干活。</sub><br> | |
| <sub>LycheeAI — small, but it gets the job done.</sub> | |
| </p> | |