跳到主要内容

cherry-studio — 本课题摘录

读了哪几篇: 03-tools-and-mcp(工具、外部能力与审批)、01-stream-pipeline(流式管线与插话)。 其余两篇本轮没读。

这一家回答了一个别家很少正面处理的问题:工具太多,塞不进一轮输入怎么办。

它对本课题回答了什么

决定一:工具太多就折叠成三把元工具

问题很具体:挂了 50 个外部工具,每个的参数说明都要塞进系统提示——光工具定义就吃掉小模型一半的窗口,还没开始聊。

做法:把不常用的折叠起来,系统提示里只放三把元工具,让模型自己"先搜再调"。

不折叠(工具少): 折叠(工具多):
工具 A 的完整说明 搜工具 ← 按分组搜有哪些可用
工具 B 的完整说明 看工具 ← 看某个工具的完整签名
工具 C 的完整说明 ──▶ 调工具 ← 按名字带参数调任意工具
…(50 个,撑爆) + 一段清单列出有哪些分组

(依据:Agent 库 · Cherry Studio · 工具、MCP 与审批 —— 工具太多时把不常用的折叠进 tool_search / tool_inspect / tool_invoke 三把元工具,系统提示里只留元工具加一段列举命名空间的 DEFERRED_TOOLS 段)

这是本课题第一个决定里一个真实的岔路: 工具清单不一定要全量进提示词,它可以变成一个"可查询的目录"。

代价很清楚:模型要多花一到两轮去搜和看,才能开始干活。

折叠这件事本身要先算划算 —— 这才是这一家最值钱的地方

除了"估算的工具说明超过窗口 10%"这个阈值,还有两道闸:

说的是
可折叠的工具至少要有 5 个池子不够大,"搜索再调"就不比直接内联划算
省下的 token 得超过三把元工具本身的开销(约 500)否则折叠是净亏

(依据:Agent 库 · Cherry Studio · 工具、MCP 与审批 —— shouldDefer 除了「估算 token 超上下文 10%」外还有两道闸——MIN_AUTO_DEFER_COUNT=5 与 META_TOOLS_OVERHEAD_TOKENS=500,没有这两道闸小工具集加小上下文模型会触发折叠却净亏 token)

这一条要抄的不是那两个数字,是那个思路: 任何"为了省资源而加的机制"本身也要花资源,要先算净收益再上。

压缩、折叠、摘要——三样都有这个陷阱。 前面 dexter 的"先用不花钱的办法"是同一族。

三档折叠策略:总是折叠 / 按判定 / 永不折叠。

"永不折叠"这一档留给谁,是关键判断:需要用户审批的工具永不被折叠。

理由不难想:审批门工具的完整说明必须在模型眼前,否则它不知道自己在申请什么。 一般化:凡是有副作用、有风险的工具,不能藏在目录后面。

搜工具只暴露被折叠的那些——已经内联的搜出来是冗余。

还有一个细节:搜的时候若返回了完整签名,会记进一本"已看过"的账,这样第一次调用不会被"你没先看过"的门挡掉。

第四把元工具(把整个工具表当接口的沙箱代码执行器)故意不注入——它是提权面,被刻意留在门外。

决定四:中途插话不打断,而是排队 + 让步 + 续接

用户在模型还在回答时又发了一句话,该怎么办?

它的答案不是打断重来: 不粗暴中止当前回答(那会丢半截输出、还要重发),而是让当前这一步干净地收尾,再接着回答新消息。

用户在流中再发一句
→ 落库为一条普通用户消息,压进一个先进先出队列
→ 运行中的回合看到队列里有东西 → 在下一个步边界「干净停」
→ 收尾时看到队列非空 → 不结束会话,而是接一轮续答
→ 每完成一轮取出一条

(依据:Agent 库 · Cherry Studio · 流式 AI 管线 —— 聊天 steering 是 enqueue+yield+chain 而非 abort-and-restart——插话入 FIFO 队列,运行中的 turn 在下一个 step 边界干净停,onExecutionDone 看到队列有货就 chain 一个续接轮)

两条不变量值得记:

不变量为什么
续接只在干净结束之后才发生被中止或报错时队列丢弃,已落库的那句话留在历史里让人重发
因让步而停时,状态记为"成功"而不是"暂停"它是被设计地、在步边界主动停的

第二条是本课题"什么时候停"这一问的一个精细点: "停"有很多种,记错了状态,上层就会做错决定。 kun 的"截断要单列"、agentscope 的"超轮数单列"、fara 的反例——这已经是第四次撞上"停止原因不能混"。

对比 kun 的插话处理: kun 是"插话变成一条普通用户消息,循环在安全点取货"; cherry-studio 是"插话让当前回合在步边界停,然后开新一轮"。 前者不停,后者停了再续。前者更省,后者更容易实现。

代理会话那一侧还有一种更强的: 如果连接支持,直接把插话注入到当前回合里(在下次工具调用前),不开新轮、不入队;若回合在注入前就结束了,再退回排队。

一条运行时的所有权规则

流的所有权在主进程,窗口只是订阅者——关窗不断流、不丢数据。

审批状态也一样:主进程是唯一的写者,界面只负责渲染卡片、收集决定、把决定送回去。

这跟本课题的"循环放哪一层"直接相关: 凡是"关掉界面还应该继续"的东西,就不能由界面持有。 kimi-code 说循环不拥有界面,cherry-studio 说界面也不拥有循环。

它没回答什么

  • 历史怎么压——这两篇没讲。
  • 怎么认出模型要调工具——它用现成的工具调用。
  • 并发——这两篇没讲一批工具怎么并行。

坑与代价

  • 折叠让模型多走一到两轮。 搜、看、再调,三次调用才做成一件事。
  • 元工具"按名字带参数调任意工具"绕过了参数校验的天然位置。 参数是模型自己拼的一段结构,没有厂商侧的格式约束兜着。

    判断(无锚): 折叠这条路只在工具数量真的很大时才值得走。我们的最小原型只有两三个工具,完全不需要。 如果错,会错在: 如果第一版就打算接一批外部工具服务器,那工具数会立刻膨胀到几十个,那时再改成折叠要动提示词结构。

  • 两套工具系统在这个仓库里并存(普通聊天一套、另一种会话另一套),文档专门提醒"别把两者混了"——这本身就是一个警告:工具注册表容易长出第二份。