* 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>
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 重构(
createSkillWithFilesTX 拆分、skill 同名 find-or-create) - 不做:接入 Anthropic 官方 marketplace(plugin 体系跟单体 skill 形态不匹配)、接入 ClawHub(战略对位错误 + 实际使用率低,见 §5)
1. 背景与目标
1.1 当前现状
当前用户创建一个 Agent 需要走的步骤:
- 进
/agents页面 → 点 "Create Agent" - 手工填 name / description / runtime / model
- 手工写 instructions(空白文本框,用户自己思考措辞)
- 创建完后进 Agent 详情页 → 点 "Add Skill" → 一个一个挑 skill 关联
- 如果 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)
用户选模板时,后端:
- 对每个 skill 引用,复用现有
/api/skills/import的 fetcher(fetchFromSkillsSh/fetchFromGitHub)拉内容 - 物化到 workspace(同名复用 / 新建)
- CreateAgent + setAgentSkills
- 整个流程一个事务
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)
关键洞察:这个模式完全通用化了。复用它只需要:
- 新的 context JSONB type(比如
"skill-find"、"agent-create") - 新的 prompt builder
- 新的"完成检测 + 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} - 后端流程(全部在一个事务里):
- 加载 template JSON
- 对每个 skill source_url:
- 调用
detectImportSource(url)(skill.go:586-617)分发到对应 fetcher - 通过 GetSkillByWorkspaceAndName 检查 workspace 是否已有同名 skill
- 有 → 复用现有 skill_id
- 无 → 调
createSkillWithFilesInTx(待重构,见 §4)物化
- 调用
CreateAgent(复用 agent.go:CreateAgent 的内部逻辑)- 批量
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) - 后端流程:
- enqueue 任务:
agent_task_queue加一行,context JSONB ={type: "skill-find", prompt} - 返回 202 + task_id
- enqueue 任务:
- 请求:
- Daemon prompt builder:
daemon/prompt.go加buildSkillFindPrompt(类比 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}, ...]
- 喂给 agent 的 prompt 大致:
- 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 爬虫
- 几十到上百条精选 skill,每条:
- 完成通知:写 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 不必要)
- 独立"Find Skill"页面(
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_IDenv,服务端用它做 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 模式:
- 新 query
GetSkillByWorkspaceAndName(server/pkg/db/queries/skill.sql) - Phase 1 流程改成:
- 对每个模板 skill,先查 workspace 是否已有同名
- 有 → 复用现有 skill_id,跳过 import
- 无 → 调
createSkillWithFilesInTx物化
- 响应里返回
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.go 加 create 子命令。
工作量:小(~50 行)
4.5 [非 blocker] System Agent 问题
之前误判为 hard blocker,实际不是:
Quick-create Issue 当前的设计就要求用户预先有一个 agent 才能用——AI 路径不为"零 agent 起步"服务。Phase 2/3 沿用这个前提,所以新 workspace 没 agent 时 AI 功能不可用是符合现有产品模型的,不需要 bootstrap 一个 system agent。
产品自然解锁路径:
- 新用户进 workspace
- 用 Phase 1 Template(无需 AI、无需现有 agent)创建第一个 agent
- 之后 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) |