Merge remote-tracking branch 'origin/master' into hermes/hermes-1e775b03
# Conflicts: # server-rs/crates/api-server/src/app.rs # server-rs/crates/api-server/src/creation_entry_config.rs # server-rs/crates/api-server/src/puzzle.rs # server-rs/crates/spacetime-client/src/lib.rs # src/components/platform-entry/PlatformEntryFlowShellImpl.tsx
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
# 远端作品列表压测排查报告
|
||||
|
||||
时间:2026-05-12 06:16 CST
|
||||
目标:`http://82.157.175.59`
|
||||
SSH:远端生产机 root 账号(具体私钥路径仅保留在本机环境,不写入仓库)
|
||||
|
||||
## 背景
|
||||
|
||||
远端 `k6-works-list.js` 压测中:
|
||||
|
||||
- smoke 通过。
|
||||
- baseline 10 VU:无 HTTP 错误,但 p95/p99 超阈值。
|
||||
- 50 RPS spike:`http_req_failed` / `works_list_shape_error_rate` 约 21.99%。
|
||||
- 100 RPS spike:`http_req_failed` / `works_list_shape_error_rate` 约 25.47%。
|
||||
- 从 k6 check 看,失败主要集中在 `puzzle_gallery_list`,`custom_world_gallery_list` 基本正常。
|
||||
|
||||
## 已完成排查
|
||||
|
||||
### 1. 服务器进程与资源
|
||||
|
||||
远端服务监听:
|
||||
|
||||
- Rust api-server:`127.0.0.1:8082`,systemd 服务 `genarrative-api.service`。
|
||||
- SpacetimeDB:`127.0.0.1:3101`,systemd 服务 `spacetimedb.service`。
|
||||
- Nginx:公网 80 反代 `/api/*` 到 `127.0.0.1:8082`。
|
||||
|
||||
服务器规格/状态:
|
||||
|
||||
- 2 vCPU。
|
||||
- 内存约 1.9GiB。
|
||||
- Swap 约 1.9GiB,已有约 600MiB 使用。
|
||||
- `/` 磁盘约 69%。
|
||||
- Rust api-server 当前 CPU 不高。
|
||||
- SpacetimeDB 当前 CPU 不高。
|
||||
|
||||
发现一个独立异常:
|
||||
|
||||
- PM2 下旧 `server-node` 进程 `genarrative` 正在重启风暴。
|
||||
- cwd:`/work/Genarrative/server-node`
|
||||
- 错误:连接 `127.0.0.1:5432` PostgreSQL 被拒绝。
|
||||
- PM2 restart 次数已超过 33 万。
|
||||
- 该进程不是当前公网 `/api/*` 使用的 Rust api-server,但会制造额外 CPU/内存/日志抖动。
|
||||
|
||||
### 2. 压测窗口服务端日志
|
||||
|
||||
子任务聚合了 2026-05-12 04:50-05:05 的 nginx 与 api-server 日志。
|
||||
|
||||
nginx access:
|
||||
|
||||
- `/api/runtime/puzzle/gallery`:4661 次,全部 200。
|
||||
- `/api/runtime/custom-world-gallery`:4659 次,全部 200。
|
||||
|
||||
api-server journal:
|
||||
|
||||
`/api/runtime/puzzle/gallery`:
|
||||
|
||||
- completed:4661
|
||||
- status:200 全部
|
||||
- slow_request:0
|
||||
- latency_ms:min 13 / p50 30 / p90 43 / p95 50 / p99 62 / max 88
|
||||
|
||||
`/api/runtime/custom-world-gallery`:
|
||||
|
||||
- completed:4659
|
||||
- status:200 全部
|
||||
- slow_request:0
|
||||
- latency_ms:min 0 / p50 1 / p90 5 / p95 7 / p99 13 / max 49
|
||||
|
||||
结论:
|
||||
|
||||
- 在服务端视角,两个接口在该窗口都没有 5xx,也没有慢请求。
|
||||
- 这与 k6 客户端侧 30s timeout / failed check 存在明显不一致。
|
||||
- 需要进一步区分:客户端侧网络/连接耗尽/本机 k6 执行环境问题,还是 k6 统计混合/响应解析问题。
|
||||
|
||||
### 3. k6 脚本行为
|
||||
|
||||
文件:`scripts/loadtest/k6-works-list.js`
|
||||
|
||||
无 `AUTH_TOKEN` 时,每轮 iteration 顺序请求两个接口:
|
||||
|
||||
1. `GET /api/runtime/puzzle/gallery`
|
||||
2. `GET /api/runtime/custom-world-gallery`
|
||||
|
||||
`DETAIL_RATIO=0` 时不会请求详情。
|
||||
|
||||
`works_list_shape_error_rate` 不只代表字段结构错误,只要下面任意 check 失败都会计入:
|
||||
|
||||
- status is 200
|
||||
- returns json object
|
||||
- has collection
|
||||
- list item shape
|
||||
|
||||
因此 timeout、非 JSON、非 200、响应结构不符合都会表现为 shape error。
|
||||
|
||||
数据文件实际路径:
|
||||
|
||||
- `scripts/loadtest/data/works-list.local.json`
|
||||
|
||||
脚本里 `data/works-list.local.json` 是相对 k6 脚本文件解析的,因此本身合理。
|
||||
|
||||
### 4. 代码层疑似瓶颈
|
||||
|
||||
虽然这次远端服务端日志没有复现慢请求,但代码层仍发现一个真实性能隐患。
|
||||
|
||||
`/api/runtime/puzzle/gallery` 调用链:
|
||||
|
||||
- `server-rs/crates/api-server/src/app.rs:1192`
|
||||
- `server-rs/crates/api-server/src/puzzle.rs:1385-1409`
|
||||
- `server-rs/crates/spacetime-client/src/puzzle.rs:367-381`
|
||||
- `server-rs/crates/spacetime-module/src/puzzle.rs:430-443`
|
||||
- `server-rs/crates/spacetime-module/src/puzzle.rs:1393-1404`
|
||||
|
||||
关键实现:
|
||||
|
||||
- `list_puzzle_gallery_tx` 对 `puzzle_work_profile().iter()` 全表扫描。
|
||||
- 再过滤 `publication_status == Published`。
|
||||
- 对每个公开作品调用 `build_puzzle_work_profile_from_row_with_recent_count`。
|
||||
- 该函数调用 `count_recent_public_work_plays(ctx, "puzzle", &row.profile_id, now_micros)`。
|
||||
|
||||
`count_recent_public_work_plays`:
|
||||
|
||||
- 文件:`server-rs/crates/spacetime-module/src/runtime/profile.rs:1296-1321`
|
||||
- 当前实现对 `public_work_play_daily_stat().iter()` 全表扫描过滤。
|
||||
- 但表定义已有复合索引:
|
||||
- `server-rs/crates/spacetime-module/src/runtime/profile.rs:242-248`
|
||||
- `by_public_work_play_daily_stat_work_day(source_type, profile_id, played_day)`
|
||||
- 当前统计函数未使用该索引。
|
||||
|
||||
复杂度风险:
|
||||
|
||||
```text
|
||||
puzzle gallery ~= O(puzzle_work_profile 全表扫描 + Published作品数 * public_work_play_daily_stat 全表扫描)
|
||||
```
|
||||
|
||||
`custom-world-gallery` 与 puzzle 的差异:
|
||||
|
||||
- custom-world 使用 `CustomWorldGalleryEntry` 公开读模型表。
|
||||
- puzzle 直接从 `puzzle_work_profile` 即席拼装。
|
||||
- 两者都调用 recent count,但 puzzle 更容易受作品表规模和统计表规模影响。
|
||||
|
||||
## 当前判断
|
||||
|
||||
本次排查有两个层面的结论:
|
||||
|
||||
1. 生产服务端日志没有证明 `puzzle/gallery` 在 04:50-05:05 窗口真的 30s 慢或 5xx。
|
||||
- api-server 记录的 p95 只有 50ms。
|
||||
- nginx 看到两个接口都是 200。
|
||||
- 所以 k6 侧的 30s timeout 需要进一步从客户端网络、连接池、Windows/k6 执行环境、summary 混合统计角度验证。
|
||||
|
||||
2. 代码层确实存在可修的性能隐患。
|
||||
- `count_recent_public_work_plays` 未使用已有索引。
|
||||
- puzzle gallery 对每个作品重复做 recent count。
|
||||
- puzzle gallery 未使用 `publication_status` 索引或读模型。
|
||||
|
||||
## 建议下一步
|
||||
|
||||
### A. 先处理服务器 PM2 重启风暴
|
||||
|
||||
建议确认旧 Node 服务是否仍需要。
|
||||
|
||||
如果不需要,应停止并禁用 PM2 中的旧 `server-node`:
|
||||
|
||||
```bash
|
||||
PM2_HOME=/home/ubuntu/.pm2 pm2 stop genarrative
|
||||
PM2_HOME=/home/ubuntu/.pm2 pm2 delete genarrative
|
||||
PM2_HOME=/home/ubuntu/.pm2 pm2 save
|
||||
```
|
||||
|
||||
这是生产侧操作,执行前需要确认。
|
||||
|
||||
### B. 单接口短压验证客户端/服务端不一致
|
||||
|
||||
不要继续用混合脚本大压。
|
||||
|
||||
建议新增或临时使用单接口 k6 脚本,分别只测:
|
||||
|
||||
- `/api/runtime/puzzle/gallery`
|
||||
- `/api/runtime/custom-world-gallery`
|
||||
|
||||
并在同一时间窗口并行采集:
|
||||
|
||||
- k6 客户端 summary
|
||||
- nginx access 请求数/状态码
|
||||
- api-server journal latency
|
||||
- 本机到服务器网络错误/timeout
|
||||
|
||||
目标是确认 timeout 是不是发生在客户端侧连接/网络,而不是服务端处理慢。
|
||||
|
||||
### C. 修复代码性能隐患
|
||||
|
||||
优先级建议:
|
||||
|
||||
1. `count_recent_public_work_plays` 改为使用 `by_public_work_play_daily_stat_work_day` 复合索引,或至少改成批量统计,避免 N 次全表扫描。
|
||||
2. `list_puzzle_gallery_tx` 使用 `by_puzzle_work_publication_status` 索引查询 Published,或参考 custom-world 建立 `puzzle_gallery_entry` 公开读模型。
|
||||
3. gallery 列表页不要实时逐条扫描统计表,可维护读模型或批量聚合 `recent_play_count_7d`。
|
||||
|
||||
### D. 调整 k6 脚本输出
|
||||
|
||||
建议 k6 summary 按 endpoint tag 输出或新增单接口模式,否则 overall 指标会把 puzzle/custom-world 混在一起。
|
||||
|
||||
建议增加:
|
||||
|
||||
- `ENDPOINT=puzzle_gallery_list`
|
||||
- `ENDPOINT=custom_world_gallery_list`
|
||||
|
||||
让脚本只跑一个 endpoint,避免诊断时混淆。
|
||||
@@ -0,0 +1,343 @@
|
||||
# Genarrative 视觉小说“一句话生成”最小闭环落地计划
|
||||
|
||||
生成时间:2026-05-13 11:22
|
||||
工作区:`C:/proj/Genarrative/.worktrees/hermes-visual-novel`
|
||||
参考文档:`C:/Users/DSK/Documents/Interactive-fiction/一句话生成视觉小说整体流程总结.md`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把 Interactive-fiction 总结文档中的“一句话生成视觉小说”流程,映射并落地到 Genarrative 现有视觉小说能力中,优先做成一个可端到端验证的最小闭环:
|
||||
|
||||
1. 用户在视觉小说入口输入一句话并选择画风。
|
||||
2. 前端进入生成过程页,展示分阶段进度。
|
||||
3. 后端创建视觉小说创作会话,并基于 seedText 生成 `VisualNovelResultDraft`。
|
||||
4. 生成完成后进入草稿结果页,可看到世界观、角色、场景、剧情阶段、开场选择。
|
||||
5. 草稿可编译/保存为作品 profile,并进入视觉小说运行态测试/正式游玩。
|
||||
|
||||
本计划只覆盖 Genarrative 内部最小闭环,不引入 Interactive-fiction 原项目的独立 TXT 播放记录、分享播放包、外部活动运营、独立账号/交易/资产系统。
|
||||
|
||||
## 2. 当前上下文与已发现实现
|
||||
|
||||
### 2.1 Interactive-fiction 总结文档提炼
|
||||
|
||||
参考文档将整体流程分为:
|
||||
|
||||
- 输入侧:一句话创意、主题/风格、可选文档或素材。
|
||||
- 生成侧:理解意图、扩展世界观、角色、场景、剧情阶段、开场与选择。
|
||||
- 编辑侧:草稿页可查看和调整生成结果。
|
||||
- 运行侧:从草稿进入视觉小说游玩,支持剧情推进、玩家选择、历史与状态。
|
||||
- 资产侧:角色立绘、背景、音乐/音效可作为后续增强,最小闭环可先使用文字描述与空资产占位。
|
||||
|
||||
### 2.2 Genarrative 已有实现基础
|
||||
|
||||
已确认项目中视觉小说相关能力并非从零开始:
|
||||
|
||||
- 前端入口表单:
|
||||
- `src/components/visual-novel-creation/VisualNovelAgentWorkspace.tsx`
|
||||
- 已有“一句话创作” textarea、6 个视觉画风选项、提交按钮“生成视觉小说草稿”。
|
||||
- 前端入口 payload/progress:
|
||||
- `src/components/visual-novel-creation/visualNovelEntryGeneration.ts`
|
||||
- 已有 `VisualNovelEntryFormPayload`、锚点展示、一句话/画风生成进度步骤。
|
||||
- 前端平台主流程:
|
||||
- `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
|
||||
- 已接入 `createVisualNovelDraftFromForm`,会创建 session、stream message、进入 `visual-novel-generating`,完成后进入 `visual-novel-result`。
|
||||
- 前端 API client:
|
||||
- `src/services/visual-novel-creation/visualNovelCreationClient.ts`
|
||||
- 已封装 session/message/action/compile 接口。
|
||||
- 共享契约:
|
||||
- `packages/shared/src/contracts/visualNovel.ts`
|
||||
- 已定义 `VisualNovelResultDraft`、world/characters/scenes/storyPhases/opening/runtimeConfig/work/run/history 等结构。
|
||||
- 后端 API:
|
||||
- `server-rs/crates/api-server/src/visual_novel.rs`
|
||||
- 已有创建 session、发消息、流式消息、执行 action、compile、work、runtime run 等接口。
|
||||
- 后端 prompt:
|
||||
- `server-rs/crates/api-server/src/prompt/visual_novel.rs`
|
||||
- 已有 `VISUAL_NOVEL_CREATION_SYSTEM_PROMPT`、结构化输出契约、runtime GM prompt、repair prompt。
|
||||
- SpacetimeDB 模块:
|
||||
- `server-rs/crates/spacetime-module/src/visual_novel.rs`
|
||||
- 已有 session/message/work/run/history/event 表与 procedure。
|
||||
- 文档参考:
|
||||
- `docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`
|
||||
- `docs/technical/VISUAL_NOVEL_IMPLEMENTATION_HANDOFF_2026-05-07.md`
|
||||
- `docs/technical/VISUAL_NOVEL_PROMPT_AND_LLM_TOOLS_VN03_2026-05-05.md`
|
||||
|
||||
### 2.3 关键实现判断
|
||||
|
||||
当前项目已经实现了视觉小说的主要骨架,本次不应大规模重写。更合理的落地方式是补齐“一句话生成”闭环中最容易断裂的点:
|
||||
|
||||
- 入口输入与画风信息是否被稳定传给后端 prompt。
|
||||
- 后端生成 draft 后是否自动保存/关联可编辑 work profile。
|
||||
- 生成过程页是否能清晰展示 Interactive-fiction 文档中提到的阶段。
|
||||
- 结果页是否有足够的字段展示与继续游玩入口。
|
||||
- 运行态是否能基于 opening/choices 正常启动,而不依赖尚未生成的图片/音乐资产。
|
||||
|
||||
## 3. 拟采用方案
|
||||
|
||||
### 3.1 最小闭环范围
|
||||
|
||||
本次优先实现:
|
||||
|
||||
1. “一句话 + 视觉画风”作为 `sourceMode: 'idea'` 的 seedText。
|
||||
2. 后端生成完整 `VisualNovelResultDraft`,包括:
|
||||
- world
|
||||
- 3-6 个角色
|
||||
- 3-8 个场景
|
||||
- 3-6 个剧情阶段
|
||||
- opening narration/firstDialogue/2-4 个 choices
|
||||
- runtimeConfig
|
||||
3. 若 LLM 输出失败,使用 repair 或确定性 fallback,保证可回到草稿页并显示错误/警告。
|
||||
4. 结果页支持保存/编译为 work profile。
|
||||
5. work profile 支持启动 runtime run,opening 能展示初始场景、旁白、对话和选择。
|
||||
|
||||
暂不做或仅预留:
|
||||
|
||||
- 真实图片/音乐生成队列。
|
||||
- 多文档解析导入的完整链路。
|
||||
- 复杂分镜/节点图编辑器。
|
||||
- 外部 Interactive-fiction 项目的播放器、TXT 记录包、分享活动、独立账号系统。
|
||||
|
||||
### 3.2 与 Genarrative 架构的映射
|
||||
|
||||
| Interactive-fiction 概念 | Genarrative 落点 |
|
||||
| --- | --- |
|
||||
| 一句话创意 | `VisualNovelEntryFormPayload.ideaText` / `seedText` |
|
||||
| 画风/主题 | `seedText` 中的“视觉画风/画风要求”,后续可结构化为 metadata |
|
||||
| 世界观设定 | `VisualNovelResultDraft.world` |
|
||||
| 角色设定 | `VisualNovelResultDraft.characters` |
|
||||
| 场景设定 | `VisualNovelResultDraft.scenes` |
|
||||
| 剧情阶段/章节 | `VisualNovelResultDraft.storyPhases` |
|
||||
| 开场文本与选项 | `VisualNovelResultDraft.opening` |
|
||||
| 运行时剧情推进 | `VisualNovelRuntimeStep[]` + run snapshot/history |
|
||||
| 发布/作品库 | `VisualNovelWorkProfileRecord` / works API |
|
||||
|
||||
## 4. 分步计划
|
||||
|
||||
### Step 1:补齐入口 payload 与生成过程语义
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `src/components/visual-novel-creation/VisualNovelAgentWorkspace.tsx`
|
||||
- `src/components/visual-novel-creation/visualNovelEntryGeneration.ts`
|
||||
- `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
|
||||
|
||||
任务:
|
||||
|
||||
1. 保持现有 6 个画风选项,但确认每个 option 的 prompt 会进入 `seedText`。
|
||||
2. 将生成过程阶段从当前 3 步细化为更贴合参考文档的 4-5 步,例如:
|
||||
- 理解一句话创意
|
||||
- 扩展世界观与玩家身份
|
||||
- 设计角色/场景/剧情阶段
|
||||
- 生成开场与选择
|
||||
- 准备可编辑草稿
|
||||
3. 生成过程页的 anchor 保留“一句话”和“视觉画风”,必要时增加“生成目标:视觉小说草稿”。
|
||||
4. 确认 `createVisualNovelDraftFromForm` 对失败状态会保留返回入口/重试能力。
|
||||
|
||||
验收点:提交一句话后能进入 `visual-novel-generating`,看到阶段进度;完成后进入 `visual-novel-result`。
|
||||
|
||||
### Step 2:增强后端 creation prompt 与 fallback 约束
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `server-rs/crates/api-server/src/prompt/visual_novel.rs`
|
||||
- `server-rs/crates/api-server/src/visual_novel.rs`
|
||||
- 如已有 domain crate:`server-rs/crates/module-visual-novel/**` 或相关 normalize/validate 文件
|
||||
|
||||
任务:
|
||||
|
||||
1. 在 creation prompt 中显式吸收 Interactive-fiction 的“一句话生成”目标:
|
||||
- 从 seedText 提取核心创意、视觉风格、故事类型。
|
||||
- 生成可直接运行的 opening 和 choices。
|
||||
- 图片/音乐资产先置 null,但必须有可生成图像的描述。
|
||||
2. 强化输出约束:
|
||||
- `opening.sceneId` 必须指向存在且 availability 为 `opening` 的 scene。
|
||||
- `opening.initialChoices` 必须 2-4 个。
|
||||
- `storyPhases[0]` 必须包含 opening scene 和主要角色。
|
||||
- `publishReady` 的判定与 validationIssues 一致。
|
||||
3. 检查 `submit_visual_novel_message_turn` / `resolve_action_draft` / compile 相关代码:
|
||||
- 如果 LLM 失败,是否已有 fallback;没有则补确定性 fallback draft。
|
||||
- 如果 draft 不完整,是否会 normalize/repair 并写入 session。
|
||||
4. 保留现有“不要输出旧 TXT 播放记录、分享播放包、外部商业字段”的约束,避免把参考项目的外部概念误并入 Genarrative。
|
||||
|
||||
验收点:后端给定 seedText 时,返回 session.draft 不为空且满足共享契约。
|
||||
|
||||
### Step 3:确认草稿结果页、保存/编译与作品库链路
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
|
||||
- `src/components/visual-novel-creation/**`
|
||||
- `src/services/visual-novel-works*` 或相关 visual novel works client
|
||||
- `server-rs/crates/api-server/src/visual_novel.rs`
|
||||
- `packages/shared/src/contracts/visualNovel.ts`
|
||||
|
||||
任务:
|
||||
|
||||
1. 查找并确认 `visual-novel-result` 页面组件:
|
||||
- 是否显示 workTitle/workDescription/world/characters/scenes/storyPhases/opening。
|
||||
- 是否有保存/发布/开始试玩按钮。
|
||||
2. 确认 `compileVisualNovelWorkProfile` 或 `executeVisualNovelAction({kind:'compile_work_profile'})` 会生成/更新 work profile。
|
||||
3. 确认作品架上使用 `profileId` 而不是 sessionId 作为稳定作品 ID。
|
||||
4. 如果结果页缺少“一句话来源/画风”的可视化提示,可在结果页或 summary 中补轻量展示,避免用户以为画风丢失。
|
||||
|
||||
验收点:生成完成后能保存为作品;作品出现在“我的作品/创作架”;再次打开能读取同一 draft。
|
||||
|
||||
### Step 4:确认运行态 opening 闭环
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `src/components/visual-novel-runtime/**`
|
||||
- `src/services/visual-novel-runtime*`
|
||||
- `server-rs/crates/api-server/src/visual_novel.rs`
|
||||
- `server-rs/crates/api-server/src/prompt/visual_novel.rs`
|
||||
- `packages/shared/src/contracts/visualNovel.ts`
|
||||
|
||||
任务:
|
||||
|
||||
1. 启动 visual novel work run 时,优先使用 `draft.opening` 生成第一轮 runtime snapshot/history。
|
||||
2. 如果没有图片/音乐,前端 runtime shell 必须可用文字 fallback,不应白屏或阻断游玩。
|
||||
3. 玩家选择 `choice` 后,后端 runtime GM prompt 生成下一轮 `VisualNovelRuntimeStep[]`。
|
||||
4. 确认正式游玩入口调用 `work_play_start`,并满足已有埋点约定:
|
||||
- `scope_kind=work`
|
||||
- `scope_id=稳定作品 ID`
|
||||
- metadata 包含 `playType/workId/sourceRoute/userId` 等。
|
||||
|
||||
验收点:从生成出的作品进入运行态,能看到 opening 并点击至少一个选择推进一轮。
|
||||
|
||||
### Step 5:补测试与文档
|
||||
|
||||
涉及文件:
|
||||
|
||||
- 前端测试:按仓库现有测试布局查找 `*.test.ts` / `*.test.tsx`
|
||||
- Rust 测试:`server-rs/crates/api-server/src/**` 或 domain crate tests
|
||||
- 文档:可追加到 `docs/technical/` 或 `.hermes/shared-memory/decision-log.md`(如团队约定需要)
|
||||
|
||||
建议测试:
|
||||
|
||||
1. TypeScript 单元测试:
|
||||
- `buildVisualNovelEntryGenerationProgress` 阶段输出。
|
||||
- `buildVisualNovelEntryGenerationAnchorEntries` 能展示一句话和画风。
|
||||
2. Rust 单元测试:
|
||||
- creation prompt 包含 seedText、sourceMode、输出契约。
|
||||
- draft normalize/fallback 能生成合法 opening/choices。
|
||||
- runtime opening 或 first-step 构造不依赖图片/音乐。
|
||||
3. 集成/手工测试文档:
|
||||
- 访问平台视觉小说入口。
|
||||
- 输入一句话。
|
||||
- 选择画风。
|
||||
- 点击生成。
|
||||
- 查看结果页。
|
||||
- 保存作品。
|
||||
- 启动试玩并点击选择。
|
||||
|
||||
## 5. 可能改动文件清单
|
||||
|
||||
高概率改动:
|
||||
|
||||
- `src/components/visual-novel-creation/VisualNovelAgentWorkspace.tsx`
|
||||
- `src/components/visual-novel-creation/visualNovelEntryGeneration.ts`
|
||||
- `src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`
|
||||
- `server-rs/crates/api-server/src/prompt/visual_novel.rs`
|
||||
- `server-rs/crates/api-server/src/visual_novel.rs`
|
||||
- `packages/shared/src/contracts/visualNovel.ts`
|
||||
|
||||
中概率改动:
|
||||
|
||||
- `src/components/visual-novel-runtime/**`
|
||||
- `src/services/visual-novel-creation/**`
|
||||
- `src/services/visual-novel-runtime/**`
|
||||
- `src/services/visual-novel-works/**`
|
||||
- `server-rs/crates/spacetime-module/src/visual_novel.rs`
|
||||
- `server-rs/crates/spacetime-client/**` 生成/绑定文件,若 SpacetimeDB contract 需要更新
|
||||
|
||||
低概率/仅文档:
|
||||
|
||||
- `docs/technical/VISUAL_NOVEL_IMPLEMENTATION_HANDOFF_2026-05-07.md`
|
||||
- `docs/prd/AI_NATIVE_VISUAL_NOVEL_TEMPLATE_PRD_2026-05-05.md`
|
||||
- `.hermes/shared-memory/decision-log.md`
|
||||
|
||||
## 6. 验证计划
|
||||
|
||||
### 6.1 静态检查
|
||||
|
||||
在 worktree 根目录执行:
|
||||
|
||||
```bash
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
如仓库无统一 typecheck,则按 package scripts 选择最接近的前端类型检查命令。
|
||||
|
||||
### 6.2 前端定向测试
|
||||
|
||||
优先运行与 visual novel / platform entry 相关测试,如存在:
|
||||
|
||||
```bash
|
||||
npm test -- visual-novel
|
||||
npm test -- platform-entry
|
||||
```
|
||||
|
||||
若仓库使用 vitest:
|
||||
|
||||
```bash
|
||||
npm run test -- visual-novel
|
||||
```
|
||||
|
||||
### 6.3 Rust 定向测试
|
||||
|
||||
在 `server-rs` 下运行 visual novel 相关测试:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server visual_novel
|
||||
cargo test -p shared-contracts visual_novel
|
||||
```
|
||||
|
||||
如改动 SpacetimeDB module:
|
||||
|
||||
```bash
|
||||
cargo test -p spacetime-module visual_novel
|
||||
```
|
||||
|
||||
### 6.4 人工验收步骤
|
||||
|
||||
1. 启动本地 dev 栈。
|
||||
2. 访问 Genarrative 主站。
|
||||
3. 进入创作/视觉小说入口。
|
||||
4. 输入:`一个雨夜,失忆的高中生在旧图书馆发现一本会回应她心声的日记。`
|
||||
5. 选择任一画风,例如“映画动画”。
|
||||
6. 点击“生成视觉小说草稿”。
|
||||
7. 预期:进入生成过程页,能看到分阶段进度。
|
||||
8. 预期:完成后进入草稿结果页,包含标题、简介、世界观、角色、场景、剧情阶段和 opening choices。
|
||||
9. 点击保存/编译作品。
|
||||
10. 从作品入口进入试玩。
|
||||
11. 预期:opening 文本出现,至少 2 个选择可点击;点击后剧情继续推进一轮。
|
||||
|
||||
## 7. 风险、权衡与开放问题
|
||||
|
||||
### 7.1 风险
|
||||
|
||||
- 现有视觉小说代码已较完整,贸然新增一套 parallel pipeline 会制造重复逻辑;应复用当前 `VisualNovelResultDraft` 与 creation agent flow。
|
||||
- LLM 输出不稳定可能导致草稿结构不完整;需要 normalize/repair/fallback 确保最小闭环。
|
||||
- 视觉/音乐资产生成未接入时,UI 必须接受 null asset,否则运行态可能白屏。
|
||||
- `PlatformEntryFlowShellImpl.tsx` 文件很大,改动需局部、谨慎,避免影响其他玩法入口。
|
||||
- 若改动 SpacetimeDB 表结构,可能牵涉 publish、client binding、清库/迁移;最小闭环阶段应尽量避免 schema 变更。
|
||||
|
||||
### 7.2 权衡
|
||||
|
||||
- 先让文字版视觉小说完整跑通,再补角色立绘/背景图生成。
|
||||
- 先用 `seedText` 承载画风,再考虑把 `visualStyleId/Label/Prompt` 结构化进 draft metadata。
|
||||
- 先用现有 result/work/runtime 页面闭环,不引入新编辑器。
|
||||
|
||||
### 7.3 开放问题
|
||||
|
||||
1. 用户是否要求把 Interactive-fiction 原项目中的具体 UI 样式/页面布局迁移到 Genarrative?当前计划只迁移流程语义,不迁移独立 UI。
|
||||
2. 画风是否需要成为作品可编辑字段?当前以 seedText/prompt 影响生成内容,后续可在 draft 中增加 metadata。
|
||||
3. 文档导入模式是否本期要做?当前计划聚焦一句话模式,document 模式只保留契约能力。
|
||||
4. 是否需要真实图片/音乐生成?当前计划作为后续增强,不纳入最小闭环。
|
||||
|
||||
## 8. 建议实施顺序
|
||||
|
||||
1. 先做只改 prompt/progress/少量前端展示的轻量闭环修补。
|
||||
2. 运行前后端定向测试,确认现有能力是否已足够。
|
||||
3. 如果后端没有 fallback 或 normalize,再补 Rust 层确定性兜底。
|
||||
4. 手工跑通“一句话 -> 生成 -> 结果页 -> 保存 -> 试玩”。
|
||||
5. 最后再考虑是否需要资产生成、文档导入、结构化画风 metadata。
|
||||
Reference in New Issue
Block a user