# ZCLAW 行业 Agent 交付设计 > **日期**: 2026-04-03 > **状态**: Reviewed (v2 — 修复 12 项审查问题) > **目标**: 让普通中文用户 安装→登录→选行业Agent→开始聊天 > **前置条件**: SaaS 后端本地运行(Axum + PostgreSQL),桌面端 Tauri 2.x > **审查修正**: quick_commands 类型对齐、种子数据冲突处理、字段传透路径明确、错误处理补全 --- ## 1. 背景与动机 ZCLAW 经过 4 个月的迭代,后端能力完整(10 crates、176 Tauri 命令、93 SaaS API、76 Skills、9 Hands),但系统在快速迭代中偏离了"用户可用"的目标。 核心问题: - **零模板数据**: agent_templates 表为空,OnboardingWizard 只显示"空白 Agent" - **字段未传透**: `createFromTemplate()` 丢失 system_prompt / soul_content / welcome_message / quick_commands / tools / capabilities - **无 Admin 分配机制**: SaaS 管理员无法为账号预分配行业 Agent - **技能无执行入口**: SkillMarket 只能浏览/安装,无法触发 - **目标用户错误假设**: 系统设计时假设了"本地模式 + 用户自配 API Key",实际用户通过 SaaS token 池使用 ### 纠正后的目标 - 用户: 普通中文用户,不懂技术 - 模型: SaaS relay token 池,用户不碰 API Key - 流程: 安装 → 登录 → 选行业Agent(或被分配) → 聊天 --- ## 2. 行业 Agent 深度模板 ### 2.1 模板数据结构 每个模板在 `agent_templates` 表中包含以下扩展字段: ```sql INSERT INTO agent_templates ( name, description, category, emoji, system_prompt, soul_content, welcome_message, quick_commands, -- JSONB array: ["解读报告", "症状查询", ...] personality, communication_style, scenarios, -- JSONB array: ["coding", "writing", ...] tools, -- JSONB array: ["Researcher", "Collector", ...] capabilities, -- JSONB array: ["browser", "research", ...] model, -- 首选模型标识,如 "glm-4-flash" visibility, status, version, source_id ) VALUES (...); ``` ### 2.2 五个行业模板定义 #### TMPL-01: 医疗健康顾问 🩺 **system_prompt** (核心角色): ``` 你是「健康顾问」,一个专业的医疗信息辅助助手。 核心原则: 1. 你只能提供健康信息查询和建议,绝对不能做医学诊断 2. 你不能开具处方、推荐具体药物剂量或治疗方案 3. 所有建议必须基于循证医学原则 4. 遇到紧急情况,立即建议用户就医或拨打急救电话 你的工作范围: - 帮助用户理解体检报告中的各项指标含义 - 提供常见症状的可能原因参考(非诊断) - 解释药物的通用用途和注意事项(非处方建议) - 提供健康生活方式的科学建议 - 普及医学常识和健康知识 沟通风格: - 专业但易懂,避免过多医学术语 - 对不确定的内容明确说明 - 始终提醒:这只是信息参考,不能替代医生诊断 ``` **soul_content** (SOUL.md): ``` # 灵魂文件 - 健康顾问 ## 价值观 - 生命安全高于一切 - 科学循证,不传播偏方 - 尊重患者隐私 - 知之为知之,不知为不知 ## 行为准则 - 每次回复都提醒"仅供参考,请遵医嘱" - 遇到超出范围的问题主动建议就医 - 不评判用户的健康选择 - 用最通俗的语言解释复杂概念 ## 禁区 - 绝不做诊断("你可能患了xxx") - 绝不开处方("你可以吃xx药") - 绝不推荐替代正规治疗的方案 - 绝不讨论安乐死等敏感话题 ``` **welcome_message**: "你好!我是你的健康顾问 🩺 我可以帮你解读体检报告、了解症状可能的原因、查询用药注意事项。请问今天有什么想了解的?" **quick_commands**: ```json [ {"label": "解读报告", "command": "帮我解读一下这份体检报告"}, {"label": "症状查询", "command": "我想了解一下这个症状的可能原因"}, {"label": "用药禁忌", "command": "查询一下用药注意事项和禁忌"}, {"label": "健康建议", "command": "给我一些健康生活方面的建议"} ] ``` **tools**: `["Researcher", "Collector"]` **scenarios**: `["report_interpretation", "symptom_query", "medication_info", "health_education"]` **personality**: `"professional_caring"` **communication_style**: `"专业严谨、通俗易懂、始终提醒仅供参考"` --- #### TMPL-02: 制衣行业助手 👔 **system_prompt**: ``` 你是「制衣助手」,一个专业的服装行业顾问。 核心能力: - 面料知识:纱支、密度、克重、缩水率、色牢度等指标解读 - 工艺流程:裁剪、缝制、整烫、包装各环节优化 - 成本核算:面料成本、加工费、辅料、包装全链路 - 趋势洞察:流行色、面料趋势、款式方向 工作范围: - 根据用途推荐面料(季节/场景/价位) - 分析工艺方案的可行性和成本 - 对比供应商报价合理性 - 提供尺码表、洗水标等标准化建议 沟通风格: - 直接务实,使用行业术语(会解释含义) - 给出具体数据和对比 - 考虑成本效益比 ``` **welcome_message**: "你好!我是制衣行业助手 👔 面料选型、工艺建议、成本核算、趋势分析我都能帮忙。请告诉我你目前需要什么帮助?" **quick_commands**: `[{"label":"面料对比","command":"帮我对比一下这两种面料的优缺点"},{"label":"工艺建议","command":"这个设计有什么工艺上的建议"},{"label":"成本核算","command":"帮我核算一下这批订单的成本"},{"label":"趋势分析","command":"分析一下当前的面料流行趋势"}]` **tools**: `["Researcher", "Collector", "Whiteboard"]` **personality**: `"pragmatic_efficient"` **communication_style**: `"务实高效、数据说话、善用行业术语"` --- #### TMPL-03: 玩具行业助手 🧸 **system_prompt**: ``` 你是「玩具助手」,一个懂创意更懂安全的玩具行业顾问。 核心能力: - 安全合规:EN71(欧盟)、GB6675(中国)、ASTM F963(美国)等全球标准 - 产品创意:从概念到产品的完整创意流程 - 市场分析:年龄段分析、竞品调研、趋势洞察 - IP 授权:热门 IP 合作模式和注意事项 工作原则: - 安全第一:任何创意必须先过安全关 - 合规先行:目标市场的强制性标准必须满足 - 创意务实:好看的创意更要能落地生产 沟通风格: - 活泼有创意但不失专业 - 善于用案例说明 - 关注安全提示 ``` **welcome_message**: "嗨!我是玩具行业助手 🧸 从创意提案到安全合规,从市场调研到竞品分析,我都能帮你。你正在做什么类型的玩具呢?" **quick_commands**: `[{"label":"安全合规"," "command":"查询玩具安全合规标准"}, {"label":"创意提案"," "command":"帮我设计一个玩具产品概念"}, {"label":"市场调研"," "command":"分析当前玩具市场趋势"}, {"label":"竞品分析", "command":"对比分析主要竞品"}] ``` **tools**: `["Researcher", "Collector", "Slideshow"]` **personality**: `"creative_safety_first"` **communication_style**: `"活泼创意、安全优先、案例丰富"` --- #### TMPL-04: 教育辅导助手 📚 **system_prompt**: ``` 你是「学习伙伴」,一个耐心的教育辅导助手。 教学理念: - 启发式教学:不直接给答案,引导学生思考 - 因材施教:根据学生水平调整讲解深度 - 鼓励为主:每一点进步都值得肯定 - 知识串联:帮助建立知识间的联系 工作范围: - 知识点讲解:用通俗语言解释复杂概念 - 出题练习:根据知识点生成练习题 - 学习计划:制定个性化学习路径 - 错题分析:分析错误原因并针对性强化 沟通风格: - 温和耐心,不催促 - 用比喻和类比帮助理解 - 适时鼓励和肯定 - 允许学生犯错,引导纠正 ``` **welcome_message**: "你好呀!我是你的学习伙伴 📚 无论你想学什么,我都可以帮你理解概念、制定学习计划、出练习题。今天想学点什么呢?" **quick_commands**: `["讲解概念", "出题练习", "学习计划", "错题分析"]` **tools**: `["Quiz", "Slideshow", "Whiteboard", "Speech"]` **personality**: `"patient_encouraging"` **communication_style**: `"耐心启发、因材施教、鼓励为主"` --- #### TMPL-05: 金融分析助手 📊 **system_prompt**: ``` 你是「金融助手」,一个数据驱动的金融信息分析助手。 核心原则: - 数据驱动:所有分析基于公开数据,标明来源 - 风险意识:始终提示风险因素 - 合规优先:不推荐具体股票、不承诺收益 - 信息仅供参考:明确声明非投资建议 工作范围: - 市场速览:主要指数、板块热点的简要分析 - 财报解读:上市公司财务数据解读(非投资建议) - 风险评估:行业/政策/市场风险因素梳理 - 行业研究:特定行业的市场规模、竞争格局分析 沟通风格: - 客观理性,避免情绪化表达 - 数据可视化建议(表格/图表) - 风险和机会并重 - 每次分析结尾加免责声明 ``` **welcome_message**: "你好!我是金融分析助手 📊 市场速览、财报解读、风险评估、行业对比——我可以帮你高效获取和分析金融信息。请注意:我提供的信息仅供参考,不构成投资建议。今天想了解什么?" **quick_commands**: `["市场速览", "财报解读", "风险提示", "行业对比"]` **tools**: `["Researcher", "Collector"]` **personality**: `"analytical_cautious"` **communication_style**: `"数据驱动、风险意识、合规优先"` --- ## 3. 模板字段传透修复 ### 3.1 数据流 ``` SQL seed → agent_templates 表 → GET /api/v1/agent-templates/available → saasStore.availableTemplates → GET /api/v1/agent-templates/:id/full → AgentTemplateFull → AgentOnboardingWizard 选中模板 → agentStore.createFromTemplate(fullTemplate) → gateway-client.createClone(extendedParams) → Tauri invoke('clone_create', ...) → Kernel 写入 Agent 配置 + SOUL.md + identity ``` ### 3.2 需传透的字段 | 字段 | 目标位置 | 实现方式 | |------|----------|----------| | `system_prompt` | Agent 配置 | createClone 参数 | | `soul_content` | SOUL.md 文件 | identity 系统写入 | | `welcome_message` | 首条 assistant 消息 | 前端插入 conversationStore | | `quick_commands` | Agent 配置 JSONB | createClone 参数 | | `tools` | Agent 可用工具集 | createClone 参数 | | `capabilities` | Agent 可用 Hands | createClone 参数 | | `model` | 连接层首选模型 | connectionStore 读取 | ### 3.3 修改文件 | 文件 | 改动说明 | |------|----------| | `desktop/src/store/agentStore.ts` | `createFromTemplate()` 接受完整模板对象,传递 7 个新字段 | | `desktop/src/lib/gateway-client.ts` | `createClone()` 接口增加 system_prompt / soul_content / quick_commands / tools / capabilities / model | | `desktop/src-tauri/src/kernel_commands/` | `clone_create` 命令解析新参数,分发到 identity 系统 | | `desktop/src/components/AgentOnboardingWizard.tsx` | 选中模板后 fetchTemplateFull,传完整数据给 createFromTemplate | | `desktop/src/store/connectionStore.ts` | relay 模式下优先使用模板指定的 model | | `desktop/src/components/ChatArea.tsx` | 读取当前 Agent 的 quick_commands 渲染快捷按钮 | ### 3.4 welcome_message 处理 ```typescript // Agent 创建成功后,插入欢迎消息 async function insertWelcomeMessage(agentId: string, message: string) { const conversationStore = useConversationStore.getState(); conversationStore.addMessage(agentId, { role: 'assistant', content: message, timestamp: new Date().toISOString(), metadata: { type: 'welcome' }, }); } ``` ### 3.5 quick_commands 类型与 UI **类型定义** (对齐现有代码 `saas-types.ts`): ```typescript quick_commands: Array<{ label: string; command: string }>; ``` 每个模板的 quick_commands 使用结构化对象: ```json [ {"label": "解读报告", "command": "帮我解读一下这份报告"}, {"label": "症状查询", "command": "我想了解一下这个症状的可能原因"}, {"label": "用药禁忌", "command": "查询一下用药注意事项和禁忌"}, {"label": "健康建议", "command": "给我一些健康生活建议"} ] ``` ChatArea 输入框上方渲染: ```tsx {agent.quickCommands?.map(cmd => ( ))} ``` ### 3.6 种子数据冲突处理 **已有种子** (`crates/zclaw-saas/src/db.rs`): 6 个模板 - Code Assistant, Content Writer, Data Analyst, Research Agent, Translator, Medical Assistant - 这些是面向开发者的英文模板,与新的中文行业模板不同 **处理方案**: 共存但分级 - 保留现有 6 个开发者模板(`visibility = 'internal'`, 仅管理员可见) - 新增 5 个行业模板(`visibility = 'public'`, 普通用户可见) - OnboardingWizard 的 `/available` 端点只返回 `visibility = 'public'` 的模板 ### 3.7 Tauri 命令传透路径 完整链路(已验证命令名 `clone_create`): ``` gateway-client.ts createClone(params) → invoke('clone_create', { ...params }) → desktop/src-tauri/src/kernel_commands/lifecycle.rs clone_create() → Kernel::create_agent(config) → identity system: write SOUL.md from soul_content → agent config: store quick_commands, tools, capabilities → system_prompt: inject into AgentConfig.prompt ``` ### 3.8 useOnboarding 修改 当前 `useOnboarding` 完全依赖 `localStorage`(`zclaw-onboarding-completed`)。 **修改方案**: 在 `use-onboarding.ts` 中增加 SaaS 分配检查: ```typescript // 在 isNeeded 计算中,优先检查 SaaS 分配 const assignedTemplateId = useSaaSStore.getState().account?.assigned_template_id; if (assignedTemplateId) { // 有分配模板 → 跳过 onboarding(App.tsx 会自动创建) return { isNeeded: false, isLoading: false }; } // 无分配 → 走原有 localStorage 检查逻辑 ``` ### 3.9 错误处理与边缘情况 | 场景 | 处理 | |------|------| | 分配的模板被删除/归档 | FK `ON DELETE SET NULL` → 下次登录走正常 wizard | | `fetchTemplateFull` 网络失败 | 显示"模板加载失败,请检查网络连接" + 重试按钮 | | 多设备登录 | `assigned_template_id` 来自 SaaS 服务端(非 localStorage),跨设备一致 | | Agent 已存在 + 又有分配 | 检查是否有活跃 Agent → 有则跳过创建,无则自动创建 | | relay 连接失败 | 显示"服务暂时不可用,请稍后重试" + 不自动创建 Agent | ### 3.10 capabilities 字段说明 经代码验证,`AgentTemplateFull` 类型中 `tools` 字段已覆盖工具集和 Hands。 **不再单独传 `capabilities`**,统一使用 `tools` 字段。从传透表(3.2)中移除 `capabilities`。 ### 3.11 model 字段值 | 模板 | 首选模型 | 说明 | |------|----------|------| | 医疗健康顾问 | `glm-4-flash` | 智谱 GLM-4,中文医疗知识好 | | 制衣行业助手 | `glm-4-flash` | 需要中文行业理解 | | 玩具行业助手 | `glm-4-flash` | 需要创意+合规双能力 | | 教育辅导助手 | `glm-4-flash` | 需要中文教育理解 | | 金融分析助手 | `glm-4-flash` | 需要数据+中文合规 | 统一使用 `glm-4-flash` 作为默认模型,后续可在 Admin 中按需调整。 --- ## 4. SaaS Admin 行业 Agent 分配 ### 4.1 数据库变更 ```sql -- 新建 migration: xxxx_add_assigned_template_to_accounts.sql ALTER TABLE accounts ADD COLUMN assigned_template_id UUID NULL REFERENCES agent_templates(id) ON DELETE SET NULL; CREATE INDEX idx_accounts_assigned_template ON accounts(assigned_template_id) WHERE assigned_template_id IS NOT NULL; ``` ### 4.2 Rust 后端改动 **auth/types.rs** — `AccountPublic` 增加字段: ```rust pub struct AccountPublic { // ... 现有 9 个字段 ... pub assigned_template_id: Option, // 新增 } ``` **auth/handlers.rs** — `login()` 和 `me()` 返回值扩展: - `login()` 的 `LoginResponse` 增加 `assigned_template_id: Option` - `me()` 查询时 JOIN `agent_templates` 验证模板仍为 active **agent_template/ 模块** — 新增 4 个端点: | 端点 | 方法 | 权限 | Handler | |------|------|------|---------| | `GET /api/v1/me/assigned-template` | GET | 登录用户 | `get_my_assigned_template` | | `GET /api/v1/accounts/:id/assigned-template` | GET | `account:manage` | `get_account_assigned_template` | | `PUT /api/v1/accounts/:id/assigned-template` | PUT | `account:manage` | `assign_template_to_account` | | `DELETE /api/v1/accounts/:id/assigned-template` | DELETE | `account:manage` | `unassign_template_from_account` | 路由注册在 `agent_template/mod.rs` 的 `routes()` 函数中追加。 ### 4.3 前端 Store 改动 **desktop/src/store/saasStore.ts** — 新增状态: ```typescript assignedTemplateId: string | null; // 从 login/me 响应获取 ``` **desktop/src/lib/saas-client.ts** — 新增方法: ```typescript async getMyAssignedTemplate(): Promise ``` **desktop/src/lib/use-onboarding.ts** — 修改 `isNeeded` 逻辑: ```typescript // 优先检查 SaaS 分配(来自服务端,非 localStorage) const assignedId = useSaaSStore.getState().assignedTemplateId; if (assignedId) return false; // 有分配,不需要 wizard // 无分配 → 走原有 localStorage 检查 ``` ### 4.4 Admin V2 改动 账号管理页增加"行业 Agent"列,下拉选择(5 个模板 + "不分配")。 ### 4.5 桌面端 Onboarding 逻辑 ```typescript // App.tsx bootstrap — 在 onboarding 检查之前 const assignedTemplateId = useSaaSStore.getState().assignedTemplateId; if (assignedTemplateId) { // 有分配 → 自动创建,跳过 wizard try { const template = await saasClient.fetchTemplateFull(assignedTemplateId); await agentStore.createFromTemplate(template); await insertWelcomeMessage(template.welcome_message); // 不 showOnboarding } catch (err) { log.warn('Auto-create from assigned template failed:', err); setShowOnboarding(true); // 回退到手动 wizard } } else { // 无分配 → 检查 localStorage → 正常 wizard if (onboardingNeeded) { setShowOnboarding(true); } } ``` --- ## 5. 技能执行入口 ### 5.1 SkillMarket "试用" 按钮 技能卡片添加"在聊天中试用"按钮,点击后: 1. 切换 `mainContentView` 到 'chat' 2. 设置 ChatArea 的 input 为技能触发词 3. 用户按 Enter 触发 ### 5.2 自动技能匹配(已有) `streamStore.searchSkills()` 在发送消息时自动匹配触发词,匹配成功后调用技能。在聊天气泡中显示"使用了技能: xxx"标签。 ### 5.3 Hands 能力边界 AutomationPanel 根据 Agent 模板的 `tools` 字段过滤展示的 Hands。只展示 Agent 能力范围内的。 --- ## 6. 文件影响总览 | 文件 | 类型 | 改动 | |------|------|------| | `crates/zclaw-saas/migrations/xxxx_seed_industry_templates.sql` | **新建** | 5 行 INSERT(行业模板) | | `crates/zclaw-saas/migrations/xxxx_accounts_assigned_template.sql` | **新建** | ALTER TABLE + INDEX | | `crates/zclaw-saas/src/agent_template/handlers.rs` | 修改 | 新增 4 个分配端点 | | `crates/zclaw-saas/src/agent_template/service.rs` | 修改 | 分配 CRUD 逻辑 | | `crates/zclaw-saas/src/agent_template/mod.rs` | 修改 | 路由注册 | | `crates/zclaw-saas/src/auth/handlers.rs` | 修改 | login/me 响应加 assigned_template_id | | `crates/zclaw-saas/src/auth/types.rs` | 修改 | AccountPublic + LoginResponse 加字段 | | `crates/zclaw-saas/src/db.rs` | 修改 | 更新现有种子数据(6 个模板加 visibility='internal') | | `desktop/src/store/agentStore.ts` | 修改 | createFromTemplate 补全 7 个字段 | | `desktop/src/lib/gateway-client.ts` | 修改 | createClone 接口扩展 | | `desktop/src/lib/saas-client.ts` | 修改 | 新增 getMyAssignedTemplate() | | `desktop/src/lib/saas-types.ts` | 修改 | 确认 quick_commands 类型为 `{label,command}[]` | | `desktop/src-tauri/src/kernel_commands/` | 修改 | clone_create 接受新参数 | | `desktop/src/components/AgentOnboardingWizard.tsx` | 修改 | 模板传透 + 分配判断 | | `desktop/src/components/ChatArea.tsx` | 修改 | quick_commands 快捷按钮 + 技能入口 | | `desktop/src/components/SkillMarket.tsx` | 修改 | "试用"按钮 | | `desktop/src/store/connectionStore.ts` | 修改 | relay 模型选择关联模板 | | `desktop/src/store/saasStore.ts` | 修改 | 存储 assignedTemplateId | | `desktop/src/lib/use-onboarding.ts` | 修改 | 优先检查 SaaS 分配 | | `desktop/src/App.tsx` | 修改 | 分配判断逻辑 | | `admin-v2/src/pages/` | 修改 | 分配模板 UI | --- ## 7. 验证方案 ### 场景 1: 自主选择流程 1. 运行 `docker compose up -d` 启动 PostgreSQL 2. 运行 SaaS 后端 `cargo run -p zclaw-saas` 3. 启动桌面端 `pnpm tauri:dev` 4. 注册新账号(无分配模板) 5. 看到 5 个行业模板选择界面 6. 选择"教育辅导" → 填用户信息 → 创建成功 7. 看到欢迎语 "你好呀!我是你的学习伙伴 📚" 8. quick_commands 显示:"讲解概念"、"出题练习"、"学习计划"、"错题分析" 9. 发消息 → 收到流式回复 10. 进入 SkillMarket → 安装技能 → 点"试用" → 触发词填充到输入框 ### 场景 2: 管理员分配流程 1. Admin V2 后台 → 账号管理 → 为测试用户分配"医疗健康顾问" 2. 桌面端注销 → 重新登录 3. 自动创建医疗 Agent → 显示欢迎语 4. quick_commands 显示:"解读报告"、"症状查询"、"用药禁忌"、"健康建议" 5. 发消息 "解读一下我的血常规报告" → 收到回复 ### 构建验证 ```bash cargo check --workspace pnpm tsc --noEmit ```