ARTICLE · 1290796
ZCode 插件机制分析
给 ZCode 装了个插件,当前会话毫无反应,必须开个新会话才生效。为什么?本文是分析笔记,只讲机制本身:插件是什么、装上之后改变了什么、为什么会话中途不能生效。插件怎么安装、怎么开发,不涉及。
一、插件不是程序,是数据
先看插件长什么样。
VS Code 的插件是一段代码:有入口函数,有生命周期回调,装上以后由宿主调用执行。ZCode 的插件反过来,是一个目录加一份清单文件(manifest),里面只有声明,没有代码。
清单示意:
{”name”: ”pdf”,”skills”: [”skills/pdf”],”agents”: [”agents/visual-judge.md”]}
一个插件可以声明六类资源:skills(技能文档)、agents(子代理档案)、mcpServers(工具服务)、hooks(生命周期钩子)、commands(斜杠命令)、ui(界面面板)。仅此而已。
装上之后发生什么?每个会话启动时,宿主扫描所有插件,把启用插件的资源分拣进各自的通道:技能进技能清单,子代理进派发名册,工具服务进工具注册表。分拣完成的那一刻,插件这个实体就消失了。
运行时里没有"pdf 插件"这个对象。只有技能清单里多出来的一行,和子agent里多出来的一个评审员。
这不是偷懒,是第一条设计原则:宿主从头到尾只认机制,不认任何具体插件。官方的 pdf 插件带着 41 个文件、1.1MB 的排版知识,宿主没有为它写过一行代码。
二、扩展的不是程序,是模型
第二个问题:插件装上以后,到底改变了什么?
主程序一行没变。agent loop、工具执行器、消息循环,都和装插件之前完全一样。
变的是模型的输入。模型每轮请求"看到"的东西变了。具体分四条通道:
(1)skill:改变模型知道什么。模型读技能描述,判断当前任务用不用得上。
(2)MCP:改变模型能做什么。工具有结构化的 schema,模型照着说明书调用。
(3)agent:改变模型能指挥谁。模型读名册描述,把任务派给合适的角色。
(4)hooks:改变模型受什么约束。它由宿主代码执行,模型管不着,甚至不知道它存在。
前三条好理解:给模型添知识、添工具、添帮手。第四条是反的,hooks 只拿走能力,不给能力。比如一个 PreToolUse 钩子拦截 git push:模型没有获得任何新东西,反而少了一项操作,而且模型根本不知道这件事发生了,因为 hooks 不进模型上下文,由宿主代码在固定时机执行。
四种通道的触发方式,可靠性完全不同。hooks 由宿主代码调度,跑到生命周期节点必执行,最可靠。MCP 工具有结构化的 schema,模型照着说明书调用,比较可靠。skill 和 agent 只有一段文字描述,模型读懂了才会用,最不确定。
三、模型每轮看到什么:请求的三个部分
要理解注入的位置,得先知道模型每轮请求长什么样。一次 API 请求由三个部分组成:
API 请求├── tools[] 工具定义├── system 系统提示└── messages[] 对话历史
插件的资源各有各的落点。
(1)MCP 工具的定义进 tools[]。
(2)技能清单进 system,每条只占一行,名字加描述。
(3)子代理名册进 messages,以附件形式挂在对话里。
(4)hooks 完全不进模型输入。
落点的形态也有讲究。技能清单只常驻元数据,正文按需加载。还是拿 pdf 插件举例:1.1MB 的知识平时只占清单一行字,模型接到"做一份 PDF"的任务时,才读入对应技能的正文,正文内部再按需引用具体文件。
预算是硬约束:技能清单总共 2 万字符,单条描述超过 250 字符就截断,超预算的技能降级成只列名字。
一句话:模型上下文按 token 计费,所有设计都在省着用。
四、快照制:会话启动那一刻冻结
现在可以回答开头的问题了:为什么装了插件要开新会话?
因为 ZCode 实行快照制。会话启动那一刻,插件集、技能清单、MCP 工具清单、hooks 配置全部冻结。运行中的会话看不到任何变化,新装的插件要等下一个会话。
冻结得很彻底。MCP 协议本身自带"工具列表变更通知"(tools/list_changed),ZCode 干脆不监听,工具清单在连接建立时拉一次,整个会话用到底。
唯一的例外是子代理列表:运行中真的出现了新角色,宿主往对话流里追加一条附件公告,模型读到就知道多了个帮手。注意,是追加在尾部,不是改写前面的内容。
为什么冻结?三个动机。
(1)行为一致。同一会话内能力面恒定,结果可复现。
(2)安全可审计。会话能做什么,启动那刻定死,不中途扩张。
(3)省钱。会话内输入保持逐字节不变,提供商的缓存才能命中。
五、最底层的账:前缀缓存
第三个动机,说的是前缀缓存(prompt caching)。
LLM 的每一轮对话都要把完整输入重发一遍:工具定义、系统提示、全部历史。提供商的前缀缓存能把命中部分的费用降到约十分之一,但有一条铁律:从第 0 个 token 起逐字节一致才有效。前面变一个字,从那个位置起,后面全部作废。
而请求的渲染顺序是固定的:tools 在最前,system 居中,messages 在尾。
于是改动的代价分了三等。
(1)改 tools:整个请求的缓存全废,最贵。
(2)改 system:废掉后半段。
(3)往 messages 尾部追加:前面的前缀逐字节不变,缓存完整续用,最便宜。
回头看,第四节的全部纪律都是这笔账的推论。工具清单冻结,因为它站在缓存的第一页,变了代价最大。技能清单冻结,这段文字每轮逐字节相同,白吃缓存。唯一允许的动态是尾部追加,子代理公告挂在 messages,正是最便宜的合法位置。
"装了插件要开新会话"这个小怪癖,答案是它站在缓存经济的第一页。
六、总结
ZCode 的插件是数据,不是程序。一份清单声明六类资源,宿主在会话启动时分拣进各条通道,插件实体随即消失。它改变的不是主程序,而是模型的输入:模型知道什么,能做什么,能指挥谁,受什么约束。
注入是省着用的。工具定义进 tools,技能清单进 system,子代理名册进 messages,正文一律按需加载,清单还有 2 万字符的预算。
会话启动那一刻全部冻结,新插件要开新会话。这条纪律的最后推手是前缀缓存:输入逐字节一致,费用降到约十分之一;变得越靠前越贵,唯一合法的动态是尾部追加。
一句话:传统软件的插件扩展的是程序,AI Agent 的插件扩展的是模型。模型上下文按 token 计费,这个插件系统的大半设计,都是在给这份上下文记账。
参考链接
ZCode 源码仓库:https://github.com/zai-org/ZCode MCP 协议:https://modelcontextprotocol.io Anthropic prompt caching:https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching















































































