第 23 期 | 图形化思维:Excalidraw MCP 绘图实战
🎯 学习目标
- 理解 Claude MCP (Multi-Modal Command Protocol) 如何实现终端内的富媒体渲染,特别是针对图形界面的呈现。
- 掌握 Excalidraw 的核心 JSON 结构,并理解其作为 AI 生成白板图的理想中间格式。
- 学习如何利用 React Server Apps (RSA) 扩展,将 AI 生成的 Excalidraw 数据在终端内部"流式渲染"为交互式图形。
- 探索通过自然语言提示(Prompt Engineering)直接驱动 AI 生成和修改架构图、流程图等可视化内容的方法。
📖 核心概念讲解
在传统的命令行界面中,我们习惯于文本输出。然而,随着 AI 助手能力的增强,我们对交互体验的期望也水涨船高。本期我们将深入探讨 Claude 如何超越文本,在终端内部直接“绘制”图形,实现真正的图形化思维。
23.1 Claude MCP 与 React Server Apps (RSA):终端富媒体的基石
Claude 的 MCP (Multi-Modal Command Protocol) 是其实现多模态交互的核心机制。它允许 Claude 不仅仅返回文本,还可以调用工具、执行代码,甚至渲染复杂的 UI 组件。当涉及到在终端内渲染丰富的图形界面时,React Server Apps (RSA) 成为了关键技术。
React Server Apps (RSA) 是一种将 React 组件在服务器端渲染,并将结果以特定协议(如 JSON)传输到客户端(如 Claude 的终端环境)进行显示的架构。与传统的客户端渲染不同,RSA 允许服务器完全控制渲染逻辑和数据流,从而在终端内实现高度定制和动态的 UI。
RSA 的核心优势在于:
- 服务器端逻辑与渲染分离: AI 可以在服务器端生成 UI 的状态和结构,而无需关心客户端的具体渲染细节。
- 富媒体支持: 突破纯文本限制,可以在终端内渲染图片、图表、甚至交互式组件。
- 安全性与控制: 服务器端对渲染内容拥有完全控制权,可以确保安全性和一致性。
- 低客户端开销: 终端客户端只需解析和显示由服务器端发送的轻量级 UI 描述。
在我们的场景中,Claude 会通过其工具调用机制,触发一个生成 Excalidraw JSON 的逻辑,然后将这个 JSON 数据传递给一个预定义的 RSA 组件。这个 RSA 组件负责将 Excalidraw JSON 渲染成实际的图形,并将其流式传输回 Claude 的终端。
graph LR
A[用户 Prompt] --> B(Claude AI)
B -- 调用工具 --> C{DiagramGeneratorTool}
C -- 生成 Excalidraw JSON --> D[React Server App (Excalidraw Renderer)]
D -- 渲染并流式传输 UI --> E[Claude 终端]
E -- 显示图形 --> F[用户看到图形]23.2 Excalidraw:AI 生成白板图的理想中间格式
Excalidraw 是一款流行的手绘风格白板工具,其最大的特点是其图形数据结构非常简洁且是纯 JSON 格式。这使得它成为 AI 生成和解析图形的绝佳选择。
一个 Excalidraw 图形由一系列 elements 组成,每个 element 都是一个 JSON 对象,描述了图形中的一个组件(如矩形、圆形、文本、箭头等)。这些 elements 包含了位置、大小、颜色、文本内容、连接关系等关键属性。
Excalidraw JSON 的优势:
- 结构清晰: 易于 AI 理解和生成。
- 可编程性强: 可以通过代码轻松创建、修改和组合图形元素。
- 手绘风格: 即使是 AI 生成的图,也具有亲和力,不显得过于冰冷。
- 跨平台: 可以导出为 SVG、PNG,或在 Excalidraw 应用中直接打开。
当 AI 需要生成一个架构图时,它不再需要生成复杂的 SVG 或图片文件,只需生成符合 Excalidraw 规范的 JSON 数据。随后,这个 JSON 数据会被 RSA 组件接收并渲染。
Excalidraw JSON 示例(简化版):
{
"type": "excalidraw",
"version": 2,
"elements": [
{
"id": "A",
"type": "rectangle",
"x": 100,
"y": 100,
"width": 150,
"height": 80,
"strokeColor": "#000000",
"backgroundColor": "rgba(197,208,230,0.5)",
"fillStyle": "solid",
"text": "Web Server"
},
{
"id": "B",
"type": "rectangle",
"x": 350,
"y": 100,
"width": 150,
"height": 80,
"strokeColor": "#000000",
"backgroundColor": "rgba(239,208,197,0.5)",
"fillStyle": "solid",
"text": "Database"
},
{
"id": "C",
"type": "arrow",
"x": 250,
"y": 140,
"width": 100,
"height": 0,
"startBinding": { "elementId": "A", "focus": 0 },
"endBinding": { "elementId": "B", "focus": 0 },
"strokeColor": "#000000"
}
],
"appState": {
"zoom": { "value": 1 },
"scrollX": 0,
"scrollY": 0
}
}
上述 JSON 描述了一个简单的 Web Server 连接到 Database 的架构图。AI 只需要理解这些基本元素和它们的属性,就可以构建出任意复杂的图形。
💻 模拟 Claude TUI 交互
以下模拟了用户在 Claude TUI 中通过对话生成和修改 Excalidraw 图形的过程。
> 请帮我画一个简单的 Web 应用架构图,包含一个客户端、一个Web服务器和一个数据库。
╭─ Claude ──────────────────────────────╮
│ 好的,正在为您生成 Web 应用架构图。 │
│ │
│ 思考过程: │
│ 1. 用户请求生成一个包含客户端、Web服务器和数据库的Web应用架构图。 │
│ 2. 识别到需要调用 `generate_excalidraw_diagram` 工具。 │
│ 3. 构造工具参数,将用户描述转化为 Excalidraw JSON 元素的抽象表示。 │
│ 4. 调用工具,并等待其返回 Excalidraw JSON 数据。 │
│ 5. 将返回的 Excalidraw JSON 数据传递给 React Server App 渲染器。 │
│ │
│ <tool_code> │
│ print(tools.generate_excalidraw_diagram(description="a simple web application architecture with a client, a web server, and a database"))
│ </tool_code> │
│ │
│ <tool_result> │
│ {"type": "excalidraw_data", "data": "..."} // 实际会是完整的 Excalidraw JSON 数据,此处省略
│ </tool_result> │
│ │
│ // Claude 收到渲染指令,并通过 RSA 协议在终端内渲染 Excalidraw 图形
│ // 终端内显示一个手绘风格的图:
│ // +-----------------+ +-----------------+ +-----------------+
│ // | Client | <--> | Web Server | <--> | Database |
│ // | (Browser/App) | | (Node.js/Py) | | (PostgreSQL) |
│ // +-----------------+ +-----------------+ +-----------------+
│ │
│ 这是您请求的 Web 应用架构图。您希望对此图进行哪些修改或添加? │
╰───────────────────────────────────────╯
> 在 Web 服务器和数据库之间添加一个缓存层,比如 Redis。
╭─ Claude ──────────────────────────────╮
│ 好的,正在为您的架构图添加 Redis 缓存层。 │
│ │
│ 思考过程: │
│ 1. 用户请求在现有图的 Web 服务器和数据库之间添加 Redis 缓存层。 │
│ 2. 识别到需要调用 `update_excalidraw_diagram` 工具,传入当前图的 ID 或内容,以及修改指令。
│ 3. AI 分析指令,确定如何在 Excalidraw JSON 中插入新的 Redis 元素并调整连接。
│ 4. 调用工具,获取更新后的 Excalidraw JSON 数据。 │
│ 5. 将更新后的 Excalidraw JSON 数据传递给 React Server App 渲染器。 │
│ │
│ <tool_code> │
│ print(tools.update_excalidraw_diagram(diagram_id="current_diagram_id", update_instruction="add a Redis cache layer between Web Server and Database"))
│ </tool_code> │
│ │
│ <tool_result> │
│ {"type": "excalidraw_data", "data": "..."} // 更新后的 Excalidraw JSON 数据
│ </tool_result> │
│ │
│ // 终端内显示更新后的手绘风格图:
│ // +-----------------+ +-----------------+ +---------------+ +-----------------+
│ // | Client | <--> | Web Server | <--> | Redis | <--> | Database |
│ // | (Browser/App) | | (Node.js/Py) | | (Cache) | | (PostgreSQL) |
│ // +-----------------+ +-----------------+ +---------------+ +-----------------+
│ │
│ Redis 缓存层已成功添加。还有其他需要调整的地方吗? │
╰───────────────────────────────────────╯
> 请将整个架构图导出为 SVG 格式。
╭─ Claude ──────────────────────────────╮
│ 好的,正在将当前的 Excalidraw 图形导出为 SVG 格式。 │
│ │
│ 思考过程: │
│ 1. 用户请求导出当前图为 SVG 格式。 │
│ 2. 识别到需要调用 `export_excalidraw_diagram` 工具。 │
│ 3. 构造工具参数,指定导出格式为 SVG。 │
│ 4. 调用工具,获取 SVG 数据。 │
│ 5. 将 SVG 数据以文本或文件形式提供给用户。 │
│ │
│ <tool_code> │
│ print(tools.export_excalidraw_diagram(diagram_id="current_diagram_id", format="svg"))
│ </tool_code> │
│ │
│ <tool_result> │
│ {"type": "svg_data", "content": "<svg ...>...</svg>", "filename": "architecture.svg"}
│ </tool_result> │
│ │
│ 您的架构图已导出为 SVG 格式。您可以将以下内容保存到 `architecture.svg` 文件中:
│ ```svg
│ <svg viewBox="0 0 700 300" width="700" height="300" xmlns="http://www.w3.org/2000/svg" font-family="Virgil, Segoe UI Emoji" font-size="16">
│ <!-- SVG 内容省略,实际会包含完整的 Excalidraw 渲染结果 -->
│ <rect x="10" y="50" width="150" height="80" stroke="#000" fill="rgba(197,208,230,0.5)" rx="4" ry="4"></rect>
│ <text x="40" y="95" style="white-space: pre-wrap;" font-family="Virgil, Segoe UI Emoji">Client</text>
│ <!-- 更多 SVG 元素 -->
│ </svg>
│ ```
│ 请问还需要什么帮助吗?
╰───────────────────────────────────────╯
💻 代码演示
本节将展示如何构建一个简化的 React Server App (RSA) 组件来渲染 Excalidraw JSON,以及一个模拟的 Claude 工具定义。
1. React Server App (RSA) 组件骨架
假设我们有一个名为 excalidraw-renderer-app 的 RSA。其 package.json 可能如下:
// excalidraw-renderer-app/package.json
{
"name": "excalidraw-renderer-app",
"version": "1.0.0",
"description": "A React Server App to render Excalidraw JSON in Claude's TUI.",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@excalidraw/excalidraw": "^0.17.0",
"express": "^4.18.2",
"body-parser": "^1.20.2"
}
}
核心的 index.js(或 App.js)文件会包含一个 React 组件,该组件接收 Excalidraw JSON 数据作为 props,并使用 @excalidraw/excalidraw 库在服务器端渲染。
// excalidraw-renderer-app/index.js (简化版,概念演示)
const express = require('express');
const bodyParser = require('body-parser');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const Excalidraw = require('@excalidraw/excalidraw').Excalidraw; // 仅用于类型和结构,实际渲染需客户端JS
const app = express();
const PORT = process.env.PORT || 3001;
app.use(bodyParser.json());
// 这是一个概念性的 RSA 端点,实际渲染会更复杂,需要将Excalidraw的客户端JS打包并发送
// Claude 终端会有一个内置的浏览器/渲染器来执行这些JS并显示图形
app.post('/render-excalidraw', (req, res) => {
const { excalidrawElements, appState } = req.body;
// 在服务器端,我们不能直接运行浏览器JS来渲染Excalidraw组件
// 相反,我们会生成一个包含Excalidraw数据的HTML片段,
// 或者更常见的是,生成一个轻量级的JSON描述,指示Claude终端的内置渲染器如何加载和显示Excalidraw。
// 以下是模拟的输出结构,实际会是 Claude MCP 协议的一部分。
// 为了演示,我们假设返回一个特殊格式的 JSON,指示 Claude 终端渲染 Excalidraw
const mcpCommand = {
type: "ui_component",
component: "ExcalidrawViewer", // 终端内置的 Excalidraw 渲染组件名
props: {
elements: excalidrawElements,
appState: appState,
// 其他 Excalidraw props,如 theme, zenMode 等
}
};
res.json(mcpCommand);
});
app.listen(PORT, () => {
console.log(`Excalidraw RSA listening on port ${PORT}`);
});
/*
在真实的 Claude 环境中,这个流程是高度抽象和标准化的:
1. Claude 调用工具,该工具生成 Excalidraw JSON。
2. Claude 内部机制识别到这是 Excalidraw JSON,并将其传递给一个预注册的“Excalidraw 渲染器”RSA。
3. RSA 接收 Excalidraw JSON,并将其打包成一个符合 MCP 协议的响应(可能是包含一个 React 组件的描述,或者一个可以直接在终端渲染器中执行的指令)。
4. Claude 终端接收到 MCP 响应后,调用其内置的 Excalidraw 渲染逻辑,将 JSON 数据可视化。
*/
2. 模拟 Claude 工具定义 (Tool Definition)
Claude 需要一个工具来触发 Excalidraw 图形的生成和渲染。这个工具的定义会告诉 Claude 如何调用它,以及它接受哪些参数。
// tools_schema.json
[
{
"name": "generate_excalidraw_diagram",
"description": "Generates a new Excalidraw diagram based on a natural language description and returns the Excalidraw JSON data.",
"input_schema": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A natural language description of the diagram to generate (e.g., 'a simple web architecture with client, server, and database')."
},
"theme": {
"type": "string",
"enum": ["light", "dark"],
"description": "The theme for the diagram.",
"default": "light"
}
},
"required": ["description"]
}
},
{
"name": "update_excalidraw_diagram",
"description": "Modifies an existing Excalidraw diagram based on a natural language instruction.",
"input_schema": {
"type": "object",
"properties": {
"diagram_id": {
"type": "string",
"description": "The ID of the diagram to update. This would typically be tracked by Claude or the user."
},
"update_instruction": {
"type": "string",
"description": "A natural language instruction describing the changes to make (e.g., 'add a Redis cache between server and database')."
}
},
"required": ["diagram_id", "update_instruction"]
}
},
{
"name": "export_excalidraw_diagram",
"description": "Exports an existing Excalidraw diagram to a specified format (e.g., SVG, PNG).",
"input_schema": {
"type": "object",
"properties": {
"diagram_id": {
"type": "string",
"description": "The ID of the diagram to export."
},
"format": {
"type": "string",
"enum": ["svg", "png"],
"description": "The desired export format.",
"default": "svg"
}
},
"required": ["diagram_id", "format"]
}
}
]
在实际应用中,这些工具的后端实现会负责:
- 解析用户描述: 将自然语言描述(
description或update_instruction)转换为 Excalidraw JSON 结构。这可能涉及到复杂的 NLP 和图结构生成逻辑。 - 管理图状态: 跟踪当前会话中的图的
diagram_id和其对应的 Excalidraw JSON 数据,以便进行后续修改和导出。 - 返回 Excalidraw JSON: 工具执行成功后,返回包含 Excalidraw JSON 的数据。Claude 接收到这个数据后,会通过其内部机制将其传递给上述的 RSA 渲染器。
🔧 涉及的 Tools / Commands
| 工具/命令名称 | 用途 | 说明 |
|---|---|---|
Claude MCP |
多模态内容传输协议 | Claude 与外部工具和渲染器进行富媒体数据交换的底层协议。它允许 Claude 在终端内显示除文本以外的复杂 UI,如 Excalidraw 图形,通过传递结构化数据和渲染指令实现。 |
React Server Apps (RSA) |
终端内富媒体 UI 渲染框架 | 基于 React 的服务器端渲染应用,作为 Claude MCP 的一个重要组成部分。它接收 AI 生成的 UI 描述(如 Excalidraw JSON),并在服务器端进行处理,然后将渲染指令或轻量级 UI 描述发送回 Claude 终端,由终端内置的渲染器进行最终呈现。 |
Excalidraw |
手绘风格白板工具及其 JSON 数据格式 | 一款流行的开源白板工具,其核心优势在于使用简洁的 JSON 格式描述图形元素。AI 可以轻松生成和解析这种格式的数据,使其成为 AI 驱动图形生成的理想中间件。 |
generate_excalidraw_diagram |
AI 生成新 Excalidraw 图形的工具 | Claude 通过此工具将用户的自然语言描述(如“画一个 Web 架构图”)转换为 Excalidraw JSON 数据。此工具的后端实现负责将语义转化为具体的图形元素及其属性。 |
update_excalidraw_diagram |
AI 修改现有 Excalidraw 图形的工具 | 允许 Claude 根据用户的指令(如“添加一个 Redis 缓存”)修改已生成的 Excalidraw 图形。此工具需要理解上下文,并能精确地在现有 JSON 结构上进行增删改操作。 |
export_excalidraw_diagram |
AI 导出 Excalidraw 图形为其他格式的工具 | 提供将当前显示的 Excalidraw 图形导出为 SVG、PNG 等常见图片格式的功能。这通常涉及到在服务器端利用 Excalidraw 的渲染能力将 JSON 转换为位图或矢量图。 |
Prompt Engineering |
驱动 AI 绘图的自然语言指令 | 用户与 Claude 交互时输入的自然语言指令,用于指导 AI 生成、修改或导出 Excalidraw 图形。有效的 Prompt Engineering 是获得满意图形结果的关键,需要清晰、具体的描述。 |
📝 本期要点回顾
- MCP 与 RSA 赋能终端富媒体: Claude 的 Multi-Modal Command Protocol (MCP) 结合 React Server Apps (RSA) 技术,使得在终端内部渲染 Excalidraw 这样的复杂图形成为可能,突破了传统文本界面的限制。
- Excalidraw JSON 为 AI 绘图而生: Excalidraw 简洁、可编程的 JSON 数据格式是 AI 生成和操作图形的理想中间表示。AI 可以将复杂概念转化为结构化的 JSON,再由 RSA 渲染。
- 对话式图形生成与迭代: 通过自然语言对话,用户可以直接指导 Claude 创建和修改 Excalidraw 图形,实现所见即所得的交互式绘图体验。
- 工具链协同工作: AI、自定义工具(如
generate_excalidraw_diagram)和 React Server App 渲染器形成一个紧密的工具链,共同完成从自然语言到终端图形的转化。 - 图形化思维的未来: 这种能力预示着 AI 助手将从简单的信息提供者转变为强大的可视化助手,极大地提升了技术交流和设计效率,让复杂的概念以更直观的方式呈现。
🔗 参考资料
- Excalidraw 官方网站: https://excalidraw.com/ (了解 Excalidraw 的功能和其 JSON 结构)
- React Server Components (概念启发): https://react.dev/learn/understanding-your-ui#react-server-components (虽然 React Server Apps 是一个更广义的概念,但可以从 React Server Components 中获取关于服务器端渲染和组件化架构的灵感。)
- Anthropic Tool Use 文档 (概念类比): (请查阅最新的 Anthropic Claude Tool Use 官方文档,了解如何定义和使用工具来扩展 Claude 的能力。具体链接可能随版本更新而变化,请访问 Anthropic 开发者门户。)