- MarkdownRenderer: 从 StreamingText 提取共享 react-markdown + remark-gfm 组件 - ArtifactPanel: 替换手写 MarkdownPreview 为完整 GFM 渲染,添加文件选择器下拉菜单 - 数据源: file_write/str_replace 双工具 + sendMessage/initStreamListener 双路径 - 持久化: artifactStore 添加 zustand persist + IndexedDB (复用 idb-storage)
13 KiB
产物系统参考文档
调研 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 hook,5 分钟缓存 |
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 Query,5 分钟 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_replacetool call 时自动打开面板并选中文件 autoOpen/autoSelect标志防止用户手动关闭后重复打开
代码/预览切换
- HTML/Markdown 默认 Preview,其他默认 Code
- Preview 用 Streamdown(MD) 或 iframe(HTML)
头部操作栏
- 文件选择器下拉菜单(不用返回列表即可切换)
- 复制 / 下载 / 新窗口打开 / 关闭
聊天内嵌展示
present_filestool call → 聊天流内渲染卡片网格- 点击卡片 → 侧栏打开该文件
双路径方案
- 真实文件路径 — 从后端 API 获取,React Query 缓存
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 字符 → 最大的几个溢出到磁盘 |
核心逻辑:
# 超阈值时:完整内容写入文件,上下文替换为预览
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 diff(write_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 核心差距总结
按优先级排列:
- P0 数据源断裂 — 产物几乎没有来源,是最根本的问题
- P0 无持久化 — 产物刷新即丢
- P1 Markdown 渲染残缺 — 30 行正则 vs 完整 GFM 渲染器
- P1 代码无语法高亮 — 纯
<pre>vs CodeMirror/Shiki - P2 双面板职责交叉 — ArtifactPanel vs RightPanel"文件"tab
- P2 缺少详情内文件切换 — 需返回列表才能切换文件
- P3 聊天内嵌产物卡片缺失
- 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 (安全隔离) |
核心改动清单:
-
Rust 侧(zclaw-kernel):
- 新增
artifact_create/artifact_list/artifact_readTauri 命令 - 产物写入
{workspace}/artifacts/{session_key}/ - 中间件链中 ToolEnd 事件触发产物注册
- 新增
-
前端 Store:
- artifactStore 增加 IndexedDB 持久化
- 从 streamStore 解耦产物创建逻辑到独立 hook
-
前端组件:
- 替换 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 |