Files
multica/docs/agent-quick-create-plan.md
Naiyuan Qing 623d29f276 feat(agents): one-click create from curated templates (Phase 1) (#2520)
* docs(agents): three-phase agent quick-create plan

Captures the full design for moving agent creation from manual form +
one-by-one skill attachment to a tiered experience:

- Phase 1 (this PR): one-click curated templates, AI-free.
- Phase 2 (next): AI-recommended skills via the existing quick-create
  task mechanism — no new server-side LLM dependency.
- Phase 3 (later): AI creates the whole agent end-to-end, composing
  Phase 2 with a new `multica agent create` CLI driver.

Documents the architectural decisions that keep all three phases on
existing infrastructure (no SSE, no server-side LLM SDK, no new WS
channels), the two soft blockers Phase 1 unlocks for later phases
(createSkillWithFiles TX composability + skill same-name dedupe), and
the scope decisions we explicitly opted out of (Anthropic plugin
marketplace, ClawHub UI affordances).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(skills): harden import against invalid UTF-8 and binary files

PG rejects two byte patterns in a TEXT column. Both crashed real skill
imports we hit while assembling the template catalog:

- Embedded NUL (0x00) -> SQLSTATE 22021. Already stripped by
  sanitizeNullBytes, kept as-is.
- Other invalid UTF-8 (e.g. 0x91 — Windows-1252 smart quote in a skill
  whose author saved prose from Word). sanitizeNullBytes now also runs
  strings.ToValidUTF8 over the content so the second class no longer
  takes the whole import down.

For non-text payloads (images, fonts, archives, compiled binaries),
sanitization isn't the right fix — agents never read those as text,
and the bytes can't survive a TEXT column at all. addFile now skips
them by extension before the per-bundle cap counters tick, logging
the skip so an unexpected drop leaves a breadcrumb.

Function name kept for compatibility with the many call sites; both
behaviours are strict supersets of the original.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(skills): split createSkillWithFiles for tx composition + add workspace find-or-create query

Two soft blockers cleared so create-from-template (next commit) can
fold N skill creates and the agent + binding writes into one outer
transaction:

1. createSkillWithFiles used to Begin/Commit its own tx. Caller
   composition was impossible — N invocations meant N separate
   transactions and no atomicity over the whole materialise step.
   Pull the body into createSkillWithFilesInTx(ctx, qtx, input); the
   original function becomes a thin wrapper that manages its own tx
   for standalone callers. Existing call sites: zero behaviour change.

2. Add GetSkillByWorkspaceAndName sqlc query — workspace skill lookup
   by name, anchored to UNIQUE(workspace_id, name) from migration
   008. Lets the template materialiser implement find-or-create:
   reuse the workspace's existing skill row when a template
   references the same name, rather than crashing on the unique
   constraint or polluting the workspace with `<name>-2` clones.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(agents): agent template catalog + create-from-template endpoint

Server-side foundation for Phase 1 of the quick-create roadmap (see
docs/agent-quick-create-plan.md). Adds:

- server/internal/agenttmpl/ — embed-loaded catalog of curated agent
  templates. Each template ships pre-written instructions plus a list
  of skill URLs that get materialised into the workspace at create
  time. Validation runs at startup (init() panics on a malformed
  template) so a bad JSON ships as a deploy-time defect, not a
  runtime 500. Slug must equal the filename basename so the URL
  router is mirror-symmetric with the file layout.

- 11 starter templates covering Engineering / Writing / Building /
  Testing (code-reviewer, frontend-builder, planner, docs-writer,
  one-pager, html-slides, full-stack-engineer, …).

- Three new endpoints, all behind RequireWorkspaceMember:
    GET  /api/agent-templates           — picker list (no instructions)
    GET  /api/agent-templates/:slug     — detail with instructions
    POST /api/agents/from-template      — materialise + create

  Create flow:
    1. Auth + runtime authorization happen BEFORE the GitHub fan-out
       so a 403 never wastes 20s of upstream fetches.
    2. Pre-flight dedupe by cached_name reuses workspace skills
       without an HTTP fetch — second create-from-the-same-template
       drops from 20s to <100ms.
    3. Parallel fetch (30s per-URL timeout) for the remaining skills.
    4. Single transaction: every skill insert, the agent insert, and
       the agent_skill bindings. On any upstream fetch failure the TX
       rolls back and the API returns 422 with `failed_urls` so the
       UI can name the bad source(s).
    5. extra_skill_ids (user-supplied additions) are verified through
       GetSkillInWorkspace per id before attach, so a malicious client
       can't graft a skill from another workspace via UUID guessing.

- multica agent create --from-template <slug> CLI flag dispatches to
  the new endpoint with a 60s ceiling, matching `multica skill import`.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(agents): one-click create-from-template UI

Frontend half of Phase 1. CreateAgentDialog becomes a state machine
spanning four steps:

  chooser          → Start blank / From template cards
  blank-form       → existing manual form (post-chooser)
  duplicate-form   → existing form pre-filled from a duplicated agent
  template-picker  → grid of templates, click navigates to detail
  template-detail  → instructions + skill list preview + one-click Use

Picking a template never lands on the form: name auto-deduped against
existingAgentNames, runtime = first usable one, visibility = private.
Refinement happens on the agent detail page if needed. Same rationale
the doc spells out — templates exist precisely to skip configuration.

New components, all collapsible-by-default so quick-create stays fast:
  - template-picker.tsx — categorised grid, lucide icons + semantic
    accent tokens resolved through static maps so Tailwind's JIT picks
    up every variant (dynamic class strings would silently miss).
  - template-detail.tsx — instructions preview, skill list with cached
    descriptions, Use CTA. Renders the failedURLs banner when a 422
    fires — the only step that can trigger that response.
  - instructions-editor.tsx — collapsed preview-card / expanded full
    ContentEditor.
  - skill-multi-select.tsx + skill-picker-list.tsx — shared multi-
    select surface, also adopted by the existing skill-add-dialog.
  - avatar-picker.tsx — agent avatar upload, mirrors the inspector's
    visual language.

Schema-defended client (CLAUDE.md → API Response Compatibility): the
three new endpoints are wired through parseWithFallback with lenient
zod schemas. Desktop builds outlive any given server — a future
field rename / wrapping must not white-screen older installs.
listAgentTemplates accepts both the current bare array and a future
{templates: [...]} envelope. Coverage: 7 new schema-test cases in
schema.test.ts (null body, missing skills/instructions, malformed
create response, envelope migration).

Catalog + detail go through TanStack Query with staleTime: Infinity —
workspace-independent static data, no per-mount refetch.

Other:
- skill-add-dialog becomes a true multi-select (Confirm button +
  checkbox list); attached skills are filtered out of the list.
- agents-page hands the freshly-created Agent back to the dialog so a
  follow-up setAgentSkills can attach the form-selected skills.
- agent-overview-pane drops the mx-auto/max-w-2xl frame on config-
  tab content; the wider dialog visual language reads better with
  tabs filling the column.
- Every new UI string lives in both en/agents.json and
  zh-Hans/agents.json under create_dialog.* / tab_body.skills.* —
  locales/parity.test.ts blocks drift in CI.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ci): align skill import test + drop next-only lint suppression

- TestFetchFromSkillsSh_ResolvesRootLevelSkillMd now expects assets/logo.png
  to be skipped; matches the new addFile binary-extension guard
  (6fafd86e). The .png is intentionally dropped so PG TEXT inserts don't
  hit SQLSTATE 22021.
- packages/views shares zero next/* deps, so the @next/next/no-img-element
  eslint plugin isn't loaded there. The eslint-disable directive
  referencing it produced a hard "rule not found" error in CI lint. Raw
  <img> is the right primitive in views; remove the disable comment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

* test(agents): wrap CreateAgentDialog tests in workspace/navigation providers

The dialog now calls useNavigation() and useWorkspacePaths(), both of
which throw outside their providers. The existing tests rendered the
dialog bare and tripped both new requirements:

- NavigationProvider — supply a stub adapter so push() works for the
  agent-detail redirect.
- WorkspaceSlugProvider — useWorkspacePaths() requires a slug.

The blank-vs-template chooser is now the default first step; the
existing tests target the runtime picker on the manual form, so the
helper auto-clicks "Start blank" when no template is passed
(duplicate-mode tests skip the chooser).

Manual afterEach(cleanup) + document.body wipe. Base UI's Dialog
portal renders into document.body and leaves focus-guard/inert wrapper
divs behind across tests, so the second test in the suite saw two
"All" / "My Runtime" matches and getByText failed. The wipe is local
to this file rather than the shared setup because it isn't a global
issue — only suites that open Base UI dialogs hit it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-05-13 18:26:04 +08:00

26 KiB

Agent 快速创建 — 三阶段实施计划

Status: Draft (设计阶段,未动工) Owner: TBD Last updated: 2026-05-13

TL;DR

  • 目标:降低用户创建 Agent 的门槛,从「手工填表 + 一个个挑 skill」演进到「一键模板」「AI 推荐 skill」「AI 直接创建 agent」三档
  • 三阶段:Template(必做、独立)→ Skill Finder(AI 推荐 skill)→ AI Create Agent(AI 直接创建)
  • 架构关键:Phase 2/3 复用现有 Quick-create Issue 基础设施(派任务给 agent + tool calling + inbox 通知),不引入新 LLM 调用路径
  • 不需要新基础设施:无 SSE、无 server-side LLM、无新 WS channel
  • soft blocker:两处 routine 重构(createSkillWithFiles TX 拆分、skill 同名 find-or-create)
  • 不做:接入 Anthropic 官方 marketplace(plugin 体系跟单体 skill 形态不匹配)、接入 ClawHub(战略对位错误 + 实际使用率低,见 §5)

1. 背景与目标

1.1 当前现状

当前用户创建一个 Agent 需要走的步骤:

  1. /agents 页面 → 点 "Create Agent"
  2. 手工填 name / description / runtime / model
  3. 手工写 instructions(空白文本框,用户自己思考措辞)
  4. 创建完后进 Agent 详情页 → 点 "Add Skill" → 一个一个挑 skill 关联
  5. 如果 workspace 还没有需要的 skill,得先去别处建/导入 skill(POST /api/skills/import 支持 skills.sh / GitHub / ClawHub 三种 URL)

痛点:

  • 用户得预先知道自己需要哪些 skill,这要求他对 skill 生态熟悉
  • 写 instructions 是空白文本编辑,大多数用户不知道写什么
  • 跨多页操作,体感上"创建一个能用的 Agent"是个项目,不是个动作

1.2 三阶段方案

Phase 提供给用户的能力 是否需要 AI 独立可发布
1. Template 选模板 → 自动 import 模板带的 skill + 预填 instructions
2. Skill Finder 描述需求 → AI 推荐 skill 列表 → 一键导入到 workspace (独立功能,任何场景都能用)
3. AI Create Agent 描述需求 → AI 自己 find skill + 写 instructions + 创建 agent 依赖 Phase 2

每个 phase 本身有用户价值,不需要等下一个 phase 才能用:

  • Phase 1 用户能用模板创建 agent,即使后两阶段没做
  • Phase 2 用户能在任何地方"用 AI 找 skill"(创建 agent 时、给现有 agent 加 skill 时、单纯逛 skill 时)
  • Phase 3 是 1+2 的组合

1.3 不在范围内

明确不做的事(及理由,见 §5):

  • 接入 Anthropic 官方 plugin marketplace(anthropics/claude-plugins-official)
  • 接入 ClawHub 的"发现/搜索"层(import 路径已经存在,但是死代码,建议下线)
  • 让 AI 直接装 skill 到用户本地 ~/.claude/skills/(npx skills CLI 行为)
  • Server-side LLM 调用(后端目前没有 LLM SDK,这条路引入新基础设施,而 Quick-create 模式可以避开)

2. 关键概念回顾

这一节给没参与前期讨论的同事看。已经熟悉 skill 系统的可跳到 §3。

2.1 Skill 是什么

Skill 是一个按需加载的能力包,本质是 SKILL.md 文件 + 可选附件。Anthropic 2025-12 把它发布为开放标准(agentskills.io),Cursor / OpenAI / GitHub Copilot 等都已采纳——同一份 SKILL.md 跨多个 agent 工具都能用。

每个 runtime(Claude Code / Cursor / Codex 等)启动时自动扫自己约定的目录(~/.claude/skills/.cursor/skills/ 等),读 SKILL.md 的 frontmatter 形成"我手上有这些 skill"的清单注入 system prompt。具体 skill 正文只在被触发时才进 context。

2.2 Multica 的 Skill 数据模型

3 张表(migration 008_structured_skills.up.sql):

关键字段
skill id, workspace_id, name, description, content (=SKILL.md 正文), config (含 origin 元数据)
skill_file skill_id, path, content(SKILL.md 的附件,如 examples/.md、scripts/.py)
agent_skill agent_id, skill_id(M:N 关联)

关键约束:UNIQUE(workspace_id, name) — 同 workspace 内 skill 名字必须唯一。

2.3 Skill 流转链路(数据库 → runtime)

任务运行时,skill 从 PG 到 runtime 的完整路径:

1. 数据库:skill + skill_file + agent_skill 三张表的行

2. Daemon claim 任务:
   POST /api/runtimes/{runtimeId}/tasks/claim
   handler/daemon.go:1018-1098 (ClaimTaskByRuntime)
   → service/task.go:1447-1463 (LoadAgentSkills)
   → 把 agent 关联的所有 skill 全文塞进 HTTP 响应

3. Daemon 算工作目录:
   server/internal/daemon/execenv/execenv.go:114, 124
   workDir = {WorkspacesRoot}/{wsID}/{shortTaskID}/workdir

4. Daemon 按 runtime 算 skill 目录:
   server/internal/daemon/execenv/context.go:121-158 (resolveSkillsDir)
   claude   → {workDir}/.claude/skills
   cursor   → {workDir}/.cursor/skills
   codex    → 特殊:{codexHome}/skills

5. Daemon 把字符串写成磁盘文件:
   context.go:175-204 (writeSkillFiles)
   核心就两行 os.WriteFile

6. Daemon 启动 runtime,cwd = workDir
   runtime 自己扫 .claude/skills/(等)→ 加载 frontmatter

7. 任务结束:os.RemoveAll(workDir)
   PG 是真相源,workDir 是每次任务临时复印件

核心 invariant:Multica 不教 runtime 怎么用 skill,只把文件摆到 runtime 已经会扫的位置。

2.4 Template = Instructions + Skill 引用

Template 是个静态 JSON 定义,包含:

  • 预写好的 instructions
  • 一组 skill 引用(用 URL 指向 skills.sh / GitHub)

用户选模板时,后端:

  1. 对每个 skill 引用,复用现有 /api/skills/import 的 fetcher(fetchFromSkillsSh / fetchFromGitHub)拉内容
  2. 物化到 workspace(同名复用 / 新建)
  3. CreateAgent + setAgentSkills
  4. 整个流程一个事务

skill 引用为什么用 URL 而不是内联 SKILL.md 内容:

  • 复用现有 import 基础设施,零新代码
  • skill 内容跟 GitHub 同步,不需要 vendoring 进 multica 仓库
  • 模板 JSON 体积小,git review 友好

2.5 Quick-create Issue 模式(Phase 2/3 复用的基础设施)

当前 POST /api/issues/quick-create(handler/issue.go:877-982)的流程:

1. 后端 enqueue 任务:
   - agent_task_queue 加一行,issue_id = NULL,context JSONB = {type: "quick-create", prompt: ...}
   - 立即返回 202 Accepted + task_id

2. Daemon claim 任务时识别 quick-create:
   - 检查 task.Context != nil AND !task.IssueID.Valid
   - 解析为 QuickCreateContext (service/task.go:1810-1811)

3. Daemon 构造 prompt:
   - daemon/prompt.go:45-106 (buildQuickCreatePrompt)
   - 把用户的自然语言 prompt 作为语义核心
   - 加上"调用 multica issue create CLI 命令"的指令

4. Agent 跑 LLM + tool calling:
   - LLM 输出形如 `multica issue create --title="..." --description="..."` 的命令
   - daemon 执行 CLI 命令,CLI 调 POST /api/issues 创建 issue
   - CLI 自动在请求里带上 MULTICA_QUICK_CREATE_TASK_ID env(daemon/daemon.go:2081)
     → 让创建出来的 issue 带 origin_type='quick_create' + origin_id=<task_id>

5. 后端 link + 通知:
   - 完成检测:GetIssueByOrigin(workspace_id, "quick_create", task_id)
   - LinkTaskToIssue(task_id, issue_id) 把任务行的 issue_id 补上
   - 写 inbox_item 通知用户(notifyQuickCreateCompleted, service/task.go:1908-1920)

关键洞察:这个模式完全通用化了。复用它只需要:

  1. 新的 context JSONB type(比如 "skill-find""agent-create")
  2. 新的 prompt builder
  3. 新的"完成检测 + inbox 通知"

不需要任何 daemon / 任务队列层面的改动。


3. 三阶段详细设计

Phase 1:Agent Template

目标:用户选模板 → 一键得到一个可用的 agent(自带 skill + instructions),不需要 AI 参与。

设计

  • Template 定义存放:静态 JSON,commit 在 server/internal/agenttmpl/templates/*.json
  • Template JSON 形态:
    {
      "slug": "code-reviewer",
      "name": "Code Reviewer",
      "description": "审代码用的 agent",
      "instructions": "你审代码,关注 N+1 查询、错误处理、类型安全...",
      "skills": [
        { "source_url": "https://skills.sh/obra/superpowers/tdd" },
        { "source_url": "https://github.com/foo/bar/tree/main/skills/code-style" }
      ]
    }
    
  • 新 endpoint:POST /api/agents/from-template
    • 请求:{template_slug, name, runtime_id, ...overrides}
    • 后端流程(全部在一个事务里):
      1. 加载 template JSON
      2. 对每个 skill source_url:
        • 调用 detectImportSource(url)(skill.go:586-617)分发到对应 fetcher
        • 通过 GetSkillByWorkspaceAndName 检查 workspace 是否已有同名 skill
          • 有 → 复用现有 skill_id
          • 无 → 调 createSkillWithFilesInTx(待重构,见 §4)物化
      3. CreateAgent(复用 agent.go:CreateAgent 的内部逻辑)
      4. 批量 AddAgentSkill 关联
    • 响应:{agent: {...}, imported_skill_ids: [...], reused_skill_ids: [...]}
  • 前端:CreateAgentDialog(packages/views/agents/components/create-agent-dialog.tsx)加 "From template" 模式,跟现有 manual / duplicate 模式并列
    • 模板选择器 → 预览(instructions + skill 列表)→ 提交调新 endpoint
    • 响应里的 reused_skill_ids 用 toast 提示"以下 skill 已存在,沿用了 workspace 现有版本"

起步模板清单(初版,可调)

  • code-reviewer — 代码审查
  • tdd-pair — TDD 配对编程
  • db-reviewer — 数据库 / SQL 审查
  • pr-summarizer — PR 摘要
  • docs-writer — 文档撰写

具体每个模板选哪些 skill URL,在 Phase 1 启动时单独决定(需要逛 skills.sh 选高质量 skill)。

Phase 1 改动清单

文件 / 位置 改动
server/internal/agenttmpl/(新包) 加载 JSON 模板的代码
server/internal/agenttmpl/templates/*.json(新文件) 5 个起步模板
server/internal/handler/agent.go 新 handler CreateAgentFromTemplate
server/internal/handler/skill_create.go 重构:拆出 createSkillWithFilesInTx 变体(见 §4)
server/pkg/db/queries/skill.sql GetSkillByWorkspaceAndName(见 §4)
server/cmd/server/router.go 注册新 endpoint
packages/views/agents/components/create-agent-dialog.tsx 加 template 模式
packages/core/api/agent.ts createAgentFromTemplate API 调用
packages/views/agents/components/template-picker.tsx(新文件) 模板选择器组件

Phase 2:Skill Finder

目标:用户用自然语言描述需求(如"我想审 SQL"),AI 推荐一组 skill,用户勾选一键导入到 workspace。

设计

  • 架构选型:走 quick-create 模式,不是后端直接调 LLM
  • 新 endpoint:POST /api/skills/find
    • 请求:{prompt, agent_id}(agent_id 是用来跑这个 LLM 任务的 agent,跟 Quick-create Issue 一样要求预先有 agent)
    • 后端流程:
      1. enqueue 任务:agent_task_queue 加一行,context JSONB = {type: "skill-find", prompt}
      2. 返回 202 + task_id
  • Daemon prompt builder:daemon/prompt.gobuildSkillFindPrompt(类比 buildQuickCreatePrompt)
    • 喂给 agent 的 prompt 大致:
      用户需求:{user_prompt}
      
      你的任务:从以下 curated skill 清单里选 3-5 个最相关的推荐给用户。
      
      可选 skill 清单(JSON):
      {curated_skill_index}
      
      输出:调用 `multica skill find --output-results '<JSON>'` 命令,
      JSON 形态为 [{name, description, source_url, reason}, ...]
      
  • CLI 命令(新):multica skill find --output-results <JSON>
    • 不发起 HTTP 请求,只把 JSON 写到 daemon 通过 env 指定的临时文件
    • daemon 读这个文件,把内容塞进 inbox notification 的 payload
  • Curated skill 索引:server/internal/agenttmpl/skill_index.json(新文件)
    • 几十到上百条精选 skill,每条:{name, description, source_url, tags, install_count}
    • 维护方式:工程师/产品手工维护,代码 review 卡内容质量
    • MVP 不做实时 GitHub Code Search 或 skills.sh 爬虫
  • 完成通知:写 inbox_item,type = skill_find_done,payload 含推荐结果数组
  • 前端:
    • 独立"Find Skill"页面(/skills/find/skills?ai=true)
    • skill list page 上"用 AI 找 skill"按钮入口
    • 用户输入 prompt → 提交 → 等通知 → inbox item 里展示 skill 卡片(name + description + source_url + reason)
    • 用户勾选 → 一键批量调现有 POST /api/skills/import(每个 skill 一次,可考虑加 batch endpoint 但 MVP 不必要)

Phase 2 改动清单

文件 / 位置 改动
server/internal/handler/skill.go 新 handler FindSkill(enqueue task)
server/internal/service/task.go EnqueueSkillFindTask + 完成检测 + inbox 通知
server/internal/daemon/prompt.go buildSkillFindPrompt
server/internal/daemon/daemon.go SkillFindContext 识别 + env 注入
server/cmd/multica/cmd_skill.go find --output-results 子命令
server/internal/agenttmpl/skill_index.json(新文件) curated 清单
packages/views/skills/components/find-skills-dialog.tsx(新文件) UI
packages/core/api/skill.ts findSkills API
packages/views/inbox/items/skill-find-result.tsx(新文件) inbox item 渲染

Phase 3:AI Create Agent

目标:用户描述需求,AI 自己 find skill + 写 instructions + 创建 agent。

设计

  • 架构选型:走 quick-create 模式,组合 Phase 2 的 find 能力 + 新的 agent create CLI
  • 新 endpoint:POST /api/agents/ai-draft
    • 请求:{prompt, host_agent_id}(host_agent_id 是跑这个元任务的 agent)
    • 后端:enqueue 任务,context = {type: "agent-create", prompt},返回 202 + task_id
  • Daemon prompt builder:buildAgentCreatePrompt 指挥 agent 三步走:
    1. 调用 `multica skill find --output-results ...` 选 skill
       (或直接看 curated 清单选)
    2. 基于选定 skill 写 instructions
    3. 调用 `multica agent create --name ... --instructions ... --skill-ids ...`
       创建 agent 并关联 skill
    
  • CLI 命令(新):multica agent create
    • 后端 handler 已存在(handler/agent.go:CreateAgent),只需要绑 CLI(~50 行)
    • 创建时带 MULTICA_AI_DRAFT_TASK_ID env,服务端用它做 origin 标记 + LinkTaskToAgent
  • 完成通知:inbox_item type = agent_draft_done,payload 含 agent_id + 摘要
  • 前端:CreateAgentDialog 加 "AI" 模式
    • 输入需求 → 提交 → 等通知 → inbox 通知里点击 → 跳新 agent 详情页(用户在那儿编辑/调整)

Phase 3 改动清单

文件 / 位置 改动
server/internal/handler/agent.go 新 handler AIDraftAgent(enqueue task)
server/internal/service/task.go EnqueueAgentDraftTask + 完成检测 + inbox 通知
server/internal/daemon/prompt.go buildAgentCreatePrompt
server/cmd/multica/cmd_agent.go create 子命令(handler 已有)
packages/views/agents/components/create-agent-dialog.tsx 加 "AI" 模式
packages/core/api/agent.ts aiDraftAgent API
packages/views/inbox/items/agent-draft-result.tsx(新文件) inbox item 渲染

4. Blocker 清单与修复方案

4.1 [SOFT] createSkillWithFiles 不可组合事务

问题:server/internal/handler/skill_create.go:21-71 这个函数自己 Begin() 一个事务,执行完 Commit()。Phase 1 需要在外层事务里多次调用它(import N 个 skill + createAgent + setAgentSkills 都在一个 TX),但现在没法这么用。

影响范围:Phase 1

修复方案:

// 拆成两个函数(保持原 API 向后兼容):

// 新增:接受外部 qtx,不管事务
func createSkillWithFilesInTx(
    ctx context.Context,
    qtx *db.Queries,
    input skillCreateInput,
) (*SkillWithFilesResponse, error) {
    // 不 Begin/Commit,只调 qtx.CreateSkill + qtx.UpsertSkillFile loop
}

// 改造:原函数变成包装层,内部调 InTx 版
func (h *Handler) createSkillWithFiles(
    ctx context.Context,
    input skillCreateInput,
) (*SkillWithFilesResponse, error) {
    tx, _ := h.TxStarter.Begin(ctx)
    defer tx.Rollback()
    qtx := h.Queries.WithTx(tx)
    result, err := createSkillWithFilesInTx(ctx, qtx, input)
    if err != nil { return nil, err }
    tx.Commit()
    return result, nil
}

旧调用方完全不变。Phase 1 新 endpoint 自己 Begin,然后多次调 *InTx 变体,最后统一 Commit。

工作量:小(< 100 行重构)

4.2 [SOFT] Skill 同名冲突

问题:skill 表有 UNIQUE(workspace_id, name) 约束。Phase 1 模板导入时,如果模板里的 skill 跟 workspace 已有 skill 同名,INSERT 会报 PG 错误 23505,整个 from-template 流程挂掉。

影响范围:Phase 1

修复方案:加 find-or-create 模式:

  1. 新 query GetSkillByWorkspaceAndName(server/pkg/db/queries/skill.sql)
  2. Phase 1 流程改成:
    • 对每个模板 skill,先查 workspace 是否已有同名
    • 有 → 复用现有 skill_id,跳过 import
    • 无 → 调 createSkillWithFilesInTx 物化
  3. 响应里返回 reused_skill_ids: [...],前端 toast "以下 skill 已存在,沿用现有版本"

不选择"覆盖"或"加后缀"的原因:用户可能已经改过本地版本,覆盖会丢用户修改;加后缀污染 skill 列表。

工作量:小(< 50 行 + 1 条 sqlc query)

4.3 [SOFT] 缺 multica skill find CLI

影响范围:Phase 2

方案:加一个 CLI 子命令,模仿 multica skill import 的实现(server/cmd/multica/cmd_skill.go:55-60, 323-357)。注意:这个命令不发 HTTP 请求,只是 LLM agent 用来"输出推荐结果"的 channel——它把 LLM 推荐的 JSON 写到 daemon 指定的临时文件,daemon 读完塞进 inbox notification。

工作量:小(~80 行)

4.4 [SOFT] 缺 multica agent create CLI

影响范围:Phase 3

方案:后端 handler 已有(handler/agent.go:CreateAgent),只需在 server/cmd/multica/cmd_agent.gocreate 子命令。

工作量:小(~50 行)

4.5 [非 blocker] System Agent 问题

之前误判为 hard blocker,实际不是:

Quick-create Issue 当前的设计就要求用户预先有一个 agent 才能用——AI 路径不为"零 agent 起步"服务。Phase 2/3 沿用这个前提,所以新 workspace 没 agent 时 AI 功能不可用是符合现有产品模型的,不需要 bootstrap 一个 system agent。

产品自然解锁路径:

  1. 新用户进 workspace
  2. Phase 1 Template(无需 AI、无需现有 agent)创建第一个 agent
  3. 之后 Phase 2/3 即可用,host_agent 就用刚创建的那个

5. 关键设计决策(及理由)

5.1 为什么不接 Anthropic 官方 marketplace?

结构错配。Anthropic 官方 marketplace(anthropics/claude-plugins-official)是 plugin 体系:每个 plugin 是个 bundle,包含 .claude-plugin/plugin.json + skills/ + agents/ + hooks/ + .mcp.json

Multica 只有单体 skill(SKILL.md + skill_file),没有 plugin / bundle 概念。要接入得新写 plugin parser + 拆分逻辑,工作量大,而 skills.sh 已经覆盖了同一批高质量内容(skills.sh 后端就是 GitHub raw,绝大多数 skill 作者就在 GitHub 上,Anthropic plugin 体系里的 skill 通常也在作者的 GitHub repo 里有单体副本)。

5.2 为什么走 quick-create 模式而不是后端直接调 LLM?

代码事实:server/ 目前完全没有任何 LLM SDK(grep anthropic-sdk-go / openai-go / 任何 LLM provider 都是 0 命中)。所有 LLM 调用都通过 daemon → runtime → CLI 这条路。

走 quick-create 模式的优势:

  • 不引入新基础设施(SSE / LLM client / API key 管理)
  • 复用 agent 的 instructions / model / runtime 配置(用户已经在某个 agent 里配置过的偏好自动生效)
  • 统一计费 / 用量监控(LLM 调用都计在用户 agent 的 quota 里)

代价:

  • 用户得预先有一个 agent(参见 §4.5,这跟 Quick-create Issue 现状一致)
  • LLM 调用通过 daemon 多一跳,延迟略增(但不阻塞 202 响应)

5.3 为什么 Skill Finder 是 endpoint 不是 SKILL.md?

Skill Finder 名字里的 "Skill" 是它的产物(找的是 skill),不是它自己实现成 SKILL.md

如果做成 SKILL.md 文件:

  • 它得装进某个 agent 里才能用 → 单点功能变得需要前置配置
  • skill 教 agent 调什么?调 npx skills(装到本地,目标错)?调 Multica API(那要写 tool channel,绕一大圈)
  • AI 创建 Agent(Phase 3)那条路要"启动 agent → agent 调 skill → skill 调 tool",链路复杂三倍

做成 endpoint:

  • 用户独立可用(独立 UI 入口)
  • AI 创建 Agent 后端直接调 endpoint,两个功能共用一段逻辑
  • 简单

5.4 Curated Skill 索引 vs 实时搜索

MVP 用 curated 清单(几十条精选 URL + 摘要 commit 在 repo 里)。理由:

  • 质量可控
  • 不踩 GitHub Code Search rate limit
  • 不被 LLM 编 URL(LLM 知识 cutoff + hallucinate URL 是真问题)
  • 维护成本低

进阶可加 search_skills(query) tool 实时打 GitHub Code Search,等用户反馈"清单太窄"再做。

5.5 不做 ClawHub(顺手清理建议)

现状:POST /api/skills/import 当前支持 3 个 source(fetchFromClawHub skill.go:642-744、fetchFromSkillsSh skill.go:757-879、fetchFromGitHub skill.go:1363-1463)。ClawHub 是个独立 HTTP 客户端,不复用 GitHub 基础设施。

判断(详见之前讨论):

  • ClawHub 服务的是 OpenClaw 平台(Multica 同生态位竞品的内容生态)
  • UI 没有发现/搜索层,用户只能粘 URL,而 ClawHub 装机量远低于 skills.sh,用户主动逛的概率极低
  • 独立代码路径,API 演进时单独跟进

建议(独立于本计划,可以一起做也可以延后):

  • SELECT count(*) FROM skill WHERE config->'origin'->>'type' = 'clawhub' 看实际使用量
  • 接近 0 → 渐进下线(先去 UI SourceCard,后续 release 删 fetcher)
  • 有量 → 留着,但仍不为它做新功能

6. 实施依赖与排期

[Phase 1] Template
  └── 独立,无依赖
  └── 包含 2 个 soft blocker 的修复(§4.1 §4.2)
       ↓
[Phase 2] Skill Finder
  └── 依赖 Phase 1 中的 skill import 路径(已存在,沿用)
  └── 含 1 个 soft blocker(§4.3)
       ↓
[Phase 3] AI Create Agent
  └── 依赖 Phase 2(复用 find skill 能力)
  └── 含 1 个 soft blocker(§4.4)

真实排期建议:

  • Phase 1 可单独发版,有独立价值
  • Phase 2 独立可发版(找 skill 是高频独立场景)
  • Phase 3 等 Phase 2 ready 后开始

每个 phase 启动时单独开 PR 设计 doc,本文档只是路线图。


7. 风险与缓解

风险 缓解
GitHub rate limit(模板 import 多个 skill 时) 已有 GITHUB_TOKEN env 支持(skill.go:1163-1166),5000/h 配额够用。生产环境确保配置
模板里引用的 skill repo 被作者删除 from-template handler 容错:某个 skill fetch 失败 → 整个事务回滚,前端展示具体哪个 URL 挂了。模板自己也定期 review
LLM 推荐编造 URL(Phase 2) 用 curated 清单作为 context,不让 LLM 自由发挥 URL,推荐范围限定在清单内
Phase 3 LLM 写出离谱 instructions 用户在 inbox 通知里点击 → 跳新 agent 详情页编辑模式,不直接进入"已就绪"状态。用户必须确认
模板格式后续要演进(加字段) Template JSON 加 version 字段,后端按 version 兼容老格式
Curated skill 清单过时(作者改 repo / 删 skill) 加 CI 任务定期跑一遍清单 URL,挂掉的报警通知维护者

8. 不在本文档范围(已识别的下一步话题)

  • 跨 workspace 模板共享 / marketplace 化(用户能把自己的 agent 存成模板分享)
  • 实时 GitHub Code Search tool(Phase 2 进阶)
  • Server-side LLM 调用基础设施(如果未来需要 streaming 等场景)
  • ClawHub 下线决策(独立讨论,见 §5.5)
  • Skill 版本管理(workspace skill 版本号 / 升级提示)

附录 A:代码索引

给接手开发的同事的快速参考。每条 file:line 都在本计划里被引用过,记录在这里方便跳转。

主题 位置
Skill DB 模型 server/migrations/008_structured_skills.up.sql:4-32
Skill 创建 handler + 事务 server/internal/handler/skill.go:143-162 + skill_create.go:21-71
Skill import 入口(支持 3 个 source) server/internal/handler/skill.go:1538
Skill import source 分发 server/internal/handler/skill.go:586-617 (detectImportSource)
Skills.sh fetcher server/internal/handler/skill.go:757-879 (fetchFromSkillsSh)
GitHub fetcher server/internal/handler/skill.go:1363-1463 (fetchFromGitHub)
ClawHub fetcher server/internal/handler/skill.go:642-744 (fetchFromClawHub)
Agent 创建 handler server/internal/handler/agent.go:380-399 (request) + :422-564 (CreateAgent)
Agent 创建 sqlc server/pkg/db/queries/agent.sql:19-25
Agent-Skill 关联 sqlc server/pkg/db/queries/agent.sql:86-103
当前 Agent Duplication(前端模式) packages/views/agents/components/agents-page.tsx:286-301(post-create skill copy)
Agent 创建 dialog packages/views/agents/components/create-agent-dialog.tsx
Skill add dialog packages/views/agents/components/skill-add-dialog.tsx
Quick-create Issue handler server/internal/handler/issue.go:877-982 (QuickCreateIssue)
Quick-create task enqueue server/internal/service/task.go:488+ (EnqueueQuickCreateTask)
Daemon claim + load skills server/internal/handler/daemon.go:1018-1098 + service/task.go:1447-1463
Daemon prompt build server/internal/daemon/prompt.go:17-36 (dispatch) + :45-106 (buildQuickCreatePrompt)
Daemon execenv prepare server/internal/daemon/execenv/execenv.go:103-176
Skill 目录约定(runtime mapping) server/internal/daemon/execenv/context.go:121-158 (resolveSkillsDir)
Skill 文件落盘 server/internal/daemon/execenv/context.go:175-204 (writeSkillFiles)
Quick-create 完成检测 + inbox server/internal/service/task.go:1810-1949
LinkTaskToIssue server/internal/handler/agent.go:97-105
Quick-create Issue 前端 modal packages/views/modals/quick-create-issue.tsx:48-570+
Multica CLI 入口 server/cmd/multica/main.go:62-79
Skill CLI 命令 server/cmd/multica/cmd_skill.go:17-96(已有 import,无 find)
Agent CLI 命令 server/cmd/multica/cmd_agent.go:101-112(已有 list/get,无 create)