Initial commit: clean project structure

- Backend: FastAPI + SQLAlchemy + Celery (Python 3.11+)
- Frontend: Vue 3 + TypeScript + Pinia + Tailwind
- Admin Frontend: separate Vue 3 app for management
- Docker Compose: 9 services orchestration
- Specs: design prototypes, memory system PRD, product roadmap

Cleanup performed:
- Removed temporary debug scripts from backend root
- Removed deprecated admin_app.py (embedded UI)
- Removed duplicate docs from admin-frontend
- Updated .gitignore for Vite cache and egg-info
This commit is contained in:
zhangtuo
2026-01-20 18:20:03 +08:00
commit e9d7f8832a
241 changed files with 33070 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
# Code Review Report (2nd follow-up)
## What¡¯s fixed
- Provider cache now loads on startup via lifespan (`app/main.py`), so DB providers are honored without manual reload.
- Providers support DB-stored `api_key` precedence (`provider_router.py:77-104`) and Provider model added `api_key` column (`db/admin_models.py:25`).
- Frontend uses `/api/generate/full` and propagates image-failure warning to detail via query flag; StoryDetail displays banner when image generation failed.
- Tests added for full generation, provider failover, config-from-DB, and startup cache load.
## Remaining issue
1) **Missing DB migration for new Provider.api_key column**
- Files updated model (`backend/app/db/admin_models.py:25`) but `backend/alembic/versions/0001_init_providers_and_story_mode.py` lacks this column. Existing databases will not have `api_key`, causing runtime errors when accessing or inserting. Add an Alembic migration to add/drop `api_key` to `providers` table and update sample data if needed.
## Suggested action
- Create and apply an Alembic migration adding `api_key` (String, nullable) to `providers`. After migration, verify admin CRUD works with the new field.

View File

@@ -0,0 +1,147 @@
# 记忆系统开发指南 (Development Guide)
本文档详细说明了 PRD 中定义的记忆系统的技术实现细节。
## 1. 数据库架构变更 (Schema Changes)
目前的 `MemoryItem` 表结构尚可,但需要增强字段以支持丰富的情感和元数据。
### 1.1 `MemoryItem` 表优化
建议使用 Alembic 进行迁移,增加以下字段或在 `value` JSON 中规范化以下结构:
```python
# 建议在 models.py 中明确这些字段,或者严格定义 value 字段的 Schema
class MemoryItem(Base):
# ... 现有字段 ...
# 新增/规范化字段建议
# value 字段的 JSON 结构规范:
# {
# "content": "小兔子战胜了大灰狼", # 记忆的核心文本
# "keywords": ["勇敢", "森林"], # 用于检索的关键词
# "emotion": "positive", # 情感倾向: positive/negative/neutral
# "source_story_id": 123, # 来源故事 ID
# "confidence": 0.85 # 记忆置信度 (如果是 AI 自动提取)
# }
```
### 1.2 `StoryUniverse` 表优化 (成就系统)
目前的成就存储在 `achievements` JSON 字段中。为了支持更复杂的查询(如"获得勇气勋章的所有用户"),建议将其重构为独立关联表,或保持 JSON 但规范化结构。
**当前 JSON 结构规范**:
```json
[
{
"id": "badge_courage_01",
"type": "勇气",
"name": "小小勇士",
"description": "第一次在故事中独自面对困难",
"icon_url": "badges/courage.png",
"obtained_at": "2023-10-27T10:00:00Z",
"source_story_id": 45
}
]
```
---
## 2. 核心逻辑实现
### 2.1 记忆注入逻辑 (Prompt Engineering)
修改 `backend/app/api/stories.py` 中的 `_build_memory_context` 函数。
**目标**: 生成一段自然的、不仅是罗列数据的 Prompt。
**伪代码逻辑**:
```python
def format_memory_for_prompt(memories: list[MemoryItem]) -> str:
"""
将记忆项转换为自然语言 Prompt 片段。
"""
context_parts = []
# 1. 角色记忆
chars = [m for m in memories if m.type == 'favorite_character']
if chars:
names = ", ".join([c.value['name'] for c in chars])
context_parts.append(f"孩子特别喜欢的角色有:{names}。请尝试让他们客串出场。")
# 2. 近期情节
recent_stories = [m for m in memories if m.type == 'recent_story'][:2]
if recent_stories:
for story in recent_stories:
context_parts.append(f"最近发生过:{story.value['summary']}。可以在对话中不经意地提及此事。")
# 3. 避雷区 (负面记忆)
scary = [m for m in memories if m.type == 'scary_element']
if scary:
items = ", ".join([s.value['keyword'] for s in scary])
context_parts.append(f"【绝对禁止】不要出现以下让孩子害怕的元素:{items}")
return "\n".join(context_parts)
```
### 2.2 成就提取与通知流程
当前流程在 `app/tasks/achievements.py`。需要完善闭环。
**改进后的流程**:
1. **Story Generation**: 故事生成成功,存入数据库。
2. **Async Task**: 触发 Celery 任务 `extract_story_achievements`
3. **LLM Analysis**: 调用 Gemini 分析故事,提取成就。
4. **Update DB**: 更新 `StoryUniverse.achievements`
5. **Notification (新增)**:
* 创建一个 `Notification``UserMessage` 记录(需要新建表或使用 Redis Pub/Sub
* 前端轮询或通过 SSE (Server-Sent Events) 接收通知:"🎉 恭喜!在这个故事里,小明获得了[诚实勋章]"
### 2.3 记忆清理与衰减 (Maintenance)
需要一个后台定时任务Cron Job清理无效记忆避免 Prompt 过长。
* **频率**: 每天一次。
* **逻辑**:
* 删除 `ttl_days` 已过期的记录。
*`recent_story` 类型的 `base_weight` 进行每日衰减 update或者只在读取时计算数据库存静态值推荐读取时动态计算以减少写操作
*`MemoryItem` 总数超过 100 条时,触发"记忆总结"任务,将多条旧记忆合并为一条"长期印象" (Long-term Impression)。
---
## 3. API 接口规划
### 3.1 获取成长时间轴
`GET /api/profiles/{id}/timeline`
**Response**:
```json
{
"events": [
{
"date": "2023-10-01",
"type": "milestone",
"title": "初次相遇",
"description": "创建了角色 [小明]"
},
{
"date": "2023-10-05",
"type": "story",
"title": "小明与魔法树",
"image_url": "..."
},
{
"date": "2023-10-05",
"type": "achievement",
"badge": {
"name": "好奇宝宝",
"icon": "..."
}
}
]
}
```
### 3.2 记忆反馈 (人工干预)
`POST /api/memories/{id}/feedback`
允许家长手动删除或修正错误的记忆。
* **Action**: `delete` | `reinforce` (强化,增加权重)

View File

@@ -0,0 +1,93 @@
# 梦语织机 (DreamWeaver) 记忆系统升级 PRD
> 版本: v1.0 | 状态: 规划中 | 优先级: High
## 1. 核心愿景 (Vision)
将当前的"数据存储"升级为有温度的**"情感连接系统"**。
我们不只是在记住数据,而是在**维护孩子与故事世界的关系**。让每一个故事不再是孤立的碎片,而是构建孩子专属"故事宇宙"的砖瓦。
---
## 2. 产品痛点与解决方案
| 用户角色 | 核心痛点 | 解决方案 | 预期价值 |
|---------|---------|---------|---------|
| **孩子** | "上次的小兔子怎么不认识我了?" <br> 故事之间缺乏连续性,只有单次体验。 | **角色一致性与记忆注入** <br> 故事开头主动提及往事,角色性格延续。 | 建立情感依恋,提升沉浸感。 |
| **家长** | "这App除了生成故事还能干嘛" <br> 无法感知产品的长期教育价值。 | **显性化成长轨迹** <br> 词汇量统计、主题变化、成就徽章可视化。 | 提高付费意愿,提供社交货币。 |
| **平台** | 用户用完即走,缺乏留存壁垒。 | **沉没成本与情感资产** <br> 积累的记忆越多,越舍不得离开。 | 提升长期留存率 (LTV)。 |
---
## 3. 功能架构:记忆分层模型
### 3.1 层级 1: 核心档案 (Identity Layer)
*性质:永久、静态、显性*
* **数据**: 姓名、年龄、性别。
* **输入**: 家长在 Onboarding 阶段手动输入。
* **作用**: 决定故事的基础适龄性和称呼。
### 3.2 层级 2: 故事宇宙 (Universe Layer)
*性质:长期、动态积累、半显性*
* **主角设定**: 姓名、性格特征(勇敢/害羞)、外貌特征(戴眼镜/卷发)。
* **常驻配角**: 从随机故事中涌现出的固定伙伴(如"爱吃胡萝卜的松鼠奇奇")。
* **世界观**: 故事发生的背景(魔法森林、未来城市、海底世界)。
* **成就系统**: 孩子获得的虚拟奖励(勇气勋章、小小探险家)。
### 3.3 层级 3: 工作记忆 (Working Memory)
*性质:短期、自动衰减、隐性*
* **关键情节**: 最近 3 个故事的结局和核心冲突。
* **情感标记**: 孩子对特定内容的反应(根据“重播”、“跳过”推断)。
* **新学词汇**: 故事中出现的高级词汇。
---
## 4. 关键功能特性 (Feature Specs)
### 4.1 智能开场白 (Memory Injection)
在生成新故事时Prompt 必须包含一段"记忆唤醒"指令。
* **示例**: "小明,还记得上周我们帮小松鼠找回了松果吗?今天,小松鼠带来了一位新朋友..."
* **策略**: 提取权重最高的 Top 3 记忆注入 Prompt。
### 4.2 成长时间轴 (Growth Timeline)
一个可视化的 H5 页面或 App 模块,以时间轴形式展示里程碑。
* **节点类型**:
* 🌟 **初次相遇**: 创建角色的第一天。
* 📖 **阅读打卡**: 累计阅读 10/50/100 本。
* 🏅 **获得成就**: 获得"诚实勋章"。
* 🧠 **能力解锁**: 第一次阅读"科幻"题材。
### 4.3 成就仪式感 (Achievement Ceremony)
* **触发**: 故事生成并分析后,如果获得新成就。
* **表现**: 弹窗动画 + 音效 + "恭喜获得 [勇气] 徽章"。
* **分享**:允许生成带二维码的成就海报。
---
## 5. 记忆类型扩展 (Memory Types)
| 类型 Key | 描述 | 来源 | 过期策略 |
|---------|------|------|---------|
| `recent_story` | 最近读过的故事梗概 | 阅读事件 | 30天衰减 |
| `favorite_character` | 孩子喜欢的角色 | 重播/高评分 | 长期有效 |
| `scary_element` | 孩子害怕/不喜欢的元素 | 跳过/负反馈 | 长期有效 (避雷) |
| `vocabulary_growth` | 新掌握的词汇 | 故事分析 | 90天衰减 |
| `emotional_highlight` | 高光时刻 (如: 特别开心的情节) | 互动数据 | 60天衰减 |
---
## 6. 实施路线图 (Roadmap)
### Phase 1: 基础建设 (v0.3.0)
* [x] 数据库 `MemoryItem` 表 (已存在)。
* [ ] 扩展 `MemoryItem` 类型字段,支持更多维度。
* [ ] 优化 `_build_memory_context`,支持更自然的 Prompt 注入。
* [ ] 前端:简单的"近期回忆"展示列表。
### Phase 2: 可视化与成就 (v0.4.0)
* [ ] 实现"成就提取器" (Achievement Extractor) 的闭环通知。
* [ ] 前端:开发"我的成就"和"成长时间轴"页面。
* [ ] 增加故事开场白的动态生成逻辑。
### Phase 3: 深度智能 (v0.5.0+)
* [ ] 引入向量数据库,实现基于语义的记忆检索 (不仅是时间最近)。
* [ ] 情感分析模型:分析用户行为推断情感倾向。

View File

@@ -0,0 +1,246 @@
# Provider 系统开发文档
## 当前版本功能 (v0.2.0)
### 已完成功能
1. **CQTAI nano 图像适配器** (`app/services/adapters/image/cqtai.py`)
- 异步生成 + 轮询获取结果
- 支持 nano-banana / nano-banana-pro 模型
- 支持多种分辨率和画面比例
- 支持图生图 (filesUrl)
2. **密钥加密存储** (`app/services/secret_service.py`)
- Fernet 对称加密,密钥从 SECRET_KEY 派生
- Provider API Key 自动加密存储
- 密钥管理 API (CRUD)
3. **指标收集系统** (`app/services/provider_metrics.py`)
- 调用成功率、延迟、成本统计
- 时间窗口聚合查询
- 已集成到 provider_router
4. **熔断器功能** (`app/services/provider_metrics.py`)
- 连续失败 3 次触发熔断
- 60 秒后自动恢复尝试
- 健康状态持久化到数据库
5. **管理后台前端** (`app/admin_app.py`)
- 独立端口部署 (8001)
- Vue 3 + Tailwind CSS 单页应用
- Provider CRUD 管理
- 密钥管理界面
- Basic Auth 认证
### 配置说明
```bash
# 启动主应用
uvicorn app.main:app --port 8000
# 启动管理后台 (独立端口)
uvicorn app.admin_app:app --port 8001
```
环境变量:
```
CQTAI_API_KEY=your-cqtai-api-key
ENABLE_ADMIN_CONSOLE=true
ADMIN_USERNAME=admin
ADMIN_PASSWORD=your-secure-password
```
---
## 下一版本优化计划 (v0.3.0)
### 高优先级
#### 1. 智能负载分流 (方案 B)
**目标**: 主渠道压力大时自动分流到后备渠道
**实现方案**:
- 监控指标: 并发数、响应延迟、错误率
- 分流阈值配置:
```python
class LoadBalanceConfig:
max_concurrent: int = 10 # 并发超过此值时分流
max_latency_ms: int = 5000 # 延迟超过此值时分流
max_error_rate: float = 0.1 # 错误率超过 10% 时分流
```
- 分流策略: 加权轮询,根据健康度动态调整权重
**涉及文件**:
- `app/services/provider_router.py` - 添加负载均衡逻辑
- `app/services/provider_metrics.py` - 添加并发计数器
- `app/db/admin_models.py` - 添加 LoadBalanceConfig 模型
#### 2. Storybook 适配器
**目标**: 生成可翻页的分页故事书
**实现方案**:
- 参考 Gemini AI Story Generator 格式
- 输出结构:
```python
class StorybookPage:
page_number: int
text: str
image_prompt: str
image_url: str | None
class Storybook:
title: str
pages: list[StorybookPage]
cover_url: str | None
```
- 集成文本 + 图像生成流水线
**涉及文件**:
- `app/services/adapters/storybook/` - 新建目录
- `app/api/stories.py` - 添加 storybook 生成端点
### 中优先级
#### 3. 成本追踪系统
**目标**: 记录实际消费,支持预算控制
**实现方案**:
- 成本记录表:
```python
class CostRecord:
user_id: str
provider_id: str
capability: str # text/image/tts
estimated_cost: Decimal
actual_cost: Decimal | None
timestamp: datetime
```
- 预算配置:
```python
class BudgetConfig:
user_id: str
daily_limit: Decimal
monthly_limit: Decimal
alert_threshold: float = 0.8 # 80% 时告警
```
- 超预算处理: 拒绝请求 / 降级到低成本 provider
**涉及文件**:
- `app/db/admin_models.py` - 添加 CostRecord, BudgetConfig
- `app/services/cost_tracker.py` - 新建
- `app/api/admin_providers.py` - 添加成本查询 API
#### 4. 指标可视化
**目标**: 管理后台展示供应商指标图表
**实现方案**:
- 添加指标查询 API:
- GET /admin/metrics/summary - 汇总统计
- GET /admin/metrics/timeline - 时间线数据
- GET /admin/metrics/providers/{id} - 单个供应商详情
- 前端使用 Chart.js 或 ECharts 展示
### 低优先级
#### 5. 多租户 Provider 配置
**目标**: 每个租户可配置独立 provider 列表和 API Key
**实现方案**:
- 租户配置表:
```python
class TenantProviderConfig:
tenant_id: str
provider_type: str
provider_ids: list[str] # 按优先级排序
api_key_override: str | None # 加密存储
```
- 路由时优先使用租户配置,回退到全局配置
#### 6. Provider 健康检查调度器
**目标**: 定期主动检查 provider 健康状态
**实现方案**:
- Celery Beat 定时任务
- 每 5 分钟检查一次所有启用的 provider
- 更新 ProviderHealth 表
#### 7. 适配器热加载
**目标**: 支持运行时动态加载新适配器
**实现方案**:
- 适配器插件目录: `app/services/adapters/plugins/`
- 启动时扫描并注册
- 提供 API 触发重新扫描
---
## API 变更记录
### v0.2.0 新增
| Method | Route | Description |
|--------|-------|-------------|
| GET | `/admin/secrets` | 列出所有密钥名称 |
| POST | `/admin/secrets` | 创建/更新密钥 |
| DELETE | `/admin/secrets/{name}` | 删除密钥 |
| GET | `/admin/secrets/{name}/verify` | 验证密钥有效性 |
### 计划中 (v0.3.0)
| Method | Route | Description |
|--------|-------|-------------|
| GET | `/admin/metrics/summary` | 指标汇总 |
| GET | `/admin/metrics/timeline` | 时间线数据 |
| POST | `/api/storybook/generate` | 生成分页故事书 |
| GET | `/admin/costs` | 成本统计 |
| POST | `/admin/budgets` | 设置预算 |
---
## 适配器开发指南
### 添加新适配器
1. 创建适配器文件:
```python
# app/services/adapters/image/new_provider.py
from app.services.adapters.base import AdapterConfig, BaseAdapter
from app.services.adapters.registry import AdapterRegistry
@AdapterRegistry.register("image", "new_provider")
class NewProviderAdapter(BaseAdapter[str]):
adapter_type = "image"
adapter_name = "new_provider"
async def execute(self, prompt: str, **kwargs) -> str:
# 实现生成逻辑
pass
async def health_check(self) -> bool:
# 实现健康检查
pass
@property
def estimated_cost(self) -> float:
return 0.01 # USD
```
2. 在 `__init__.py` 中导入:
```python
# app/services/adapters/__init__.py
from app.services.adapters.image import new_provider as _new_provider # noqa: F401
```
3. 添加配置:
```python
# app/core/config.py
new_provider_api_key: str = ""
# app/services/provider_router.py
API_KEY_MAP["new_provider"] = "new_provider_api_key"
```
4. 更新 `.env.example`:
```
NEW_PROVIDER_API_KEY=
```