# Prompt 服务架构参考

> 基于 2026-05-05 代码分析，源码位于 `/home/ubuntu/projects/ephron.ren/prompt/`

## 项目结构

```
prompt/
├── src/
│   ├── main.py              # FastAPI 入口，挂载路由和中间件
│   ├── config.py             # 环境变量配置（AUTH_SECRET_KEY, DATABASE_PATH）
│   ├── routes/
│   │   ├── pages.py          # 页面路由（Jinja2 模板渲染，HTMLResponse）
│   │   ├── api.py            # 公开 API（/api/prompts, /api/prompts/{key}）
│   │   ├── admin.py          # 管理后台路由（/admin/*，需 Cookie 认证）
│   │   └── service_api.py    # 服务端 API（/api/service/*，需 Bearer Token）
│   └── services/
│       ├── prompts.py        # 提示词 CRUD + 版本管理
│       ├── db.py             # SQLite 连接 + 建表
│       └── auth.py           # 认证辅助
├── templates/
│   ├── base.html             # 基础模板（暗色主题、Inter + JetBrains Mono）
│   ├── public/
│   │   ├── index.html        # 列表页（搜索、分类筛选、标签过滤、卡片网格）
│   │   └── detail.html       # 详情页（内容展示、复制按钮、示例区域）
│   └── admin/
│       ├── index.html        # 管理列表
│       ├── edit.html         # 编辑表单
│       ├── new.html          # 新建表单
│       └── versions.html     # 版本历史
├── static/
│   ├── css/ds/               # 设计系统 CSS（tokens, components, layout, motion）
│   └── js/ds/ui.js           # UI 交互（modal、toast、通用组件）
└── tests/
```

## 数据模型

### prompts 表
```sql
CREATE TABLE prompts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    key TEXT NOT NULL UNIQUE,              -- URL 标识，如 "deep-research-prompt"
    title TEXT NOT NULL,
    description TEXT,
    category TEXT NOT NULL DEFAULT '未分类',
    tags TEXT,                             -- 逗号分隔
    is_template INTEGER NOT NULL DEFAULT 0,
    variables TEXT,                        -- 模板变量（逗号分隔）
    example_input TEXT,
    example_output TEXT,
    recommended_model TEXT NOT NULL DEFAULT '通用',
    is_active INTEGER NOT NULL DEFAULT 1,
    draft INTEGER NOT NULL DEFAULT 0,
    created_by TEXT,
    updated_by TEXT,
    ownership_type TEXT NOT NULL DEFAULT 'human',  -- 'human' | 'service'
    handoff_to_human INTEGER NOT NULL DEFAULT 0,
    current_version_id INTEGER,
    created_at TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
```

### prompt_versions 表
```sql
CREATE TABLE prompt_versions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    prompt_key TEXT NOT NULL,
    version INTEGER NOT NULL,
    content TEXT NOT NULL,
    created_by TEXT,
    created_at TEXT NOT NULL DEFAULT (datetime('now')),
    FOREIGN KEY (prompt_key) REFERENCES prompts(key) ON DELETE CASCADE,
    UNIQUE(prompt_key, version)
);
```

## API 端点

### 公开 API（无需认证）
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/prompts` | 列表（支持 search, tag, category, limit, offset） |
| GET | `/api/prompts/{key}` | 详情（支持 version 查询参数） |

### 服务端 API（Bearer Token）
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/service/prompts` | 列表 |
| GET | `/api/service/prompts/{key}` | 详情 |
| POST | `/api/service/prompts` | 创建草稿 |
| PATCH | `/api/service/prompts/{key}` | 更新草稿 |
| DELETE | `/api/service/prompts/{key}` | 删除草稿 |

### 页面路由（返回 HTML）
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` 或 `/prompts` | 列表页（支持 q, category, tag 查询参数） |
| GET | `/prompts/{key}` | 详情页 |
| GET | `/admin/*` | 管理后台（需登录 + 权限） |

## 设计系统

- **主题**: 暗色（bg-primary: #09090b, accent: #3b82f6）
- **字体**: Inter（正文）+ JetBrains Mono（代码）
- **CSS 变量**: 定义在 `:root` 中，所有组件引用变量
- **卡片组件**: `.prompt-card` 使用 `bg-secondary` + border + hover 效果
- **标签**: `.tag` 类，带 `tag-bg` 背景
- **复制按钮**: `.copy-btn`，绝对定位在内容块右上角
- **CSP**: `connect-src 'self'`，`script-src 'self' 'unsafe-inline'`，`cdn.jsdelivr.net` 已白名单

## 现有功能特性

1. **版本管理**: 每次编辑创建新版本，支持版本切换
2. **模板变量**: `is_template=true` 时，`variables` 字段定义可替换变量
3. **草稿系统**: `draft` 字段 + `ownership_type` 区分人类/服务创建
4. **搜索过滤**: 支持关键词搜索、分类筛选、标签过滤
5. **复制功能**: 前端 `navigator.clipboard.writeText()` 实现

## 待实现功能（PRD 已写）

- **调用测试**: 详情页测试 Tab，填变量 → 调 LLM → 流式输出
- **集合**: `collections` + `collection_items` 表，组织相关提示词

## 开发注意事项

- 路由区分：`response_class=HTMLResponse` → 页面路由，返回 dict/Pydantic → API
- Key 自动生成：创建 prompt 时 key 可能被截取/简化，以响应返回值为准
- Key 不可变：PATCH 不能修改 key 字段
- 服务端只能编辑自己创建的草稿（`created_by == actor_id` + `ownership_type == "service"` + `draft == true`）
- 所有时间使用 SQLite `datetime('now')` 存储
