Files
st-react/docs/Clannad_v3.1_analysis.md
2026-03-13 21:51:42 +08:00

9.8 KiB
Raw Blame History

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 中新增以下字段:

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 数据迁移

为现有角色卡补充默认字段值:

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/ 中新增或扩展角色卡片组件:

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 或在组件级别定义样式:

/* 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 沙箱:

const FrontendCardSandbox: React.FC<{ htmlContent: string }> = ({ htmlContent }) => {
  return (
    <iframe
      srcDoc={htmlContent}
      sandbox="allow-scripts"
      title="前端卡内容"
      className="frontend-card-iframe"
    />
  );
};

10.3.4 postMessage 通信

定义消息类型:

// 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 条目:

{
  "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 性能测试

指标 目标
图片首次加载 < 1s5G 网络)
懒加载触发 视口进入后 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

# 请求
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 扩展

# 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 → 进阶 → 完整三阶段逐步落地。