Commit bb9bdf66 authored by edy's avatar edy

chore: remove deprecated docs from tracking and add to .gitignore

Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent db1345d0
# 安装包瘦身回滚计划
## 当前结论
上一轮回滚不合格:它删除了 runtime cleanup,导致新安装包变成完整 payload,体积超过最近可用备份包。
- 最近可用基线:`dist/installer/backup/千匠问天-20260430-164341.exe`
- 基线大小:`538,315,023 bytes`
- 回滚后的异常包:`dist/installer/千匠问天-Setup-0.1.0.exe`
- 异常包大小:`596,924,319 bytes`
- 增量:`58,609,296 bytes`,约 `55.9 MiB`
## 回滚原则
瘦身失败时,必须回到最近可用小包基线,不能回到更大的完整 runtime payload。
允许保留:
- `ffmpeg/bin/ffmpeg.exe`
- `ffmpeg/bin/ffprobe.exe`
- `playwright-browsers/`
- 当前 runtime 必需 Python 依赖
必须恢复:
- runtime payload cleanup
- cleanup 统计写入 manifest
- OpenClaw docs 裁剪但保留 workspace templates
- Python tests、cache、pyc/pyo、source map 等无运行价值文件清理
## 修正方式
1. 恢复 `build/scripts/materialize-runtime-payload.ps1` 中 cleanup 相关逻辑。
2. 确认 `materializationInputs.schemaVersion` 回到 cleanup 对应版本。
3. 不修改 `apps/desktop/electron-builder.yml`,当前 `compression: store` 是既有状态。
4. 删除并重新生成 runtime payload。
5. 重新打包。
6. 对比新包大小,必须不大于 `538,315,023 bytes`
## 验证命令
```powershell
corepack pnpm materialize:runtime
corepack pnpm package
corepack pnpm typecheck
corepack pnpm smoke:ffmpeg-runtime
```
必要时再运行:
```powershell
corepack pnpm smoke:installer
```
## 接受标准
- `vendor/openclaw-runtime/runtime-manifest.json` 包含 `cleanupSummary``payloadStats`
- 新安装包大小 `<= 538,315,023 bytes`
- `ffmpeg.exe``ffprobe.exe` 仍在 runtime payload 中。
- 抖音视频/ffprobe 相关 smoke 不因瘦身缺少二进制而失败。
## 失败处理
如果新包仍然大于基线,不继续扩大回滚范围;先分析 manifest 的 `topLevelBreakdown``payloadStats`,定位新增体积来源后再补充 cleanup 规则。
排查结论:问题真实存在,根因在 workspace/xhs/workspace_entry.py:859。
事实链:
- 第一次确认后,状态是 awaiting_confirmation。
- 用户说“采集1条就行”不是“确认”,代码会在 workspace/xhs/workspace_entry.py:859 清掉原来的关键词/业务需求/目标人群,重新分类。
- 重新分类时,extract_collect_params("采集1条就行") 实际得到:
{"xhs_action":"collect","keywords":["1条就行"],"total_count":1}
- 所以它随后提示还缺 business_need、target_audience,再补充后就变成“关键词:1条就行”。
影响范围:只影响“确认页之后用户补充/修改采集数量”的多轮采集流程;不是采集执行、生成内容、飞书上传本身的问题。
建议修复点:awaiting_confirmation 状态下,如果用户输入的是“采集1条/改成1条/1条就行”,应合并到原 payload 的 total_count,保留原 keywords/business_need/target_audience,然后重新展示确认页。
## Summary
- 目标:用户在确认页后说“采集1条就行 / 改成3条 / 1条”时,只更新采集数量,不丢失已确认的关键词、业务需求、目标人群。
- 原则:不靠固定句子硬编码,基于“当前会话状态 + 字段级增量解析 + payload 合并”处理。
## Key Changes
- 在 awaiting_confirmation 状态下,除“确认/取消”外,不直接清空状态。
- 对用户新输入做字段级解析:
- 若能解析出 total_count,合并进原 payload。
- 若解析出关键词、业务需求、目标人群,也只覆盖对应字段。
- 未解析出有效字段时,再按现有逻辑结束当前确认状态并重新分类。
- 抽出通用增量解析函数,例如 extract_collect_updates(text):
- 支持数量表达:1条、采集1条、改成1条、一条、只要1条。
- 返回结构化字段,不返回完整新任务,避免把“1条就行”当关键词。
- 调整关键词提取规则:
- 采集动词后如果主体是数量/确认/修改语义,不作为关键词。
- 关键词仍通过原主题句或显式“关键词是...”提取。
## Risks
- 风险 1:过度合并,把用户的新任务误当成修改旧任务。
- 控制方式:只有当前状态是 awaiting_confirmation 且输入解析到明确字段修改时才合并。
- 风险 2:数量中文表达解析不全。
- 控制方式:复用现有中文数字解析 parse_ref_index 思路,扩展为通用 1-20 数量解析。
- 风险 3:影响确认后的重新发起任务。
- 控制方式:如果输入包含新的完整采集主题和关键词,则按新任务重新分类,不合并旧 payload。
- 风险 4:LLM 与本地正则结果不一致。
- 控制方式:确认态下优先使用本地字段级解析;LLM 只用于新任务分类,不用于覆盖完整 payload。
## Test Plan
- 复现原问题:
- 帮我采集 推荐上海平价川味火锅的笔记
- 种草引流涨粉 给热爱美食的人看
- 采集1条就行
- 期望确认页关键词仍是“推荐上海平价川味火锅”,采集数量为 1。
- 数量修改:
- 确认页后输入 改成3条
- 期望只更新 total_count=3。
- 直接确认:
- 确认页后输入 确认
- 期望行为不变,进入采集执行。
- 新任务覆盖:
- 确认页后输入 帮我采集北京烤肉笔记
- 期望按新任务重新进入参数确认,不沿用旧关键词。
- 回归:
- python -m py_compile workspace\xhs\workspace_entry.py
- corepack pnpm smoke:xhs-local-project-package
\ No newline at end of file
# 安装包旧 URL 残留与 `_greenlet` 运行时缺陷修复方案
## 1. 背景
当前安装包在 A 电脑上暴露出两类独立问题:
1. 运行时云端地址仍命中旧地址 `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`,导致绑定失败。
2. 删除缓存后,应用可以启动,但在专家页触发小红书专家链路时,报错 `DLL load failed while importing _greenlet: 找不到指定的模块。`
结合项目代码与日志,两个问题不是同一个根因:
- 旧 URL 问题来自本地配置项残留,不是 `runtime-cloud-cache.json` 回退。
- `_greenlet` 问题发生在专家页触发的 Python + Playwright 自动化链,不是启动阶段本身失败。
## 2. 现状结论
### 2.1 旧 URL 问题
- 当前默认运行时云端地址已经是新地址:
- `apps/desktop/src/main/services/app-config.ts`
- 但只要本地 `app-config.json` 中存在 `runtimeCloudApiBaseUrl`,运行时就优先使用配置值。
- 启动日志已经明确显示:
- `baseUrl = https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
- `baseUrlSource = config`
结论:
- A 电脑不是“删了缓存还在读缓存”,而是“本地配置还保留旧地址”。
- 仅删除 `runtime-cloud-cache.json` 不足以修复;还需要处理 `app-config.json` 中的旧值。
### 2.2 `_greenlet` 问题
- 第二份 startup log 显示应用最终进入 `ready`,说明启动链路本身已完成。
- 专家页小红书项目会进入 Python/Playwright 自动化链。
- 当前运行时探测和 installer smoke 只校验了顶层包能否导入,例如 `playwright`
- 但并没有显式验证:
- `greenlet`
- `from playwright.async_api import async_playwright`
- 专家页真实触发后的项目执行路径
结论:
- 当前安装包验证口径偏浅。
- 安装包即使通过现有 smoke,仍可能在真实专家执行链路里因 `_greenlet` 或其 DLL 依赖失败。
## 3. 修复目标
修复后需要满足以下结果:
1. 旧机器升级或重装后,不会继续使用废弃的 runtime cloud URL。
2. 新机器首次安装时,默认直接使用新地址,无需手工清缓存或改配置。
3. 安装包里的 Python 运行时必须能真实支持专家页小红书链路所需的 Playwright/greenlet 依赖。
4. installer 验证必须能在构建阶段拦截这类问题,而不是等到用户机器上暴露。
## 4. 修复方案
### 4.1 运行时云端地址迁移
`apps/desktop/src/main/services/app-config.ts` 增加已废弃 runtime cloud URL 的迁移逻辑:
- 定义废弃地址列表,至少包含:
- `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
- 在配置加载归一化时,如果 `runtimeCloudApiBaseUrl` 命中废弃地址,则自动替换为当前默认地址:
- `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
- 替换后立即持久化写回 `app-config.json`
约束:
- 只迁移“已知废弃地址”
- 其他用户手工填写的非空自定义地址仍保留
- 不依赖用户手工删除配置文件
同时补充诊断:
- 启动日志中记录一次配置迁移事件
- diagnostics 中保留运行时云端地址的最终值,并在可能的情况下标记发生过迁移
### 4.2 打包 Python 运行时补齐 `greenlet`
在运行时 Python 依赖定义中显式纳入 `greenlet`,而不是只依赖 `playwright` 的间接依赖关系:
- 更新:
- `build/runtime/python/requirements.in`
- `build/runtime/python/runtime-requirements.lock.txt`
- 确认 `build/scripts/materialize-runtime-payload.ps1` 物化后的运行时 payload 中实际包含对应依赖
目标:
- 安装包中的 `resources/vendor/openclaw-runtime/python` 具备完整依赖
- 不把 `greenlet` 缺失问题留给最终用户机器
如果最终确认 Windows 下还依赖额外系统 DLL 或 VC runtime,则需要二选一:
1. 能随安装包自带则一并处理
2. 不能自带则在运行时探测和错误提示里明确报缺少系统依赖
### 4.3 提升 Python 运行时探测粒度
修改 `packages/runtime-manager/src/index.ts`
- 在运行时探测清单中加入 `greenlet`
- 对关键模块不再只用 `find_spec`
- 至少对以下内容做真实导入探测:
- `import greenlet`
- `from playwright.async_api import async_playwright`
判定规则调整为:
- 只有关键依赖真实可导入时,`pythonReady` 才能为 `true`
- 如导入失败,错误信息要直接反映缺失模块或 DLL 问题
这样可以避免:
- 顶层包存在,但底层二进制扩展无法加载
- 系统显示 `pythonReady = true`,专家页一执行就崩
### 4.4 补强 installer smoke
修改 `build/scripts/installer-smoke.ps1`,把当前浅层导入校验升级为真实关键依赖校验。
现有检查仅覆盖:
- `import playwright`
应至少追加:
- `import greenlet`
- `from playwright.async_api import async_playwright`
若其中任意一步失败,则直接将安装包判定为:
- `payload-validation-failure`
同时在 smoke 输出中增加以下字段,便于回溯:
- `greenletImportProbe`
- `playwrightAsyncImportProbe`
- 对应 stderr 或异常消息
### 4.5 增加专家页真实链路 smoke
在现有 smoke 基础上新增一条安装包级别的真实专家链路验证:
- 启动已安装应用
- 进入 experts 页
- 选择小红书专家
- 发送一条最小测试消息
- 至少验证流程能进入对应 Python 自动化入口,不在 `_greenlet` 处崩溃
该 smoke 不要求真的发布内容,但要覆盖真实调用路径。
目标:
- 防止“安装包通过了基本 smoke,但真实专家链路仍不可用”
### 4.6 优化用户可见错误
当专家页项目执行失败时,错误呈现需要区分:
1. 启动失败
2. 项目执行失败
3. Python 运行时依赖缺失
改进方向:
- 主进程保留完整 stderr 到日志
- UI 不直接暴露生硬 Python 原始异常
-`_greenlet` / DLL 类错误归类为:
- 打包运行时依赖缺失或损坏
- 页面提示用户导出 diagnostics,而不是只看 startup log
## 5. 测试与验收
### 5.1 旧 URL 迁移验证
1. 人工构造包含旧 `runtimeCloudApiBaseUrl``app-config.json`
2. 启动应用
3. 确认配置被自动改写为新地址
4. 确认启动日志记录了迁移行为
5. 确认 runtime-cloud 请求实际打到新地址
### 5.2 Python 运行时验证
1. 在物化后的打包 Python 环境中执行:
- `import greenlet`
- `from playwright.async_api import async_playwright`
2. 验证 `pythonReady` 只有在两者都通过时才为 `true`
3. 人为破坏依赖后,确认系统能明确报错而不是误判 ready
### 5.3 安装包验证
1. 新机器或全新用户目录安装
- 应默认使用新 runtime cloud 地址
2. 带旧配置目录启动
- 应自动迁移旧地址
3. 运行 installer smoke
- 必须通过 `greenlet``async_playwright` 检查
4. 运行专家页真实链路 smoke
- 不应再出现 `_greenlet` 导入失败
## 6. 风险与默认决策
默认决策:
- 旧地址采用自动迁移,不要求用户手工处理
- `greenlet` 作为打包运行时显式依赖维护
- installer smoke 必须覆盖关键二进制依赖的真实导入
- 专家页真实链路增加单独 smoke,不再只依赖浅层 import 检查
当前假设:
- 当前有效 runtime cloud 默认地址保持为 `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
-`xuphfkscoptnjoaecbvn.supabase.co/functions/v1` 已视为废弃地址
- 小红书专家链路继续基于打包 Python + Playwright 运行
- 旧 URL 问题主要影响升级机或迁移机;`_greenlet` 问题则属于安装包本身质量缺口,新电脑也可能遇到
## 7. 后续执行顺序建议
建议按以下顺序落地:
1. 先修 `app-config` 的旧 URL 自动迁移
2. 再补齐 Python 打包依赖与运行时探测
3. 然后升级 installer smoke
4. 最后补上 experts 页真实链路 smoke 与错误提示优化
这样可以先消除旧机器必现问题,再封堵安装包在新机器上的真实运行风险。
# 2026-04-16 桌面端专家入口问题交接
## 背景
本轮处理的是桌面端以下问题:
1. 小红书和抖音专家页不显示
2. 内容账号规划专家、知乎专家是否为独立专家要按项目事实核查
3. 左侧栏多数专家点不了,要求除独立专家外其余专家点击后跳首页对话
4. 左侧专家列表过长,挤占会话管理,需要独立滚动
5. 首页对话发送“你好”仍路由到抖音,且消息马上消失
## 已完成
### 1. 修复首页快捷专家不可点击
文件:
- `apps/desktop/src/main/services/expert-catalog.ts`
改动:
- `promptAvailable` 由“仅检查 promptFile 是否存在”改为:
-`starterPrompt` 即可用
-`promptFile` 文件存在也可用
影响:
- 公众号专家、X 专家、Tiktok 专家、海报专家、GEO 专家、平台精准线索专家恢复可点击
### 2. 修复首页对话串到抖音、消息消失
文件:
- `apps/ui/src/App.tsx`
改动:
- `chat` 视图发送时,优先使用 `home-chat``sessionScopeProjectId`
- 不再优先使用 `workspace.currentProjectId`
- 新增 `openHomeChat()`,保证切回首页对话时同步归一到 `home-chat`
- 左侧“对话”导航点击改为走 `openHomeChat()`
- 首页快捷专家点击也改为走 `openHomeChat(true)`
影响:
- 首页对话不再把消息建进抖音项目
- 消息不会因为会话作用域错位而“发送后消失”
### 3. 恢复专家页真实项目来源
文件:
- `apps/ui/src/App.tsx`
改动:
- 新增 `buildProjectExpertDefinition()`
- 新增 `projectExpertEntries`
- `expertPageProjects` 不再只取 `standaloneExpertEntries`
- 改为基于真实 `visibleProjects` 生成专家页项目
影响:
- 仓库里真实存在的 `xhs` / `douyin` 项目可以重新进入专家页
- 左侧专家栏也改为优先显示真实项目专家,再拼首页快捷专家
### 4. 修复左侧栏滚动布局
文件:
- `apps/ui/src/styles.css`
改动:
- `sidebar-bottom` 改为两段式布局
- 新增 `.sidebar-expert-scroll`
- 新增 `.sidebar-session-section`
- 让专家区和会话区分别滚动
影响:
- 专家列表过长时,不再把会话管理区整体挤没
### 5. 补齐 smoke 类型定义
文件:
- `apps/ui/src/App.tsx`
改动:
- `SmokeUiSnapshot` 新增 `sessionScopeProjectId`
目的:
- 修复 `typecheck` 因 smoke 快照字段缺失导致的编译错误
## 已验证
已通过:
- `corepack pnpm typecheck`
- `powershell -ExecutionPolicy Bypass -File build/scripts/default-chat-smoke.ps1`
- `powershell -ExecutionPolicy Bypass -File build/scripts/project-routing-smoke.ps1`
- `powershell -ExecutionPolicy Bypass -File build/scripts/desktop-expert-entry-smoke.ps1 -SkipMaterializeRuntime`
`desktop-expert-entry-smoke` 已覆盖:
- `content-account-planning`
- `zhihu`
- `wechat-official-account`
- `x-platform`
- `tiktok`
- `poster`
- `geo`
- `precision-leads`
## 关键事实结论
### 1. 小红书 / 抖音专家页消失的主因
不是项目没了,是前端把专家页数据源改成只看 `standalone` 专家定义,导致真实项目 `workspace/xhs``workspace/douyin` 被过滤掉。
### 2. 其他专家点不了的主因
不是按钮组件坏了,是可用性判定错了。首页快捷专家只有 `starterPrompt`,没有 `promptFile`,因此被错误判成不可用。
### 3. 内容账号规划专家 / 知乎专家当前状态
按仓库事实,它们当前仍不是“仓库内已落地的真实独立专家项目”。
说明:
- 当前仓库 `workspace/` 下没有它们对应真实项目目录
- 但 smoke 已经证明:当用户数据里存在对应项目时,UI 入口和会话隔离链路可以工作
结论:
- 现在更准确的说法是:入口机制和隔离路由已支持
- 但仓库内真实项目包还没补齐
## 当前未完成
### 1. 肉眼页面确认未完成
原因不是代码未生效,而是本机存在一个旧的 Electron 进程:
- `PID 20424`
表现:
- 我已执行 `corepack pnpm build`
- 新的 renderer 已成功输出到 `apps/desktop/dist/renderer`
- 但旧进程无法被我当前环境结束,导致用户看到的仍可能是旧界面
### 2. 旧进程状态
用户侧执行过:
```powershell
taskkill /PID 20424 /F /T
```
结果:
- 失败
- 提示该进程属于父进程 `23116` 的子进程
- 原因是 `拒绝访问`
结论:
- 最稳妥处理是重启机器
## 已完成但需要用户重启后确认的点
重启后需要肉眼确认:
1. 左侧或专家页是否能看到“小红书专家”“抖音专家”
2. 点击公众号/X/Tiktok/海报/GEO/平台精准线索,是否跳转首页对话,而不是进入专家页
3. 首页发送“你好”,是否仍会跑到抖音
4. 发送后的消息是否还会消失
5. 左侧专家区和会话区是否都可以独立滚动
## 重启后继续执行顺序
1. 先启动桌面端
2. 先看小红书 / 抖音专家是否出现
3. 再测首页快捷专家跳转是否正确
4. 再测首页发送“你好”
5. 若页面仍不对,先确认是否真的是新进程和新包
## 如果重启后仍异常,优先检查
### A. 是否加载了新 renderer
重点看:
- `apps/desktop/dist/renderer/index.html`
- `apps/desktop/dist/renderer/assets/index-*.js`
### B. 是否仍有旧 Electron 残留
命令:
```powershell
Get-Process | Where-Object { $_.ProcessName -match 'electron|QianjiangClaw' } | Select-Object Id,ProcessName,Path
```
### C. 如果页面还是没有小红书 / 抖音专家
继续检查:
- `workspace.getSummary()` 返回的 `workspace.projects`
- `visibleProjects`
- `projectExpertEntries`
- `expertPageProjects`
## 相关文件
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/desktop/src/main/services/expert-catalog.ts`
## 备注
本轮没有继续接“龙虾密钥真实绑定态页面验证”,因为用户当前首先反馈的是“小红书和抖音专家在页面上仍没看到”,而该现象与旧 Electron 残留更直接相关。后续等重启后再继续做真实页面 smoke。
# 2026-04-17 客户端模型配置打通进度
## 目标
让客户在桌面客户端自行配置项目所使用的模型信息,不依赖项目包内固定写死;项目从云端配置接口拉取 zip 并解压后,实际执行时由客户端把模型配置注入到项目运行时。
## 已完成
### 1. 客户端配置结构已支持 `modelId`
- 已扩展共享类型,`expertModelConfig.image/copywriting/video` 均支持 `modelId`
- 已扩展桌面端配置持久化与归一化逻辑,保存后不会丢失 `modelId`
- 已扩展 IPC 返回值,前端加载配置时可以拿到 `modelId`
涉及文件:
- [packages/shared-types/src/index.ts](D:/qjclaw/packages/shared-types/src/index.ts)
- [apps/desktop/src/main/services/app-config.ts](D:/qjclaw/apps/desktop/src/main/services/app-config.ts)
- [apps/desktop/src/main/ipc.ts](D:/qjclaw/apps/desktop/src/main/ipc.ts)
### 2. 客户端设置页已可配置项目模型
- 已在设置页增加图片模型 `model_id` 输入
- 已在设置页增加文案模型 `model_id` 输入
- 保存设置后,冒烟态和配置回显会校验 `modelId`
涉及文件:
- [apps/ui/src/App.tsx](D:/qjclaw/apps/ui/src/App.tsx)
### 3. 项目运行前注入逻辑已打通
- 新增项目运行时模型注入服务
- 在执行 `workspace_entry.py` 前,根据当前项目 ID 和客户端已保存配置生成 `memory/project.env`
- 同时把生成后的环境变量注入子进程,确保项目执行链路可直接读取
当前已支持:
- `xhs`
- `QWEN_BASE_URL`
- `QWEN_API_KEY`
- `QWEN_MODEL`
- `QWEN_VISION_MODEL`
- `XHS_IMAGE_PROVIDER=env:xhsImage`
- `XHS_IMAGE_BASE_URL`
- `XHS_IMAGE_API_KEY`
- `XHS_IMAGE_MODEL`
- `douyin`
- `DOUYIN_WRITER_LLM_BASE_URL`
- `DASHSCOPE_API_KEY`
- `QWEN_API_KEY`
- `DOUYIN_WRITER_LLM_MODEL`
- `SEEDREAM_ARK_BASE_URL`
- `SEEDREAM_ARK_API_KEY`
- `SEEDREAM_MODEL`
涉及文件:
- [apps/desktop/src/main/services/project-model-runtime.ts](D:/qjclaw/apps/desktop/src/main/services/project-model-runtime.ts)
- [apps/desktop/src/main/services/project-workspace-executor.ts](D:/qjclaw/apps/desktop/src/main/services/project-workspace-executor.ts)
- [apps/desktop/src/main/ipc.ts](D:/qjclaw/apps/desktop/src/main/ipc.ts)
### 4. 打包链路和端到端冒烟已验证通过
- `corepack pnpm typecheck` 通过
- `corepack pnpm package` 通过
- 安装器冒烟通过
- `xhs` 云端 zip -> 解压 -> 激活项目 -> workspace-entry -> `project.env` 注入 冒烟通过
- `douyin` 云端 zip -> 解压 -> 激活项目 -> workspace-entry -> `project.env` 注入 冒烟通过
结果文件:
- [installer-smoke-result.json](D:/qjclaw/.tmp/installer-smoke/20260416-221908/installer-smoke-result.json)
- [xhs result.json](D:/qjclaw/.tmp/xhs-expert-cloud-bundle-smoke/result.json)
- [douyin result.json](D:/qjclaw/.tmp/douyin-expert-cloud-bundle-smoke/result.json)
对应冒烟脚本:
- [build/scripts/installer-smoke.ps1](D:/qjclaw/build/scripts/installer-smoke.ps1)
- [build/scripts/xhs-expert-cloud-bundle-smoke.ps1](D:/qjclaw/build/scripts/xhs-expert-cloud-bundle-smoke.ps1)
- [build/scripts/douyin-expert-cloud-bundle-smoke.ps1](D:/qjclaw/build/scripts/douyin-expert-cloud-bundle-smoke.ps1)
- [apps/desktop/src/main/index.ts](D:/qjclaw/apps/desktop/src/main/index.ts)
## 当前未完成
### 1. 视频模型链路暂未纳入本次范围
- 当前只重点打通了图片模型和文案模型
- `video.modelId` 类型和配置持久化已预留
- 但视频模型的运行时注入、前端独立配置项联动、项目实际消费逻辑,本次未继续展开
### 2. 目前是“客户端本地配置生效”,不是“云端统一下发模型配置”
- 现在已经支持客户在本机客户端配置模型并立即作用于项目执行
- 但还没有把这份模型配置并入远端配置中心做统一下发
- 如果后续要做多端一致、换机继承、后台代运营配置,需要再单独设计云端配置存储和同步
### 3. 还没有单独补业务说明文档
- 当前代码和烟测已齐
- 但还没有补一份给实施/运维/客户的“字段含义、支持项目、配置示例、常见错误”操作文档
## 结论
当前“客户在客户端配置模型 -> 项目从云端拉 zip -> 客户端解压并执行 -> 执行前注入模型环境变量”主链路已经打通,并已通过打包后的端到端冒烟验证。
如果下一步继续推进,优先级建议是:
1. 补一份面向实施和客户的配置说明文档
2. 决定是否要把模型配置接入云端配置中心
3. 视业务需要再补视频模型链路
# 2026-04-21 桌面单实例续接记录
## 目标
本轮要落实的口径是:
- 已有客户端在运行时,再次启动不应继续跑第二个主流程
- 首实例应恢复/显示/聚焦原窗口
- 不应新增一轮 runtime/gateway 启动或冲突日志
对应自动验证命令:
```powershell
corepack pnpm smoke:desktop-single-instance
```
## 已完成
代码侧已落地以下改动:
- `apps/desktop/src/main/index.ts`
- 增加 `app.requestSingleInstanceLock()`
- 增加 `second-instance` 处理
- 增加主窗口跟踪与唤回逻辑:
- `mainWindow`
- `focusMainWindow()`
- `restoreOrCreateMainWindow()`
- `createTrackedMainWindow()`
- 增加单实例 smoke 辅助状态与输出:
- `QJCLAW_SMOKE_SINGLE_INSTANCE`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_READY_PATH`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_EVENT_PATH`
- `runSingleInstanceSmoke()`
- `secondInstanceEventCount`
- `lastSecondInstanceEventSnapshot`
- `activate` 事件改为统一走 `restoreOrCreateMainWindow("activate")`
- `build/scripts/desktop-single-instance-smoke.ps1`
- 已新增单实例 smoke 脚本
- 流程是:
- 起第一个 Electron 实例
- 等首实例写 `ready`
- 起第二个 Electron 实例
- 校验第二个实例快速退出
- 校验首实例收到 `second-instance`
- 校验窗口数仍为 `1`
- 校验 `Launching bundled runtime command` 次数不增加
- 校验日志中没有:
- `gateway already running`
- `Port 18889 is already in use`
- `schtasks /End /TN`
- `package.json`
- 已新增脚本:
- `smoke:desktop-single-instance`
- `build/scripts/README.md`
- 已登记 `desktop-single-instance-smoke.ps1`
## 已验证
- 已通过:
```powershell
corepack pnpm typecheck
```
结果:通过。
## 当前阻塞
自动 smoke 目前还没有跑通,阻塞点不是 TypeScript 编译,而是本机环境残留了多个无法在当前权限下结束的 `electron.exe`
已观察到的残留 PID:
- `10420`
- `19736`
- `23484`
- `23564`
- `23588`
- `23820`
- `24384`
现象:
- `corepack pnpm smoke:desktop-single-instance` 失败
- 失败点是首实例迟迟没有写出 `ready` 文件
- 手工尝试结束残留进程时,`taskkill` / `Stop-Process` 都报 `Access is denied`
- 在残留进程存在时,单实例锁环境不干净,自动验证结果不可信
失败输出里最关键的信息是:
- `First Electron instance never reported single-instance readiness.`
相关失败产物路径:
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\startup\startup-latest.log`
## 已尝试但未完成
已执行过这些命令,但因为权限问题未能清掉残留进程:
```powershell
taskkill /IM electron.exe /F /T
```
```powershell
Stop-Process -Id 10420,19736,23484,23564,23588,23820,24384 -Force
```
校验残留进程的命令:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
当前结论:
- 代码改动已经在仓库里
- 当前最先要解决的是机器上的残留 Electron 进程
- 在这个问题解决前,不要把 smoke 失败直接归因到单实例代码本身
## 下次继续时先做什么
优先顺序:
1. 先清掉所有残留 `electron.exe`
2. 再确认 `Get-Process electron -ErrorAction SilentlyContinue` 没有输出
3. 再跑单实例 smoke
4. 若 smoke 仍失败,再根据 `.tmp` 里的 trace/log 继续定位
推荐命令:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
如果仍杀不掉,直接重启机器。
重启或清理完成后先跑:
```powershell
corepack pnpm smoke:desktop-single-instance
```
如果还失败,再补跑:
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
目的:
- `desktop-single-instance-smoke` 用来验证单实例口径本身
- `startup-binding-smoke` 用来判断是不是更基础的桌面启动链路也有问题
## 继续排查时要看的文件
- `D:\qjclaw\apps\desktop\src\main\index.ts`
- `D:\qjclaw\build\scripts\desktop-single-instance-smoke.ps1`
- `D:\qjclaw\package.json`
- `D:\qjclaw\build\scripts\README.md`
失败时优先看:
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\startup\startup-latest.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\runtime-manager.log`
## 当前工作区事实
和这次单实例相关的工作区改动包括:
- `apps/desktop/src/main/index.ts`
- `build/scripts/desktop-single-instance-smoke.ps1`
- `package.json`
- `build/scripts/README.md`
其中:
- `build/scripts/desktop-single-instance-smoke.ps1` 当前是新增文件
- 其余几个文件已有未提交改动
## 一句话结论
单实例代码和 smoke 脚本已经落地,`typecheck` 已通过;下次续接前先解决本机残留的高权限 `electron.exe`,否则自动验证不会有可信结果。
## 2026-04-21 最新进展补充
### 本轮已继续完成的代码侧工作
- `apps/desktop/src/main/index.ts`
- 已把单实例入口继续接完整:
- `app.requestSingleInstanceLock()`
- `second-instance` 接管并聚焦现有主窗口
- `mainWindow` 跟踪
- `focusMainWindow()`
- `restoreOrCreateMainWindow()`
- `createTrackedMainWindow()`
- 已补单实例 smoke 专用信号:
- `QJCLAW_SMOKE_SINGLE_INSTANCE`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_READY_PATH`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_EVENT_PATH`
- `runSingleInstanceSmoke()`
- 已补窗口状态与日志辅助信息:
- `secondInstanceEventCount`
- `lastSecondInstanceEventSnapshot`
- `mainWindowLoadState`
- `window.load-state` 启动日志
- 已在 smoke 模式下加:
- `app.disableHardwareAcceleration()`
- `build/scripts/desktop-single-instance-smoke.ps1`
- 已去掉对 `Start-Process` 返回 PID 的“提前失败”判断
- 现在不会因为 Electron 首个 PID 变化而误判首实例退出
### 本轮已执行的验证
已执行并通过:
```powershell
corepack pnpm --filter @qjclaw/desktop build
```
```powershell
corepack pnpm typecheck
```
已执行但失败:
```powershell
corepack pnpm smoke:desktop-single-instance
```
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
### 关键新结论
- 当前问题已经不能再只归因到“单实例 smoke 本身”
- `startup-binding-smoke` 也失败了,说明更底层的桌面启动链路本身就不稳定
- `startup-binding-smoke` 的失败点是:
- Electron 还没写出 smoke output 就退出了
- 退出码是 `-2147483645`
- `desktop-single-instance-smoke` 仍然失败,但现在可以确认:
- 不再是脚本盯着首个 PID 导致的误判
- 更像是首实例窗口/渲染链路没稳定进入可用状态
### 这轮看到的最关键日志现象
- 单实例 smoke 的 trace 只走到:
- `runSingleInstanceSmoke:start`
- 基础启动 smoke 的 trace 走到:
- `runSmokeTest:start`
- `runSmokeTest:renderer-loading`
- `runSmokeTest:loading-renderer-state`
- `startup-latest.log` 里,主窗口 load-state 目前只看到:
- `created`
- 没有继续看到:
- `did-start-loading`
- `dom-ready`
- `did-finish-load`
- `did-fail-load`
- `render-process-gone`
这说明:
- 主窗口创建是发生了的
- 但后续页面加载事件没有稳定落到日志里
- 现象更接近 Electron / Chromium 级别的启动异常或系统态残留干扰
### 运行时相关补充观察
- `runtime-manager.log` 里持续可见:
- `Launching bundled runtime command`
- `Bundled runtime direct spawn was blocked with EPERM; retrying via PowerShell wrapper.`
- `Bundled runtime failed to spawn: spawn EPERM`
这不是本轮唯一问题,但说明当前环境里还有额外权限限制噪音,不能把 smoke 失败简单归因到单实例代码。
### 当前机器状态的新结论
当前机器上确认有一颗高权限残留 `electron.exe`
- PID:`7216`
- Parent PID:`12892`
- 但父进程已经不存在
- `Get-Process` 结果里:
- `HandleCount = 0`
- `Path` 为空
- `StartTime = 2026/4/21 15:30:31`
已尝试但仍失败:
```powershell
taskkill /PID 7216 /F /T
```
```powershell
Stop-Process -Id 7216 -Force
```
以及通过 `schtasks``SYSTEM` 身份触发 `taskkill`
当前判断:
- 这颗进程已经接近“残留/僵尸进程”
- 继续在当前系统态下跑 smoke,结果不可信
- 最稳妥做法仍然是先重启机器
### 更新后的下次继续顺序
1. 先重启机器,清掉 PID `7216`
2. 重启后先确认:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
3. 如果没有输出,先跑:
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
4. 如果基础启动 smoke 通过,再跑:
```powershell
corepack pnpm smoke:desktop-single-instance
```
5. 如果基础启动 smoke 仍失败,优先看:
- `D:\qjclaw\.tmp\startup-binding-smoke\result.json`
- `D:\qjclaw\.tmp\startup-binding-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\startup-binding-smoke\logs\startup\startup-latest.log`
- `D:\qjclaw\.tmp\startup-binding-smoke\logs\runtime-manager.log`
### 当前这轮最准确的一句话结论
单实例代码和单实例 smoke 已继续补强,但新的验证结果表明当前主要阻塞已经下沉到更底层的 Electron 启动链路与机器残留进程状态;在重启清掉高权限残留 `electron.exe` 之前,继续跑 smoke 不会有可信结果。
This diff is collapsed.
# 2026-04-24 抖音客户端配置接入进度
## 2026-04-25 当前状态
截至 2026-04-25,抖音项目这一轮的主结论如下:
- 客户端真实配置注入链路已打通,运行时以客户端配置为主口径
- `workspace_entry.py` 的 pending intake / continue 主链路已恢复到可验证状态
- `VectCut` 已从本轮阻断项中移除,不再作为抖音运行时必填
- 客户端聊天附件入口已从“仅图片”扩展为“图片 + 通用资料文件”
- PDF 附件已在桌面 smoke 中完成真实落盘验证
当前更准确的表述不是“只差 UI 验收”,而是:
- 配置接入、运行时注入、pending intake 回归、通用附件上传已经完成并有验证
- 下一阶段仍然是客户端真实聊天验收
## 本轮新增完成项
### 1. 客户端真实配置接入
- 客户端设置保存后,抖音项目运行时改为从客户端配置注入 `project.env`
- `QJC_CLIENT_CONFIG_ACTIVE=1` 已在 smoke 中确认
- 抖音相关模型配置已覆盖:
- `VIDEO_LLM_ANALYZER_*`
- `REPLICATION_BRIEF_*`
- `SEEDANCE_*`
- `VOLC_*`
- `QINIU_*`
- `VectCut` 不再是本轮闭环前置阻断项
### 2. Pending Intake 回归修复已稳定
- `run_intake()` 已恢复为可调用主路径
- `workspace_entry.py` 的 pending intake / continue 逻辑已重新验证
- 当前不再继续主动改 `run_from_request.py` / `coordinator.py` 主业务编排
### 3. 客户端通用附件上传已接入
- 共享类型 `ChatAttachment.kind` 已从仅 `image` 扩展为 `image | file`
- 桌面端新增统一附件选择接口 `pickAttachments()`
- 当前聊天附件支持:
- 图片:`png/jpg/jpeg/webp/gif/bmp`
- 文档资料:`pdf/ppt/pptx/xls/xlsx/csv/tsv/doc/docx/txt/md/json`
- 图片继续落到 `inputs/images/main`
- 文档资料落到 `inputs/assets/manual`
- 聊天输入支持多附件发送
### 4. PDF 真实样本 smoke 已通过
本轮使用以下真实样本完成验证:
- `C:\Users\EDY\Desktop\douyin-example\2025欧文酵室招商手册(1).pdf`
已确认的事实:
- smoke 中附件类型为 `file`
- PDF 被真实落盘到项目目录:
- `projects/douyin/inputs/assets/manual/...pdf`
- assistant 回复中已包含 `Project attachments:` 回显
- 说明附件没有停留在 UI 层,而是实际进入了抖音项目工作区
## 已完成范围
- 客户端抖音模型配置读写、保存、注入
- 抖音运行时 `project.env` 注入
- `workspace_entry.py` 的 pending intake / continue 适配
- `run_intake()` 回归修复
- `VectCut` 非阻断化
- 客户端聊天附件从“仅图片”扩展到“通用资料文件”
- PDF 资料落盘到 `inputs/assets/manual`
- smoke 对真实配置和真实 PDF 的验证
## 当前未完成范围
- Pending Intake UI 手工闭环验收
- 三条业务链路聊天闭环验收
- OmniHuman / Seedance 聊天闭环验收
- 验收过程中暴露问题后的针对性回修
## 下一步顺序
1. Pending Intake UI 闭环验收
2. `research_replication + crawler``specified_sample + link``direct_creation + none` 三条链路聊天闭环验收
3. OmniHuman / Seedance 聊天闭环验收
4. 若验收暴露问题,再做最小范围回修
## 边界
- 不主动重写 `run_from_request.py` 三条主链路逻辑
- 不主动重写 `coordinator.py` 主业务编排
- `VectCut` 继续排除在本轮主线通过门槛之外
## 验证记录
- 2026-04-25:`python workspace/douyin/test_intake_integration.py` 28/28 通过
- 2026-04-25:`python workspace/douyin/test_pending_intake.py` 68/68 通过
- 2026-04-25:`corepack pnpm typecheck` 通过
- 2026-04-25:`corepack pnpm build` 通过
- 2026-04-25:`build/scripts/douyin-expert-cloud-bundle-smoke.ps1 -SettingsConfigPath "C:\Users\EDY\Desktop\龙虾密钥&模型配置信息.txt" -AttachmentFixturePath "C:\Users\EDY\Desktop\douyin-example\2025欧文酵室招商手册(1).pdf" -SkipMaterializeRuntime`
- 真实配置注入通过
- PDF 附件落盘通过
- assistant `Project attachments:` 回显通过
- 首次运行仍偶发 `Gateway connection closed (1012)`,重试后通过
## 2026-04-26 最新进度
### 已验证通过
- 客户端真实聊天链路中,上传 PDF 并描述需求后,可以走通纯画面 Seedance 视频生成。
- 已修复 Seedance split 最终视频 0 秒 / 缺音轨问题:
- `run_pipeline.py` 在无 `ffprobe` 时改用 `ffmpeg -i` 兜底解析媒体时长。
- audio compose 前后增加最终产物校验,避免 0 秒视频被写成 success。
- `workspace_entry.py` 已支持读取 `results.video_seedance.manifest` 内的嵌套最终视频字段,并在必要时用 `final_video_concat.mp4 + narration_audio_path` 兜底重新 mux。
- 已用坏产物目录验证修复:
- `E:\testworkspace2\projects\douyin\output\招商\seedance-split-runs\20260426-184026`
- 修复后 `final_video.mp4` 时长约 30 秒,包含视频流和音频流。
- `latest_seedance_split.mp4` 已同步更新。
- `D:\qjclaw\workspace\douyin` 已重新打包上传到云端配置。
- 本地开发版客户端已重启,`5173` 和内置网关 `18889` 正常监听。
### 当前仍有问题
- 数字人口播链路仍待排查。
- 上传音频 + 数字人图片生成链路仍待排查。
- 输入抖音链接并走样本/复刻相关链路仍待排查。
- Seedance 平台返回 `OutputVideoSensitiveContentDetected` 时,目前按平台拒绝生成处理,只提示用户调整需求,不做自动改写 prompt。
### 下一步建议
1. 先验证数字人口播:确认音频、数字人图片、模型配置、输出目录和失败日志。
2. 再验证“传音频 + 数字人图片”链路:区分是附件落盘、参数传递、OmniHuman 调用还是后处理失败。
3. 最后验证抖音链接链路:重点看 crawler/downloader/analyzer/selected sample continue 是否在客户端聊天里闭环。
\ No newline at end of file
# 2026-04-27 小红书客户端接入适配方案
## 结论
小红书项目当前真实源码目录是 `D:\qjclaw\workspace\xiaohongshu-client-package`,不是 `D:\qiclaw\...`
该目录目前更接近原始客户端交付包,不是 qjclaw 可直接执行的 workspace 项目。后续接入应新建或迁移到 `D:\qjclaw\workspace\xhs`,并固定项目 ID 为 `xhs`
## 当前项目事实
- 源项目存在于 `workspace\xiaohongshu-client-package`
- 当前缺少 qjclaw 必需的 `project.json`
- 当前缺少 qjclaw 直接执行入口 `workspace_entry.py`
- 当前没有输出 qjclaw 可识别的 `QJC_WORKSPACE_EVENT\t...` 事件协议。
- 当前没有声明 `boundSkillIds`,客户端无法按项目过滤并展示小红书相关技能。
- 当前没有 qjclaw bundle 排除规则,`.env`、运行数据、缓存、生成物和本地调试产物需要显式排除。
已有入口和能力:
- `agent_server.py`:FastAPI 后端,默认监听 `8888`,暴露 `/status``/api/crawler/start``/api/checkpoints``/api/generate``/api/result/latest` 等接口。
- `main.py`:CLI/采集主流程,支持关键词、业务需求、目标人群、主题、数量等参数。
- `regen_v2.py``generate_content.py``print_latest_result.py`:基于已有采集结果生成内容和读取最新结果。
- `requirements.txt`:声明 Python runtime 依赖。
- `CLIENT_INTEGRATION.md`:说明当前项目可用 FastAPI 或 CLI 集成,但这不是 qjclaw workspace 协议。
## 缺口
小红书项目要进入 qjclaw 客户端执行链,至少需要补齐:
- `project.json`
- `workspace_entry.py`
- `workspaceAutomation`
- `defaultEntry.type = "workspace-entry"`
- `entries` 中的 `workspace-entry`
- `workspaceEntryEnabled: true`
- `ready: true`
- `boundSkillIds`
- `bundlePackaging.excludePaths`
- `QJC_WORKSPACE_EVENT` 事件输出
- 客户端模型/密钥配置到项目环境变量的映射
## 适配目录策略
使用新目录:
```text
D:\qjclaw\workspace\xhs
```
原因:
- 项目 ID 固定为 `xhs`,与已有脚本 `smoke:xhs-local-project-package``smoke:xhs-expert-cloud-bundle` 保持一致。
- 保留 `xiaohongshu-client-package` 作为原始交付包,避免在源包上混入 qjclaw 适配层。
- 后续 bundle、客户端项目记录、技能绑定都以 `xhs` 为唯一项目标识。
## project.json 方案
`workspace\xhs\project.json` 使用 qjclaw 已验证字段,不新增未被客户端消费的字段。
建议结构:
```json
{
"id": "xhs",
"name": "xiaohongshu-client-package",
"platform": "xiaohongshu",
"projectType": "automation-project",
"description": "Xiaohongshu content collection, analysis, copywriting and publishing project.",
"version": "2026-04",
"workspaceAutomation": {
"runtime": "python",
"script": "workspace_entry.py",
"args": [
"--prompt",
"{prompt}",
"--prepared-prompt",
"{preparedPrompt}",
"--project-root",
"{projectRoot}",
"--session-id",
"{sessionId}"
]
},
"bundlePackaging": {
"excludePaths": [
".env",
"datas/runtime",
"datas/checkpoints",
"datas/excel_datas",
"datas/media_datas",
"datas/generated",
"datas/uploads",
"datas/debug_pages",
"node_modules",
"__pycache__",
"**/__pycache__",
"**/*.pyc"
]
},
"defaultEntry": {
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["xiaohongshu", "xhs", "小红书"],
"capabilities": ["collection", "analysis", "copywriting", "image-generation", "publish"]
},
"entries": [
{
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["xiaohongshu", "xhs", "小红书"],
"capabilities": ["collection", "analysis", "copywriting", "image-generation", "publish"]
}
],
"boundSkillIds": [
"xhs-create-note",
"xhs-pipeline",
"xhs-publish-note",
"xhs-research"
],
"workspaceEntryEnabled": true,
"ready": true
}
```
## workspace_entry.py 方案
`workspace_entry.py` 只作为 qjclaw 适配层,不重写小红书业务主流程。
职责:
- 解析 `--prompt``--prepared-prompt``--project-root``--session-id`
- 读取客户端注入的附件环境变量,例如 `QJC_PROJECT_ATTACHMENTS_JSON``QJC_PROJECT_MAIN_IMAGE`
- 将用户输入路由到采集、分析、生成、发布等已有脚本或函数。
- 将内部执行状态转换为 `QJC_WORKSPACE_EVENT\t{...}`
- 输出 `started``status``completed``error`,必要时输出 `delta`
最小事件格式:
```text
QJC_WORKSPACE_EVENT {"type":"started","runId":"<session-id>"}
QJC_WORKSPACE_EVENT {"type":"status","runId":"<session-id>","stage":"xhs","label":"Running XHS workflow"}
QJC_WORKSPACE_EVENT {"type":"completed","runId":"<session-id>","content":"..."}
QJC_WORKSPACE_EVENT {"type":"error","runId":"<session-id>","message":"..."}
```
## 模型和环境变量映射
客户端配置应在运行时写入项目环境,不提交 `.env`
小红书项目当前依赖的关键环境变量:
- 文本/视觉模型:`QWEN_API_KEY``QWEN_MODEL``QWEN_VISION_MODEL`
- 图片生成:`ARK_API_KEY``VOLCENGINE_API_KEY``IMAGE_API_KEY``IMAGE_MODEL``ARK_IMAGE_MODEL`
- 小红书登录:`COOKIES`
- 飞书写入:`FEISHU_APP_ID``FEISHU_APP_SECRET``FEISHU_APP_TOKEN``FEISHU_TABLE_ID``FEISHU_TABLE_MAP`
- 采集控制:`XHS_FETCH_COMMENTS``XHS_STATUS_FILE``XHS_RUN_LOG_FILE``XHS_EVENTS_FILE`
接入时由 qjclaw 的项目执行链注入 `project.env`,不要让 renderer 直接读写密钥、cookie、runtime 文件或子进程。
## FastAPI 端口策略
`agent_server.py` 默认端口是 `8888`。接入 qjclaw 时不应把常驻 FastAPI 服务作为唯一入口。
推荐策略:
- 首选 `workspace_entry.py` 直接调用已有 Python 函数或 CLI。
- 若必须复用 FastAPI,则由 `workspace_entry.py` 按需启动本地服务并做端口探测。
- 避免固定占用 `8888` 导致多个项目或多次会话冲突。
- FastAPI 只作为内部适配手段,qjclaw 客户端可见状态仍通过 `QJC_WORKSPACE_EVENT` 返回。
## Python runtime 依赖
按当前 `requirements.txt`,需要纳入 runtime 安装/校验:
- `PyExecJS`
- `requests`
- `loguru`
- `python-dotenv`
- `retry`
- `openpyxl`
- `playwright`
- `openai`
- `fastapi`
- `uvicorn`
- `python-multipart`
- `pdfplumber`
- `python-docx`
- `certifi`
另需确认:
- Playwright Chromium 已安装。
- Windows runtime 可执行 Python 依赖安装。
- Node/npm 仅在确实需要 `plugin` 或 JS 侧辅助能力时保留,否则不要作为 qjclaw 主执行入口。
## Bundle 排除规则
必须排除:
- `.env`
- 小红书 Cookie
- 飞书 Secret
- 模型 API Key
- `datas/runtime`
- `datas/checkpoints`
- `datas/excel_datas`
- `datas/media_datas`
- `datas/generated`
- `datas/uploads`
- `datas/debug_pages`
- `node_modules`
- `__pycache__`
- `**/*.pyc`
应保留:
- Python 源码
- `requirements.txt`
- `project.json`
- `workspace_entry.py`
- 必需的 `apis``xhs_utils``plugin``asset-dropbox-understanding` 等项目能力目录
- 必需的静态 JS 辅助文件
## 抖音隔离
本方案不修改 `workspace\douyin`
边界:
- 不改抖音项目代码。
- 不改抖音分支。
- 不复用抖音项目 ID、技能 ID 或业务状态文件。
- 只复用 qjclaw 通用执行链、bundle 机制、客户端配置注入机制和 smoke 验证方式。
## 实施顺序
1.`workspace\xiaohongshu-client-package` 迁移或复制出 `workspace\xhs`
2.`workspace\xhs` 新增 `project.json`
3.`workspace\xhs` 新增 `workspace_entry.py`
4. 将客户端模型、飞书、小红书 Cookie 配置映射到运行时环境变量。
5. 配置 `bundlePackaging.excludePaths`,确保密钥和历史运行数据不进入 bundle。
6. 先打通 CLI 直连执行,再决定是否保留 FastAPI 内部调用。
7. 用 smoke 验证 xhs 项目包、云端 bundle、客户端聊天入口。
8. 回归抖音 smoke,确认通用执行链没有被小红书适配破坏。
## 验证清单
适配完成后至少运行:
```powershell
corepack pnpm typecheck
corepack pnpm build
corepack pnpm smoke:xhs-local-project-package
corepack pnpm smoke:xhs-expert-cloud-bundle
corepack pnpm smoke:douyin-local-project-package
corepack pnpm smoke:douyin-expert-cloud-bundle
```
通过标准:
- `xhs` 项目能被项目包 materialize。
- `xhs` 项目记录包含 `workspace-entry``boundSkillIds`
- 运行时能收到 `QJC_WORKSPACE_EVENT`
- 客户端聊天能得到小红书工作流的 `completed` 或明确 `error`
- bundle 不包含 `.env`、Cookie、API Key、飞书 Secret 或历史运行数据。
- 抖音项目 smoke 仍通过。
# 2026-04-27 小红书自然语言入口优化方案
## 背景结论
当前小红书专家让用户输入 JSON 指令是不合适的。JSON 只应作为内部自动化或调试兼容入口,客户端用户侧应直接输入自然语言需求。
现状问题集中在 `workspace\xhs\workspace_entry.py`
- 完整执行路径目前主要依赖 `xhs_action` JSON 指令。
- 普通自然语言无法补齐参数时,会兜底返回“请使用 JSON 指令”。
- 入口曾使用 `prepared-prompt` 做自然语言关键词判断;该字段包含系统上下文,容易误触发“发布”等动作。
优化目标:
- 用户自然语言输入需求。
- 入口脚本负责解析、追问、确认、执行。
- 长流程必须确认后才启动。
- 继续只读取客户端注入的模型、Cookie、飞书环境变量。
## 需要修改的文件范围
核心修改:
- `workspace\xhs\workspace_entry.py`
- 自然语言意图解析。
- QWEN 参数抽取。
- 多轮补参状态。
- 参数确认后执行。
- 风险控制和环境校验。
可能需要小改:
- `workspace\xhs\project.json`
- 一般不需要改;如需可补充入口能力描述。
需要更新 smoke:
- `build\scripts\local-project-package-smoke.ps1`
- 补自然语言入口、追问、确认流程断言。
- `build\scripts\xhs-expert-cloud-bundle-smoke.ps1`
- 云端 bundle 入口断言改为自然语言完成/追问,不再依赖 JSON 示例。
不应修改:
- `apps\ui` 渲染层。
- 共享 IPC 类型。
- `workspace\douyin` 业务逻辑。
- 小红书原始包 `workspace\xiaohongshu-client-package`
## 交互与执行设计
入口参数使用策略:
- `--prompt`:唯一用户原文来源。
- `--prepared-prompt`:只作为模型解析上下文,不参与关键词或动作判断。
- `--session-id`:用于隔离多轮补参状态。
自然语言解析策略:
- 优先调用客户端注入的 QWEN:
- `QWEN_BASE_URL`
- `QWEN_API_KEY`
- `QWEN_MODEL`
- QWEN 输出必须校验 schema。
- 只允许白名单动作:
- `collect`
- `generate_from_link`
- `generate_from_collected`
- `preview_latest`
- `check_config`
- `locate_latest`
- `publish_latest`
- QWEN 不可用或解析失败时,降级为规则解析和自然语言追问。
多轮补参:
- pending state 存到 `memory\xhs_workspace_sessions\`。
- 文件按 `session-id` 隔离。
- 状态 TTL:24 小时。
- 完成、取消、切换动作后清理。
采集动作必填字段:
- `keywords`
- `business_need`
- `target_audience`
- `theme`
生成动作必填字段:
- `generate_from_link`:`ref_url`、`topic`
- `generate_from_collected`:`ref_index`、`brand`、`location`
确认机制:
- `collect`、`generate_from_link`、`generate_from_collected` 必须先输出确认摘要。
- 用户明确回复“确认 / 开始 / 执行”后才调用 CLI。
- 用户回复“取消 / 重来”时清理 pending state。
- `check_config`、`preview_latest`、`locate_latest` 可直接执行。
- `publish_latest` 只在用户明确输入“发布 / 现在发布 / 立即发布”时触发。
数量保护:
- `total_count` 默认 5。
- `total_count` 最大 20。
- `top_n` 不超过 `total_count`。
## 风险点与处理
误启动采集:
- 不能忽视。
- 所有长流程强制确认后执行。
- 未确认时只返回参数摘要。
`prepared-prompt` 误判:
- 必须修。
- 自然语言判断只读取 `--prompt`。
- `prepared-prompt` 不再用于动作关键词匹配。
模型解析失败:
- 不能忽视。
- QWEN 失败时返回自然语言追问。
- 不再要求用户输入 JSON。
- 模型输出必须做 action 白名单和字段校验。
会话状态污染:
- 不能忽视。
- 按 session 隔离。
- 完成、取消、切换动作后清理。
- 加 24 小时 TTL。
旧 smoke 兼容:
- 可以低风险处理。
- 不保留“要求 JSON”的用户体验。
- 更新 smoke 到自然语言追问/确认断言。
## 测试计划
脚本级验证:
```powershell
python -m py_compile workspace\xhs\workspace_entry.py
```
自然语言用例:
- 输入“帮我采集防晒霜赛道,面向通勤女性,先抓 5 条”。
- 预期:缺字段时追问,字段完整时返回确认摘要。
- 输入“确认”。
- 预期:开始执行;若缺 `COOKIES`、`QWEN_API_KEY`、飞书配置,则明确 error。
- 输入“取消”。
- 预期:清理 pending state。
- 输入“小红书链接 + 生成内容需求”。
- 预期:进入链接生成补参/确认流程。
- 输入“最新结果 / 预览结果”。
- 预期:直接预览。
- 输入“现在发布”。
- 预期:只做发布条件校验,不误触发其他自然语言。
回归验证:
```powershell
corepack pnpm typecheck
corepack pnpm build
corepack pnpm smoke:xhs-local-project-package
corepack pnpm smoke:xhs-expert-cloud-bundle
corepack pnpm smoke:douyin-local-project-package
corepack pnpm smoke:douyin-expert-cloud-bundle
```
通过标准:
- 小红书专家自然语言入口不再提示用户输入 JSON。
- 长流程不会在未确认时启动。
- 缺环境变量时给出明确错误。
- qjclaw 自动执行不触发扫码登录。
- `prepared-prompt` 中的系统上下文不影响动作判断。
- xhs 本地包、云端 bundle、抖音回归 smoke 全部通过。
This diff is collapsed.
# 前端 UI 与交互分阶段改造计划
## 背景
当前 renderer 曾长期集中在 `apps/ui/src/App.tsx``apps/ui/src/styles.css`
- `App.tsx` 同时承担 IPC mock、workspace 状态、聊天流、专家入口、设置页、插件页、smoke hooks 和主要 JSX。
- `styles.css` 存在多轮核心布局类定义,后续 UI 调整容易互相覆盖。
- 参考项目 `D:\cloud-desk-hero-main``features/*``stores/*``components/ui/*``lib/ipc/*` 分层方式适合当前项目,但不建议整套直接搬迁。
改造目标是渐进式拆分架构、稳定交互、统一视觉,而不是一次性重写。
## 总体原则
- 保留现有 Electron IPC、preload bridge、shared-types 合约。
- 保留现有 smoke hooks 和自动化依赖的 DOM/selector。
- 保留桌面两栏骨架:左侧 sidebar,右侧 workspace。
- 继续使用核心结构类:`conversation-shell``conversation-main-layout``conversation-content-area``conversation-panel`
- 不直接全量引入参考项目的 shadcn/Radix/Tailwind 3 配置;当前项目是 React 19 + Tailwind 4。
- 每阶段都必须能独立构建、验证、回滚。
## Phase 1:架构拆分准备,不改视觉
目标:先把纯逻辑和 mock 层从巨型 `App.tsx` 拆出,保持 UI 外观和行为不变。
修改范围:
- 新增 `apps/ui/src/lib/desktop-api.ts`,迁移 `desktopApi``mockDesktopApi`、mock stream 相关逻辑。
- 新增 `apps/ui/src/lib/chat-utils.ts`,迁移消息合并、markdown 渲染、附件判断、时间格式化等纯函数。
- 新增 `apps/ui/src/lib/constants.ts`,迁移默认 skill、附件扩展名、composer 尺寸常量。
- `App.tsx` 暂时仍负责顶层状态和 JSX,只减少内联工具逻辑。
当前状态:已完成。
代码事实:
- `apps/ui/src/lib/desktop-api.ts` 已存在。
- `apps/ui/src/lib/chat-utils.ts` 已存在。
- `apps/ui/src/lib/constants.ts` 已存在。
## Phase 2:Shell 与页面分区
目标:建立类似参考项目的 `features/*` 结构,但继续复用现有 CSS class 和交互行为。
修改范围:
- 新增 `features/shell/Sidebar.tsx`
- 新增 `features/shell/TopBar.tsx`
- 新增 `features/chat/ChatWorkspace.tsx`
- 新增 `features/experts/ExpertsView.tsx`
- 新增 `features/plugins/PluginsView.tsx`
- 新增 `features/settings/SettingsView.tsx`
- 新增 `features/knowledge/KnowledgeView.tsx`
- `App.tsx` 改为视图编排层。
当前状态:基本完成。
代码事实:
- `apps/ui/src/features/shell/AppSidebar.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/shell/Sidebar.tsx` 已存在。
- `apps/ui/src/features/shell/TopBar.tsx` 已存在。
- `apps/ui/src/features/chat/ConversationWorkspaceView.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/ChatWorkspace.tsx` 已存在。
- `apps/ui/src/features/experts/ExpertsView.tsx` 已存在。
- `apps/ui/src/features/plugins/PluginsView.tsx` 已存在。
- `apps/ui/src/features/settings/SettingsView.tsx` 已存在。
- `apps/ui/src/features/settings/SettingsPanels.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/knowledge/KnowledgeView.tsx` 已存在。
剩余边界:
- `App.tsx` 仍保留顶层编排、smoke actions、部分业务状态和回调。
## Phase 3:状态管理分层
目标:参考 store 思路,把 UI 状态、聊天状态、workspace 状态拆开。
修改范围:
- 新增 `useWorkspaceState`
- 新增 `useChatState`
- 新增 `useExpertsState`
- 新增 `useSettingsState`
- 新增 `useUiState`
- 先用 React hooks/context 实现;如状态复杂度仍高,再评估引入 `zustand`
- 下沉 `sendPrompt`、stream event、session 切换、专家入口选择等逻辑。
当前状态:部分完成。
代码事实:
- `apps/ui/src/features/chat/useProjectSessions.ts` 已存在。
- `apps/ui/src/features/chat/useSessionMessages.ts` 已存在。
- `apps/ui/src/features/chat/useMessageTraces.ts` 已存在。
- `apps/ui/src/features/chat/useChatStreamingController.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/useChatSessionsController.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/usePromptSubmission.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/useHomeIntentSuggestion.ts` 已存在。
- `apps/ui/src/features/settings/useSettingsState.ts` 已存在。
- `apps/ui/src/features/shell/useHomeNavigation.ts` 已存在并承载 `openHomeChat``activateHomeShortcut``handleNavSelection`
剩余边界:
- `App.tsx` 当前仍有 2054 行。
- `App.tsx` 仍保留 smoke actions、部分顶层状态、页面级编排和部分业务回调;conversation/settings 的页面 props 组装已提到 return 前的 typed props 常量。
- `zustand` 未引入。
## Phase 4:设计系统与交互升级
目标:吸收参考项目视觉体系和组件化优点,但适配当前桌面工具产品。
修改范围:
- 整理 `styles.css`,确保核心布局类只保留最终生效的一套定义。
- 建立轻量 `apps/ui/src/components/ui/*`
- `Button`
- `IconButton`
- `Panel`
- `Tabs`
- `StatusChip`
- `ScrollArea`
- 统一颜色、间距、圆角、focus、hover、disabled、loading、error 状态。
- 优化聊天、专家、插件、设置页的桌面工具型布局。
当前状态:未完成,继续收口中。
代码事实:
- `apps/ui/src/components/ui/Button.tsx` 已存在。
- `apps/ui/src/components/ui/IconButton.tsx` 已存在。
- `apps/ui/src/components/ui/Panel.tsx` 已存在。
- `apps/ui/src/components/ui/ScrollArea.tsx` 已存在。
- `apps/ui/src/components/ui/StatusChip.tsx` 已存在。
- `Tabs` 未实现。
- `lucide-react` 未引入。
- `apps/ui/src/styles.css` 已收口为 8 行入口 import,实际样式拆到 `apps/ui/src/styles/*.css`
- `apps/ui/src/styles/base.css``chat.css``components.css``knowledge.css``plugins.css``settings.css``shell.css``theme-openclaw.css` 已存在并承载主样式。
- `conversation-shell``conversation-main-layout``conversation-content-area``conversation-panel` 等核心骨架类仍保留。
- 本轮已清理部分旧注释、空弹层注释块,并删除被末端 revamp 层覆盖的 `conversation-workspace` 早期视觉声明;布局、滚动和核心骨架类保留。
- 本轮已修复 `ConversationWorkspaceView` 中一处可见 mojibake 文案,恢复为“当前对话”。
剩余边界:
- 继续收口 `styles.css` 中确认安全的历史重复规则。
- 暂不迁移 composer submit、新建会话、home intent actions、smoke actions、stream 主链路这些高风险节点。
- 暂不引入 `lucide-react`、Radix、shadcn、zustand。
## 当前验收状态
截至 2026-05-08,最近一轮验证已通过:
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
- `corepack pnpm smoke:settings`
本轮验证:
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
- `corepack pnpm smoke:settings`
- 编码检查:`App.tsx``styles.css``ConversationWorkspaceView.tsx`、本文档均无 UTF-8 BOM;UI/文档错码标记扫描通过。
- 人工检查:Vite renderer chat 页面可打开,左侧栏/右侧 workspace 高度稳定,message list 与 composer 未出现横向溢出;Playwright 后续 nav 点击探针超时,未完成五页人工验收。
本轮未完成:
- 未新增 `Tabs`
- 未引入 `lucide-react`、Radix、shadcn、zustand。
- 未迁移 stream 主链路、composer submit、新建会话、home intent actions、smoke actions。
## 当前结论
四个阶段没有全部完成。
- Phase 1:已完成。
- Phase 2:基本完成。
- Phase 3:部分完成。
- Phase 4:未完成,仍处于 CSS 收口和低风险组件接入阶段。
下一步优先级:
- 继续压薄 `App.tsx` 中的顶层编排,但不动 stream 主链路和 smoke action 协议。
- 继续收口 `styles.css`,只删除确认被最终规则覆盖且不影响布局/滚动的历史规则。
- 如要推进设计系统,先补 `Tabs` 或扩大现有 UI 组件接入,但必须逐页 smoke 验证。
This diff is collapsed.
# QianjiangClaw Architecture
Updated: 2026-03-23
## 1. Stable Desktop Boundary
The stable boundary remains:
- Renderer
- UI only
- does not own raw runtime credentials
- does not directly start runtime processes
- preload
- exposes controlled desktop APIs to Renderer
- Electron Main
- owns secret access
- owns runtime cloud fetch
- owns managed runtime config generation
- owns bundled runtime lifecycle
- GatewayClient / RuntimeManager / cloud clients
## 2. Current Runtime-Cloud Design
Stage D step 1 uses a runtime-first cloud integration model.
The current key rule is:
- the user must first bind an OpenClaw employee `api_key`
- Electron Main uses that key to call `POST /openclaw-employee-config`
- the returned payload becomes the source of truth for runtime business configuration
This `api_key` is not the LLM vendor key. It is the OpenClaw employee identity credential.
## 3. Required Runtime-Side Clients
Main should include or add these runtime-side pieces:
- `OpenClawConfigClient`
- calls `POST /openclaw-employee-config`
- fetches runtime payload for startup
- later:
- heartbeat client for `POST /openclaw-heartbeat`
- event client for `POST /openclaw-employee-events`
## 4. Startup Flow for Stage D Step 1
Installer startup should follow this order:
1. Desktop app loads local settings.
2. Desktop app reads stored employee `api_key` from secret storage.
3. If missing, runtime startup is blocked and UI asks the user to bind the key.
4. If present, Main calls `POST /openclaw-employee-config` with:
- `api_key`
- `action = "init"`
5. Main validates returned runtime payload.
6. Main merges cloud payload with local machine-specific fields.
7. Main writes local managed runtime config.
8. `RuntimeManager.start()` launches bundled runtime using that managed config.
9. Gateway becomes available for the desktop product.
## 5. Runtime Config Responsibility Split
Cloud payload should provide runtime business configuration such as:
- `persona_prompt`
- `welcome_message`
- `work_hours`
- `auto_reply_rules`
- `resource_quota`
- `llm.model_id`
- `llm.temperature`
- `llm.max_tokens`
- `llm.provider.name`
- `llm.provider.base_url`
- `llm.provider.api_key`
- `skills`
- `channels`
- `config_version`
- runtime reporting endpoints
Desktop Main should still provide local host-specific fields such as:
- workspace path
- runtime state dir
- runtime logs dir
- managed config output path
- bundled Gateway bind and port
- generated gateway auth token for local control path
## 6. Security Rules
- Employee `api_key` lives in `SecretManager/keytar`.
- Renderer never receives raw `api_key`.
- Managed runtime config may contain effective runtime credentials, but diagnostics must not export raw sensitive values.
- If cloud config fetch fails, bundled runtime must remain unavailable instead of fake-ready.
## 7. Current State
Already in place:
- installer packaging
- bundled runtime payload delivery
- runtime detect/start/stop/restart/health/tailLogs
- managed config generation path exists locally
- diagnostics and UI pages already exist
Still missing for Stage D step 1:
- user-bound employee `api_key` flow
- `OpenClawConfigClient` wired to `POST /openclaw-employee-config`
- managed config generation from cloud payload
- real installer usability based on cloud-driven startup
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# QianjiangClaw Commercial Product Blueprint
Updated: 2026-03-23
## 1. Product Direction
Current commercial direction is:
- The Windows installer remains the single user-facing entry point.
- Bundled OpenClaw runtime ships inside the installer.
- The first real delivery milestone in Stage D is not full business cloud integration.
- The first real delivery milestone is: installer can become truly usable by letting the user bind an OpenClaw employee `api_key`, fetch runtime config from cloud, and start bundled runtime automatically.
## 2. Product Principles
Current fixed principles:
- The user must first bind an OpenClaw employee `api_key` before OpenClaw can be used.
- This `api_key` is the runtime employee identity key, not the upstream model vendor key.
- The user should not manually enter provider, model, base_url, model vendor api_key, local Node path, OpenClaw entry path, or Gateway token in the normal product flow.
- The desktop app stores the employee `api_key` in Electron Main secret storage.
- Renderer remains a UI layer only and does not own raw credentials.
## 3. First Real Usable Flow
Stage D step 1 user flow should be:
1. User installs the Windows app.
2. User opens the desktop app.
3. User goes to settings or first-run activation view.
4. User binds an OpenClaw employee `api_key`.
5. Electron Main calls `POST /openclaw-employee-config` with `{ api_key, action: "init" }`.
6. Main receives cloud runtime payload.
7. Main generates local managed runtime config.
8. Bundled runtime starts.
9. Gateway becomes available.
10. User can start using the installer product.
## 4. Required Runtime Cloud APIs
Current required runtime-side APIs are:
- `POST /openclaw-employee-config`
- `POST /openclaw-heartbeat`
- `POST /openclaw-employee-events`
For Stage D step 1, only the first endpoint is required for initial delivery.
## 5. Runtime Config Strategy
The current runtime config strategy is:
- user provides employee `api_key`
- cloud returns runtime payload
- desktop app writes local managed config
- bundled runtime starts from that managed config
The `openclaw-employee-config` payload should provide business-controlled runtime fields such as:
- `persona_prompt`
- `welcome_message`
- `work_hours`
- `auto_reply_rules`
- `resource_quota`
- `llm.model_id`
- `llm.temperature`
- `llm.max_tokens`
- `llm.provider.base_url`
- `llm.provider.api_key`
- `skills`
- `channels`
- `config_version`
- `endpoints.heartbeat`
- `endpoints.events`
The desktop app should still add local machine-specific values such as:
- workspace path
- runtime state/log paths
- bundled Gateway bind and port
- local managed config output path
## 6. Security Rules
- Employee `api_key` stays in Electron Main and secret storage.
- Raw `api_key` must not be exposed to Renderer.
- Diagnostics export must not dump raw credentials.
- If employee `api_key` is missing, bundled runtime must not pretend to be available.
- If `openclaw-employee-config` fetch fails, bundled runtime must not start.
## 7. What Is Not Stage D Step 1 Yet
Not part of this first delivery slice:
- full product-side login, account, credits, and Skills production integration
- heartbeat and event reporting implementation
- full entitlement or billing product UX
- final release polish such as icon, signing, and upgrade path
## 8. Acceptance for This First Delivery Slice
Stage D step 1 should only be considered complete when:
- user can bind an OpenClaw employee `api_key`
- app can successfully call `POST /openclaw-employee-config`
- app can generate managed runtime config from real cloud payload
- bundled runtime starts from that config
- installer becomes truly usable without manual provider/model/base_url configuration
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# 客户端抖音专家附件打通计划
Status: proposed
Date: 2026-04-10
## 1. 背景
当前客户端到 Douyin expert 的主链路,已经基本具备:
1. 专家页选择 `douyin` 项目
2. 会话绑定到 `project:douyin:*`
3. 默认命中项目 `workspace-entry`
4. 启动项目级执行链路
但这条链路目前仍然基本是“纯文本对话链路”。
对 Douyin 项目来说,这会直接卡住数字人能力,也会限制纯画面路线里的引用素材输入。
## 2. 当前代码事实
### 2.1 已打通部分
- 专家页可以命中项目级 `workspace-entry`
- Douyin 本地纯画面工作流已经有真实成功样例
- Douyin 主流程已支持:
- `--image`
- `--audio`
- `--seedance-reference-image`
- `--seedance-reference-video`
- `--seedance-reference-audio`
### 2.2 当前明确缺口
- 客户端专家页输入框目前只有文本输入,没有图片上传入口
- `DesktopApi.chat.sendPrompt()` / `streamPrompt()` 当前只有:
- `sessionId`
- `prompt`
- `skillId`
- IPC 层当前也只透传纯文本 prompt
- `project-workspace-executor` / `project-workspace-agent-runner` 当前只有文本 prompt 输入,没有附件协议
- Douyin 数字人链路当前明确要求必须有图片
### 2.3 对 Douyin 的直接影响
- 数字人路线:当前客户端无法把图片传给 expert,因此无法从客户端直接跑通数字人
- 纯画面路线:无图纯文案可以跑,但参考图 / 参考视频 / 参考音频能力当前无法从客户端输入
## 3. 目标
本次目标不是一次性做完整多模态聊天,而是先补齐最小闭环:
1. 客户端专家页支持选择并发送 1 张本地图片
2. 图片在 main 进程被复制到当前项目目录下的稳定路径
3. `workspace-entry` 能收到这张图的结构化输入
4. Douyin expert 能把这张图用于数字人主图输入
5. smoke 不再只验证 `workspace-entry launch`,至少验证“附件已进入项目执行层”
## 4. 方案范围
### 4.1 本次纳入
- 图片附件协议
- 专家页单图上传
- main 进程项目内落盘
- `workspace-entry` 附件透传
- Douyin expert 单图数字人最小闭环
- smoke 验证升级
### 4.2 本次不纳入
- 多图上传
- 音频上传
- 剪贴板图片
- URL 素材粘贴
- 通用多模态聊天体系重构
- 去修改 Douyin 项目“数字人必须传图”的业务约束
## 5. 实施方案
### 5.1 聊天协议与 IPC
扩展以下调用签名,新增可选 `attachments`
- `DesktopApi.chat.sendPrompt`
- `DesktopApi.chat.streamPrompt`
新增最小附件结构,v1 先只支持图片:
- `kind: "image"`
- `name`
- `mimeType`
- `localPath`
要求:
- 与当前纯文本调用兼容
- 无附件时行为保持不变
- renderer -> preload -> ipc -> main 全链路统一使用同一份附件结构
### 5.2 专家页 UI
在现有 composer 上补一个最小上传入口:
- 支持选择 1 张本地图片
- 已选择图片可见
- 重选时以最后一次选择覆盖
- 发送成功后清空附件
- 发送失败时保留附件,便于重试
本阶段不引入复杂交互:
- 不做多图排序
- 不做拖拽上传
- 不做素材管理器
### 5.3 Main 进程项目内落盘
main 进程收到图片后,不直接把临时路径传给项目,而是先复制到当前项目目录:
- 目标目录:`inputs/images/main/`
要求:
- 生成稳定文件名
- 绑定当前 `projectId` / `sessionId`
- 禁止跨项目复用错误路径
- 会话未绑定项目时直接返回明确错误
### 5.4 Workspace-entry 执行层
扩展:
- `ProjectExecutionRequest`
- `ProjectWorkspaceExecutionInput`
- `project-workspace-agent-runner` 的输入结构
执行策略:
- 保留原始 `prompt`
- 同时透传 `attachments`
- 如果当前 bundled agent 暂时不能直接吃结构化图片输入,则在 runner 层集中做一次降级,把图片路径转换成稳定提示注入
约束:
- 不在 renderer 层拼接图片说明
- 不在多个中间层重复组装 prompt
- 所有附件降级策略集中在执行层单点处理
### 5.5 Douyin expert 约定
v1 统一约定:
- 客户端上传的单张图片,默认视为“数字人主图 / main image”
这样可以先解当前最刚需的场景:
- 从客户端对话 Douyin expert
- 上传人物图
- 继续生成数字人口播视频
纯画面路线后续可以复用同一套附件协议,再细分为:
- `seedance_image`
- `reference_image`
- `reference_video`
- `reference_audio`
但这些不作为本次第一阶段的强验收项。
## 6. 验收标准
### 6.1 功能验收
满足以下结果即视为本阶段完成:
1. 专家页可选择并发送 1 张图片
2. 图片能落到当前项目 `inputs/images/main/`
3. Douyin expert 在项目执行层能读取到该图片路径
4. 数字人路线不再因为“客户端无法传图”而阻断
### 6.2 回归要求
以下行为不能被破坏:
1. 无附件的纯文本专家对话行为不变
2. 当前已打通的 `workspace-entry` 路由不回退为 skill fallback
3. 现有 Douyin 纯画面文本生成链路不回归
### 6.3 smoke 验收
至少新增或升级以下验证:
1. 专家页上传图片后,附件成功透传到项目执行层
2. `douyin expert cloud-bundle smoke` 不再只接受 `workspace-entry launch`
3. 保留现有纯文本 smoke,确保兼容旧行为
如果环境允许,再补一条本地真实验证:
1. 上传人物图
2. 用文案 + TTS 走数字人
3. 输出 `video_omnihuman.json` 或等效成功产物
## 7. 实施顺序
建议按以下顺序推进,避免前后返工:
1. 先补 shared types / preload / ipc 的附件协议
2. 再补专家页单图上传 UI
3. 再补 main 进程项目内落盘
4. 再补 workspace-entry executor / runner 附件透传
5. 最后补 Douyin smoke 和真实验证
## 8. 默认假设
- v1 只支持单张图片
- v1 只支持本地文件
- v1 不支持音频上传
- v1 不改 Douyin 数字人必须传图的既有约束
- 结构化多模态能力如果当前 runtime 未直接支持,允许 runner 层先做集中降级适配
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment