358 lines
9.8 KiB
Markdown
358 lines
9.8 KiB
Markdown
# Clannad_v3.1.png 案例分析与落地要点
|
||
|
||
## 目标角色卡概览
|
||
|
||
- **图像**:@docs/Clannad_v3.1.png(图片来源、版权信息见下方字段)
|
||
- **风格**:Clannad 系列日系校园少女题材,具有较强的情感表达与背景设定需求
|
||
- **已知需求**:在云酒馆系统中以“前端卡”方式呈现,并能注入背景信息、变量、互动 UI
|
||
|
||
---
|
||
|
||
## 10.1 数据字段与数据模型需求
|
||
|
||
| 字段名 | 说明 | 备注 |
|
||
|--------|------|------|
|
||
| portrait_url | 图片主地址 | 建议使用 CDN 托管,支持 webp/AVIF 高效格式 |
|
||
| portrait_alt | 无障碍文本描述 | 供屏幕阅读器使用,描述角色外观特征 |
|
||
| thumbnail_url | 缩略图地址 | 列表页展示使用,建议 200x200 以内 |
|
||
| portrait_caption | 图片简短说明 | 显示在图片下方的简短描述 |
|
||
| image_source | 版权信息/出处 | 记录图片来源、授权信息 |
|
||
| worldbook_id | 关联的 Worldbook | 用于注入背景设定 |
|
||
| worldbook_entries | 世界书条目集合 | 用于背景信息注入 |
|
||
| name | 角色名称 | 现有字段 |
|
||
| description | 角色描述 | 现有字段 |
|
||
| personality | 性格设定 | 现有字段 |
|
||
| scenario | 场景设定 | 现有字段 |
|
||
| first_mes | 开场白 | 现有字段 |
|
||
| mes_example | 对话示例 | 现有字段 |
|
||
| system_prompt | 系统提示词 | 现有字段 |
|
||
| extensions | 扩展数据 | MVU 相关字段 |
|
||
| variables | 变量定义 | MVU 相关字段 |
|
||
|
||
---
|
||
|
||
## 10.2 后端改动要点
|
||
|
||
### 10.2.1 数据模型扩展
|
||
|
||
在 `model/app/ai_character.go` 中新增以下字段:
|
||
|
||
```go
|
||
type AICharacter struct {
|
||
// ... 现有字段 ...
|
||
|
||
// 图片相关字段
|
||
PortraitURL string `json:"portraitUrl" gorm:"type:varchar(500)"`
|
||
PortraitAlt string `json:"portraitAlt" gorm:"type:varchar(200)"`
|
||
ThumbnailURL string `json:"thumbnailUrl" gorm:"type:varchar(500)"`
|
||
PortraitCaption string `json:"portraitCaption" gorm:"type:varchar(200)"`
|
||
ImageSource string `json:"imageSource" gorm:"type:varchar(500)"` // 版权信息
|
||
|
||
// Worldbook 关联
|
||
WorldbookID *uint `json:"worldbookId"`
|
||
}
|
||
```
|
||
|
||
### 10.2.2 API 接口
|
||
|
||
- **上传图片**:`POST /api/v1/app/character/:id/portrait`
|
||
- 鉴权:需要用户登录
|
||
- 校验:文件大小(建议最大 5MB)、格式(支持 jpg/png/webp/gif)
|
||
- 返回:portrait_url、thumbnail_url
|
||
|
||
- **获取角色图片信息**:`GET /api/v1/app/character/:id`
|
||
- 返回字段包含 portrait_url、portrait_alt、thumbnail_url、portrait_caption、image_source
|
||
|
||
### 10.2.3 Worldbook 关联增强
|
||
|
||
在 `conversation.go` 的 system prompt 构建流程中,增加对角色关联 Worldbook 的背景注入:
|
||
|
||
- 读取 `character.WorldbookID`
|
||
- 通过 WorldbookEngine 查询对应的启用条目
|
||
- 将背景信息注入到 system_prompt 中(需控制 token budget)
|
||
|
||
### 10.2.4 数据迁移
|
||
|
||
为现有角色卡补充默认字段值:
|
||
|
||
```sql
|
||
ALTER TABLE ai_characters
|
||
ADD COLUMN IF NOT EXISTS portrait_url VARCHAR(500),
|
||
ADD COLUMN IF NOT EXISTS portrait_alt VARCHAR(200),
|
||
ADD COLUMN IF NOT EXISTS thumbnail_url VARCHAR(500),
|
||
ADD COLUMN IF NOT EXISTS portrait_caption VARCHAR(200),
|
||
ADD COLUMN IF NOT EXISTS image_source VARCHAR(500),
|
||
ADD COLUMN IF NOT EXISTS worldbook_id UINT REFERENCES worldbooks(id);
|
||
```
|
||
|
||
---
|
||
|
||
## 10.3 前端实现要点
|
||
|
||
### 10.3.1 角色卡片组件
|
||
|
||
在 `web-app/src/components/` 中新增或扩展角色卡片组件:
|
||
|
||
```tsx
|
||
interface CharacterCardProps {
|
||
portraitUrl?: string;
|
||
portraitAlt?: string;
|
||
thumbnailUrl?: string;
|
||
portraitCaption?: string;
|
||
// ... 其他字段
|
||
}
|
||
|
||
const CharacterPortrait: React.FC<CharacterCardProps> = ({
|
||
portraitUrl,
|
||
portraitAlt,
|
||
thumbnailUrl,
|
||
portraitCaption
|
||
}) => {
|
||
if (!portraitUrl) return null;
|
||
|
||
return (
|
||
<div className="character-portrait">
|
||
<img
|
||
src={portraitUrl}
|
||
alt={portraitAlt || '角色图像'}
|
||
loading="lazy"
|
||
className="portrait-image"
|
||
/>
|
||
{portraitCaption && (
|
||
<p className="portrait-caption">{portraitCaption}</p>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 10.3.2 样式隔离
|
||
|
||
使用 CSS Modules 或在组件级别定义样式:
|
||
|
||
```css
|
||
/* CharacterPortrait.module.css */
|
||
.character-portrait {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.portrait-image {
|
||
max-width: 300px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.portrait-caption {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-top: 8px;
|
||
}
|
||
```
|
||
|
||
### 10.3.3 沙箱渲染
|
||
|
||
对于需要执行脚本的前端卡,使用 iframe 沙箱:
|
||
|
||
```tsx
|
||
const FrontendCardSandbox: React.FC<{ htmlContent: string }> = ({ htmlContent }) => {
|
||
return (
|
||
<iframe
|
||
srcDoc={htmlContent}
|
||
sandbox="allow-scripts"
|
||
title="前端卡内容"
|
||
className="frontend-card-iframe"
|
||
/>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 10.3.4 postMessage 通信
|
||
|
||
定义消息类型:
|
||
|
||
```ts
|
||
// types/frontend-card.ts
|
||
interface FrontendCardMessage {
|
||
type: 'GET_VARIABLE' | 'SET_VARIABLE' | 'SEND_MESSAGE' | 'TRIGGER_ACTION';
|
||
payload: Record<string, any>;
|
||
}
|
||
|
||
interface FrontendCardResponse {
|
||
type: 'VARIABLE_VALUE' | 'ACTION_RESULT' | 'ERROR';
|
||
payload: Record<string, any>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10.4 Worldbook 与背景注入
|
||
|
||
### 10.4.1 背景信息转 Worldbook 条目
|
||
|
||
将 Clannad 角色的背景故事、人物关系、剧情要点等转换为 Worldbook 条目:
|
||
|
||
```json
|
||
{
|
||
"keys": ["冈崎汐", "汐", "女儿"],
|
||
"content": "冈崎汐,主人公冈崎朋也的女儿。5岁时因事故去世,后来以幽灵的形式再次出现在父亲面前...",
|
||
"enabled": true,
|
||
"constant": true,
|
||
"position": 1,
|
||
"order": 1
|
||
}
|
||
```
|
||
|
||
### 10.4.2 Token Budget 控制
|
||
|
||
在 `buildContextManagedSystemPrompt` 中,对 Worldbook 注入的内容进行 token 预算控制:
|
||
|
||
- **优先级**:Worldbook 触发条目 > CharacterBook 内嵌条目 > MesExample
|
||
- **预算分配**:system_prompt 总预算的 20-30% 用于 Worldbook 背景注入
|
||
- **截断策略**:超出预算时,从最低优先级条目开始丢弃
|
||
|
||
---
|
||
|
||
## 10.5 安全性与合规
|
||
|
||
### 10.5.1 图片安全
|
||
|
||
- **来源限制**:只允许从受信任的 CDN 或本地上传
|
||
- **内容审查**:上传图片需经过内容审核(可接入第三方审核服务)
|
||
- **元数据清理**:清理图片 EXIF 信息,防止泄露拍摄设备、地点等隐私
|
||
|
||
### 10.5.2 脚本安全
|
||
|
||
- **沙箱隔离**:前端卡内的 JavaScript 必须在 iframe 沙箱中运行
|
||
- **API 白名单**:只暴露受限的 API 接口,映射到后端受控服务
|
||
- **CSP 策略**:
|
||
|
||
```
|
||
Content-Security-Policy:
|
||
default-src 'self';
|
||
script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;
|
||
style-src 'self' 'unsafe-inline';
|
||
img-src 'self' https://cdn.example.com data:;
|
||
connect-src 'self' https://api.example.com;
|
||
```
|
||
|
||
### 10.5.3 审核流程
|
||
|
||
- **新卡审核**:用户上传新角色卡后,需经过管理员审核
|
||
- **定期检查**:对已上线卡片进行定期安全扫描
|
||
- **举报机制**:提供用户举报入口
|
||
|
||
---
|
||
|
||
## 10.6 迁移策略
|
||
|
||
### 10.6.1 MVP 阶段(第一阶段)
|
||
|
||
- 只引入 portrait_url、portrait_alt、thumbnail_url 字段
|
||
- 前端仅展示图片,不改变现有对话逻辑
|
||
- 不支持前端卡脚本执行
|
||
|
||
### 10.6.2 进阶阶段(第二阶段)
|
||
|
||
- 接入 Worldbook 背景注入
|
||
- 支持 portrait_caption 显示
|
||
- 引入基础的 iframe 沙箱渲染
|
||
|
||
### 10.6.3 完整阶段(第三阶段)
|
||
|
||
- 支持完整的前端卡脚本执行
|
||
- 实现 postMessage 通信
|
||
- 支持 RPG、VN 等复杂交互系统
|
||
|
||
### 10.6.4 回滚策略
|
||
|
||
- 每个阶段保留回滚开关
|
||
- 遇到兼容性问题时,可一键回退到上一阶段
|
||
- 数据库字段支持版本标记
|
||
|
||
---
|
||
|
||
## 10.7 验证与测试
|
||
|
||
### 10.7.1 功能测试
|
||
|
||
| 测试用例 | 预期结果 |
|
||
|---------|---------|
|
||
| 上传角色图片 | 图片正确保存,返回正确的 portrait_url |
|
||
| 图片加载失败 | 显示默认占位图,alt 文本正确 |
|
||
| 无图片角色卡 | 正常显示,不崩溃 |
|
||
| Worldbook 注入 | 背景信息正确注入到对话上下文 |
|
||
| 懒加载图片 | 首屏不加载,滚动后加载 |
|
||
|
||
### 10.7.2 安全性测试
|
||
|
||
| 测试用例 | 预期结果 |
|
||
|---------|---------|
|
||
| XSS 注入(图片描述) | 被转义,不执行 |
|
||
| 恶意脚本(前卡内) | 在沙箱中被阻止,无法访问父窗口 |
|
||
| 跨域请求 | 被 CSP 拦截 |
|
||
|
||
### 10.7.3 性能测试
|
||
|
||
| 指标 | 目标 |
|
||
|-----|------|
|
||
| 图片首次加载 | < 1s(5G 网络) |
|
||
| 懒加载触发 | 视口进入后 200ms 内 |
|
||
| 内存占用 | 前端卡渲染时 < 50MB 额外 |
|
||
|
||
---
|
||
|
||
## 10.8 版权信息与来源
|
||
|
||
### 10.8.1 必填字段
|
||
|
||
- **image_source**:图片来源(如「官方美术集」「同人创作」)
|
||
- **license_type**:许可证类型(如「MIT」「CC BY-SA 4.0」「原创」)
|
||
- **original_artist**:原作者名称(若为同人创作)
|
||
|
||
---
|
||
|
||
## 10.9 附录/接口示例
|
||
|
||
### 10.9.1 角色图片上传 API
|
||
|
||
```bash
|
||
# 请求
|
||
POST /api/v1/app/character/1/portrait
|
||
Authorization: Bearer <token>
|
||
Content-Type: multipart/form-data
|
||
|
||
# 响应
|
||
{
|
||
"portraitUrl": "https://cdn.example.com/characters/1/portrait.webp",
|
||
"thumbnailUrl": "https://cdn.example.com/characters/1/thumb.webp",
|
||
"portraitAlt": "粉色头发的少女,穿着校服"
|
||
}
|
||
```
|
||
|
||
### 10.9.2 角色详情 API 扩展
|
||
|
||
```bash
|
||
# GET /api/v1/app/character/1
|
||
|
||
{
|
||
"id": 1,
|
||
"name": "冈崎汐",
|
||
"portraitUrl": "https://cdn.example.com/characters/1/portrait.webp",
|
||
"portraitAlt": "粉色头发的少女,穿着校服",
|
||
"thumbnailUrl": "https://cdn.example.com/characters/1/thumb.webp",
|
||
"portraitCaption": "Clannad 主角冈崎朋也的女儿",
|
||
"imageSource": "官方美术集",
|
||
"worldbookId": 5,
|
||
// ... 其他字段
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 结论
|
||
|
||
通过独立分析文档分离,便于版本管理与跨团队协作。请在实现过程中严格遵循安全沙箱与版权规范,确保云酒馆的扩展能力可控且可维护。
|
||
|
||
本落地要点覆盖了从数据模型、后端 API、前端渲染、Worldbook 集成、安全合规到迁移测试的完整流程,建议按 MVP → 进阶 → 完整三阶段逐步落地。
|