Files
ycode/docs/落地方案.md
T

186 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# YCode 落地方案
> 历史初始方案。当前运行架构优先使用 OMP RPC sidecar,自建网关保留为回退;现行目标是借助 REA 进行视觉、交互与功能 1:1 复刻。实际完成度见 `接手状态-2026-10-15.md`;本文件旧里程碑和排除项不作为缩减范围的依据。
**日期**:2026-10-14
**范围**:Electron 桌面应用 · 第一个里程碑 = 最小端到端闭环(能对话 + 能跑一个工具)· 自建 OpenAI 兼容网关
---
## 0. 前置决策(已确认)
| 项 | 决策 | 理由 |
| --- | --- | --- |
| 产品形态 | Electron 桌面应用 | 与对标形态一致,可复用同批开源 UI 栈 |
| 首里程碑 | 最小端到端闭环 | 一次打通 UI ↔ Agent ↔ 网关 ↔ 工具,最快验证价值 |
| 模型接入 | 自建网关(自有 API Key,OpenAI 兼容) | 协议可控、供应商可换、密钥不出主进程 |
| 产物策略 | **clean-room** | 不复制对标产品的源码 / CSS / 资源,只用其公开架构与交互模式 |
---
## 1. 目标架构
```
┌──────────────────────────────────────────────────────┐
│ Renderer(React 19 + Zustand) │
│ 对话区 · 右栏上下文 · 设置弹窗 │
│ ⚠ 无 Node / fs / 网络能力 │
└───────────────┬──────────────────────────────────────┘
│ contextBridge(preload 白名单 API)
│ invoke: config/session/chat
│ event : chat:event(流式增量)
┌───────────────▼──────────────────────────────────────┐
│ Main(Electron 主进程 · ESM) │
│ ├ ipc.ts 唯一对外边界 │
│ ├ config.ts safeStorage 加密 Key │
│ ├ sessions.ts 会话与消息持久化 │
│ ├ agent/loop.ts Agent 循环(模型 ↔ 工具) │
│ └ gateway/ 自建网关(OpenAI 兼容 + SSE) │
└───────────────┬──────────────────────────────────────┘
│ HTTPS Bearer(Key 只在此处出现)
▼
自有上游(OpenAI 兼容 /chat/completions)
```
### 关键设计点
1. **单向分层**:`renderer → preload → ipc → agent → gateway → 上游`。反向只有 `chat:event` 一条流式通道。
2. **密钥不出主进程**:渲染进程只能拿到 `{ hasApiKey, keyStorage }`,永远拿不到明文。
3. **循环与工具解耦**:`loop.ts` 只管编排,工具是纯函数 `(args, ctx) => ToolResult`,新增工具只需改 `tools/index.ts` 注册表。
4. **网关只管协议**:模型路由、上下文裁剪、重试策略不放网关,后续可独立演进。
---
## 2. 一次回合的完整链路
```
用户按 Enter
→ renderer: chat.send({sessionId, text})
→ ipc: 落库用户消息 + 占位助手消息,返回 {runId, assistantMessageId}
→ loop: 拼 system + 历史 + 本轮 → gateway.stream()
→ gateway: POST /chat/completions (stream:true) → 解析 SSE
├ delta.content → emit {type:'text'} → 界面逐字追加
└ delta.tool_calls → 按 index 归并分片
→ 若产生工具调用:
emit tool_call → 执行工具 → emit tool_result
→ 把 assistant(tool_calls) + tool(result) 回填上下文 → 继续下一轮
→ 无工具调用 → emit done → 更新助手消息终态
```
取消:`chat:cancel(runId)` → `AbortController.abort()` → 网关抛 `AbortError` → `emit cancelled`。
---
## 3. 里程碑拆解
### M1 · 最小端到端闭环(本次已落地)
- [x] Electron 壳(`contextIsolation: true` / `sandbox: true` / 无 `nodeIntegration`)
- [x] preload 白名单 API(CJS,因为沙盒要求)
- [x] 自建网关:OpenAI 兼容 + SSE 流式 + 流式工具调用组装
- [x] Agent 循环:多轮工具调用、终止条件、取消
- [x] 工具:`list_dir`、`read_file`(只读 + 工作区逃逸防护)
- [x] 配置持久化:`safeStorage` 加密 Key,明文降级时 UI 告警
- [x] 会话持久化 + 首条消息自动命名
- [x] 界面:会话列表 / 对话区 / 右栏上下文 / 设置弹窗 / 明暗主题
- [x] 类型检查 + 构建通过
### M2 · 体验与可靠性
- [ ] Markdown 渲染(代码高亮、表格、KaTeX)
- [ ] 消息级操作:复制、编辑重发、重新生成
- [ ] 工具卡分组折叠(连续调用合并成摘要)
- [ ] 断流重试与错误分类提示
- [ ] 会话搜索与重命名
### M3 · 能力扩展
- [ ] 工具扩展:`write_file` / `edit_file` / `run_shell`(带权限确认)
- [ ] 审批流:写操作与命令执行需用户确认
- [ ] MCP 客户端接入
- [ ] Skill 加载(`SKILL.md` 约定)
### M4 · 平台化
- [ ] 迁移到 SQLite
- [ ] 项目 / 工作目录多实例
- [ ] 右栏真实面板(产物预览、文件树、任务)
- [ ] 打包分发(electron-builder,Windows 优先)
---
## 4. 安全基线
| 风险 | 处置 |
| --- | --- |
| 渲染进程被注入后提权 | `sandbox: true`、无 `nodeIntegration`、CSP 限制 `script-src 'self'` |
| 密钥泄露 | 只在主进程;`safeStorage` 加密;日志不打印 |
| 工具越权读写 | 路径解析后做 `relative()` 逃逸检查,越界直接拒绝 |
| 外链钓鱼 | `setWindowOpenHandler` 一律 `deny` + `shell.openExternal` |
| 任意命令执行 | M1 **不提供** shell 工具;M3 引入时必须走审批 |
---
## 5. 视觉对齐依据(对标实测值)
配色与排版**不再自拟**,全部取自两项实测证据:
1. 真实 WorkBuddy 运行时布局实测 —— `docs/reference/workbuddy-chat-layout.md`(CDP computed style / 元素矩形)
2. 安装包 token 表 —— `tokens-Df-i-zsO.css`、`contexts-BkjwYSZT.css`
### 调色板(浅色)
| 用途 | 值 |
| --- | --- |
| 正文 / 主文字 | `#000` |
| 次级 / 三级文字 | `rgba(0,0,0,.7)` / `rgba(0,0,0,.5)` |
| 主背景 / 次背景 | `#fff` / `#f2f2f2` |
| hover / active | `#ebebeb` / `#e6e6e6` |
| 边框(主/次/三级) | `#e6e6e6` / `#ebebeb` / `#f2f2f2` |
| 品牌 / 成功 / 警告 / 危险 / 信息 | `#00c29a` / `#0cbf5b` / `#ff7800` / `#f64041` / `#3370ff` |
**注意:UI 主体是中性黑白灰,品牌绿只用于状态点缀**,不用于主按钮或大面积色块。
### 圆角与间距
- 圆角:`sm 4px` / `md 6px` / `lg 8px` / 气泡 `16px` / 输入框 `24px`
- 间距基准 4px;对话内容列左右内边距 `40px`
### 字号
| 角色 | 值 |
| --- | --- |
| caption / body | `12px` / `14px`(行高 18 / 21) |
| 用户气泡 | `13px` / 19px |
| Markdown 根 | `17.143px`,行高 `1.75`;段落 `.875em` → 15px / 26.25px |
| 代码块 | `12.857px` / 22.5px,padding `12px 16px` |
| 表头 | 底色 `#f7f7f7`,单元格 padding `8px 12px` |
### 结构尺寸
- 左栏 `280px`;对话内容列 `max-width 832px` 居中,左右 `padding 40px`(正文列 752px)
- 助手头部:`24px` 圆形标识 + `10px` 间距,行高 `24.5px`,名称 `14px/600`,下间距 `12px`
- 用户气泡:`#f2f2f2`、padding `8px 12px`、圆角 `16px 16px 0`、`min-width 40px`、`max-height 310px`
- 输入容器:`1px solid #e6e6e6`、圆角 `24px`、白底 + 双层轻阴影;编辑区 `min-height 64px`、横向 `padding 16px`;工具栏 `56px`、按钮 `32px`
- 滚动:消息列表 `flex:1 + min-height:0 + overflow-y:auto`,输入区 `flex-shrink:0` 固定底部
> 落地方式:CSS 使用**自有类名**(`.msg--assistant`、`.bubble` 等),仅取实测数值,
> 不复制对标产品的类名与样式表结构。
---
## 6. 与对标分析的关系
`docs/` 下三份分析报告(`WorkBuddy-AI-实现分析报告`、`WorkBuddy-AI-复刻可行性评估`、`WorkBuddy-UI复刻可行性评估`)用于**指导选型与交互模式**,不作为复制来源:
- 可用:组件划分思路、交互状态机、协议约定、开源库清单
- 不可用:对方源码、CSS、图标与图片资源、文案
---
## 7. 首次运行清单
1. `npm install && npm run dev`
2. ⚙ 设置:填 baseUrl / 模型名 / API Key;选工作目录
3. 提问「列出工作目录里有哪些文件」→ 应看到工具卡出现、执行、结果回填、模型总结