What is the tech stack and implementation of this PPT webapp?
帮我详细分析这个 repo 实现的 ppt webapp 的技术栈组成和实现原理
完美!现在我有了所有需要的信息。让我创建一份详细的分析文档:
##🎯 Presentation App 详细技术栈分析
这是一个 AI驱动的PPT生成Web应用,由Claude AI Agent根据自然语言指令动态生成演示文稿。以下是完整的技术栈和实现原理分析:
##📋 项目整体架构
┌─────────────────────────────────────────────────────────────┐
│ 前端 (Next.js) │
│ React 19 + TypeScript + Tailwind CSS │
│ ├─ 实时流式聊天界面 │
│ ├─ 幻灯片可视化和编辑 │
│ ├─ 上下文文件和模板上传 │
│ └─ PPTX/PDF导出 │
└────────────────┬────────────────────────────────────────────┘
│ HTTP/SSE
┌────────────────▼────────────────────────────────────────────┐
│ 后端 (FastAPI + Claude Agent) │
│ ├─ FastAPI 服务器 │
│ ├─ Claude Agent SDK (多轮对话) │
│ ├─ LlamaParse (文档解析) │
│ ├─ Session 管理 (SQLite + 文件系统) │
│ └─ PPTX/PDF 转换 (Node.js 子进程) │
└────────────────┬────────────────────────────────────────────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Claude SQLite Node.js
API 数据库 (pptxgenjs)
##🔧 技术栈详解
###前端技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| Next.js | 15.5.9 | 全栈框架,服务端渲染 |
| React | 19.0.0 | UI 组件库 |
| TypeScript | 5 | 类型安全 |
| Tailwind CSS | 4 | 样式框架 |
| PostCSS | 8 | CSS处理 |
| ESLint | 9 | 代码质量 |
核心依赖只有5个,非常精简!
###后端技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| FastAPI | >=0.109.0 | Web框架 |
| Uvicorn | >=0.27.0 | ASGI服务器 |
| Anthropic SDK | >=0.52.0 | Claude API |
| Claude Agent SDK | >=0.1.0 | AI Agent框架 |
| Pydantic | >=2.0.0 | 数据验证 |
| LlamaParse | >=0.6.0 | 文档解析 |
| MCP | >=1.0.0 | Model Context Protocol |
| httpx | >=0.27.0 | 异步HTTP客户端 |
###数据库和存储
- SQLite: Session 元数据持久化
- 文件系统: Presentation JSON 数据存储
- localStorage: 前端 Session 恢复
###导出工具链
| 工具 | 版本 | 用途 |
|---|---|---|
| pptxgenjs | 3.12.0 | PPTX 生成 |
| Puppeteer | 23.0.0 | PDF 渲染 |
| Chromium | System | 浏览器引擎 |
##🏗️ 核心实现原理
###1. 前端架构
####页面结构 (web/src/app/page.tsx)
// 核心状态管理
const [userSessionId, setUserSessionId] = useState<string | null>(null);
const [agentSessionId, setAgentSessionId] = useState<string | null>(null);
const [presentation, setPresentation] = useState<Presentation | null>(null);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [slides, setSlides] = useState<Slide[]>([]);
Session 恢复流程:
- 从URL查询参数获取
sessionId - 从 localStorage 恢复聊天历史
- 从后端 API 获取当前幻灯片
- 生成新的URL以便分享
####组件划分
| 组件 | 职责 |
|---|---|
ChatPanel | 流式聊天交互,显示Agent活动日志 |
SlideViewer | 当前幻灯片的查看和编辑 |
SlideRenderer | HTML内容渲染,支持双击编辑 |
SlideGrid | 幻灯片缩略图列表 |
ExportMenu | PPTX/PDF导出 |
ContextFilesUpload | 文件上传和解析 |
TemplateUpload | 样式模板上传 |
ApiKeyGate | API密钥管理 |
####流式通信 (lib/api.ts)
export async function* streamAgent(options): AsyncGenerator<StreamEvent> {
const response = await fetch(`${API_BASE}/agent-stream`, {
method: "POST",
body: formData,
});
const reader = response.body?.getReader();
// 逐行处理 Server-Sent Events (SSE)
while (true) {
const { done, value } = await reader.read();
// 解析并yield JSON事件
}
}
流式事件类型:
init: Agent初始化status: 处理进度tool_use: 工具调用(幻灯片操作)assistant: Claude的文本响应complete: 完成,返回session_iderror: 错误消息
###2. 后端架构
####FastAPI 主服务器 (backend/main.py)
关键端点:
| 端点 | 方法 | 功能 |
|---|---|---|
/health | GET | 健康检查 |
/agent-stream | POST | 核心流式交互 |
/session/{id} | GET | 获取会话状态 |
/session/{id}/slides | GET | 获取所有幻灯片 |
/session/{id}/slides/{idx} | PATCH | 更新单个幻灯片 |
/session/{id}/export | GET | 导出PPTX |
/session/{id}/export/pdf | GET | 导出PDF |
/parse-files | POST | 解析上下文文件 |
/parse-template | POST | 解析样式模板 |
/validate-api-key | POST | 验证LlamaParse密钥 |
/validate-anthropic-key | POST | 验证Anthropic密钥 |
####CORS 配置
cors_origins_env = os.environ.get("CORS_ORIGINS", "*")
# 支持单一来源或多个逗号分隔的来源
# 生产环境可配置为特定域名
###3. Claude Agent 集成 (backend/agent.py)
####核心工具集
Agent 通过以下工具操纵演示文稿:
@tool("create_presentation", "Create a new presentation", {"title": str})
@tool("add_slide", "Add a new slide", {"html": str, "position": int, "layout": str})
@tool("update_slide", "Update slide HTML", {"slide_index": int, "html": str})
@tool("delete_slide", "Delete a slide", {"slide_index": int})
@tool("reorder_slides", "Reorder slides", {"from_index": int, "to_index": int})
@tool("list_slides", "List all slides", {})
@tool("get_slide", "Get a specific slide", {"slide_index": int})
@tool("set_theme", "Set presentation theme", {"theme": dict})
@tool("get_pending_edits", "Get staged edits", {})
@tool("commit_edits", "Apply all pending edits", {})
####待编辑(Pending Edits)模式
@dataclass
class PendingEdit:
edit_id: str # 唯一编辑ID
slide_index: int # 目标幻灯片
operation: str # ADD, UPDATE, DELETE, REORDER
params: dict # 操作参数
preview: str # 人类可读描述
优势:
- 编辑先暂存,后提交(原子性)
- Agent可以生成多个编辑后一次性应用
- 支持编辑的撤销和复核
####Agent 选项构建 (_create_agent_options)
options = ClaudeAgentOptions(
system_prompt="""You are a professional presentation designer...""",
mcp_servers={"presentation": pres_server}, # MCP服务器
allowed_tools=[...], # 白名单工具
resume=resume_session_id, # 多轮对话恢复ID
)
####多模态支持
def _build_multimodal_content(session, instructions) -> list[dict]:
"""支持样式模板截图作为视觉上下文"""
content_blocks = []
# 添加参考截图
for screenshot in session.style_template["screenshots"]:
content_blocks.append({
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": screenshot["data"],
}
})
# 添加用户指令
content_blocks.append({
"type": "text",
"text": instructions
})
return content_blocks
###4. Session 管理 (backend/session.py)
####数据结构
@dataclass
class PresentationSession:
session_id: str # 用户session ID
presentation: Optional[Presentation] # 演示文稿对象
pending_edits: list[PendingEdit] # 待应用的编辑
applied_edits: list[dict] # 已应用编辑历史
context_files: list[dict] # 上下文文件
style_template: Optional[dict] # 样式参考
is_continuation: bool # 是否延续之前的对话
claude_session_id: Optional[str] # Claude Agent session ID
created_at: datetime
updated_at: datetime
####线程安全的 SessionManager
class SessionManager:
def __init__(self):
self._lock = threading.Lock()
self._sessions: dict[str, PresentationSession] = {}
self._init_db() # SQLite 初始化
def get_or_create_session(self, session_id: Optional[str]):
with self._lock:
# 线程安全的session获取/创建
pass
def save_session(self, session: PresentationSession):
# 同时保存到SQLite和JSON文件
with self._lock:
self._save_to_db(session)
self._save_to_json(session)
def cleanup_old_sessions(self, cutoff: datetime):
# 清理24小时前的session
pass
####自动清理
async def cleanup_old_sessions():
"""后台任务,每小时清理一次"""
while True:
cutoff = datetime.now() - timedelta(hours=24)
cleaned = session_manager.cleanup_old_sessions(cutoff)
await asyncio.sleep(3600)
###5. 文档解析 (backend/parser.py)
####LlamaParse 集成
async def parse_files_stream(files: list[dict], parse_mode: str):
"""流式处理文件解析,实时返回进度"""
for idx, file_data in enumerate(files):
yield {"type": "progress", "current": idx + 1}
try:
if LLAMAPARSE_AVAILABLE and LLAMA_CLOUD_API_KEY:
# 高质量解析 (使用vision model)
parsed_text = await parse_with_llama(
content, filename, parse_mode
)
else:
# 降级处理 (basic extraction)
parsed_text = parse_basic(content, filename, content_type)
results.append({
"filename": filename,
"text": parsed_text,
"success": True
})
except Exception as e:
results.append({
"filename": filename,
"success": False,
"error": str(e)
})
支持的格式: PDF, DOCX, TXT, Markdown, PNG, JPG 等
两种解析模式:
cost_effective: 更快更便宜,适合文本提取premium: 更精准,保留格式和结构
###6. PPTX/PDF 转换 (backend/pptx_converter/)
####Node.js 转换流程
// convert.js: HTML → PPTX
import pptxgen from 'pptxgenjs';
const pptx = new pptxgen();
pptx.title = inputData.title;
// 为每个幻灯片处理HTML内容
for (const slide of inputData.slides) {
const pres_slide = pptx.addSlide();
// 关键: 解析HTML并转换为PPTX对象
// 处理文本、颜色、字体、图像等
pres_slide.addShape(pptx.ShapeType.rect, {
x: 0, y: 0, w: "100%", h: "100%",
fill: { color: backgroundColor }
});
// 添加文本、表格、图形等
}
pptx.writeFile(outputPath);
####HTML 到 PPTX 的映射
支持的HTML标签:
<h1>-<h6>: 标题 → 标题样式文本<p>: 段落 → 文本框<strong>/<b>: 粗体<em>/<i>: 斜体<ul>/<ol>: 列表 → 项目符号<div style="...">: 自定义容器<img>: 图像(base64或URL)<table>: 表格 → PPTX表格
颜色处理:
function parseColor(colorStr) {
// 支持: #hex, rgb(), rgba(), 命名颜色等
// 自动转换为PPTX格式 (6位十六进制)
}
function isLightColor(hex) {
// 智能计算亮度,用于对比度调整
}
####PDF 转换 (convert-pdf.js)
import puppeteer from 'puppeteer';
// 使用Puppeteer渲染HTML到PDF
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium'
});
const page = await browser.newPage();
await page.setContent(html);
await page.pdf({
path: outputPath,
format: 'A4',
width: '960px',
height: '540px'
});
PDF优势: 像素完美的渲染,保留所有HTML/CSS样式
##🔄 数据流和交互流程
###创建演示文稿的完整流程
用户输入指令
│
▼
[前端] streamAgent() - 发送SSE请求
│
▼
[后端] /agent-stream 端点
│
├─> 创建或恢复 Session
│
├─> 初始化 ClaudeSDKClient
│
└─> 发送给 Claude Agent
│
├─► Agent 理解指令
│
├─► Agent 调用工具 (add_slide, update_slide等)
│ │
│ └─► PendingEdit 被添加到 session.pending_edits
│
├─► Agent 调用 commit_edits()
│ │
│ └─► PendingEdit 应用到 session.presentation
│
└─► 返回完成信号 + session_id
│
▼
[前端] 接收流式事件
│
├─► init: 显示 "Agent 初始化中..."
├─► status: 更新进度
├─► tool_use: 显示具体操作 (如"添加第2张幻灯片")
├─► assistant: 显示 Claude 的文本回复
└─► complete: 刷新幻灯片列表,保存session
│
▼
用户看到新幻灯片被添加到编辑器
###导出流程
用户点击 "导出PPTX"
│
▼
[前端] exportPptx(sessionId)
│
▼
[后端] /session/{id}/export
│
├─► session_manager.load_session(sessionId)
│
├─► 构建 input_data = {title, slides: [{html, width: 960, height: 540}], theme}
│
├─► 写入临时JSON文件
│
├─► 子进程运行: node pptx_converter/convert.js input.json output.pptx
│
├─► convert.js 使用 pptxgenjs 生成PPTX
│ │
│ └─► 解析每个slide的HTML,转换为PPTX对象
│
├─► 读取输出的PPTX字节
│
└─► 返回给前端 (Content-Type: application/...presentationml.presentation)
│
▼
[前端] 浏览器下载文件
##📊 模型和数据结构
###Slide 模型 (backend/models.py)
@dataclass
class Slide:
index: int # 幻灯片位置
html: str # 完整的HTML内容
layout: SlideLayout # 预设布局 (TITLE, TITLE_CONTENT等)
notes: str = "" # 演讲者备注
布局预设:
class SlideLayout(Enum):
TITLE = "title" # 标题幻灯片
TITLE_CONTENT = "title_content" # 标题+内容
TWO_COLUMN = "two_column" # 两栏布局
BLANK = "blank" # 空白 (自由设计)
###Presentation 模型
@dataclass
class Presentation:
title: str
slides: list[Slide]
theme: dict # {primaryColor, fontFamily, ...}
##🛡️ 安全特性
###API 密钥验证
@app.post("/validate-api-key")
async def validate_api_key(api_key: str):
"""验证 LlamaParse API 密钥"""
if not api_key.startswith("llx-"):
raise HTTPException(400, "Invalid format")
# 实际请求 LlamaParse API 验证
response = await client.get(
"https://api.cloud.llamaindex.ai/api/v1/projects",
headers={"Authorization": f"Bearer {api_key}"}
)
@app.post("/validate-anthropic-key")
async def validate_anthropic_key(request: dict):
"""验证 Anthropic API 密钥"""
client = anthropic.Anthropic(api_key=api_key)
client.messages.create(...) # 发送最小测试请求
###CORS 灵活配置
# 开发环境: "*"
# 生产环境: 环境变量 CORS_ORIGINS
# 支持多个来源: "https://app.vercel.app,http://localhost:3000"
##🐳 Docker 部署
###Dockerfile 特点
# 多运行时镜像: Python 3.11 + Node.js 20
FROM python:3.11-slim
# 安装 Node.js 20
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
# 为 Puppeteer (PDF导出) 安装 Chromium
RUN apt-get install -y chromium
# Puppeteer 使用系统 Chromium
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
# 安装 Python 和 Node.js 依赖
# 复制 PPTX 转换器
# 暴露端口 8000
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8000/health
# 启动 Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
###Render.yaml 部署配置
services:
- type: web
name: presentation-api
runtime: docker
healthCheckPath: /health
envVars:
- ANTHROPIC_API_KEY (同步=false)
- LLAMA_CLOUD_API_KEY (同步=false)
- CORS_ORIGINS
- DATA_DIR: /app/data
disk:
mountPath: /app/data
sizeGB: 1 # 持久化存储用于sessions
##🎨 UI/UX 特性
###实时反馈系统
-
Chat Panel:
- 显示用户消息和Agent回复
- Agent活动日志(工具调用、状态更新)
- 流式显示(逐步显示)
-
Slide Renderer:
- 双击编辑幻灯片内容
- 即时预览
- 保存/取消按钮
-
Slide Grid:
- 所有幻灯片缩略图
- 快速导航
- 拖拽重新排序(潜在功能)
-
Export Menu:
- PPTX 和 PDF 两种格式
- 一键下载
##🔑 关键设计模式
###1. 流式处理 (Streaming)
所有长时间运行的操作都使用 SSE 流:
- Agent 交互
- 文件解析
- 导出
优势: 用户能看到实时进度,不会卡顿
###2. Session 驱动
每个用户的工作状态完全由 Session 管理:
- 可恢复 (通过 URL session 参数)
- 可分享 (共享 session ID)
- 持久化 (SQLite + JSON)
###3. 待编辑模式 (Pending Edits)
- Agent 生成的编辑先暂存
- 用户可复核后再提交
- 支持事务性应用
###4. 多模态 AI
- 文本指令
- 样式模板截图 (作为视觉上下文)
- 上下文文件 (PDF, DOCX等)
##📈 扩展性
###可扩展的工具系统
通过 MCP (Model Context Protocol),可以轻松添加新工具:
@tool("new_tool", "description", {"param": type})
async def tool_new_tool(args: dict) -> dict:
# 新功能实现
return {"result": ...}
###模式系统
两种解析模式支持成本和质量的权衡
###API 密钥管理
支持用户自带 API 密钥,无需服务器存储
##🎯 总结
| 方面 | 特点 |
|---|---|
| 架构 | 前后端分离,异步流式通信 |
| AI 集成 | Claude Agent SDK + MCP 工具框架 |
| 数据流 | Session 驱动,待编辑模式,事务性应用 |
| 存储 | SQLite元数据 + JSON演示文稿 + localStorage会话 |
| 导出 | HTML → Node.js (pptxgenjs) → PPTX/PDF |
| 部署 | Docker 多运时,Render.yaml 配置 |
| 实时性 | SSE 流式推送,前端实时显示进度 |
| 可扩展 | MCP 工具框架,易于添加新功能 |
这个项目是一个精心设计的现代 Web 应用,充分利用了 Claude AI 的能力,同时保持了代码的简洁性和可维护性!
END OF POST