Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Agent 可移植性

An agent 不是文件。它是一条配置条目,引用了 providers、profiles、bundles 和 MCP 服务器,同时还包括磁盘上的一个工作区目录。要将一个 agent 迁移到另一台安装环境,需要同时迁移这两部分,并判断哪些内容根本不应迁移。

智能体包是其可移植形式:

<bundle>/
├── zeroclaw-agent.toml    — manifest: format version, root alias, provenance,
│                             required secrets, carried skill bundles,
│                             dropped refs, risk flags
├── config.toml            — the config closure this agent needs
├── workspace/             — the agent's workspace tree
└── skills/<alias>/        — content of each skill bundle the agent references

config.toml 是一个片段,而不是完整的配置文件。它只包含智能体所引用的条目,并且在信任之前应当先仔细阅读。

导出

sh

zeroclaw agents export <alias> --out ./my-agent
标志效果
--out <dir>目标 bundle 目录,若不存在则自动创建。
--force替换已有内容的目标位置。

Export 对安装目录只读,并报告三项内容:接收方运营者将接受的能力、已被清除的凭据,以及无法迁移的配置。

发布,而非合并。 导出内容在目标目录旁的临时目录中构建,完成后再替换到目标位置,因此:

  • --force 会_替换_目标位置。早前导出的、新清单未描述的文件会被删除,而不是残留在那里,看起来像是 bundle 的一部分。
  • 导出过程中报告的故障(例如工作区文件无法读取或磁盘已满)会使目标位置保持原样。暂存目录树会被丢弃,已存在的 bundle 会被恢复,因此不会留下一个被误认为完整的部分写入 bundle。
  • 一个与导出过程所读取的目录树存在重叠的目标路径,会在创建任何内容之前被拒绝,双向均是如此,且对工作区及每个技能包同样适用:如果 --out _位于_某个源目录内部,复制过程就会消耗自己的输出;如果 --out _包含_某个源目录,则会替换正在导出的目录树。
  • 保留的身份路径组件和技能内容目录名称使用所有受支持目标都能生成的命名规则。因此禁止使用分隔符和控制字符、Windows 保留名称(例如带或不带扩展名的 con、nul 或 lpt1),以及以空格或点号结尾的名称。不安全的身份路径会被直接移除。不安全的技能别名会保留在配置闭包中,以便手动导入器重命名或重新绑定它,但其内容不会放置到 skills/<alias>/ 下,清单会报告这一省略。在格式 1 中,根代理别名保持不变,因此在目标平台上复制默认工作区之前,请先重命名与该目标不兼容的别名。
  • 闭包在写出之前即已证明。渲染后的 config.toml 会被重新解析,并在仅包含其自身内容的配置上,运行导入端安装所执行的同一套校验。若源安装能够满足、而该包却无法满足的引用(例如从未配置的 provider 或 bundle 条目),则会拒绝导出并指出对应字段,而不是生成一个在到达目标端时才失败的包。
  • --force 准许的是一个目录,而不仅仅是一个路径字符串。导出器会保留父目录句柄和已准许的目标目录身份,并在发布前重新检查二者。若这些检查发现目标目录发生了变化,则会拒绝该操作。这些检查可以降低被替换的风险,但它们并不是与最终删除或重命名操作之间的原子比较并交换(compare-and-swap)。因此,在导出发布期间,请勿让其他进程修改目标目录。

如果导出过程中途被终止

换入操作需要两次重命名,而不是一次:先把现有的 bundle 移开,再把暂存的 bundle 移入。这足以回滚导出报告的失败,但并不具备崩溃原子性。如果进程或主机在这两次重命名之间崩溃,目标位置将缺失,而之前的 bundle 完好地保存在旁边,其名称与暂存目录配对:

<parent>/
├── .zeroclaw-export-<token>       staged bundle, incomplete (safe to delete)
└── .zeroclaw-export-old-<token>   the previous bundle, unchanged

通过将 .zeroclaw-export-old-<token> 重命名回目标路径来恢复。两个目录都是隐藏目录,并共享相同的 <token>,因此崩溃的导出会留下一对匹配的目录。成功的导出则两者都不会留下。

闭包携带的内容

从 [agents.<alias>] 开始,导出时会追踪所有可以在其他位置重新构建的引用:

  • the agent 的 risk_profile 和 runtime_profile 条目;
  • 这些是 skill_bundles、knowledge_bundles 和 mcp_bundles 条目;
  • [mcp.servers] 条目中,这些 bundle 实际授予的部分,按运行时使用的同一路径解析,因此被 bundle 的 exclude 移除的服务器,在该 bundle 中同样不会出现;
  • 每个由智能体指定的 provider 条目(model_provider、classifier_provider、summary_provider、tts_provider、transcription_provider)均为无密钥配置。
  • 所携带的运行时配置文件在其自身的 context_compression.summary_provider 中指定的提供方,智能体从不提及,但目标端会按配置文件逐一校验;
  • 每个在运行时深度和循环规则下可达的模型提供方回退,同样以无密钥方式携带;
  • 内容,即每个被引用的 skill 包在 skills/<alias>/ 下的全部文件。Skills 位于安装范围内的 <install>/shared/skills/ 目录树中,而不在 agent 的工作区里,因此仅导入配置会导致导入的 agent 缺少其所需的 skills。每个包自身的 include 和 exclude 会过滤复制内容,因此被该包排除的 skill 不会随之迁移。

清单中的 skill_bundles 列表记录的是 bundle 包含 的内容,而不是导出操作打算携带的内容。若某个 bundle 已在 agent 上配置,但导出主机上没有对应目录,则它会被列入 dropped,原因为 source_missing。此时不会设置 carried_skills 标志,因此产物永远不会声称存在一个实际并不存在的 skills/<alias>/ 目录树。

测试闭包的标准是它能独立成立:在没有其他条目的安装环境中解析时,它应通过 Config::validate()。

与 schema 默认值相等的值会被剪除,因此该片段只显示用户实际做出的选择,而不是整个 schema。

留下的内容(刻意保留)

每一项省略都会记录在清单的 dropped 列表中并附上原因,因此不会有任何内容悄无声息地消失。

Dropped原因
channels为仅存在于源安装上的账户和凭据命名。
delegates, workspace.access, workspace.read_memory_from列出在目标环境中将不存在的同级代理。
workspace.path源主机绝对路径。
identity.aieos_path仅当路径在格式自身的路径语法下能解析为导出工作区内的文件时才保留:使用 /-分隔,不含 ..,不含反斜杠,不含盘符或 UNC 前缀,也不含控制字符,因为导入主机可能以不同方式解析这些内容。保留的路径会被改写为该语法(规范化,并在每个导出平台上统一使用 /-分隔),因此 config.toml 中的字符串即为指向所携带文件的名称。指向 memory/ 的路径,以及其对应文件未被此次复制携带的路径,同样会被丢弃。
delegate_same_risk_profile将其设置为 false:否则,同一配置文件的自动委派会触达此代理从未与之配对过的目标代理。
risk_profiles.<alias>.approval_route命名一个源安装渠道及其独立审批人。该项会被移除并予以报告,以防同名目标渠道悄然继承审批权限。移除该项也会一并移除源配置文件的独立审批人策略,因此若需要此职责分离,请在启用该代理之前,在目标渠道上重新绑定一个独立审批人策略。
skill_bundles.<alias>.directory当路径为绝对路径时将被丢弃,因为它指向源主机,而目标端会为该别名解析其默认位置。位于 <install>/shared/ 之外的目录(该目录由 skill-bundle 契约管理)连同其内容一起被丢弃:bundle 的配置会随之迁移,但其中的 skills 不属于安装可导出的内容。
a2a对外暴露的界面;必须经过有意识的重新发布。
cron_jobsbundle 格式 1 不携带此内容。
workspace/memory/记忆存储。见下文。
workspace/MEMORY_SNAPSHOT.md存储从中重新加载的核心记忆导出:另一种形式的记忆。

记忆不会随之迁移

Bundle 格式 1 不包含内存数据,也没有可以选择启用的标志。存储是一个处于 WAL 模式的实时 SQLite 数据库:智能体运行期间,其已提交的状态分散在数据库文件及其 -wal 旁路文件中。如果逐个复制这些文件,可能得到损坏或陈旧的数据库。打包文件不应成为发现这一问题的地方。要携带内存数据,需要一个真正的快照边界,即在存储静默状态下,通过 SQLite 自身的备份 API 获取快照。这可以留待之后的格式版本再添加。

记忆在磁盘上的两种形式都会留在原处:位于 workspace/memory/ 下的存储,以及工作区根目录下的 MEMORY_SNAPSHOT.md(智能体据此重新填充存储)。导入的智能体以空记忆开始。

工作区内的符号链接会被跳过而不会被跟随:链接的目标可能位于工作区之外,而且在接收端主机上它的解析结果也可能不同。

源根目录本身会首先接受检查。若工作区或技能包目录是符号链接,则直接拒绝,而不是跟随它。因为跟随符号链接会在下文任何逐项检查运行之前,使整个副本超出该包声明所包含的目录树范围。请将配置指向真实目录,然后重新导出。

它的祖先目录同样需要检查,而且是通过构造方式检查,而非事后比对。当来源使用默认位置时,会从安装根目录开始逐个检查每个组成部分(先是 shared,然后是 skills,最后是 bundle 目录;agents,然后是别名,最后是 workspace),每一步都通过前一个组成部分的句柄打开,并在每一步拒绝符号链接。被重定向的 shared/skills 或 agents 无论指向哪里都会被拒绝,即使指向安装目录内的其他位置也不例外,因为 skill 内容只会通过 shared/ 下的真实目录读取,默认 workspace 也只会通过 agents/ 下的真实目录读取。安装根目录_之上_的符号链接属于操作者自身的路径,会被跟随,其信任级别与配置文件所在位置相同。操作者在别处配置的 workspace.path 则豁免:此时以配置的位置作为边界,只有其最后一个组成部分会因为是链接而被拒绝。

工作区在导出运行期间保持活动状态,因为拥有它的代理可能通过普通的工具调用对其写入。因此,复制过程从不按名称重新打开路径。工作区根目录只打开一次,其下的每个条目都通过其所在目录的句柄进行分类 and 读取,且两个步骤都拒绝穿越符号链接。正是这一点使跳过成为一种保证而非检查:如果某个条目在此期间被替换为链接,导出将失败,而不会被重定向。

拒绝跟随某个名称,并不等于证明这些字节属于当前目录树,因此还要对复制即将读取的句柄再执行两项检查。带有多个名称的文件会被跳过,因为硬链接是同一对象的第二个名称,而该对象可能位于主机上的任何位置。此外,打开的对象必须就是先前被分类的那个对象,比较依据是文件系统标识而非文件形态:如果某个条目被取消链接并替换为主机上的另一个文件,那么它仍会通过所有测试,因为它同样是位于同一目录内、只有一个链接的普通文件。

拒绝该链接很重要,即使它指向工作区_内部_也是如此。该打包内容本身有内容边界,而指向工作区根目录之内的链接仍可能跨越这些边界:如果一个已准入的文件被替换为指向 memory/brain.db 的链接,就会以已准入的文件名携带记忆存储;如果一个已准入的技能被替换为指向被该打包排除的某个技能的链接,就会携带被排除的内容。这两种情况都不会逃离工作区,但都是不允许的。

凭证

每个 schema 标记为机密的字段都会被清空为空字符串,其配置路径列在清单的 required_secrets 中。这些路径是 zeroclaw config set 所接受的路径,因此填写捆绑包时只需直接复制粘贴:

zeroclaw config set providers.models.anthropic.main.api-key
zeroclaw config set mcp.servers.github.env.GITHUB_TOKEN

脱敏是经过验证的,而非想当然:如果加密配置的密文残留在导出闭包中,导出将中止,而不会写出打包文件。

不做脱敏处理的情况

Scrubbing 会清空 schema 中标记为 secret 的字段。它不是凭据检测,也不会检查它所携带的值。闭包中的其他所有字符串都会按配置原样传递:

按原样保留凭据在以下情况下会保存到该位置:
mcp.servers.<name>.url该端点携带令牌或已签名的查询字符串。
mcp.servers.<name>.command、.args通过命令行传递密钥,而不是通过 env。
providers.*.api_url自托管端点嵌入了访问令牌。

清单在 risk_flags 中原样重复了 stdio 服务器的命令行,因此 args 中的凭据既会出现在清单里,也会出现在配置片段中。

部分携带的字段依赖于目标端本地状态,即使它们不是机密值。requires_openai_auth = true 需要目标端自身的 OpenAI 认证存储。模型提供方和 MCP 的 tls_ca_cert_path 值指向导出主机上的文件,必须替换为目标端本地的 CA 路径。allowed_roots 会被携带,并标记为 extra_filesystem_roots;在启用导入的配置文件之前,请检查或移除其中的每个路径。

分享 bundle 前请阅读这些值。以下两项需要你亲自检查,不能仅依赖 schema:这里描述的字符串,以及 Bundle 内容未被扫描 中描述的随附文件。

Bundle 内容未被扫描

脱敏(Scrubbing)是基于 schema 对配置闭包(config closure)执行的一次驱动式处理。它不会触及 bundle 所携带的文件,其他任何环节也同样不会。过滤器决定 which 文件会被传输,但没有任何环节检查这些被传输文件的内部内容。

每个通过工作区和技能过滤器的常规文件都会被逐字节复制,且不会被扫描机密信息。完全不会被导出的内容在本页其他位置列出:记忆存储及其快照、符号链接、特殊文件和硬链接、技能包的 exclude 排除的技能,以及技能包根目录下的零散状态文件。

Skill 包根目录下的散落文件和特殊条目不属于 skill 内容树。它们不会被复制,也不计入递归的跳过条目统计中,因此如果这一区别对你的交接工作很重要,请单独检查源 bundle 根目录。

因此,工作区中的 .env 文件、粘贴到笔记中的 API 令牌、远程 URL 含有凭据的 .git/config,或智能体正在处理的私密文档,都会原样传输。导出结果会显示携带了多少个文件,但无法说明这些文件中的内容。

在共享数据包之前,请像在公开仓库之前审阅代码那样,自己阅读随附的文件。config.toml 和清单文件是导出内容能够担保的部分。

风险标记

清单的 risk_flags 列表列出了软件包中每项会扩大接收端安装信任边界的能力,并与授予该能力的配置路径相绑定。

|标志||Raised by

Wait, I should translate “Raised by” to zh-CN. The correct output is:

提交人| |–|–––––––––––––––––––––––––––––––––––––––––| |full_autonomy|level = "full",不设置逐项操作的审批关卡。| |filesystem_escape|_实际生效_的策略为无限制状态:workspace_only = false、level = "full"(此时会无视 workspace_only 的设置,强制关闭限制),或 workspace.unrestricted_filesystem = true。| |sandbox_disabled|sandbox_enabled = false.| |approval_bypass|block_high_risk_commands 或 require_approval_for_medium_risk 被关闭。| |env_passthrough|非空的 shell_env_passthrough:宿主机环境变量会传递到 shell 子进程。| |extra_filesystem_roots|非空的 allowed_roots。| |delegation_enabled|delegation_policy.mode = "allow"。| |process_spawn|一个 stdio MCP 服务器,用于在目标主机上启动本地进程。| |untrusted_startup_context|MCP 服务器的 pinned_resources:由服务器控制的文本,在启动时读取到系统提示词中。| |carried_skills|该包承载技能包的内容:智能体读取的指令,以及它可能运行的文件。|

来自不受信任来源的捆绑包属于不受信任的输入。在导入之前,请像运行脚本前阅读脚本那样,先阅读 config.toml 和清单中的风险标志。

导入中

尚未实现。目前需要手动应用捆绑包:将 config.toml 合并到你的安装配置中,并为与现有别名冲突的别名加上命名空间前缀;将 workspace/ 复制到智能体的工作区目录;将每个 skills/<alias>/ 复制到你的安装为该捆绑包别名解析的目录中(除非捆绑包设置了 directory,否则为 <install>/shared/skills/<alias>);并提供 required_secrets 中列出的凭据。

未来的 zeroclaw agents import 将强制执行以下两条规则,手动合并时也应遵循:

  • 导入操作绝不会覆盖已有条目。 引用 risk_profiles.default 的数据包不得修改_你的_ default 配置文件。请为传入的别名添加命名空间,或明确将代理指向本地别名。
  • 合并后的配置在保存前必须通过 Config::validate() 校验。 悬空引用意味着导入失败,而不是留到下次启动时才出问题。

在实体化路径之前,将目标无法表示的任何根代理或技能包别名重命名,然后更新片段中的相应引用。当在 Unix 上生成的包应用于 Windows 时,这一点尤为重要。

版本格式

Bundle 带有 format_version = 1。导出的闭包是自包含的:在没有其他条目的全新安装上,它也能正常加载和校验。