Skip to main content
Back to Showcase

BMB Agent —— 由 MiniCPM 驱动的个人助手:)

Author:AlukaModel:MiniCPM5-2B

Letting a 2B on-device model take over schedules and todos, with real-world lessons

封面故事汇​

MiniCPM5-2B与Kimi-2.6携手入住Hana Agent(OpenHanako)

他们会发生什么样的故事呢?

第一章、你好 花子​

将花子(Hana Agent)的源码git clone下来,在本地进行开发部署。在该示例中:端侧负责工具调用、coding、长文本输出的是MiniCPM5-2B(bumblebuttpow/MiniCPM5-2B-heretic-abliterated-MLX-8bit),以及负责传递图片内容的Kimi-2.6(节约成本)。

在 OpenHanako(Hana Agent)框架下,我们试图构建一个轻量化的本地个人助手。硬件环境限定在M4 16GB(下方展示配置),为了在有限的内存空间内运行,我们选择在源码下集成mlx-lm对MiniCPM5-2B进行8bit推理(事实上,一开始我使用了MLX-4bit量化版本,模型的指令遵循能力、长文本能力实在是灾难级别)。由于内存预算紧张,上下文窗口限制在 64K,max_token限制在2048

图片展示了在MacBook Air M4 16GB硬件环境下,使用fastfetch命令获取的系统信息。显示了操作系统为macOS Golden Gate 27.0 (26A428) arm64,主机为MacBook Air (13-inch, M4, 2025),内核为Darwin 27.0.0,系统运行时间为2天5小时,包数量为58(brew)等。还列出了CPU、GPU、内存、Swap、磁盘、本地IP、电池状态、电源适配器等信息,其中内存为16GB,Swap为724.19 MB,电池电量为80%。该图片与文档中介绍硬件环境限定在M4 16GB的内容相关。

然而,在基础环境落成后的首次工具调用测试中,系统便遭遇了典型的“小模型多任务并发崩溃”。我们向 Agent 下发了如下指令:“请写一段优美的文字,并使用 write 工具将其保存到工作区中,文件名为 poem.md”。

预期的执行链路是:模型先完成文本生成,然后触发结构化的 Function Calling(write 工具),最终在指定路径生成 poem.md。

但 MiniCPM5-2B 的实际输出出现了严重的指令漂移与格式污染:

惨不忍睹的回复:

这张图片展示了MiniCPM5-2B模型对指定任务的实际输出内容,对应文档中提及的“指令漂移与格式污染”问题。图片中显示的任务要求是使用write工具将内容保存为指定文件poem.md,但模型实际生成的内容将保存路径改为了save_test.md,还错误保留了``标签、CDATA结界,并输出了散文《落花》,呈现出严重的格式混乱与指令偏移,和文档描述的问题完全吻合。

小猫太想表现了。结果,它把 <function> 标签、CDATA 结界和散文《落花》搅成了一锅粥,要求的 poem.md 也幻听成了 save_test.md。

  1. 工具调用格式与自然语言生成混淆:模型直接输出了 <function name="write"> 等 XML 标签,甚至套用了 <![CDATA[,将代码结构与散文《落花》的正文强行缝合在了一起。
  2. 参数幻觉:指令明确要求保存为 poem.md,模型却将路径幻觉成了 save_test.md。
  3. 执行中断:由于输出格式不符合框架的解析规范,write 工具并未被真正触发,任务失败。

经过分析:这并非模型基础能力不足,而是 2B 级别小模型在“规划与执行”分离上的典型困境。在当前的上下文中,模型需要同时处理三重压力:

  • 承载官方较长系统提示词(AGENT.md)的上下文开销;
  • 维持严格的工具调用 JSON/XML Schema 格式对齐;
  • 执行开放式文本(散文)的生成逻辑。

对于 16GB 端侧环境和 2B 参数量的模型而言,注意力预算和 KV Cache 的分配不足以支撑这种多线程并发。当格式约束与内容生成产生冲突时,模型便出现了“精神分裂”式的输出。

第二章、给新家做个大扫除​

第一次测试下来发现,OpenHanako 对于端侧模型来说,功能繁多而且也用不上。所以我就从源码开始,对整个 Agent 做了一次“大扫除”。

我的原则很简单:MiniCPM 看不到的能力,就不能出现在提示词和工具表里;用户数据和历史配置不能因为功能裁剪而被清空。因此每一刀都尽量从 Provider、Engine、路由、工具注册、React 状态、Electron IPC、构建入口和依赖闭包一起处理,而不是只删一个按钮。

这轮实际删掉或禁用的部分包括:

  • Computer Use:桌面 GUI 控制链路、macOS/Windows provider、Swift helper、聊天浮层和设置入口。旧配置不清空,接口保留兼容读取,修改动作返回 410 FEATURE_UNAVAILABLE。
  • 多余社交平台:删除 Telegram、QQ、钉钉接入;只保留微信、飞书。历史会话的平台标记和旧配置迁移契约仍保留。
  • 频道/多 Agent 协作入口:频道页面、Tab、侧栏和后台调度不再装配;普通聊天页保持原来的使用方式。
  • 图片/视频生成:删除生成运行时、聊天卡片、设置入口和 /api/desk/beautify/cover 后台任务。图片读取、附件上传、预览、封面图库和视觉辅助链路仍然保留。
  • 发布与自动更新相关负担:安装包签名 seed、自动更新辅助代码等端侧用不上的流程移除,项目保持源码运行模式。

同时,Agent 的核心没有动:会话持久化与恢复、WebSocket 事件流、人格文件、手动固定记忆、轻量滚动摘要、Skills、书桌、附件预览、PathGuard、命令沙盒、写入审批、checkpoint 和审计日志都还在。

这一步做完后,MiniCPM 的工具面从原来的大杂烩收缩到 11 个基础工具:读文件、搜索、列表、编辑、写入、终端、任务清单、暂存交付文件等。Skills 也不再把完整 SKILL.md 塞进系统提示词,而是先只注入名称、描述和位置,模型判断需要时再调用 read 按需读取。对 2B 模型来说,这比“功能齐全”重要得多。

第三章、守规矩和断舍离​

OpenHanako/Pi SDK 的工具调用偏向 OpenAI-compatible tool_calls,而 MiniCPM5 原生工具协议是 `` XML。第一次失败时那种 XML、CDATA 和正文混在一起的样子,正是两边协议没有划清边界的症状。

所以我加了一层 MiniCPM XML tool adapter:

  1. 流式输出时先识别 `` 边界,普通文本可以增量显示,XML 段落不提前泄漏给用户;
  2. 解析 `` 和 CDATA,容忍分片到达的标签;
  3. 校验函数名、参数 JSON Schema、重复调用和未闭合结构;
  4. 转成标准 tool_calls 交给 HanaAgent 原有工具系统;
  5. 把工具结果回填给 MiniCPM,继续下一轮 Agent loop。

如果 XML 不完整、工具不存在、参数非法,就直接返回结构化错误,不执行任何副作用。工具执行仍然走原来的 ResourceIO、PathGuard、OS sandbox、审批和 checkpoint 机制。

系统提示词也被重写成 MiniCPM 专用的 5 条硬规则,只保留“如何选择工具、如何写文件、何时停止”这些必要规则;Kimi 等云端模型继续使用原来的完整提示词。VisionBridge 的职责同样拆开:Kimi 只负责 OCR、题干、公式、标签和图形关系的转录,MiniCPM 负责最终推理和回答。MiniCPM 的请求里不会塞图片 Base64。

上下文策略同样保守:32K 是唯一交互上限,日常建议 12K-16K;超过约 12K 输入时优先触发会话压缩。采样上,工具调用和文件操作使用低温 0.1-0.2,创意写作才提高temp。这样“守规矩”和“写东西”不再抢同一份注意力预算。

第四章、把家改成自己喜欢模样​

大扫除之后,就可以安心装修了。

第一处是模型参数链路。设置页保存 context、maxOutput 和采样参数后,会经过 Provider Registry、model-sync,最终进入 MLX 请求的 max_tokens、temperature、top_p、top_k、min_p 和 repetition_penalty。我的日常默认值是:

Ini
context = 16384
maxOutput = 1024
temperature = 0.2
topP = 0.9
topK = 40
minP = 0.05
repetitionPenalty = 1.05

第二处是 MLX-LM 服务本身。为了避免 16GB 统一内存被多份 KV cache 吃空,服务默认单请求、单 decode、单 prompt cache,prompt cache 限制在 3GB,预填充步长设为 1024。多轮长文会更倾向复用当前前缀,而不是让多个后台任务同时抢内存。

第三处是给写作流加了一套会话级 /novel 指令:continue 续写、detailed 扩写、review 审稿、recall 恢复人物线与伏笔。这些命令由输入扩展层拦截,不依赖 2B 模型理解指令本身;策略存在 session metadata 里,关闭再恢复会话仍然生效。它裁剪的只是发给模型的历史副本,原始 JSONL 和摘要不会丢。

最后是人格与 Skills。Agent 仍然是“一个文件夹”:人格、头像、记忆和技能可以一起备份;Hanako、Butter、Ming 这些模板没有被裁掉,只是端侧默认少装、少注入。家还是原来的味道,只是房间少了很多用不上的家具。

第五章、文学素养和数理能力​

文本串烧​

这里图片解释一下,我分别让MiniCPM5-2B描写了四季的风景,并测试上下文是否混乱或丢失。经测试,MiniCPM5-2B非常顺滑的完成了我的各种要求,最后将四季变换串联在一起。

(可以点击放大图查看具体内容)

这张图片是由MiniCPM驱动的BMB Agent相关的内容展示,核心是作者将之前描写的四季景色按顺序串联成一篇完整短文,分为春、夏、秋、冬四个季节段落,分别描绘了春日柳丝拂岸、燕飞花香,夏日水色映天、荷香蛙鸣,秋日稻穗飘香、硕果满枝,冬日暖阳铺雪、静谧安然的景致,整体形成了一幅完整的自然画卷。该内容对应文档中“文本串烧”相关的文学创作成果,体现了MiniCPM助手完成内容整合串联的文学能力。

图片展示了一段关于春天莺歌燕舞场景的描述。春风轻抚,嫩芽破土而出,柳丝垂挂,燕子掠过,衔草飞远。溪水潺潺,田野新绿,桃花杏花交织成云霞。孩童奔跑,老农漫步,山峦披绿,鸟鸣风铃交织成曲。这段描述与上文“小说续写”中节选的自己写的小说开头相呼应,体现了对自然生机与欢愉的描绘,展现了MiniCPM在文学素养方面的表现。

这张图片展示的是对话界面内容,包含两位角色的对话信息。右上角标注“小 小晨”的消息气泡内容为“之前我让你描写了关于哪个季节的场景”;左侧标注“枫”的消息气泡内容则回应称,之前描写的是春天的场景,还提到通过描绘春风、嫩芽、燕子、溪水、桃花等元素,来表现春天莺歌燕舞的生机盎然状态,这一对话内容对应文档中第五章“文本串烧”相关的测试场景,体现了MiniCPM在处理文学描述类任务的相关交互情况。

小说续写​

这里我节选了一段自己写的小说开头,并没有设定后续发展故事线,感情基调。

图片展示了一篇名为“续写:大道万千,红尘客梦,皆为一念之间”的文字内容。故事讲述了名叫张寻然的少年在迷迷糊糊中闯入自己红尘梦境,经历战乱、流浪、被俘等艰难处境,最终在城池轮廊处遇到老伯,老伯告知其城内安全,但城外有贼军追来。画面中还呈现了张寻然被乱流推倒、脚被踩住等情节,以及远处贼军旗帜逼近的场景。该图片与上下文紧密相关,是故事发展的具体描述。

续写成果:

图片展示的是小说《张寻然》中的一段文字。内容讲述了张寻然在逃亡途中遇到老伯倒地后,坚定前行,途中发现怀抱着破布片的孩童,孩童眼睛因恐惧发红。张寻然虽犹豫,但最终捡起破布片,继续前行。文字通过细腻描写,展现了张寻然在逃亡过程中的坚定信念和对孩童的同情,与上下文情节紧密相关,为后续故事发展埋下伏笔。

读后续写​

题目:

图片展示的是文档中读后续写题目部分的内容。文档要求根据所给段落开头续写两段,使之构成完整短文。图片呈现了续写题目中的一段材料,内容为“在课堂讨论中,我被邀请解释我名字的含义。我的许多同学对此感兴趣,课后还来找我。”图片与上下文紧密相关,是读后续写题目的材料部分,为续写内容提供了背景信息。

这张图片呈现的是一篇英文叙事短文,内容是作者在课堂上被邀请解释自己名字“秋雨”(Autumn Rain)的含义,作者起初因紧张难以清晰表达,后来聚焦于名字背后的文化内涵完成分享,还提及课后同学们因名字的文化意义与作者产生共鸣交流的经历,文末点明语言是沟通彼此的桥梁而非仅是交流工具的感悟,该文本对应文档中“读后续写”的题目内容,是续写所需的原文题材料。

虽然回答中出现了中文,但这并不影响MiniCPM抓住了本文的要点,主题纲要,思想精华。

数学推理和coding​

MiniCPM5-2B发挥出了非常完美的数学推理能力~~(可以不用在手机上问qwen了~~

本题是2026年考研数学一的一道大题,解答的非常正确!!

右侧是coding的测试,给出的代码也很正确,对于本地部署的模型来说能力很强。

这张图片展示的是2026年考研数学一相关的一道函数极值求解大题的完整解答过程,由MiniCPM5-2B模型完成。内容包含题目要求解答的二元函数,后续步骤依次为计算偏导数、求解驻点、计算二阶偏导数、用海森行列式判断驻点类型,最后求出函数极值,其中核心结果极值点为极小值点(-2,0),该点对应的极值为8/e²,(0,0)点为鞍点。结合上下文可知,这是MiniCPM5-2B模型在数学推理方面的能力展示,其解答过程正确,体现出该模型在数学推理和coding上的出色能力。

图片展示了一段Python代码,用于找出整数数组中乘积最大的非空连续子数组并返回其乘积。代码定义了maxProduct函数,先判断输入数组是否为空,为空则返回0。接着遍历数组,计算当前子数组乘积,更新最大乘积和最小乘积,最后返回全局最大乘积。代码还包含测试示例,分别对[2,3,-2,4]、[-2,0,-1]、[-2,3,-4]等数组调用函数,输出对应结果。该代码与上文提到的MiniCPM5-2B在数学推理和coding方面的表现相关,展示了其编程能力。

第六章、工具小能手​

在Tool use方面,我没有很深度的去体验,在这放上几个例子,展示从不可用到可用的过程。

工具调用失败​

这是一条与工具指令执行相关的对话内容,是工具调用相关的测试示例。对话中用户“小晨”要求先写出约100字的正文,无需调用任何工具,待正文完成后,再用write指令将相同正文写入指定文件CodeLife/novel.md,过程中不要使用read、ls、find命令,也不用检查目标文件;对话另一方“枫”执行相关操作后,展示出了两次完全相同的正文内容,这一内容对应工具使用测试中“遵循指令成功”的相关示例。

重复调用​

图片展示了BMB Agent与用户的一次交互。用户要求先写一段不少于20字的正文,完成后调用write指令将内容写入CodeLife/novel.md,第一步骤不要调用工具,且不要先read、ls、find。BMB Agent回复了符合要求的正文,内容为夜晚场景,窗外月光、池水、日记本等元素,还给出了novel.md文件的路径。该图片与上下文紧密相关,是展示BMB Agent遵循指令成功完成任务的示例。

遵循指令成功​

�该图片展示了BMB Agent的工具调用遵循指令成功的示例。用户向助手下达“写一段优美的诗句,展现给我,然后写入poem5.md”的指令后,助手生成了一首意境悠然的诗句,内容包含“星光不问赶路人”“月色只照我程”等语句,且助手成功完成文件写入操作,页面下方显示“已将诗句写入poem5.md文件”的确认信息,直观呈现出该助手在工具调用方面已能成功遵循用户指令完成相关操作。

写在最后​

Author:Aluka

Peppermeow29 - Overview

感谢各位能够读到这,这不是一篇非常好的技术Blog,更像是一篇魔改后体验分享,为了给MiniCPM做一些定制,踩了很多坑,没给大家放出来~~(haha)。碍于时间关系、电脑配置、技术水平(token缺乏)和学业压力,只能测试到这种程度,项目中还有很多Bug🐞要去修复完善。如果你对这个项目感兴趣,不妨fork一下,在个人电脑上(使用Claude Code、Codex、Zcode...)~~继续Coding下去,也可以提交Issues继续完善这个项目。

最后衷心感谢开源项目OpenHanako(原主奉上!!)

GitHub - liliMozi/openhanako: A personal AI agent with memory, personality, and autonomy.

Project info

Author
Aluka