ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Multica 技能导入全解析:从 URL 与本地归档到 agent 绑定的完整机制

Multica 技能导入全解析:从 URL 与本地归档到 agent 绑定的完整机制 Multica 技能导入全解析从 URL 与本地归档到 agent 绑定的完整机制【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica本文基于 Multica 内置技能multica-skill-importing及其配套的行为—源码映射文档展开。该技能描述了“把一个具体技能导入当前 workspace”的唯一受支持路径与所有边界行为适用于开发者、AI Agent 与二次开发者理解 Multica 的 skill 数据库导入、同名冲突、归档解析、provenance 记录与 agent 绑定的真实语义。读完本文你将掌握multica skill importURL / 本地.skill/.zip、--on-conflict四种冲突策略、multica skill refresh、multica agent skills add / set的正确用法以及每条操作对应的底层源码路径。Multica 的技能体系存在一个核心不变量只有当某个技能存在于当前 workspace 的技能数据库中它才算“为 Multica 安装”。把技能放进数据库的唯一受支持入口是 workspace 的导入端点POST /api/skills/import它同时接受托管 URL 与上传的本地归档.skill/.zip并由multicaCLI 驱动。因此所有“导入/安装某个技能”的诉求都应收敛到这条单一链路上而不是使用npx skills add之类的本地安装器——后者写入的是外部/本地技能环境Multica 既无法管理也无法为 agent 绑定它这正是 SKILL.md 反复强调的“Incorrect → correct”反例。为什么需要一张“行为—源码映射表”在阅读下文之前值得先理解本仓库为这个内置技能提供的工程化保障。skill-importing-source-map.md 是一张evidence layer证据层SKILL.md 中的每一条行为声明都对应到server/下真实的file:line代码路径确保 Agent 在按技能执行时不会凭感觉猜测接口语义。由于源码行号会随提交漂移该文档顶部给出了重新推导锚点的标准命令这本身也是阅读本仓库源码的通用方法grep -n func (h \*Handler) ImportSkill server/internal/handler/skill.go grep -n func runSkillImport server/cmd/multica/cmd_skill.go grep -n func IsReservedContentPath server/internal/skill/reserved.go事实上以当前仓库代码为准重新grep可以发现部分行号已漂移例如ImportSkill现位于 skill.go 而非映射表中记录的 1882 行这正是该文档建议“先重新 grep 再信任”的原因。下面所有行号均以本文写作时仓库代码为准。导入端点的双通道路由技能导入的所有行为都汇聚在POST /api/skills/import这一个路由上。路由注册位于 router.gor.Post(/import, h.ImportSkill)处理器ImportSkill定义在 skill.go。处理器会按请求的 Content-Type 分流multipart/form-data体 → 走“本地归档导入”路径importSkillFromArchive提交file字段的.skill/.zip字节与可选的on_conflict字段JSON 体→ 走“托管 URL 导入”路径请求体为ImportSkillRequestskill.gotype ImportSkillRequest struct { URL string json:url OnConflict string json:on_conflict,omitempty }两条路径最终都收敛到共享的收尾函数finishSkillImportskill.go它把抽取出的文件映射为创建请求、把来源信息写入config.origin、创建技能并在同名冲突时按on_conflict策略分派。CLI 侧与之一一对应。multica skill import命令定义在 cmd_skill.go核心 flags 包括Flag说明默认--url url托管源导入clawhub.ai / skills.sh / github.com与--file二选一必填--file path本地.skill/.zip归档导入与--url互斥--on-conflict strategy同名冲突策略fail/overwrite/rename/skipfail--output format输出格式json或 tablejsonrunSkillImportcmd_skill.go强制“恰好一个来源”--file读取归档后通过客户端方法以 multipart 提交--url则以 JSON 体提交到同一端点并对结构化的 HTTP 错误体做归一化解析。URL 源家族识别与 GitHub URL 解析detectImportSourceskill.go负责识别 URL 的来源家族并做scheme 归一化缺http:///https://前缀时自动补全主机识别结果说明skills.sh/www.skills.shsourceSkillsSh直接抓取clawhub.ai/www.clawhub.aisourceClawHub直接抓取github.com/www.github.comsourceGitHub走 GitHub raw / contents API裸 slug无 hostsourceClawHub默认路由到 ClawHub其它 host400报错信息会点名受支持的三个源对应到 CLI 上以下形式都是合法输入multica skill import --url clawhub.ai/owner/skill --output json multica skill import --url skills.sh/owner/repo/skill --output json multica skill import --url github.com/owner/repo --output json multica skill import --url github.com/owner/repo/tree/main/path/to/skill --output json multica skill import --url github.com/owner/repo/blob/main/path/to/SKILL.md --output jsonGitHub URL 的解析在parseGitHubURL[skill.go](https://link.gitcode.com/i/306eee43508be968a72726eb2af66cce#L1833-L1886中实现支持三种形态裸的owner/repo取默认分支的仓库根、/tree/{ref}/...指定分支/标签下的目录、/blob/{ref}/.../SKILL.md指定文件且最后一节必须是SKILL.md大小写不敏感。这里有一个值得展开的实现细节GitHub Web URL 并不在分支名与仓库内路径之间做分隔因此当 ref 本身含/例如release/v2时URL 存在歧义——owner/repo/tree/release/v2/skills/foo无法一眼判断release是分支、v2是路径还是release/v2才是分支。为此代码先做“乐观切分”refSegments[0]作为 ref随后resolveGitHubRefAndPathskill.go通过 GitHub commits API从最长前缀到最短前缀逐个探测哪一个候选是真实分支/标签/commit SHA并在探测被限流时回退到乐观切分并给出告警提示设置GITHUB_TOKEN以支持含斜杠 ref 的消歧。探测使用Accept: application/vnd.github.v3.sha以最小化响应体skill.go。抓取成功后技能名/描述取自SKILL.md的 frontmatter缺省名回退为skillDir的 basename再不行取仓库名skill.go来源被记录为 provenance见后文config.origin。支持文件优先通过单次递归git tree元数据先行校验导入上限再下载rate-limit 或截断时回退到逐目录 contents 爬取skill.go。本地归档导入.skill/.zip.skill文件本质上就是标准 zip——Anthropic skill-creator 的package_skill产物一个普通.zip技能目录同样可用。这是由multica skill import --file path --output json触发的路径处理器入口为importSkillFromArchiveskill_import_archive.go。归档路径的完整处理链上传体上限请求体用http.MaxBytesReader限制为maxImportArchiveUploadSize16 MiB 压缩态skill_import_archive.go防止在解压上限生效前被无限压缩流拖垮。解析技能根parseSkillArchiveskill_import_archive.go用zip.NewReader解压定位到最浅层的SKILL.md所在目录作为技能根rootPrefix。因此“根目录平铺SKILL.md”与“my-skill/SKILL.md单层包装”两种布局都能被接受。frontmatter 取名校验名称取自SKILL.mdfrontmatter若缺name字段则依次回退到包装目录名、再回退到上传文件名去扩展名skillNameFromArchiveskill_import_archive.go最终为空则整体报错避免无主技能入库。安全过滤只采纳位于技能根之下的文件任意深度的SKILL.md都被当作保留主内容而非支持文件跳过详见“保留路径”一节isIgnoredArchiveEntryskill_import_archive.go剔除点文件dotfiles、__MACOSX目录以及 license 类文件每个条目经validateFilePath做zip-slip / 绝对路径防护单条目读取用io.LimitReader封顶readZipFileskill_import_archive.go杜绝“zip 头虚报小体积但解压超限”的放大攻击。限额复用逐条通过importedSkill.addFileskill.go计入 bundle 级上限与 URL 导入完全一致单个文件读取失败或缺省超限条目被跳过而非拖垮整个导入。归档路径与 URL 路径最终都返回结构化结果信封并遵守完全相同的--on-conflict策略无 legacy 兼容负担因此总是结构化输出。导入限额总表以下限额集中在 skill.go 与归档上传路径中URL 与归档两种来源共同遵守限额数值代码位置单文件大小1 MiBskill.go单次导入 bundle 总大小支持文件之和8 MiBskill.go支持文件数量上限256skill.go归档上传体积压缩态16 MiBskill_import_archive.gorename 策略尝试后缀上限-2…-5150 次skill.go服务端抓取总时限45 秒skill.go抓取失败的 HTTP 映射在importFetchErrorResponseskill.go超限 →413抓取超时 →504源临时不可用 →503其余 →502。45 秒抓取时限刻意小于反代网关超时让慢速/超大源以可读 API 错误暴露而非代理层的裸 504。结构化响应信封与来源记录config.origin当前 CLI 导入使用的响应是结构化导入结果信封SkillImportResultskill.go{ status: created|updated|conflict|skipped|failed, reason: ..., skill: { ...: SkillWithFilesResponse when created/updated }, existing_skill: { id: ..., name: ..., can_overwrite: true } }其中的字段语义依据 SKILL.md 与结构体定义status/reason结果总览skill仅当created/updated时出现为 workspace 级SkillWithFilesResponseexisting_skill冲突 / skipped / failed 且因已存在技能导致时携带既有技能身份ExistingSkillIdentityskill.go含can_overwrite是否可覆盖提示skill.config.originprovenance来源追踪——形如{type: github|clawhub|skills_sh, source_url, ...}仅在源提供了 origin 时写入skill.go因此读取时需按“可能缺省”处理。SkillWithFilesResponseskill.go内嵌SkillResponseid, workspace_id, name, description, content, config, created_by, created_at, updated_atskill.go并附加files []SkillFileResponsepathcontent。从结构推断凡是需要向用户汇报的“读响应字段”都应当直接从这些返回字段中取而不是猜测导入是否成功。Legacy 兼容不带on_conflict的旧客户端仍走旧契约——重复导入返回409响应体为{error: a skill with this name already exists, existing_skill: {id, name}}skill.go。当前 CLI 会把这种旧形态归一化为status: conflict并以非零码退出。遇到更老的仅返回纯字符串 body 的409时正确做法是自行用multica skill list --output json/multica skill get skill-id --output json找回既有技能而非循环重试或改名硬绕。同名冲突的四种策略同名冲突处理集中体现在finishSkillImportskill.go与分派器resolveImportSkillConflictskill.go中。结构化的冲突流程先在创建前按名字预查lookupSkillByName创建时若撞上并发导致的主键唯一约束冲突isUniqueViolation会回落到同一条结构化分派路径保证并发场景下也不产生竞态漏洞skill.go。策略行为HTTP权限要求fail默认什么都不做报告status: conflict理由会建议 overwrite 或 rename409—overwrite就地更新同名技能保留skill ID、created_by、created_at与 agent 绑定替换description、content、provenance 与支持文件200updated仅限原创建者非创建者返回failedrename新建技能并追加-2/-3等后缀最多尝试 50 次createRenamedImportedSkillskill.go既有技能不动201createdreason 注明 renamed—skip既有技能保持不动报告status: skipped200—overwrite的底层实现是overwriteSkillWithFiles在同一事务中做创建者校验、名称匹配校验并完整替换 description/content/config/filesskill.go。它的失败会被拆分为可读错误目标技能已不存在 → 409、非创建者 → 403、名称已不再匹配 → 409skillImportOverwriteFailureskill.go。对应的四个真实可复制命令示例源自 SKILL.md# 安全默认review-helper 已存在时以 statusconflict 失败 multica skill import --url https://skills.sh/acme/repo/review-helper --output json # 覆盖同名技能保留其 ID 与 agent 绑定 multica skill import --url https://skills.sh/acme/repo/review-helper --on-conflict overwrite --output json # 保留原技能导入一份副本如 review-helper-2 multica skill import --url https://skills.sh/acme/repo/review-helper --on-conflict rename --output json # 批处理友好已存在则跳过并标记 multica skill import --url https://skills.sh/acme/repo/review-helper --on-conflict skip --output jsonAgent 绑定additive add 与 replace-all set导入技能后“让某 agent 可用该技能”是独立、可变更的绑定操作且两条命令语义截然不同——这是实践中极易踩坑的地方multica agent skills add agent-id --skill-ids skill-id --output json是增量添加服务端AddAgentSkillsskill.go只插入新分配不清理既有分配对应路由POST /api/agents/{id}/skills/addrouter.gomultica agent skills set agent-id --skill-ids skill-id ...是整体替换服务端SetAgentSkillsskill.go先清除该 agent 的全部当前分配再精确重加你传入的 ID 集合对应PUT /api/agents/{id}/skills。CLI 定义对此有明确注释“without replacing existing assignments”与“replaces all current assignments”cmd_agent.go。因此误用set只传一个新技能 ID 会抹掉该 agent 此前的所有技能。普通“新增”意图应使用add。绑定完成后必须以multica agent skills list agent-id --output jsonGET/api/agents/{id}/skills验证目标技能 ID 确实在列再对外宣告“该技能已对 agent 可用”。更新已导入技能multica skill refresh当目标是“拉取某个已在 workspace 中的技能的最新版”时正确的入口是refresh而非重敲一遍 import URLCLI 定义在 cmd_skill.gomultica skill refresh skill-id --output json它发送POST /api/skills/{id}/refresh空 body处理器RefreshSkill位于 skill_refresh.go。服务端流程从该技能存储的config.origin.{type, source_url}读取 provenanceparseSkillOriginskill_refresh.go只有github/skills_sh/clawhub三种来源可刷新refreshableOriginSourceskill_refresh.go手动创建、本地归档导入、从本地 runtime 复制而来的技能无此来源会得到 422按存储的source_url重跑对应的抓取器fetchImportedSkillFromOriginskill_refresh.go随后就地覆盖保留 skill ID、created_by、created_at、agent 绑定与 labels采纳上游重命名新名字替换 description、SKILL.md内容、provenance 与全套支持文件通过带NewNameAllowOverwrite的overwriteSkillWithFilesskill_create.go。refresh 的权限范围宽于import-overwrite技能创建者或 workspace 的owner/admin均可而 import 的overwrite策略仅限创建者skill_refresh.go。refresh 失败时应如实上报而非循环重试422无刷新来源手工创建/归档导入/本地 runtime 复制应改为重新 import409上游改名后与 workspace 内另一技能撞名未做任何改动403调用者既非创建者也非 workspace 管理员502/503/504/413上游抓取失败消失 / 不可用 / 超时 / 超限技能保持原状。refresh 的响应是普通SkillWithFilesResponse与multica skill get --with-content同形不是导入结果信封。保留路径语义为什么支持文件不能叫 SKILL.md一个技能的主内容是它的SKILL.md。这个文件名在 reserved.go 中被声明为ContentFilename且是保留的daemon 在准备执行环境时会亲自把技能的Content写到该路径reserved.go因此任何“支持文件”不得再占用它。判定函数IsReservedContentPathreserved.go先filepath.Clean再做大小写不敏感比较因此./SKILL.md、sub/../SKILL.md这类非规范拼写也会被命中。这条规则在不同端点上的表现是路径形态相关的导入 / 创建时skill_create.go清单中名为SKILL.md的支持文件会被静默丢弃continue导入照常成功但该条目不会出现在返回的files中PUT /api/skills/{id}整体替换文件时UpdateSkillskill.go同样静默跳过专用的单文件端点PUT /api/skills/{id}/filesUpsertSkillFileskill.go才会触发硬性 400错误文案为 “SKILL.md is reserved for the primary skill content”。因此如果导入后发现某个预期的支持文件缺失先检查它是否恰好叫SKILL.md改名到非保留路径即可不必怀疑导入逻辑出错。读回技能默认只取元数据multica skill get与multica skill files list默认返回的是元数据而非正文——每个文件的path、字节级size与内容 hashSHA-256外加SKILL.md主体的content_size/content_hash。这样做的工程理由是若把每个文件正文内联单个约 600KB 的技能在慢链路上将无法完整获取甚至“用来诊断大技能的命令本身会被大技能拖垮”该动机在 skill.go 的注释中有完整记录与仓库 issue GH #7498 相关。multica skill get skill-id --output json # 仅元数据 multica skill get skill-id --with-content --output json # 内联正文 multica skill files list skill-id # 路径与大小CLI 侧的skillIncludeQuerycmd_skill.go只在显式传--with-content时发送?includecontent否则一律?includemetadata。而在服务端resolveSkillIncludeskill.go对“不带?include”的请求默认返回 content——这是为已安装的桌面构建与旧版 CLI 保留的兼容默认并有专门的兼容性测试覆盖TestSkillEndpointsWithoutIncludeStillReturnContent见 skill_metadata_test.go。元数据模式下文件体的 size/hash 都在Postgres 内计算ListSkillFileMetadata见 server/pkg/db/queries/skill.sql文件体从不离开数据库hash 是对原始 UTF-8 字节计算convert_to而非可能把反斜杠转义读歪的content::bytea强转这一点有专门测试TestSkillFileHashCoversRawUTF8Bytes佐证。正确用法对照错误——绕过 Multicanpx skills add https://skills.sh/owner/repo/skill技能或许在本地存在但 Multica 无法把它作为 workspace 技能管理。错误——绑定用错命令普通新增却整体替换只传新技能 ID 的set会清空 agent 其余技能普通新增应使用add。正确导入multica skill import --url https://skills.sh/owner/repo/skill --output json正确绑定有意变更该 agent 的技能分配时multica agent skills add agent-id --skill-ids skill-id --output json multica agent skills list agent-id --output json相关源码与文档导航技能本体Agent 视角的完整行为清单SKILL.md行为—源码证据映射表含验证命令skill-importing-source-map.md导入处理器与全部响应结构server/internal/handler/skill.go归档导入解析与安全过滤skill_import_archive.go技能创建/覆盖事务skill_create.go刷新已导入技能skill_refresh.go保留文件名语义server/internal/skill/reserved.goCLI 命令定义server/cmd/multica/cmd_skill.go、server/cmd/multica/cmd_agent.go路由注册server/cmd/server/router.go对应测试skill_import_archive_test.go、skill_refresh_test.go、skill_metadata_test.go、cmd_skill_test.go行号以当前仓库代码为准如需在后续版本中复核任意锚点请先按本文开头给出的grep模式重新定位再阅读其周边代码。【免费下载链接】multicaMake humans and AI agents work as one team — open-source and self-hostable.项目地址: https://gitcode.com/GitHub_Trending/mu/multica创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表