feat: 更新 CLAUDE.md 文档,完善数据模型和渲染管线说明,修正命令行测试描述

This commit is contained in:
carry
2026-06-08 21:25:36 +08:00
parent b767106d61
commit 47f54e9770
+65 -60
View File
@@ -17,7 +17,7 @@ MVP 为纯前端 React 应用,使用 mock 数据演示。无后端、无真实
```bash ```bash
npm run dev # Vite 开发服务器 → http://localhost:5173 npm run dev # Vite 开发服务器 → http://localhost:5173
npm run build # tsc 类型检查 + vite 构建 npm run build # tsc 类型检查 + vite 构建
npm test # vitest 运行全部测试(26 个,位于 export.test.ts parseSkill.test.ts npm test # vitest 运行全部测试(2 个文件export.test.ts + parseSkill.test.ts,共 ~26 个用例
npm run preview # 预览生产构建 npm run preview # 预览生产构建
npx vitest run src/__tests__/export.test.ts # 运行单个测试文件 npx vitest run src/__tests__/export.test.ts # 运行单个测试文件
npx vitest run src/__tests__/parseSkill.test.ts npx vitest run src/__tests__/parseSkill.test.ts
@@ -27,107 +27,112 @@ npx vitest run src/__tests__/parseSkill.test.ts
### 数据模型 (`src/types/protocol.ts`) ### 数据模型 (`src/types/protocol.ts`)
`PromptEnvelope { version, model, messages: Message[] }` — 顶层协议文档。 `PromptEnvelope { version, model?, messages: Message[] }` — 顶层协议文档。可选 `model` 字段用于导出时指定模型名(默认 `"gpt-4-turbo"`)。
`Message { id, role: "system"|"user"|"assistant"|"tool", segments: Segment[], timestamp }` — 单条聊天消息。 `Message { id, role: "system"|"user"|"assistant"|"tool", segments: Segment[], timestamp }` — 单条聊天消息。`role: "tool"` 用于独立的工具调用消息。
`Segment` 是基于 `kind` 的可辨识联合类型,共 11 种变体:`text``static_var``system_prompt``memory``skills``tool_overview``tool_call_request``tool_call_result``document``long_text``media` `Segment` 是基于 `kind` 的可辨识联合类型,共 11 种变体:`text``static_var``system_prompt``memory``skills``tool_overview``tool_call_request``tool_call_result``document``long_text``media`
部分 Segment 类型(MemorySegment、SkillSegment)有可选 `description` 字段,用于在 UI 中解释这些项的用途。 关键类型细节:
- `StaticVarSegment``name``value`、可选 `description` — 会话级变量,如 `{{current_date}}``"2026年6月8日"`。**不在消息气泡内渲染**,由 `MessageList.extractSessionVars()` 提取到 `SessionBar` 横栏中
ToolItem 有可选 `schema` 字段(JSON Schema 对象),在工具总览面板中可展开查看。 - `SkillItem``name``description`L1 始终可见)、`body`L2 触发时加载的 Markdown 正文)——遵循 Anthropic SKILL.md 规范
- `ToolItem` 有可选 `schema` 字段(JSON Schema 对象),导出时作为 `tools[].function.parameters`
DocumentSegment 有可选 `parsedContent` 字段,点击「查看解析」可展开 AI 对文档内容的提取结果。 - `DocumentSegment` 有可选 `parsedContent` 字段,「查看解析」时用 `MarkdownRenderer` 展示
- `MemorySegment``SkillSegment` 有可选 `description` 字段,在 UI 中解释其用途
### Skills 系统 ### Skills 系统
Skills 遵循 Anthropic SKILL.md 规范,实现渐进式披露: 遵循 Anthropic SKILL.md 规范,实现渐进式披露。管线
``` ```
SKILL.md 文件 (YAML frontmatter + Markdown body) SKILL.md 文件 (YAML frontmatter + Markdown body)
├─ parseSkillMarkdown() → ParsedSkill { name, description, body, ... } ├─ parseSkillMarkdown() → ParsedSkill { name, description, body, bodyLineCount, ... }
│ (src/utils/parseSkill.ts) YAML 通过极简手写解析器提取(不依赖外部库) │ (src/utils/parseSkill.ts) 极简手写 YAML 解析器(不依赖外部库),提取顶层 string 键值
├─ skills-loader.ts → PARSED_SKILLS (Record<string, ParsedSkill>) ├─ skills-loader.ts → PARSED_SKILLS (Record<string, ParsedSkill>)
│ (src/data/skills-loader.ts) 通过 Vite ?raw 导入 4 个真实 SKILL.md 文件 │ (src/data/skills-loader.ts) 通过 Vite ?raw 导入 4 个真实 Anthropic SKILL.md 文件
│ + toSkillItem() / getRealSkills() / getAllRealSkillItems()
├─ skills.ts → ALL_SKILLS (Record<string, SkillItem>) ├─ skills.ts → ALL_SKILLS (Record<string, SkillItem>)
│ (src/data/skills.ts) 8 个内置自定义 skill,用于 Demo C/D/E │ (src/data/skills.ts) 8 个手写 skillcode-review、deep-research、verify、simplify、
│ loop、summarize、translate、qa
│ + getSkills(names) 按名称选取
UI: SkillsView → L1 名称+描述(始终可见),L2 body(点击展开) demos-loader.ts → buildSkillLookup() 合并两个源 → hydrateSkills(envelope)
│ (src/data/demos/demos-loader.ts) 按 name 匹配,补全 JSON 中只包含 name+description 的 skill items
│ + validateEnvelope() 运行时类型守卫
│ + loadEnvelope() = validateEnvelope + hydrateSkills
└─ UI: SkillsView → SkillDisclosure 逐项展开:L1 name+description(始终可见),L2 body(点击展开)
``` ```
**两类 skill 数据源:**
- `src/data/skills.ts` — 8 个手写 SkillItemcode-review、deep-research、verify 等),通过 `getSkills(names)` 按名称选取
- `src/data/skills-loader.ts` — 4 个真实 Anthropic SKILL.md 文件(webapp-testing、pdf、doc-coauthoring、mcp-builder),通过 `parseSkillMarkdown()` 解析,`getRealSkills(names)` 获取
**parseSkillMarkdown 测试:** 位于 `src/__tests__/parseSkill.test.ts`,覆盖 YAML 解析、body 提取、必填字段校验、可选字段、统计信息等 7 个用例。
### 渲染管线 ### 渲染管线
消息在渲染前经过 `MessageList` 两步预处理:
1. **`extractSessionVars()`** — 从所有消息中提取 `static_var` segment,构建 `varMap`,并将其从消息体中移除。变量展示在 `SessionBar`(对话区顶部横栏)
2. **`extractToolMessages()`** — 将 `tool_call_request`/`tool_call_result` segment 拆分为独立 `role: "tool"` 消息
处理后的消息按 Segment kind 分发渲染:
``` ```
SegmentRenderer (按 segment.kind 分发) SegmentRenderer (按 segment.kind 分发)
├─ TextSegmentView → MarkdownRendererreact-markdown + GFM ├─ TextSegmentView → MarkdownRendererreact-markdown + remark-gfm,所有 HTML 元素自定义 Tailwind
├─ StaticVarBadge → 蓝色内联 pill 标签 ├─ static_var → 返回 null(已由 SessionBar 提取)
├─ SystemPromptView → CollapsiblePanel(灰色,默认折叠 ├─ SystemPromptView → CollapsiblePanel(灰色),支持 {{var}} 模板可视化渲染
├─ MemoryView → CollapsiblePanel(紫色,默认折叠 ├─ MemoryView → CollapsiblePanel(紫色)
├─ SkillsView → CollapsiblePanel(绿色,默认折叠 ├─ SkillsView → CollapsiblePanel(绿色) SkillDisclosure 逐项逐层展开
├─ ToolOverviewView → CollapsiblePanel(橙色,默认折叠,可展开 JSON Schema ├─ ToolOverviewView → CollapsiblePanel(橙色),ToolItemRow 可独立展开 JSON Schema
├─ ToolCallRequestView → 深色终端风格代码块,参数以标签化键值行展示 ├─ ToolCallRequestView → 深色终端风格,参数以标签化键值行展示 + 长值折叠
├─ ToolCallResultView → 绿/红状态条(失败默认展开,成功默认折叠) ├─ ToolCallResultView → 绿/红状态条(失败默认展开,成功默认折叠)
├─ DocumentCard → 文件图标 + 文件名 + 大小 + 摘要预览 ├─ DocumentCard → 文件图标 + 文件名 + 大小 + snippet,可选「查看解析」展开 Markdown
├─ LongTextView → 折叠态展示前2行预览 + 展开态用 MarkdownRenderer 渲染 ├─ LongTextView → 折叠态前2行预览 + 展开态用 MarkdownRenderer 渲染
└─ MediaView → 图标 + 类型标签 + alt 文本 └─ MediaView → 图片有 URL 时直接渲染缩略图;加载失败/无URL/非图片则图标占位
``` ```
`CollapsiblePanel` 是共享折叠容器组件。折叠/展开状态通过 `useState(segment.collapsed)` 管理——协议数据提供默认值,UI 控制运行时切换 `CollapsiblePanel` 是共享折叠容器,接收 `title``icon``color``bgColor``defaultCollapsed``badge` props
`MarkdownRenderer` 封装 `react-markdown` + `remark-gfm`。所有 HTML 元素(h1h4、p、code、pre、table、blockquote 等)均有自定义 Tailwind 样式,针对聊天气泡内嵌场景做了紧凑化处理 `MarkdownRenderer` 是所有文本类组的共享 Markdown 渲染组件,覆盖 h1-h4、p、code、pre、table、blockquote、list 等所有常见元素的 Tailwind 样式
### 状态管理
`ChatContext``src/context/ChatContext.tsx`)持有当前 `envelope``activeDemo` 索引。切换 Demo 场景时替换整个 envelope。ChatInput 已禁用(MVP 无真实输入)。
### 导出管线 (`src/utils/export.ts`) ### 导出管线 (`src/utils/export.ts`)
`exportToOpenAIFormat(envelope)` 输出 OpenAI 兼容 JSON,结构为 `{ model, messages, tools? }` `exportToOpenAIFormat(envelope): OpenAIExport` 输出 `{ model, messages: OpenAIMessage[], tools?: OpenAITool[] }`——完整的 OpenAI Chat Completions request body。
- 结构性 Segment`system_prompt``memory``skills``tool_overview`)提取为一条 `role: "system"` 的头部消息 关键映射:
- `tool_overview` 的 items 转为顶层 `tools` 数组(OpenAI function-calling 格式) - `system_prompt`/`memory`/`skills` → 合并为一条 leading `role: "system"` 消息
- `tool_call_request` → 带 `tool_calls` 数组的 assistant 消息;`tool_call_result` `role: "tool"` 消息 - `system_prompt` 中的 `{{var}}` 占位符在导出时展开
- `static_var` 展开为变量值 - `tool_overview` → 顶层 `tools[]``item.schema` 映射为 `tool.function.parameters`
- `text` `long_text` 原文输出 - `tool_call_request` `role: "assistant"` 消息带 `tool_calls[]`(自动生成 call_id
- `media``altText` 作为回退文本 - `tool_call_result` → 独立 `role: "tool"` 消息,通过 call_id 与请求配对
- `segmentToText()` 对结构性/tool Segment 返回 `null`——它们在消息级别统一处理 - `static_var` → user/assistant 消息中展开为值;system 消息中只参与模板展开,**不**直接出现在 system 消息正文中
- 含图片 media 的 user 消息 → 多模态 `content` 数组 (`[{type: "text"}, {type: "image_url"}]`)
- `document` / `long_text` → 原文输出(带 `[Document: ...]` 标注)
- `segmentToText()` 对结构性/tool Segment 返回 `null`——在消息级别统一处理
### Demo 数据 (`src/data/demos.ts` → `src/data/demos/`) ### Demo 数据 (`src/data/demos/`)
6 个场景(AF),每个场景是一个完整的 `PromptEnvelope`,对话内容为中文。默认激活索引 4(场景 F——真实 Anthropic Skills 加载)。 6 个场景(AF),每个是独立的 JSON 文件(`demo-{a..f}.json`),通过 `manifest.json` 索引。默认激活场景 A`"default": true` 在 manifest 中标记为场景 F,但 ChatContext 初始化为 `useState(0)`)。
| 索引 | 场景 | 内容 | 加载流程:`JSON import (unknown) → validateEnvelope() → hydrateSkills() → PromptEnvelope`
|------|--------|---------------------------------------------------|
| 0 | 场景 A | 基础对话 + System Prompt + Memory |
| 1 | 场景 B | 工具调用:请求 → 执行(成功 & 失败) |
| 2 | 场景 C | 文档解析:点击「查看解析」看 AI 如何提取文档内容 |
| 3 | 场景 D | 综合:覆盖全部 11 种 Segment |
| 4 | 场景 F | 真实 Anthropic SkillsSKILL.md 文件加载 + parseSkillMarkdown 解析) |
| 5 | 场景 E | 日志分析:异常检测 + 安全审计 + 性能分析 |
每个场景文件位于 `src/data/demos/demo-{a..f}.ts``demos.ts` 仅做聚合导出。 ### 状态管理
`ChatContext``src/context/ChatContext.tsx`)持有 `envelope``activeDemo``demos`。切换场景时替换整个 envelope。默认激活索引 0(场景 A)。ChatInput 已禁用。
### 颜色系统 ### 颜色系统
| 颜色 | 对应 Segment | | 颜色 | Segment / 用途 |
|--------|--------------------------------------| |--------|-------------------------------------|
| 蓝色 | `static_var` | | 蓝色 | `static_var`SessionBar + badge |
| 灰色 | `system_prompt` | | 灰色 | `system_prompt` |
| 紫色 | `memory` | | 紫色 | `memory` |
| 绿色 | `skills` | | 绿色 | `skills` |
| 橙色 | `tool_overview` | | 橙色 | `tool_overview` |
| 深色 | `tool_call_request`(代码块) | | 深色 | `tool_call_request`(代码块) |
| 绿/红 | `tool_call_result`(成功/失败) | | 绿/红 | `tool_call_result`(成功/失败) |
| 琥珀 | `role: "tool"` 消息气泡 |
### 布局 ### 布局
双栏:左侧 `ChatView`MessageList + ChatInput),右侧 `ProtocolPanel`实时 OpenAI Format JSON 视图,支持复制/下载)。顶栏有 Demo 场景切换按钮。 双栏:左侧 `ChatView`SessionBar + MessageList + ChatInput),右侧 `ProtocolPanel`双 TabOpenAI Format / Raw Protocol,支持复制/下载,底部统计信息)。顶栏有 6 个 Demo 场景切换按钮。