carry fb8bdc0fb6 feat: 实现真实算术工具调用循环,补全协议双向映射闭环
- 新增 tools.ts: calculate 工具定义 + 安全表达式执行引擎
- 重写 sendMessage: 非流式 tool execution loop,最多 5 轮迭代
- Live 模式使用独立系统上下文(仅含 calculate 工具,不依赖 demo)
- exportToOpenAIFormat 补上独立 role:tool 消息的导出分支
- 新增 standalone role:tool 导出测试用例
- 49 个测试全部通过
2026-06-09 15:08:45 +08:00
2026-06-07 13:44:36 +08:00

Prompt Envelope Protocol

让 LLM 上下文在聊天界面中可见、可读、可理解。

HCI 课程设计项目 · 设计系 · 2026


问题:聊天界面的"黑盒"困境

以 ChatGPT、Claude 为代表的 AI 聊天产品已拥有数亿用户,但其交互设计存在一个普遍问题——上下文不透明

当你在聊天框中输入一个问题,模型在回复时参考了:

  • 系统级的行为准则system prompt),决定了回复的风格和边界
  • 跨对话的用户记忆(memory),可能包含过时或不准的信息
  • 可用的工具列表tools)和技能(skills),你可能完全不知道它们的存在
  • 模板变量在后台被静默替换(如 {{user_name}} → "小明"

你粘贴了一篇长论文让 AI 分析,界面没有任何提示哪部分被模型"看到"了。AI 调用了三个工具,你看不到调用链。你引用了一份 PDF,不知道模型提取了其中哪些段落。

这些信息——对用户——全是黑的

设计问题

当前的聊天 UI 将 LLM 的所有上下文过度封装在 textarea 里。界面只关心「输入」和「输出」,把中间的一切当作实现细节。但 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(顶层文档)

interface PromptEnvelope {
  version: '1.0'
  model?: string            // 导出时使用的模型名
  messages: Message[]
}

Message(单条消息)

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

导出兼容

协议可无损导出为标准 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
  • 不做后端/数据库/用户认证

快速开始

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                        # 顶层布局

设计反思

这个项目本质上在探索一个更根本的问题:当 AI 系统变得越来越复杂,聊天界面该怎样让用户理解"系统在做什么"?

传统的聊天界面诞生于"人对人"的隐喻——一个输入框,一个输出框。但当这个输入框背后连接的是一个拥有记忆、工具、技能和复杂约束的 AI 系统时,一对一的 chat bubble 隐喻就不够了。

Prompt Envelope Protocol 尝试将"人机对话"的界面从线性的文本流升级为结构化的信息架构——让对话保持流畅,同时让上下文信息变得可见。这是一种对聊天界面的重新思考:不只是"AI 在说话",而是"AI 在什么条件下、用了什么资源、做了哪些步骤来生成这个回复"

S
Description
No description provided
Readme 248 KiB
Languages
TypeScript 99.3%
CSS 0.3%
HTML 0.2%
JavaScript 0.2%