# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. **项目语言:中文。所有注释、文档、commit message、UI 文案均使用中文。** ## 项目概述 HCI 课程设计项目:**Prompt Envelope Protocol** — 一套让 LLM 上下文在聊天界面中可见的协议与 UI。 核心思想:每条聊天消息由若干带类型的 **Segment** 组成(而非原始文本)。每种 Segment 有独立的视觉呈现,让 system prompt、memory、tools、skills、变量、文档、多媒体成为可见的一等元素,而非隐藏的上下文。 MVP 为纯前端 React 应用,使用 mock 数据演示。无后端、无真实 LLM 调用。 ## 命令 ```bash npm run dev # Vite 开发服务器 → http://localhost:5173 npm run build # tsc 类型检查 + vite 构建 npm test # vitest 运行全部测试(2 个文件,export.test.ts + parseSkill.test.ts,共 ~26 个用例) npm run preview # 预览生产构建 npx vitest run src/__tests__/export.test.ts # 运行单个测试文件 npx vitest run src/__tests__/parseSkill.test.ts ``` ## 架构 ### 数据模型 (`src/types/protocol.ts`) `PromptEnvelope { version, model?, messages: Message[] }` — 顶层协议文档。可选 `model` 字段用于导出时指定模型名(默认 `"gpt-4-turbo"`)。 `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`。 关键类型细节: - `StaticVarSegment` 有 `name`、`value`、可选 `description` — 会话级变量,如 `{{current_date}}` → `"2026年6月8日"`。**不在消息气泡内渲染**,由 `MessageList.extractSessionVars()` 提取到 `SessionBar` 横栏中 - `SkillItem` 有 `name`、`description`(L1 始终可见)、`body`(L2 触发时加载的 Markdown 正文)——遵循 Anthropic SKILL.md 规范 - `ToolItem` 有可选 `schema` 字段(JSON Schema 对象),导出时作为 `tools[].function.parameters` - `DocumentSegment` 有可选 `parsedContent` 字段,「查看解析」时用 `MarkdownRenderer` 展示 - `MemorySegment`、`SkillSegment` 有可选 `description` 字段,在 UI 中解释其用途 ### Skills 系统 遵循 Anthropic SKILL.md 规范,实现渐进式披露。管线: ``` SKILL.md 文件 (YAML frontmatter + Markdown body) │ ├─ parseSkillMarkdown() → ParsedSkill { name, description, body, bodyLineCount, ... } │ (src/utils/parseSkill.ts) 极简手写 YAML 解析器(不依赖外部库),提取顶层 string 键值 │ ├─ skills-loader.ts → PARSED_SKILLS (Record) │ (src/data/skills-loader.ts) 通过 Vite ?raw 导入 4 个真实 Anthropic SKILL.md 文件 │ + toSkillItem() / getRealSkills() / getAllRealSkillItems() │ ├─ skills.ts → ALL_SKILLS (Record) │ (src/data/skills.ts) 8 个手写 skill:code-review、deep-research、verify、simplify、 │ loop、summarize、translate、qa │ + getSkills(names) 按名称选取 │ ├─ 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(点击展开) ``` ### 渲染管线 消息在渲染前经过 `MessageList` 两步预处理: 1. **`extractSessionVars()`** — 从所有消息中提取 `static_var` segment,构建 `varMap`,并将其从消息体中移除。变量展示在 `SessionBar`(对话区顶部横栏) 2. **`extractToolMessages()`** — 将 `tool_call_request`/`tool_call_result` segment 拆分为独立 `role: "tool"` 消息 处理后的消息按 Segment kind 分发渲染: ``` SegmentRenderer (按 segment.kind 分发) ├─ TextSegmentView → MarkdownRenderer(react-markdown + remark-gfm,所有 HTML 元素自定义 Tailwind) ├─ static_var → 返回 null(已由 SessionBar 提取) ├─ SystemPromptView → CollapsiblePanel(灰色),支持 {{var}} 模板可视化渲染 ├─ MemoryView → CollapsiblePanel(紫色) ├─ SkillsView → CollapsiblePanel(绿色), SkillDisclosure 逐项逐层展开 ├─ ToolOverviewView → CollapsiblePanel(橙色),ToolItemRow 可独立展开 JSON Schema ├─ ToolCallRequestView → 深色终端风格,参数以标签化键值行展示 + 长值折叠 ├─ ToolCallResultView → 绿/红状态条(失败默认展开,成功默认折叠) ├─ DocumentCard → 文件图标 + 文件名 + 大小 + snippet,可选「查看解析」展开 Markdown ├─ LongTextView → 折叠态前2行预览 + 展开态用 MarkdownRenderer 渲染 └─ MediaView → 图片有 URL 时直接渲染缩略图;加载失败/无URL/非图片则图标占位 ``` `CollapsiblePanel` 是共享折叠容器,接收 `title`、`icon`、`color`、`bgColor`、`defaultCollapsed`、`badge` props。 `MarkdownRenderer` 是所有文本类组的共享 Markdown 渲染组件,覆盖 h1-h4、p、code、pre、table、blockquote、list 等所有常见元素的 Tailwind 样式。 ### 导出管线 (`src/utils/export.ts`) `exportToOpenAIFormat(envelope): OpenAIExport` 输出 `{ model, messages: OpenAIMessage[], tools?: OpenAITool[] }`——完整的 OpenAI Chat Completions request body。 关键映射: - `system_prompt`/`memory`/`skills` → 合并为一条 leading `role: "system"` 消息 - `system_prompt` 中的 `{{var}}` 占位符在导出时展开 - `tool_overview` → 顶层 `tools[]`,`item.schema` 映射为 `tool.function.parameters` - `tool_call_request` → `role: "assistant"` 消息带 `tool_calls[]`(自动生成 call_id) - `tool_call_result` → 独立 `role: "tool"` 消息,通过 call_id 与请求配对 - `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/`) 6 个场景(A–F),每个是独立的 JSON 文件(`demo-{a..f}.json`),通过 `manifest.json` 索引。默认激活场景 A(`"default": true` 在 manifest 中标记为场景 F,但 ChatContext 初始化为 `useState(0)`)。 加载流程:`JSON import (unknown) → validateEnvelope() → hydrateSkills() → PromptEnvelope` ### 状态管理 `ChatContext`(`src/context/ChatContext.tsx`)持有 `envelope`、`activeDemo`、`demos`。切换场景时替换整个 envelope。默认激活索引 0(场景 A)。ChatInput 已禁用。 ### 颜色系统 | 颜色 | Segment / 用途 | |--------|-------------------------------------| | 蓝色 | `static_var`(SessionBar + badge) | | 灰色 | `system_prompt` | | 紫色 | `memory` | | 绿色 | `skills` | | 橙色 | `tool_overview` | | 深色 | `tool_call_request`(代码块) | | 绿/红 | `tool_call_result`(成功/失败) | | 琥珀 | `role: "tool"` 消息气泡 | ### 布局 双栏:左侧 `ChatView`(SessionBar + MessageList + ChatInput),右侧 `ProtocolPanel`(双 Tab:OpenAI Format / Raw Protocol,支持复制/下载,底部统计信息)。顶栏有 6 个 Demo 场景切换按钮。