Files
zclaw_openfang/docs/references/artifact-system-reference.md
iven 4c31471cd6
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
feat(artifact): 产物系统优化 — 共享渲染 + 数据源扩展 + 持久化
- MarkdownRenderer: 从 StreamingText 提取共享 react-markdown + remark-gfm 组件
- ArtifactPanel: 替换手写 MarkdownPreview 为完整 GFM 渲染,添加文件选择器下拉菜单
- 数据源: file_write/str_replace 双工具 + sendMessage/initStreamListener 双路径
- 持久化: artifactStore 添加 zustand persist + IndexedDB (复用 idb-storage)
2026-04-24 10:59:27 +08:00

310 lines
13 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.

# 产物系统参考文档
> 调研 DeerFlow 和 Hermes Agent 的产物/输出面板实现,为 ZCLAW 产物系统重构提供参考。
> 分析日期2026-04-24
---
## 一、DeerFlow 产物系统
DeerFlow 有完整的全栈产物管道,是主要参考对象。
### 1.1 端到端数据流
```
Agent tool call (write_file / str_replace / present_files)
Backend: ThreadState.artifacts (LangGraph annotated list, merge_artifacts reducer 去重)
↓ 文件写入: {base_dir}/threads/{thread_id}/user-data/outputs/
↓ 虚拟路径: /mnt/user-data/outputs/filename.ext
Backend API: GET /api/threads/{thread_id}/artifacts/{virtual_path}
↓ MIME 检测 / .skill ZIP 解压 / download vs inline
Frontend: thread.values.artifacts (string[]) → ArtifactsProvider context
ChatBox (ResizablePanelGroup) → chat(60%) | artifact panel(40%)
ArtifactFileDetail → CodeMirror(代码) / Streamdown(Markdown) / iframe(HTML)
```
### 1.2 关键文件
#### 前端核心
| 文件 | 职责 |
|------|------|
| `frontend/src/core/artifacts/utils.ts` | URL 构建、产物列表提取、路径解析 |
| `frontend/src/core/artifacts/loader.ts` | 从后端 API 获取产物文本;从 tool call args 直接提取内容 |
| `frontend/src/core/artifacts/hooks.ts` | TanStack React Query hook5 分钟缓存 |
| `frontend/src/components/workspace/artifacts/context.tsx` | ArtifactsProvider + useArtifacts() — 管理列表、选中、开关、自动选中 |
| `frontend/src/components/workspace/artifacts/artifact-file-detail.tsx` | 产物详情视图:头部(文件选择器+code/preview切换) + CodeEditor/Preview |
| `frontend/src/components/workspace/artifacts/artifact-file-list.tsx` | 卡片式列表视图,每个卡片含图标/名称/扩展名/下载/安装按钮 |
| `frontend/src/components/workspace/artifacts/artifact-trigger.tsx` | 头部触发按钮,仅在产物存在时显示 |
#### 前端渲染
| 文件 | 职责 |
|------|------|
| `frontend/src/components/workspace/code-editor.tsx` | CodeMirror 只读编辑器,支持 CSS/HTML/JS/JSON/MD/Python 语法高亮 |
| `frontend/src/components/ai-elements/code-block.tsx` | Shiki 语法高亮代码块,双主题(light/dark) |
| `frontend/src/components/ai-elements/web-preview.tsx` | iframe 网页预览,含地址栏和导航按钮 |
| `frontend/src/components/workspace/messages/markdown-content.tsx` | Streamdown 渲染 Markdown (GFM + Math + Raw HTML + KaTeX) |
| `frontend/src/core/utils/files.tsx` | 140+ 扩展名→语言映射,文件图标/类型判断 |
#### 后端
| 文件 | 职责 |
|------|------|
| `backend/.../thread_state.py` | ThreadState.artifacts 列表 + merge_artifacts 去重 reducer |
| `backend/.../present_file_tool.py` | present_files 工具 — 标准化路径,返回 Command(update) |
| `backend/.../paths.py` | 路径管理threads/{id}/user-data/{workspace,uploads,outputs} |
| `backend/app/gateway/routers/artifacts.py` | FastAPI 路由GET 产物文件MIME 检测,安全处理 |
### 1.3 支持的内容类型
| 类型 | 渲染方式 |
|------|----------|
| 代码文件 (140+ 扩展名) | CodeMirror 只读 + 语法高亮 |
| Markdown (.md) | Streamdown (GFM + Math + KaTeX + Raw HTML) |
| HTML (.html/.htm) | 沙箱 `<iframe>` (srcDoc) |
| 图片 (.png/.jpg/.svg/.webp) | `<img>` 标签,非代码文件用 iframe |
| .skill 压缩包 | ZIP 解压SKILL.md 渲染为 Markdown |
| 二进制文件 (PDF 等) | 后端 inline Content-Disposition |
| 文本文件 (.txt/.csv/.log) | CodeMirror 纯文本模式 |
### 1.4 持久化架构
**磁盘存储:**
```
{DEER_FLOW_HOME}/threads/{thread_id}/user-data/outputs/
```
**状态持久化:** artifacts 列表是 LangGraph ThreadState 的一部分,由 checkpoint 系统自动持久化。
**前端缓存:** TanStack React Query5 分钟 stale time。
### 1.5 UI/UX 设计模式
#### 分栏布局 (chat-box.tsx)
- `react-resizable-panels` 水平分栏
- 关闭态chat=100%, artifacts=0%
- 打开态chat=60%, artifacts=40%
- 300ms CSS 过渡动画
#### 自动打开 + 自动选中
- 检测到 `write_file` / `str_replace` tool call 时自动打开面板并选中文件
- `autoOpen` / `autoSelect` 标志防止用户手动关闭后重复打开
#### 代码/预览切换
- HTML/Markdown 默认 Preview其他默认 Code
- Preview 用 Streamdown(MD) 或 iframe(HTML)
#### 头部操作栏
- 文件选择器下拉菜单(不用返回列表即可切换)
- 复制 / 下载 / 新窗口打开 / 关闭
#### 聊天内嵌展示
- `present_files` tool call → 聊天流内渲染卡片网格
- 点击卡片 → 侧栏打开该文件
#### 双路径方案
1. **真实文件路径** — 从后端 API 获取React Query 缓存
2. **`write-file:` 虚拟路径** — 直接从 tool call args 提取内容,无需后端请求,支持流式显示
### 1.6 Provider 层级
```
ArtifactsProvider → 提供useArtifacts() context
ChatBox → ResizablePanelGroup
Panel(chat) → MessageList → ToolCall 自动打开产物面板
Panel(artifacts) → ArtifactFileDetail → useArtifactContent() hook
```
---
## 二、Hermes Agent 产物机制
> **结论Hermes Agent 无产物面板、无 Web 前端、无分栏布局。** 它是终端 CLI 工具,所有输出在终端内联渲染。但有值得借鉴的大输出处理机制。
### 2.1 项目定位
Hermes Agent 是 **Python CLI/TUI Agent**(类似 Claude Code通过 prompt_toolkit TUI 运行,同时支持 Telegram/Discord/Slack/WhatsApp 等 IM 平台网关。
**无 React/Next.js/Web UI。** 暴露 OpenAI 兼容 API 供 Open WebUI/LobeChat 等第三方 UI 接入。
### 2.2 大输出处理3 层防御)
这是唯一接近"产物管理"的机制,值得借鉴。
**文件:`tools/tool_result_storage.py`**
| 层级 | 机制 | 说明 |
|------|------|------|
| Layer 1 | 工具自身截断 | 每个工具限制自己的输出长度 |
| Layer 2 | `maybe_persist_tool_result` | 单个结果超阈值 → 写入沙箱临时文件,上下文中替换为 `<persisted-output>` 预览块 |
| Layer 3 | `enforce_turn_budget` | 整轮超过 200K 字符 → 最大的几个溢出到磁盘 |
核心逻辑:
```python
# 超阈值时:完整内容写入文件,上下文替换为预览
remote_path = f"{storage_dir}/{tool_use_id}.txt"
_write_to_sandbox(content, remote_path, env)
return _build_persisted_message(preview, has_more, len(content), remote_path)
# 后续 agent 可用 read_file + offset/limit 读取完整内容
```
### 2.3 预算配置
**文件:`tools/budget_config.py`**
| 参数 | 默认值 |
|------|--------|
| `DEFAULT_RESULT_SIZE_CHARS` | 100,000单工具阈值|
| `DEFAULT_TURN_BUDGET_CHARS` | 200,000整轮上限|
| `DEFAULT_PREVIEW_SIZE_CHARS` | 1,500内联预览长度|
### 2.4 CLI 渲染方式
**文件:`agent/display.py`**
- **工具进度**KawaiiSpinner 动画 + 一行摘要
- **文件编辑**:内联 colored unified diffwrite_file / patch 工具)
- **最终响应**Rich Panel 边框包裹主题色可换7 套 skin
### 2.5 会话持久化
**文件:`hermes_state.py`**
SQLite (`~/.hermes/state.db`) + FTS5 全文搜索:
- sessions 表元数据、模型配置、token 计数、费用、标题
- messages 表role、content、tool_call_id、reasoning、时间戳
### 2.6 值得借鉴的点
| 点 | 借鉴价值 |
|----|----------|
| 大输出溢出到磁盘 + 内联预览 | 解决 context window 溢出问题 |
| 3 层递进防御 | 对 ZCLAW 中间件链有参考价值 |
| 预算配置化 | 阈值可调,不同场景不同策略 |
---
## 三、对比分析ZCLAW 现状 vs 参考方案
### 3.1 现状差距
| 维度 | DeerFlow | ZCLAW 现状 | 差距 |
|------|----------|------------|------|
| 数据源 | 3 个工具(present_files/write_file/str_replace)主动注册 | 仅 streamStore 解析 tool output 的 filePath | 极窄,几乎不触发 |
| 持久化 | 磁盘文件 + LangGraph checkpoint | 纯内存 Zustand | 刷新即丢失 |
| 渲染-代码 | CodeMirror 只读 + 语法高亮 (140+ 语言) | 纯 `<pre>` 标签,无高亮 | 无高亮 |
| 渲染-Markdown | Streamdown (GFM+Math+KaTeX+RawHTML) | 手写 30 行正则渲染器 | 仅标题/粗体/列表 |
| 渲染-HTML | 沙箱 iframe | 不支持 | 无 |
| 渲染-图片 | `<img>` + iframe | 类型声明了无实现 | 无 |
| 渲染-表格 | GFM 表格 | 纯文本 `<pre>` | 无 |
| 面板布局 | react-resizable-panels 60/40 | react-resizable-panels 65/35 | 已有,可复用 |
| 自动打开 | write_file/str_replace 触发 | addArtifact 时打开 | 已有 |
| 文件选择 | 下拉菜单不离开详情视图 | 必须返回列表再选 | 体验差 |
| 聊天内嵌 | present_files → 卡片网格 | 无 | 缺失 |
| 缓存 | React Query 5min | 无 | 缺失 |
| 双路径 | 真实路径 + write-file: 虚拟路径 | 仅运行时内存 | 缺失 |
| 右面板重叠 | 单一面板 | ArtifactPanel + RightPanel"文件"tab 职责交叉 | 架构问题 |
### 3.2 核心差距总结
**按优先级排列:**
1. **P0 数据源断裂** — 产物几乎没有来源,是最根本的问题
2. **P0 无持久化** — 产物刷新即丢
3. **P1 Markdown 渲染残缺** — 30 行正则 vs 完整 GFM 渲染器
4. **P1 代码无语法高亮** — 纯 `<pre>` vs CodeMirror/Shiki
5. **P2 双面板职责交叉** — ArtifactPanel vs RightPanel"文件"tab
6. **P2 缺少详情内文件切换** — 需返回列表才能切换文件
7. **P3 聊天内嵌产物卡片缺失**
8. **P3 HTML/图片/表格渲染缺失**
### 3.3 推荐方案
#### 方案 A最小可行基于现有架构补全
在现有 ArtifactPanel + artifactStore 上修补:
- **数据源**:扩展 streamStore 中的 tool output 解析,覆盖更多工具类型
- **持久化**artifactStore 追加 IndexedDB 写入(复用 messageStore 模式)
- **Markdown**:引入 `react-markdown` + `remark-gfm` 替换手写渲染器
- **代码高亮**:引入 `shiki``highlight.js`
- **合并面板**RightPanel "文件"tab 功能合并到 ArtifactPanel删除 RightPanel 的 files tab
**工作量**~2-3 天
#### 方案 B参照 DeerFlow 重构(推荐)
借鉴 DeerFlow 架构但适配 ZCLAW Tauri 本地架构:
| DeerFlow 组件 | ZCLAW 适配 |
|---------------|------------|
| FastAPI 产物路由 | Tauri 命令 `artifact_list` / `artifact_read` / `artifact_serve` |
| 磁盘 outputs/ 目录 | `{workspace}/artifacts/{session_key}/` |
| LangGraph checkpoint | SQLite (已有 zclaw-memory) |
| React Query 缓存 | TanStack Query 或 Zustand + stale cache |
| CodeMirror 只读 | 引入 @uiw/react-codemirror |
| Streamdown MD | react-markdown + remark-gfm + rehype-katex |
| iframe HTML 预览 | Tauri webview window (安全隔离) |
**核心改动清单:**
1. **Rust 侧**zclaw-kernel
- 新增 `artifact_create` / `artifact_list` / `artifact_read` Tauri 命令
- 产物写入 `{workspace}/artifacts/{session_key}/`
- 中间件链中 ToolEnd 事件触发产物注册
2. **前端 Store**
- artifactStore 增加 IndexedDB 持久化
- 从 streamStore 解耦产物创建逻辑到独立 hook
3. **前端组件**
- 替换 MarkdownPreview → react-markdown + GFM
- 引入 CodeMirror/shiki 代码高亮
- 详情视图增加文件下拉切换
- RightPanel "文件" tab 合并或移除
**工作量**~5-7 天
#### 方案 C借鉴 Hermes 防御机制(附加)
无论选 A 还是 B都可叠加 Hermes 的大输出防御:
- 中间件链 ToolOutputGuard 层增加溢出检测
- 超阈值产物自动持久化到磁盘,上下文替换为 `<persisted-output>` 预览
- agent 可通过 read_file 回读完整内容
---
## 四、关键依赖库参考
| 库 | 用途 | DeerFlow 使用 | 推荐 |
|----|------|--------------|------|
| react-markdown | Markdown 渲染 | ✅ (Streamdown) | ✅ |
| remark-gfm | GFM 表格/删除线/任务列表 | ✅ | ✅ |
| rehype-katex | 数学公式渲染 | ✅ | 按需 |
| @uiw/react-codemirror | 代码编辑器/高亮 | ✅ | ✅ |
| shiki | 静态代码高亮 | ✅ (chat 内代码块) | ✅ |
| react-resizable-panels | 分栏布局 | ✅ | 已有 |
| @tanstack/react-query | 数据缓存 | ✅ | 可选 |
---
## 五、文件索引
| 参考项目 | 关键路径 |
|----------|----------|
| DeerFlow 前端 | `G:/deerflow/frontend/src/components/workspace/artifacts/` |
| DeerFlow 前端工具 | `G:/deerflow/frontend/src/core/artifacts/` |
| DeerFlow 布局 | `G:/deerflow/frontend/src/components/workspace/chats/chat-box.tsx` |
| DeerFlow 代码编辑 | `G:/deerflow/frontend/src/components/workspace/code-editor.tsx` |
| DeerFlow 后端路由 | `G:/deerflow/backend/app/gateway/routers/artifacts.py` |
| DeerFlow 后端工具 | `G:/deerflow/backend/packages/harness/deerflow/tools/builtins/present_file_tool.py` |
| Hermes 输出管理 | `G:/hermes-agent-main/tools/tool_result_storage.py` |
| Hermes 预算配置 | `G:/hermes-agent-main/tools/budget_config.py` |