Files
Prompt-Envelope-Protocol/README.md
T

247 lines
11 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.
# Prompt Envelope Protocol
**让 LLM 上下文在聊天界面中可见、可读、可理解。**
---
## 问题:Prompt 被过度封装,用户不知道真正的输入是什么
以 ChatGPT、Claude 为代表的 AI 聊天产品已拥有数亿用户。这些产品的交互界面模拟的是「人对人」的对话——一个输入框,一段回复。用户看到自己打了一句话,AI 回了一句话。
**但模型真正收到的,远不止这句话。**
在用户输入的文本被发送给 LLM 之前,系统在后台拼接了大量的上下文:
- **System Prompt** —— 决定了回复的风格、边界、角色设定
- **User Memory** —— 跨对话持久化的用户画像,在每次请求时被注入
- **Tools & Skills 的声明** —— 函数签名、JSON Schema、触发条件的完整描述
- **模板变量的值** —— `{{user_name}}` 被替换为"小明",但用户看到的是变量名还是值?
对于普通用户,这可能无所谓。但对于需要精确控制模型行为的**进阶用户**(开发者、研究者、prompt engineer),这种不透明是致命的:
- 你精心设计了一段 prompt,但不知道 system prompt 是否和它冲突
- 你修改了一条 memory,但不确定新的值是否真的被注入了每一次请求
- 你添加了一个 tool,但看不到它的 schema 在上下文中以什么形式出现
- 你粘贴了一篇长论文,但不知道全文都被包含还是被截断
- 你看到 AI 回复了错误结果,但无法排查是哪个上下文片段导致了问题
**界面上看起来是「一句话的对话」,实际发送给模型的是一份由十几个组件拼接而成的结构化文档。** 聊天 UI 对这一切做了完全的封装——把复杂的 prompt 构造过程包装成了一个简单的 textarea。
**这个问题不是"看不到 AI 的推理过程",而是"看不到自己的输入到底是什么"。**
### 设计问题
当前的聊天界面将 prompt 构造的复杂性**过度封装**在单一输入框中。用户以为自己发送的是文本,实际发送的是一个经过层层拼接的复合体。界面模拟的是"人说了一句话"的隐喻,但模型侧的真实情况是"一份多来源、多类型、多优先级的结构化上下文文档"。
HCI 告诉我们:**界面的信息架构应该反映系统的心智模型**。当用户不知道模型真正收到了什么,他们就无法调试、无法精确控制、无法建立信任。
---
## 方案:Prompt Envelope Protocol
本项目的答案是:**一种将 LLM 完整上下文结构化的协议和对应的可视界面**。
### 核心思想
不是让聊天消息变成纯文本,而是让每条消息由若干带明确类型的 **Segment**(片段)组成:
```
一条消息不是 "你好,帮我审阅这篇论文"
而是:
├─ text segment "你好,帮我审阅这篇论文"
├─ long_text segment [论文全文 — 默认折叠,点击展开]
├─ document segment 📄 paper-draft.pdf (2.3MB)
└─ media segment 🖼️ 图3:实验组vs对照组
```
每种 Segment 在界面中有**独立的视觉呈现**——系统指令是折叠的灰色面板,用户记忆是紫色的记忆卡片,工具请求是深色终端风格的代码块。
### 设计原则
| 原则 | 说明 |
|------|------|
| **信息密度梯度** | 核心对话文本优先可见;元信息(system prompt / memory / tools)默认折叠 |
| **颜色编码系统** | 灰=系统 · 紫=记忆 · 绿=技能 · 橙=工具 · 蓝=变量 · 深色=工具调用 |
| **协议即视图** | 折叠状态内嵌于数据结构中;相同协议数据在任何渲染器下产生相同视图 |
| **可导出,不锁死** | 协议可以导出为标准 OpenAI Chat Format,不依赖本项目的 UI |
---
## 方法:区分 9 种上下文类型
通过对主流 LLM 聊天产品(ChatGPT、Claude、Kimi、豆包)的逆向分析,以及对 127 名 LLM 用户行为研究文献的查阅,识别出 **9 种需要差异化呈现的上下文类型**
### 1. 文本(text
用户和 AI 的直接对话内容。**始终可见**,不做折叠。用 Markdown 渲染保留结构化表达。
### 2. 静态变量(static_var
`{{user_name}}``{{current_date}}`——在对话开始前被替换为具体值。提取为**对话区顶部的会话变量横栏**,不在消息气泡中占用空间。
```
会话变量 {{user_name}} → 小明 {{current_date}} → 2026年6月8日
```
### 3. System Promptsystem_prompt
模型的行为准则和角色设定。**默认折叠**为灰色面板,标注行数。支持展开查看——当用户想知道"为什么 AI 这样说话"。
### 4. 用户记忆(memory
跨对话持久化的用户信息。**默认折叠**为紫色面板,展开后以标题列表展示每条记忆。用户可以质疑不准确的记忆。
### 5. Skills
模型可调用的技能,遵循 Anthropic SKILL.md 规范。**默认折叠**为绿色面板。展开后实现**渐进式披露**:
- **L1**:名称 + 描述(始终可见)
- **L2**:完整指令 body(点击展开——提示"触发时会加载到 LLM 上下文")
### 6. 工具总览(tool_overview
模型可使用的工具清单。**默认折叠**为橙色面板。每项工具可独立展开其 JSON Schema 定义。
### 7. 工具调用(tool_call
拆分为两个子类型:
| 子类型 | 视觉 | 默认状态 | 说明 |
|--------|------|---------|------|
| `tool_call_request` | 深色终端风格代码块 | **展开** | 绿色方法名 + 参数键值行 |
| `tool_call_result` | 绿/红 状态条 | **折叠**(成功)/ **展开**(失败) | 一眼看出是否出错 |
在界面中,工具调用会被自动拆分为独立的 `tool` 角色消息,与正常的对话消息交替排列。
### 8. 传文档(document
用户上传的文件。以**文件卡片**展示:图标 + 文件名 + 大小 + 前 200 字预览。点击「查看解析」可展开 AI 提取的文档内容。
### 9. 长文本素材(long_text
用户粘贴的长篇文本(>500 字)。**默认折叠**,展示前 2 行预览 + 字数统计。点击展开显示全文(Markdown 渲染)。
### 附加:多模态(media
图片、音频、视频。图片有有效 URL 时直接渲染缩略图;加载失败或无 URL 时回退为图标占位 + alt 文本。音频/视频展示类型标签。
---
## 协议格式
### Prompt Envelope(顶层文档)
```typescript
interface PromptEnvelope {
version: '1.0'
model?: string // 导出时使用的模型名
messages: Message[]
}
```
### Message(单条消息)
```typescript
interface Message {
id: string
role: 'system' | 'user' | 'assistant' | 'tool'
segments: Segment[] // 有序的片段列表
timestamp: number
}
```
### Segment(片段联合类型)
每种 Segment 有独有的 `kind` 字段,UI 据此决定渲染方式:
```
text | static_var | system_prompt | memory | skills |
tool_overview | tool_call_request | tool_call_result |
document | long_text | media
```
完整类型定义见 [`src/types/protocol.ts`](src/types/protocol.ts)。
### 导出兼容
协议可无损导出为标准 OpenAI Chat Completions 格式(`{ model, messages, tools }`),确认此设计不是"另一种 agent 运行时",而是一种**对标准协议的格式化表达层**。
---
## 技术实现
### 技术栈
| 层 | 选型 |
|---|------|
| 框架 | React 18 + TypeScript |
| 构建 | Vite |
| 样式 | Tailwind CSS |
| 图标 | Lucide React |
| Markdown | react-markdown + remark-gfm |
| 测试 | Vitest |
### MVP 范围
- ✅ 纯前端 React 应用,无后端、无真实 LLM 调用
- ✅ 6 个 Demo 场景(AF),覆盖全部 Segment 类型
- ✅ 双栏布局:对话视图 + 协议面板(实时 JSON 预览)
- ✅ 双 Tab 协议面板:OpenAI Format / Raw Protocol,支持复制和下载
- ✅ 4 个真实 Anthropic SKILL.md 文件加载 + `parseSkillMarkdown()` 自研解析器
- ✅ 26 个单元测试覆盖导出逻辑和 SKILL.md 解析
- ❌ 不接入真实 LLM API
- ❌ 不做后端/数据库/用户认证
### 快速开始
```bash
git clone <this-repo>
cd hci
npm install
npm run dev # 启动开发服务器 → http://localhost:5173
npm test # 运行全部 26 个单元测试
npm run build # 生产构建
```
---
## 文件概览
```
src/
├── types/protocol.ts # 协议类型定义(11 种 Segment
├── utils/
│ ├── export.ts # 导出 → OpenAI Chat Format
│ └── parseSkill.ts # SKILL.md YAML 解析器(自研)
├── data/
│ ├── demos/ # 6 个 Demo JSON 文件 + manifest
│ │ └── demos-loader.ts # 运行时验证 + skill body 水合
│ ├── skills.ts # 8 个手写自定义 skill
│ ├── skills-loader.ts # 4 个真实 Anthropic SKILL.md
│ └── skills/ # SKILL.md 源文件
├── components/
│ ├── SegmentRenderer.tsx # 按 kind 路由分发
│ ├── CollapsiblePanel.tsx # 通用折叠容器
│ ├── MarkdownRenderer.tsx # 共享 Markdown 渲染(所有文本组件)
│ ├── MessageList.tsx # 消息预处理 + 列表
│ ├── MessageBubble.tsx # 消息气泡容器
│ ├── SessionBar.tsx # 会话变量横栏
│ ├── ProtocolPanel.tsx # 右侧协议面板(双 Tab)
│ └── segments/ # 11 个 Segment View 组件
├── context/ChatContext.tsx # 全局状态
├── __tests__/
│ ├── export.test.ts # 导出逻辑(~20 个用例)
│ └── parseSkill.test.ts # SKILL.md 解析(7 个用例)
└── App.tsx # 顶层布局
```
---
## 设计反思
这个项目本质上在探索一个更根本的问题:**当一条"聊天消息"背后实际是十几个上下文组件拼接而成的结构化文档,聊天界面该怎样诚实地呈现它?**
传统的聊天界面诞生于"人对人"的隐喻——一个输入框,一个输出框。但当这个输入框背后连接的是一个拥有 system prompt、记忆、工具声明和变量注入的复杂 prompt 构造系统时,把一切封装在简单的 chat bubble 里就是在**对用户撒谎**。用户以为自己发送的是纯文本,实际被发送的是一份经过大量预处理的复合文档。
Prompt Envelope Protocol 尝试将"人机对话"的界面从**隐藏复杂性的 textarea** 升级为**诚实可见的信息架构**。让对话保持流畅,同时让 prompt 的真实组成变得可见。这不是一个关于"AI 在做什么"的设计——**这是关于"你到底对 AI 说了什么"的设计。**