Multica Agent 创建全解析:从 CLI 到 `/api/agents` 的字段契约、校验链路与源码级事实
Multica 的 Agent 是一个「以 workspace 为作用域、以数据库行(agent 表)为唯一事实来源」的配置实体。本文以仓库内置技能文档 SKILL.md 为骨架,结合其配套的证据层 creating-agents-source-map.md,完整梳理 multica agent create / copy / update / skills / env 等入口的字段契约、服务端校验与拒绝条件、每条字段的持久化方式,以及 daemon 在领取任务(claim)时到底读取了哪些字段。读完你既能用 CLI 安全地创建、复制与调试 Agent,也能理解为什么 description 不等于提示词、为什么 agent update 不能改环境变量这类容易被误用的规则。
本文是「文档契约 + 源码证据」双层的说明:文档中的每一条陈述都以 file:line 指向源码,本文只引用当前仓库真实存在的文件路径与行号,可随时打开仓库交叉验证。
Agent 的核心模型:一次创建、claim 时重读
从源码结构看,一个 Agent 是 workspace 作用域内的一个数据库行(表 agent),创建行为单一入口:HTTP POST /api/agents(CLI 上对应 multica agent create)。
理解这条链路的重点是「两段式」:
- 创建:CLI 组装 JSON body → POST
/api/agents→ 服务端校验 → 落库。 - 领取任务(claim):daemon 在真正执行前会重新读取 Agent 行并组装运行时载荷(runtime payload),而不是直接使用创建时的返回结果。
因此「Agent 实际以什么配置运行」取决于持久化后的字段,而不是 create 命令的输出。这一行为在 daemon.go 中非常直白:GetAgent(task.AgentID) 先按任务取回最新 Agent 行,然后依次组装技能与载荷。任何在创建后被 agent update 修改的字段,都会在下一次任务领取时生效,无需重新创建 Agent。
Agent 的另一种状态:unbound(未绑定 runtime)
Agent 与 runtime 通过可空的 runtime_id 关联。当一个 Agent 的 runtime 被删除时,它不会被连带删除,而是进入 unbound 状态:
runtime_id变为NULL(对外以""呈现,同时runtime_bound: false,参见 agent.go 的注释与响应结构);- unbound 保留它拥有的一切(配置、历史、技能绑定),并且仍然可编辑;
- 但没有任何触发路径会运行它——所有触发都会以
agent_runtime_required拒绝; - 重新绑定用
multica agent update <id> --runtime-id <runtime-id>。
注意 unbound 与 archived(归档)是两个正交的状态,不要混为一谈。
快速上手:只读检查命令
下面三条命令都只读取状态、无副作用,适合先探测现有 Agent 的定义:
multica agent get <agent-id> --output json # 完整的持久化 Agent 记录
multica agent skills list <agent-id> --output json # 当前的技能绑定
multica agent env get <agent-id> --output json # 明文 env(仅 agent 本人或 workspace owner/admin 可读;agent 参与者一律拒绝)
agent get 返回的是持久化字段,包括 runtime_id、model、thinking_level、service_tier、custom_args、has_custom_env、custom_env_key_count 与 skills。它永远不会返回明文 custom_env——这一点在 agent.go 的 AgentResponse 定义里有注释依据(引用 MUL-2600)。
最容易被混淆的两个字段:description 与 instructions
文档特别强调这两个都是文本字段但角色天差地别:
| 字段 | 定位 | 运行时行为 |
|---|---|---|
description |
目录摘要(catalog summary),给人看的元数据 | 只用于列表/展示;daemon 不会把它注入运行时提示词。上限 255 个 Unicode code point(见 agent.go 常量 maxAgentDescriptionLength = 255,用 utf8.RuneCountInString 计数,与 Postgres char_length 一致) |
instructions |
运行时行为契约 | daemon 在 claim 时读取并随任务载荷发给 provider,作为 Agent 的持久化指令。人格(persona)、职责边界、输出规范、升级规则都应写在这里 |
一句话:把「这个 Agent 是谁、该怎么做」写进 instructions,把「它叫什么、一句话简介」写进 description。
CLI / API 入口:multica agent create
name 与 runtime-id 两者都必填,最小创建调用如下:
multica agent create --name <name> --runtime-id <runtime-id> \
--description "<short catalog summary>" \
--instructions "<runtime behavior contract>" \
--output json
runAgentCreate 的组装规则:只发送「已变更」的键
CLI 侧的关键行为在 cmd_agent.go 的 runAgentCreate:它构建一个 JSON body 后 POST 到 /api/agents,但只有某个 flag 被显式提供时才往 body 里加对应键:
description/instructions:仅当值非空时加入;runtime-config/custom-args/model/thinking-level/service-tier/visibility/max-concurrent-tasks等:仅当对应 flag 处于Changed状态时加入。
这样做的效果是:被省略的 flag 不会以空字符串的形式发出去,而是让服务端默认值生效。这与「显式传空字符串 = 清空/显式使用空值」的语义区分开。例如 agent update 上显式传 --model "" 表示清除并回落 runtime 默认,而省略 --model 表示保持不变。
--max-concurrent-tasks 在发起任何 HTTP 请求之前就做 1–50 的本地校验,校验函数见 cmd_agent_validation.go,它委托给 internal/agentconfig 的共享校验器(ValidateMaxConcurrentTasks)。
HTTP body(CreateAgentRequest)比 CLI 暴露的更多
POST /api/agents 接受这些字段:name、description、instructions、conversation_starters、avatar_url、runtime_id、runtime_config、custom_env、custom_args、model、thinking_level、service_tier、visibility、max_concurrent_tasks、mcp_config、skill_ids。请求结构定义在 agent.go。
一个值得注意的差异:conversation_starters 没有对应的 agent create / agent update CLI flag。CLI 只能在 agent copy 时把它原样带过去。要为一个全新 Agent 设置 conversation starters,要么直接调用 /api/agents,要么让人在 Web UI 上配置(见下文「Conversation starters」小节的定位指引)。
复制 Agent:multica agent copy
multica agent copy <source-agent-id> 会把一个既有 Agent 的可移植配置复制成一个全新 Agent,源 Agent 不被触碰——它是 Web 端 "Duplicate" 动作的 CLI/headless 等价物,flag 注册见 cmd_agent_copy.go。
值得强调的实现细节:copy 不经过任何专属服务端 API。runAgentCopy 先用 GET /api/agents/<id> 读回源 Agent,然后 POST 一个 CreateAgentRequest——并且把源 Agent 的 skill id 放进 skill_ids,使技能绑定在同一个 create 事务里完成(这与 agent create 不同,后者不绑定任何技能)。因此整个复制是一个原子变更。
multica agent copy <source-agent-id> --name "My Agent (copy)" # 保持同一 runtime
multica agent copy <source-agent-id> --runtime-id <target> --model <model> # 跨 runtime fork
复制规则可以总结成三组:
默认复制、无专属覆盖 flag 的:
conversation_starters。
默认复制、但可用对应 flag 覆盖的:
name(默认追加" (copy)"后缀)、description、instructions、头像、custom_args、max_concurrent_tasks、调用权限(permission_mode+ 允许名单)、已分配的 workspace 技能。
永不复制的(秘密 / 机器本地数据,读接口本来就会脱敏或掩码):
custom_env、mcp_config、runtime_config。复制后用与agent create相同的秘密安全 flag(--custom-env*、--mcp-config*、--runtime-config)重新提供新值,或用agent env set。
三条边界规则:
- 并发上限的兼容:只有源值落在 1–50 范围内时
max_concurrent_tasks才被复制;历史越界值会被省略,让新 Agent 落到服务端默认6。显式传越界的--max-concurrent-tasks会在任何 API 请求前被拒绝。 - runtime 相关字段:
model、thinking_level、service_tier只在目标 runtime 不变时才复制。--runtime-id选择不同 runtime 时会丢弃它们并强制要求--model(传--model ""表示接受目标 runtime 默认值),这与 Web Duplicate 在切换 runtime 时清空 model 的行为一致。 - 技能:
--no-skills跳过复制源 Agent 的技能绑定。
字段契约总表
SKILL.md 给出了一个逐字段的事实表,逐条完整继承如下:
| 字段 | 持久化为 | 是否校验 | 谁消费它 |
|---|---|---|---|
name |
agent.name |
必填,为空返回 400 | 列表、运行时载荷 |
description |
agent.description |
超过 255 code points 返回 400 | 仅目录/列表——不是运行时提示词 |
instructions |
agent.instructions |
无 | daemon 在 claim 时 → provider |
conversation_starters |
agent.conversation_starters(JSON 数组) |
至多 3 项;每项 label ≤ 80 code points、prompt ≤ 4000 code points | 仅供人类使用的 Chat 空状态;选中只预填输入框,不会启动一次运行 |
avatar_url |
agent.avatar_url |
无;显式非空值保留,省略/为空则生成随机 emoji:<glyph> 头像 |
仅目录/列表 UI——不是运行时提示词 |
runtime_id |
agent.runtime_id(可空) |
创建时必填(400)+ 必须解析到本 workspace 内的 runtime | 选择 runtime/provider;NULL 即 unbound |
model |
agent.model(可空) |
无(除 runtime 支持性) | daemon 读取;为空 = runtime 默认 |
thinking_level |
agent.thinking_level(可空) |
provider 层枚举 / 安全 token 门禁;未知字面量 → 400(详见下文) | daemon;为空 = runtime 默认 |
service_tier |
agent.service_tier(可空) |
仅 Codex 的安全 token;其他 provider 拒绝;精确 model/tier 配对由 daemon 校验 | daemon → Codex app-server;为空 = 本地 Codex 配置 |
custom_args |
agent.custom_args(JSON 数组) |
JSON 形状在 CLI 侧校验;服务端原样存储 | daemon(额外 CLI 开关);默认 [] |
runtime_config |
agent.runtime_config(JSON) |
JSON 形状在 CLI 侧校验;服务端原样存储 | runtime 专属配置;默认 {} |
custom_env |
agent.custom_env(JSON 对象) |
— | daemon(进程环境变量);见「Env 与秘密」 |
mcp_config |
agent.mcp_config(原始 JSON) |
CLI 校验必须是 JSON 对象或 null;服务端原样存储。创建时字面量 null 被丢弃(no-op);更新时 null 清空该列 |
daemon → provider(provider 专属 MCP 处理);读取时脱敏 |
visibility |
agent.visibility |
— | 访问控制;默认 private;决定谁能读取/路由私有 Agent——不是运行时提示词 |
max_concurrent_tasks |
agent.max_concurrent_tasks |
整数 1–50;越界返回 400 | 调度器任务上限;默认 6 |
省略或显式 null 时的默认值:max_concurrent_tasks → 6。其他「省略时」默认值:runtime_config → {}、custom_env → {}、custom_args → []、avatar_url → 随机 emoji:<glyph>、visibility → private(全部在 insert 前由服务端物化,见 agent.go 创建路径中的默认处理代码)。
两个值得单独说清的细节:
custom_args/runtime_config在 Go 侧类型是[]string/any,按原样 marshal——JSON 形状的拒绝发生在 CLI,而不是 create handler。- 1–50 的并发范围在 create 与 update 上一致。create 时省略字段默认 6,而显式传 0 会被拒绝;update 时省略保持现值。CLI 在发送 create/update 请求前做同样的范围检查(
validateAgentMaxConcurrentTasksFlag)。
创建路径的服务端校验顺序
在 agent.go 的 create handler 中,校验顺序依次是:
name为空 → 400"name is required";description超过 255 code points → 400;runtime_id为空 → 400"runtime_id is required";- conversation starters 归一化(trim + 校验至多 3 对完整 label/prompt);
visibility为空 → 默认"private";max_concurrent_tasks走共享的默认化与范围校验;runtime_id必须能解析到本 workspace(GetAgentRuntimeForWorkspace),否则 400"invalid runtime_id";- runtime 私有性检查(只有 runtime owner 能在其上创建 Agent,否则 403);
thinking_level/service_tier的 provider 层门禁。
thinking_level:provider 层词表 + daemon 逐模型校验的两段式
thinking_level 是本契约中最复杂的字段,核心原则是分两层校验:
第一层(服务端 create/update handler)只做 provider 级校验(IsKnownThinkingValue):
- 固定词表 provider(如 Pi)只接受自己的枚举字面量,未识别的直接 400。Pi 的枚举是
off|minimal|low|medium|high|xhigh|max; - 动态词表 provider(如 Codex/OpenCode)接受语法上安全的 token(safe-token),这样新目录值不必等 Multica 发版就能落库。
第二层(daemon 执行前)做精确的 model/level 配对检查:某个值对所选 model 不支持时,daemon 检查本地 model 目录,记一条警告并省略该不兼容覆盖,而不是让任务失败(对应 daemon.go 中「invalid-combination 处理」逻辑)。
由此可以解释几个文档强调的边界情况:
- Pi 的 provider 词表是固定的七档,但逐 model 的支持子集来自本地 Pi RPC model 目录(
discoverPiModelsRPC),所以「Pi 上某档对当前 model 不可用」不会在创建时被拒,而是由 daemon 在执行时省略。 - ACP runtime 里,目前只有会通过
session/new通告 effort 选择器的 runtime(当前是reasonix和hermes)走安全 token 路径,由 daemon 对照发现出的目录校验。该目录只覆盖 discovery 会话所在的那个 model,所以其他 model 在逐 model 探测机制出现前看不到选择器。 hermes一个 provider 名下其实有两个二进制:jcode 通告并施加 effort,Hermes Agent 不通告、因此没有选择器——答案来自 runtime 自己发现出的目录,而不是 provider 名。而该目录只有在有人请求过一次 model 列表后才会写入,因此一个从未被 discovery 的hermesruntime 会收到一个独立的 400("has not reported a model catalog yet"),而不是被假定为有能力。reasonix的 provider 名能唯一决定二进制,处于该状态时反而被放行。- 完全没有推理控制的 runtime(例如
copilot——它虽然在 ACP 上 discovery,却通过自己的 CLI 执行、没有可供携带 effort 的实时 ACP 会话)会拒绝每一个非空值——这个 400 是能力答案,不是 token 写错了。
CLI 端设置用 agent create / agent update 的 --thinking-level,与 --model 行为一致:它是顶层 thinking_level 字段的薄透传;update 时 --thinking-level "" 清空并回到 runtime 默认。CLI 刻意不枚举合法档位——它们是 runtime/model 相关的(Claude 当前用 low|medium|high|xhigh|max;Pi 用 off|minimal|low|medium|high|xhigh|max;Codex 的值要从 runtime 的 model 目录发现)。
service_tier:Codex 的一等速度控制
service_tier 是匹配 Codex 的一等速度控制字段,用法与 thinking_level 完全镜像:
- create/update 用
--service-tier <catalog-id>设置; - update 用
--service-tier ""清除并回到本地 Codex 配置; - 可用性与展示文案都由 runtime 的 model 目录决定(当前是
priority,展示为 Fast); - 服务端接受未来的安全 Codex 目录 id,daemon 在执行前验证精确的 model/tier 配对,过期不兼容的覆盖会被省略;
- 没有显式 model 的 Agent 会 fail closed,因为有效的
config.tomlmodel 未知,无法验证 tier。
Conversation starters(对话开场建议)
产品内叫 Conversation starters(中文:对话开场建议)——与真人沟通时请用这个名字,线上的 conversation_starters 只是实现细节。配置位置在 Agent 的 Instructions 标签页,深链接为:
/<workspace>/agents/<id>?view=instructions&focus=conversation_starters
- 最多 3 对 label + prompt,在用户打开与该 Agent 的新 Chat 时显示在输入框上方;
- 选中一条只填充输入框,绝不启动运行——它们是建议,不是动作;
- create 时省略该字段默认
[];update 时省略保持存量值,显式[]清空; - 一个完全没配置的 Agent 的空状态仍会展示三个内置的通用默认建议,所以「Chat 里有建议」不代表该 Agent 有自己的配置。
校验常数在 agent.go:最多 3 项、label ≤ 80、prompt ≤ 4000。
model vs custom_args:谁负责选模型
model 是一等持久化列,daemon 直接读取。custom_args 通常是传给 provider CLI 的原始参数。CLI 帮助文本明确提示部分 provider(codex app-server、openclaw)会拒绝出现在 custom_args 里的 --model——但那是文档化的 CLI 建议,不是服务端强制的不变量,create handler 不会去检查 custom_args 里有没有 model 字样。
另一方面,provider 后端确实会在启动前消费某些协议选择器:
- Pi 会过滤
custom_args中的--thinking,因为一等字段thinking_level独占该 flag 且必须是它的唯一来源; - ZeroClaw 会消费
--agent <alias>/--agent-alias <alias>(含=value形式),把值作为 ACPsession/new.agentAlias参数发送——zeroclaw acp本身没有这个 CLI flag。ZeroClaw 有多个 agent 且没有[acp].default_agent时应设置其中一个;单 agent 配置则应省略,让 ZeroClaw 自动选中那个 agent。
绝对不要把凭据或其他秘密放进 custom_args。daemon 的启动日志会脱敏参数值,但后端未消费的值仍会留在 provider 进程的 argv 里,可能被本机其他进程通过 ps 或 /proc 看到。provider 凭据应放 custom_env,并尽可能用 stdin 或 0600 文件输入。
Env 与秘密:三条写入通道与读取侧事实
custom_env 属于秘密材料。CLI 提供三条输入通道,其中两条能把秘密挡在 shell history 与进程列表之外:
multica agent create --name <name> --runtime-id <runtime-id> --custom-env-stdin --output json
multica agent create --name <name> --runtime-id <runtime-id> --custom-env-file <0600-json> --output json
--custom-env-stdin:从 stdin 读 JSON 对象;--custom-env-file:从文件读(建议 mode 0600);- 第三条
--custom-env <json>把值放在命令行上,shell history 和ps都能看到——真秘密不要用它。
读取侧事实(这些是容易踩错的假设)
- Agent 资源永远不暴露明文
custom_env。agent list/get/create/update以及 WS 事件只返回has_custom_env(bool)与custom_env_key_count(int)。 - 读取明文值必须走专门的
GET /api/agents/{id}/env(即multica agent env get)。它的门禁实现在 agent_env.go 的authorizeAgentEnv:- 只放行 Agent 本人的人类 owner 或 workspace 的 owner/admin;
- agent 参与者一律拒绝(
agents may not access env management endpoints,403)——一个正在运行的 Agent 读不了另一个 Agent 的秘密,即使那个 Agent 正是它的人类 owner 拥有的(MUL-2600 伪装防护,判断在 agent actor 分支里最先执行); - 纯谓词
canManageAgentEnv:workspace owner/admin,或agent.owner_id == member.user_id;NULL的owner_id永不匹配。
- 创建之后的写入不经过
agent update。通用 update handler 一见到 body 里的custom_env就 400(提示 "use PUT /api/agents/{id}/env")。明文 env 写入由PUT /api/agents/{id}/env(multica agent env set)处理,携带同样门禁并写入一条审计记录。路由在 router.go 中注册(GET /env→GetAgentEnv,PUT /env→UpdateAgentEnv),对应 SQL 写路径是UpdateAgentCustomEnv(SET custom_env = $2)——它是 env 值唯一的写入路径。
CLI 注释里也明确写了设计取舍:custom_env 有意不作为 agent update 的 flag,因为它需要走那条审计化的 env 端点。
mcp_config:也是秘密,但行为与 custom_env 不同
mcp_config 是 Agent 的 MCP server 配置(JSON 对象,如 {"mcpServers": {…}})。它同样属于秘密材料——MCP 条目常内嵌 API token——因此在 agent create 和 agent update 两端都提供与 custom_env 相同的三条通道:
multica agent create --name <name> --runtime-id <runtime-id> --mcp-config-file <0600-json> --output json
multica agent update <agent-id> --mcp-config-stdin --output json
multica agent update <agent-id> --mcp-config 'null' # 清空配置
--mcp-config-stdin / --mcp-config-file 把值挡在 shell history 与 ps 之外;内联 --mcp-config <json> 不行。CLI 要求 JSON 对象或字面量 null:顶层数组或原始值会在客户端被拒,空的 stdin/文件输入会报错而不是静默清空。
mcp_config 与 custom_env 有两条关键区别:
- 它可以通过
agent update设置。 与custom_env不同,mcp_config没有专属的审计端点——通用PUT /api/agents/{id}就接受它。按原始 body 做三态:字段省略 → 不变;字面量null→ 清空;对象 → 整体替换。对应 handler 在 agent.go 的 update 路径(专门清空用ClearAgentMcpConfig)。所以agent update上--mcp-config null即可清空。 - 读取时会序列化但脱敏。
agent get/list只对「有权限查看 Agent 秘密」的调用者返回mcp_config,否则该字段为null且mcp_config_redacted: true。agent 参与者永远看不到它;workspace 还可以对所有人强制脱敏。
provider 支持并不统一:Qwen Code 接受托管式 mcp_config——daemon 会把它写进自己拥有的 0600 临时 JSON 文件并以 --mcp-config 传入,进程退出即删除;该字段留空(null)则继承 Qwen Code 原生设置。
Workspace MCP servers:库、分配与合并
workspace 维护一个 MCP server 库(workspace Settings → MCP,或 multica workspace mcp list|add|update|remove)。加入库不会自动给任何 Agent 使用——与 workspace skill 形态一致。只有当有人把它分配给某 Agent 时才到达该 Agent:
multica workspace mcp list --output table # 找到 server id
multica agent mcp add <agent-id> <server-id> # 给某一个 Agent
multica agent mcp disable <agent-id> <server-id> # 停止发送但保留分配
multica agent mcp remove <agent-id> <server-id> # 移除分配
claim 时生效的集合是三层的并集:
| 层 | 何时到达 Agent |
|---|---|
| runtime 本地 servers | 总是(daemon 合并 runtime 自己的文件) |
| workspace servers | 已分配给本 Agent 且保持 enabled |
Agent 自己的 mcp_config |
总是;名字冲突时它获胜 |
写配置前值得知道两个推论:分配共享 server 不需要再在 mcp_config 里重列(它们是合并的);mcp_config 现在只关乎「这个 Agent 私有的 servers」——一个托管但为空的 {} 不再代表 workspace 层的任何含义,因为本来就没有继承。
还有一条隐私设计:库里存储的条目是 write-only 的——任何角色的读取都只返回 server 的 name 与 transport,永远不返回 urls、commands、headers 或 env。
Skill 绑定:add 与 set 是两个动词
创建 Agent 不会绑定任何 workspace skill——绑定是 Agent 存在之后的独立调用。两个动词不可互换:
add是累加式:把给定 id 与既有绑定合并(POST /api/agents/{id}/skills/add);set是整体替换:用恰好给定的 id 覆盖整个绑定列表(PUT /api/agents/{id}/skills);--skill-ids ''清空全部。
multica agent skills add <agent-id> --skill-ids <skill-id> --output json
multica agent skills list <agent-id> --output json
这两条命令的 HTTP 路由可以从 cmd_agent.go 的 skills 子命令定义中看到(set → PUT /skills,add → POST /skills/add)。
claim 时 daemon 组装技能的次序是:workspace 绑定的技能在前,随后追加平台内置技能。LoadAgentSkills 加载每个绑定技能的内容及其支持文件(实现见 task.go 中 LoadAgentSkills 与技能文件遍历逻辑);内置技能在编译期通过 go:embed builtin_skills 嵌入(见 builtin_skills.go),从 <name>/SKILL.md + 同级文件加载。两者最终都以技能内容(skill content)的形式到达 provider——这就是为什么「能力」要放进绑定的技能里,而不是贴进 instructions:你正在读的这份 SKILL.md 本身就是 builtin_skills/multica-creating-agents/ 下的一个内置技能。
副作用清单:哪些命令需要明确授权
只读(安全,可放心跑):agent get、agent skills list、agent env get。
会改状态的(需要显式指令,不要投机执行):
multica agent create—— 插入一条新 Agent 行;multica agent copy—— 插入一条新 Agent 行(既有 Agent 的 fork),源不被触碰;multica agent skills add/set—— 变更绑定(set有破坏性:会删掉不在新列表里的绑定);multica agent env set—— 整体覆盖custom_env并写一条审计行。
常见错误假设清单(给 Agent 与人的防呆列表)
SKILL.md 最后给出的这张清单非常值得保留,它总结了整套契约最容易踩的坑:
- 「
description就是提示词。」 不是——只有instructions会到达 runtime。一个 description 很丰富但 instructions 为空的 Agent,等于一个「有名字的 shell,没有行为契约」。 - 「create 会绑定 Agent 的技能。」 不会——事后要显式绑定。
- 「
agent update能轮换 env。」 不能——碰到custom_env就 400,请走 env 端点。 - 「update 时
mcp_config的表现和custom_env一样。」 不一样——mcp_config可以通过agent update设置(--mcp-config,--mcp-config null清空);只有custom_env被锁在专属 env 端点后面。 - 「
agent get会显示 env 值。」 它只显示has_custom_env和custom_env_key_count。 - 「每个被接受的 body 字段都有 CLI flag。」
conversation_starters没有——agent create/agent update都设不了它,agent copy只能把存量值带过去。 - 「非法的
thinking_level/model组合会在 create 时被发现。」 只有未知的 provider 级字面量会——model 级的不兼容要到运行期才暴露(daemon 记警告并省略该覆盖)。 - 「
set和add对技能来说可互换。」set替换全部绑定;在你想用add时用了set,会静默移除已有能力。
深入阅读:源码证据与一致性测试
本文所有契约背后都有可验证的源码锚点,最直接的入口是配套证据层文档 creating-agents-source-map.md,它把上文每条契约映射到 file:line、运行时效果和一条安全的只读验证命令,主要源码锚点包括:
- CLI 入口:cmd_agent.go(create/update/skills/env 子命令与 flag 注册、
runAgentCreate/runAgentUpdate的 body 组装)、cmd_agent_copy.go(copy 语义)、cmd_agent_validation.go(1–50 范围校验); - 创建/更新 handler:agent.go(字段校验、默认值物化、
thinking_level/service_tier门禁、UpdateAgent对custom_env的 400); - Env 门禁:agent_env.go;
- 路由注册:router.go(
/api/agents/{id}/env等); - claim 时注入:daemon.go(claim 重读 Agent 行、技能装载顺序、运行时载荷);
- 技能装载:task.go、builtin_skills.go(
go:embed内置技能); - 字段持久化:
server/pkg/db/generated/agent.sql.go(由queries/agent.sql生成的CreateAgent/UpdateAgent/UpdateAgentCustomEnv); - provider 模型/推理目录:
server/pkg/agent/下的models.go、thinking.go、acp_effort.go、pi.go、zeroclaw.go、qwen.go等(逐 model 校验、Pi 的--thinking过滤、ZeroClaw 的 agentAlias 伪参数、Qwen Code 的托管 MCP 注入)。
本技能与共享模板的一致性由 internal/service 下的 Go 测试守护(例如针对该技能覆盖 Agent 创建契约的一致性测试),可在 server 目录运行 go test ./internal/service 中相关用例验证文档与实现没有漂移。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00