回忆录 Story 流水线(同步) - 同步路径仅写入 Story 与章节关联,改为 mark_chapter_dirty_sync,不再内联 compose - 物化由 Celery recompose_chapter 异步完成;compose 不变量与异常时保留 dirty 的语义在 repo 中补充说明 - Evidence:大批次时降低 top_k;路由候选 story 携带 char_count/version_count;append 超长/版本过多时强制新开 story - 叙事 prompt:relevant_chunks 去重,减少重复证据噪声 - 叙事回退与忠实度 gate:返回 fallback 类型并记录结构化日志(含耗时、JSON 有效性等) Post-commit 与任务编排 - 新增 post_commit.enqueue_story_post_commit_effects:统一派发 generate_story_image(Redis 去重)、延迟 recompose_chapter、可选 memory compaction - memoir_tasks / story_service / story_image_tasks 改为调用 post-commit 入口;主图回填后按关联章节重算并调度物化与 compacs(锁委托、Redis 单例、ASR to_thread) - 更新 test_narrative_pipeline 以适配 _apply_narrative_fallbacks 返回值
Life Echo API
Life Echo 后端服务,基于 FastAPI 构建的实时语音对话回忆录生成系统。
项目简介
Life Echo API 是一个智能对话系统,通过 WebSocket 实时连接,使用 LangChain Agent 引导用户进行回忆录访谈对话,并将口语内容自动整理为结构化的书面章节,最终生成回忆录 PDF。
架构要点(多 Agent 收敛)
- 会话真源:
conversation_messages(DB)+ Redis 缓存;实时编排入口:ChatOrchestrator。 - 图像管线:正文主图
generate_story_image;章节封面try_enqueue_generate_chapter_cover→generate_chapter_cover。 - 回忆录批次:
MemoirOrchestrator.prepare_batches显式分桶后,process_memoir_segments按类别加锁并调用run_story_pipeline_for_category_batch(含StoryRouteAgent.plan_batch多 unit 写入)。
LLM 与记忆(约定文档)
- JSON 模式:结构化抽取/路由/叙事 JSON 使用
app/core/langchain_llm.py的bind_json_object_mode(与 DeepSeek JSON Output 一致);详见docs/llm-json-mode.md。适配器说明见app/adapters/llm/deepseek.py。 - 记忆检索:异步
HybridRetriever(FTS+向量)与 Celeryretrieve_evidence_sync(FTS)差异见docs/memory-retrieval.md。
技术栈
- Web 框架: FastAPI 0.115.0
- WebSocket: websockets 14.1
- AI 框架: LangChain 0.3.7 + DeepSeek/兼容 OpenAI 的 LLM
- 数据库: PostgreSQL 17 + SQLAlchemy 2.0.36 (asyncpg)
- 缓存/队列: Redis 7 + Celery 5.3
- PDF 生成: ReportLab 4.2.2 + WeasyPrint 62.3
- ASR/TTS: OpenAI Whisper API
- 认证: JWT (python-jose) + bcrypt
- 其他: Pydantic, python-dotenv
项目结构
api/
├── main.py # 应用入口(uvicorn 启动)
├── app/ # 应用主包
│ ├── main.py # FastAPI 应用定义
│ ├── core/ # 核心基础设施
│ │ ├── config.py # 配置(pydantic-settings)
│ │ ├── db.py # 数据库连接
│ │ ├── redis.py # Redis 服务
│ │ ├── security.py # JWT、密码哈希
│ │ └── task_tracker.py # 任务状态追踪
│ ├── features/ # 功能模块(各模块含 router、service、repo)
│ │ ├── auth/ # 认证(注册、登录、短信验证码)
│ │ ├── user/ # 用户信息
│ │ ├── conversation/ # 对话、WebSocket
│ │ ├── memory/ # 记忆检索
│ │ ├── memoir/ # 回忆录、章节、PDF、图像生成
│ │ ├── payment/ # 支付
│ │ ├── plan/ # 套餐
│ │ ├── quota/ # 配额
│ │ ├── tasks/ # 任务状态 API
│ │ └── content/ # 内容(TTS 等)
│ ├── adapters/ # 外部能力适配器(ASR、TTS、LLM、短信、存储等)
│ ├── ports/ # 能力契约(Protocol)
│ └── agents/ # LangChain Agent
├── tasks/ # Celery 后台任务
│ └── memoir_tasks.py # 回忆录处理任务
└── docs/ # 详细文档
环境配置
1. 安装依赖
cd api
pip install -r requirements.txt
2. 环境变量配置
创建 .env 文件(在 api/ 目录下):
# DeepSeek API 配置(推荐,优先使用)
DEEPSEEK_API_KEY=your_deepseek_api_key_here
DEEPSEEK_BASE_URL=https://api.deepseek.com # 可选,默认值
DEEPSEEK_MODEL=deepseek-chat # 可选,默认值
# 或使用通用 LLM 配置(支持其他兼容 OpenAI 的 LLM)
LLM_API_KEY=your_llm_api_key_here
LLM_BASE_URL=https://api.your-llm-provider.com # 可选
LLM_MODEL=your-model-name # 可选,默认 deepseek-chat
LLM_TEMPERATURE=0.7 # 可选,默认 0.7
# 数据库配置(PostgreSQL,推荐)
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/life_echo
# Redis 配置
REDIS_URL=redis://localhost:6379/0
# 认证配置
SECRET_KEY=your-secret-key-here # JWT签名密钥(建议使用随机字符串)
ALGORITHM=HS256 # JWT算法(默认HS256)
ACCESS_TOKEN_EXPIRE_MINUTES=120 # 访问令牌过期时间(分钟,默认120即2小时)
# 服务器配置(可选)
HOST=0.0.0.0
PORT=8000
LLM 配置优先级:
DEEPSEEK_API_KEY- 优先使用 DeepSeek(推荐)LLM_API_KEY- 通用 LLM 配置(支持其他兼容 OpenAI 格式的 LLM)
DeepSeek 配置示例:
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxx
DEEPSEEK_MODEL=deepseek-chat
3. 数据库迁移
数据库 schema 由 Alembic 管理。app/main.py 启动时会在线程中执行 alembic upgrade head(见 app/core/alembic_startup.py):对连接类错误自动重试;生产环境建议设置 ALEMBIC_STARTUP_FAIL_FAST=true,迁移失败则进程退出。仍可手动执行:
cd api
uv run alembic upgrade head
快速启动
本地开发
推荐使用一键脚本(会自动启动 PostgreSQL/Redis、检查 .venv、安装依赖并拉起 FastAPI + Celery):
cd api
./dev-up.sh
可选环境变量:
SKIP_INSTALL=1:跳过依赖安装API_HOST/API_PORT:覆盖 API 启动地址和端口CELERY_POOL:覆盖 Celery 池类型(macOS 推荐solo)
也可以使用手动方式:
cd api
# 1. 启动 PostgreSQL + Redis
docker compose -f docker-compose.dev.yml up -d
# 2. 安装依赖
pip install -r requirements.txt
# 3. 配置环境变量
export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/life_echo
export REDIS_URL=redis://localhost:6379/0
# 4. 启动 API(终端 1)
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# 5. 启动 Celery Worker(终端 2)
# macOS 使用 solo 池避免 fork 崩溃问题
celery -A tasks.celery_app worker --loglevel=info --pool=solo
# Linux/生产环境可以使用 prefork 池
# celery -A tasks.celery_app worker --loglevel=info --concurrency=4
验证服务
# 检查 PostgreSQL
docker exec life-echo-postgres-dev psql -U postgres -c "SELECT 1"
# 检查 Redis
docker exec life-echo-redis-dev redis-cli ping
生产部署(一键)
cd api
# 创建生产配置
cp .env .env.prod
# 编辑 .env.prod
# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f
服务启动后,访问:
- API 文档: http://localhost:8000/docs
- 健康检查: http://localhost:8000/health
📚 详细文档
更多详细文档请参考 docs/README.md:
- 本地开发环境配置 - 开发环境搭建指南
- WebSocket 快速测试指南 - WebSocket 快速测试
- WebSocket 测试文档 - WebSocket 详细接口文档
- 文字交流模式说明 - 文字对话模式功能说明
- 测试脚本使用说明 - 自动化测试脚本指南
API 文档
认证系统
系统使用 JWT(JSON Web Token)进行认证,采用访问令牌(Access Token)+ 刷新令牌(Refresh Token)机制:
- 访问令牌:有效期 2 小时,用于 API 请求认证
- 刷新令牌:有效期 30 天,用于刷新访问令牌
认证流程
- 用户注册:
POST /api/auth/register - 用户登录:
POST /api/auth/login→ 返回access_token和refresh_token - API 请求:在 Header 中携带
Authorization: Bearer {access_token} - 刷新令牌:
POST /api/auth/refresh→ 使用refresh_token获取新的access_token - 用户登出:
POST /api/auth/logout→ 撤销refresh_token
认证 API (/api/auth)
用户注册
POST /api/auth/register
Content-Type: application/json
{
"phone": "13800138000",
"password": "password123",
"nickname": "用户昵称",
"email": "user@example.com" // 可选
}
响应:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "random-refresh-token-string",
"token_type": "bearer"
}
用户登录
POST /api/auth/login
Content-Type: application/json
{
"phone": "13800138000",
"password": "password123"
}
响应:同注册接口
刷新访问令牌
POST /api/auth/refresh
Content-Type: application/json
{
"refresh_token": "your-refresh-token"
}
响应:
{
"access_token": "new-access-token",
"refresh_token": "same-refresh-token",
"token_type": "bearer"
}
用户登出
POST /api/auth/logout
Authorization: Bearer {access_token}
Content-Type: application/json
{
"refresh_token": "your-refresh-token"
}
获取当前用户信息
GET /api/auth/me
Authorization: Bearer {access_token}
响应:
{
"id": "user-id",
"phone": "13800138000",
"email": "user@example.com",
"nickname": "用户昵称",
"avatar_url": null,
"subscription_type": "free",
"created_at": "2024-01-15T10:00:00Z"
}
使用认证令牌
所有需要认证的 API 请求都需要在 Header 中携带访问令牌:
Authorization: Bearer {access_token}
REST API
对话管理 (/api/conversations)
注意:所有对话相关接口都需要认证。
POST /api/conversations- 创建新对话(需要认证)GET /api/conversations/{conversation_id}- 获取对话详情(需要认证,只能访问自己的对话)POST /api/conversations/{conversation_id}/end- 结束对话(需要认证,只能结束自己的对话)
章节管理 (/api/chapters)
注意:所有章节相关接口都需要认证。
GET /api/chapters- 获取用户所有章节(需要认证)GET /api/chapters/{chapter_id}- 获取章节详情(需要认证,只能访问自己的章节)POST /api/chapters/{chapter_id}/regenerate- 重新整理章节(需要认证,只能操作自己的章节)
回忆录管理 (/api/books)
注意:所有回忆录相关接口都需要认证。
GET /api/books/current- 获取当前回忆录(需要认证)POST /api/books/export-pdf- 导出 PDF(需要认证,只能导出自己的回忆录)
WebSocket API
对话 WebSocket (/ws/conversation/{conversation_id})
注意:WebSocket 连接需要认证,通过查询参数传递访问令牌。
连接地址:
ws://localhost:8000/ws/conversation/{conversation_id}?token={access_token}
实时双向通信,支持:
- 接收客户端音频数据
- 发送 Agent 响应文本
- 实时语音识别(ASR)
- 实时语音合成(TTS)
认证要求:
- 必须在查询参数中提供有效的
access_token - 只能连接属于当前用户的对话
- 如果对话不存在,将自动创建并关联到当前用户
消息格式:
客户端 → 服务端:
{
"type": "audio",
"data": "base64_encoded_audio_data"
}
服务端 → 客户端:
{
"type": "transcript",
"text": "识别出的文本",
"agent_response": "Agent 的回复"
}
{
"type": "audio",
"data": "base64_encoded_tts_audio"
}
数据库模型
User(用户)
id: 用户 IDphone: 手机号(唯一,必填)password_hash: 密码哈希email: 邮箱(可选)openid: 微信 OpenID(可选)nickname: 昵称avatar_url: 头像 URLsubscription_type: 订阅类型(free/premium)created_at: 创建时间
RefreshToken(刷新令牌)
id: 令牌 IDuser_id: 用户 ID(外键)token: 刷新令牌(唯一)expires_at: 过期时间(30天后)created_at: 创建时间is_revoked: 是否已撤销
Conversation(对话)
id: 对话 IDuser_id: 用户 IDstarted_at: 开始时间ended_at: 结束时间duration_seconds: 持续时间(秒)summary: 对话摘要status: 状态(active/ended/processing)current_topic: 当前话题conversation_stage: 对话阶段(childhood/education/career/family/beliefs/summary)
Segment(对话段落)
id: 段落 IDconversation_id: 对话 IDaudio_url: 音频 URLuser_input_text: 用户输入正文(语音 ASR 或文字输入;历史列名transcript_text)created_at: 创建时间processed: 是否已处理topic_category: 话题分类agent_response: Agent 响应
Chapter(章节)
id: 章节 IDuser_id: 用户 IDtitle: 标题content: 内容order_index: 排序索引status: 状态(draft/completed)images: 图片 URL 列表(JSON)updated_at: 更新时间category: 章节分类
Book(回忆录)
id: 回忆录 IDuser_id: 用户 IDtitle: 标题total_pages: 总页数total_words: 总字数cover_image_url: 封面图片 URLupdated_at: 更新时间
核心功能
1. 对话引导 Agent
使用 LangChain 构建的对话 Agent,根据传记结构引导用户回忆:
- 童年时光
- 教育经历
- 职业生涯
- 家庭生活
- 人生信念
- 总结回顾
2. 记忆整理 Agent
将口语对话内容整理为结构化的书面章节:
- 口语转书面语
- 内容结构化
- 章节分类
- 自动生成标题
3. 语音服务
- ASR (语音识别): 使用 OpenAI Whisper API 将音频转为文本
- TTS (语音合成): 使用 OpenAI TTS API 将文本转为语音
4. PDF 生成
使用 ReportLab 和 WeasyPrint 生成精美的回忆录 PDF 文档。
开发指南
添加新的 API 路由
- 在
routers/目录创建新的路由文件 - 定义路由函数
- 在
main.py中注册路由:
from routers import your_router
app.include_router(your_router.router)
添加新的数据库模型
- 在对应 feature 的
app/features/<feature_name>/models.py中定义模型类 - 继承
Base(从app.core.db导入) - 在
app/main.py中 import 该 models 模块以注册到 Base.metadata - 运行 Alembic 迁移
添加新的服务
- 在
app/features/<feature_name>/service.py中实现业务逻辑 - 通过
deps.py提供依赖注入 - 在对应 feature 的 router 中通过
Depends(get_xxx_service)使用
安全注意事项
- CORS 配置: 当前允许所有来源,生产环境应限制为特定域名
- API Key 安全: 确保
.env文件不被提交到版本控制 - SECRET_KEY 安全: 使用强随机字符串作为 JWT 签名密钥,生产环境必须更换
- 密码安全: 密码使用 bcrypt 哈希存储,不会以明文形式保存
- 令牌安全:
- 访问令牌短期有效(2小时),降低泄露风险
- 刷新令牌存储在数据库中,支持撤销
- 令牌过期后必须使用刷新令牌重新获取
- 数据库备份: 定期备份 PostgreSQL 数据库
- 错误处理: 所有 API 都包含适当的错误处理和权限验证
- 日志记录: 建议添加日志记录功能以便调试和监控
许可证
MIT License