This repository has been archived on 2026-08-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Cyrene/docs/cyrene-project-architecture-analysis.md
T
AskaEth 79b1b9a3fa docs: 更新项目文档反映6-7月功能升级
- 新增 dev-plan/15: 6-7月功能升级与修复汇总 (Platform Bridge/记忆/Thinker/群聊等)
- 更新 dev-plan README: 版本日期 + 新文档索引
- 更新架构分析文档: 日期、已知限制、服务端口加入Platform Bridge
- .gitignore: 开放 docs/dev-plan/ 和关键架构文档纳入版本管理

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 21:24:03 +08:00

858 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cyrene AI 项目架构与功能分析文档
> **日期**2026-07-07(原稿 2026-05-23,已更新反映近期变动)
> **分支**`dev`
> **项目根目录**`d:\Project\Code\Uni\Cyrene`
> **近期变更摘要**:详见 [15-june-july-2026-fixes.md](dev-plan/15-june-july-2026-fixes.md)
---
## 目录
1. [项目概览](#一项目概览)
2. [总体架构](#二总体架构)
3. [后端服务详解](#三后端服务详解)
- [AI-Core (8081) — 对话引擎](#31-ai-core-8081--对话引擎)
- [Gateway (8080) — API 网关](#32-gateway-8080--api-网关)
- [Memory-Service (8091) — 记忆系统](#33-memory-service-8091--记忆系统)
- [工具系统 (pkg/plugins + AI-Core)](#34-工具系统-pkgplugins--ai-core-集成)
- [IoT-Debug-Service (8083) — 模拟设备](#35-iot-debug-service-8083--模拟设备)
- [Voice-Service (8093) — 语音服务](#36-voice-service-8093--语音服务)
- [Proto (占位)](#37-proto-占位)
4. [前端详解](#四前端详解)
- [技术栈](#41-技术栈)
- [状态管理](#42-状态管理)
- [组件树](#43-组件树)
- [自定义 Hooks](#44-自定义-hooks)
- [API 模块](#45-api-模块)
- [WebSocket 消息流](#46-websocket-消息流)
5. [ethend (9090) — 调试工具](#五ethend-9090--调试工具)
6. [对话管线详解](#六对话管线详解)
7. [数据库设计](#七数据库设计)
8. [安全性](#八安全性)
9. [已知限制与改进方向](#九已知限制与改进方向)
---
## 一、项目概览
Cyrene(昔涟)是一个 AI 数字伴侣系统,以 React SPA 为前端,Go 微服务集群为后端,通过 PostgreSQL 持久化数据和 pgvector 向量搜索实现长期记忆。系统以虚拟角色"昔涟"的固定人格与用户对话,支持 IoT 智能家居操控、定时提醒、知识库管理、自动化规则引擎、语音识别/合成、图片分析、互联网搜索 (SearXNG) 等功能。
| 维度 | 数据 |
|------|------|
| 后端语言 | Go 1.24 |
| 前端框架 | React 18.3 + TypeScript 5.6 + Vite 6.0 |
| 数据库 | PostgreSQL (pgvector 向量扩展) |
| LLM 模型 | 多模型配置系统 (models.json),支持多 Provider/多用途路由回退链 |
| WebSocket 库 | gorilla/websocket (Go), 原生 WebSocket (浏览器) |
| HTTP 框架 | Gin (Gateway), net/http (其他服务) |
| 状态管理 | Zustand 4.5 |
| CSS 框架 | Tailwind CSS 3.4 |
| 包管理器 | pnpm |
| Go 代理 | GOPROXY=https://goproxy.cn,direct |
---
## 二、总体架构
```
┌──────────────────────────────────────────────────────────────┐
│ Browser (React SPA) │
│ localhost:5173 (dev) / 生产由 Gateway 托管静态文件 │
└───────────┬──────────────────────────────────┬───────────────┘
│ WebSocket (ws://gateway:8080/ws/chat)
│ + REST API (gateway:8080/api/v1/*)
┌──────────────────────────────────────────────────────────────┐
│ Gateway (:8080) │
│ Gin Router → JWT Auth → Rate Limiter → Handlers │
│ WebSocket Hub: 会话状态/消息缓存/IoT广播 │
│ 规则引擎: 定时/事件触发自动化 │
└───┬──────────┬──────────┬──────────┬─────────────┘
│ SSE │ HTTP │ HTTP │ HTTP
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌──────────────┐
│AI-Core │ │Memory │ │Voice │ │IoT Debug │
│ :8081 │ │:8091 │ │:8093 │ │ :8083 │
│ │ │ │ │ │ │ │
│编排器 │ │PGVector│ │whisper │ │8个模拟设备 │
│意图分析 │ │语义搜索│ │edge-tts│ │toggle/set/ │
│子会话 │ │去重衰减│ │TTS回退│ │history API │
│人格系统 │ │ │ │ │ │传感器波动 │
│思考引擎 │ │ │ │ │ │ │
│工具调用 │ │ │ │ │ │ │
└────┬───┘ └────┬───┘ └────┬───┘ └──────────────┘
│ │ │ │
└──────────┴──────────┴──────────┘
┌───────▼───────┐
│ PostgreSQL │
│ + pgvector │
└───────────────┘
```
**核心数据流**:用户消息 → Gateway WebSocket → AI-Core SSE → 编排器并行处理(意图分析 → 子会话分派 → LLM 合成 → 审查拆分) → Gateway 解析 → WebSocket 逐条返回前端。
---
## 三、后端服务详解
### 3.1 AI-Core (:8081) — 对话引擎
**目录**`backend/ai-core/`
**入口**[cmd/main.go](backend/ai-core/cmd/main.go)
**包数量**11 个内部包,约 40 个 `.go` 文件
#### 3.1.1 启动流程
1. 加载 `.env` 环境变量
2. 初始化人格加载器(`persona.NewLoader`)——从 `internal/persona/` 目录读取 YAML 配置
3. 初始化 LLM 适配器:优先加载 `models.json``ModelSelector`;无配置文件时回退到 `.env`
4. 初始化记忆系统(`memory.NewStore` + `NewRetriever` + `NewExtractor`)——PostgreSQL 持久化
5. 初始化会话历史存储(`context.NewConversationStore`)——内存缓存,上限 50 条
6. 初始化 IoT 客户端(`tools.NewIoTClient`)——连接 IoT Debug Service
7. 注册 13 个 LLM 可调用工具(`tools.NewRegistry`
8. 启动后台思考引擎(`background.NewThinker`
9. 构建编排器 v2.0`orchestrator.NewOrchestrator`
10. 注册 HTTP 端点:`/api/v1/chat`SSE)、`/api/v1/memory/search``/api/v1/memory`CRUD)、`/api/v1/health`
#### 3.1.2 包结构
| 包 | 文件 | 职责 |
|----|------|------|
| `orchestrator/` | `orchestrator.go`, `intent_analyzer.go`, `synthesizer.go` | 对话编排 v2.0:意图分析→子会话分派→综合生成 |
| `subsession/` | `manager.go`, `registry.go`, `general_provider.go`, `memory_provider.go`, `iot_provider.go`, `review_provider.go` | 子会话框架:4 个提供者并行执行 |
| `llm/` | `adapter.go`, `openai.go`, `stream.go`, `selector.go` | LLM 抽象层:OpenAI 兼容协议、流式输出、断句器、多模型路由选择器 |
| `config/` | `loader.go` | 模型配置加载器:只读加载 models.json |
| `memory/` | `store.go`, `retriever.go`, `extractor.go`, `client.go` | 记忆系统:存储/检索/提取/HTTP 客户端 |
| `persona/` | `loader.go`, `injector.go` | 人格管理:YAML 加载、系统提示构建、风格注入 |
| `tools/` | `registry.go`, `calculator_tool.go`, `datetime_tool.go`, `crypto_tool.go`, `file_tool.go`, `http_tool.go`, `iot_client.go`, `iot_control_tool.go`, `iot_tools.go`, `json_tool.go`, `markdown_tool.go`, `random_tool.go`, `text_tool.go`, `web_fetch.go`, `web_search.go` | 工具系统:13 个 LLM 可调用工具 + IoT 客户端 |
| `context/` | `builder.go` | 上下文构建器:会话历史/人格/子会话结果组合为 LLM 提示 |
| `background/` | `thinker.go` | 后台思考引擎:事件驱动(chat 后/沉默)自主思考 |
| `model/` | `message.go`, `session.go`, `sub_session.go`, `memory.go` | 共享数据模型:消息、会话、子会话、记忆 |
#### 3.1.3 Orchestrator v2.0 管线
```
用户输入 → ProcessInput()
├─ 1. 意图分析 (intentAnalyzer.Analyze)
│ ├─ isStrongIoTCommand() 快速通道 (0s)
│ ├─ isGreeting() 快速通道 (0s)
│ └─ LLM 意图分析 (fallback)
├─ 2. 加载人格配置 → BuildSystemPrompt
├─ 3. 子会话分派 (subManager.Dispatch)
│ ├─ general_provider (通用对话意图)
│ ├─ memory_provider (记忆检索)
│ ├─ iot_provider (IoT 设备查询/操控)
│ └─ review_provider (审查拆分)
│ 快速通道: greeting/纯聊天 → 跳过所有子会话
├─ 4. 200ms 超时等待子会话结果
├─ 5. Synthesizer 流式生成 (ChatWithTools + 最多5轮工具调用循环)
├─ 6. 流式输出 delta → SSE
├─ 7. parseReviewMessages() 审查拆分 → StreamReview SSE
├─ 8. 断句信息 → StreamSegments SSE
├─ 9. StreamDone → [DONE]
└─ 10. 后处理: 缓存回复 + 异步记忆提取
```
**快速通道条件**
- IoT 命令:`controlWords ∩ msg AND deviceWords ∩ msg` → 跳过 LLM 意图分析(节省 2-3s)
- 纯问候/聊天无 IoT 无 Memory:跳过所有子会话分派
**`parseReviewMessages()`** — 括号匹配状态机:
- 输入:`"(歪着头看你) 叶酱,客厅灯早就开着啦♪..."`
- 输出:`[{type: "action", content: "歪着头看你"}, {type: "chat", content: "叶酱,客厅灯早就开着啦♪..."}]`
- 支持 `()``()` 两种括号
- `splitReviewLongMessage()`:80 字符智能断句(句号/感叹号/问号/逗号边界)
#### 3.1.4 Intent Analyzer
**文件**[intent_analyzer.go](backend/ai-core/internal/orchestrator/intent_analyzer.go)
**快速通道关键词**
```go
controlWords = ["打开", "关闭", "关掉", "关上", "调到", "设置", "开关", "调节", "调高", "调低", "开一下", "关一下"]
deviceWords = ["灯", "空调", "窗帘", "电视", "风扇", "加湿器", "插座", "门锁", "传感器"]
```
**意图类型**`iot_control`, `iot_query`, `greeting`, `chat`, `story`, `memory_trigger`, `knowledge`, `task`
#### 3.1.5 IoT Provider (子会话)
**文件**[iot_provider.go](backend/ai-core/internal/subsession/iot_provider.go)
- `Execute()`:收集所有 device-action 对 → 批量执行
- 上下文窗口:±30 字节 + 全文回退逻辑
- 操作检测:`hasOpen`/`hasClose` 布尔值判断
- 通过 `personaDir` 字段加载 IoT 回复人格配置
#### 3.1.6 后台思考引擎
- 事件驱动:`TriggerPostChatThink()` 在每次对话后触发
- 沉默触发:用户长时间不活动
- 思考内容可持久化到 memory-service
---
### 3.2 Gateway (:8080) — API 网关
**目录**`backend/gateway/`
**入口**[cmd/main.go](backend/gateway/cmd/main.go)
**包数量**8 个内部包,约 30 个 `.go` 文件
#### 3.2.1 启动流程
1. 加载配置 (`config.Load`)
2. 确保上传目录 `./uploads/` 存在
3. 初始化 7 个持久化 Store(降级:连接失败不影响启动)
4. 种子数据:自动创建 admin 用户 + 清理旧 admin 用户
5. 初始化 WebSocket Hub + 闲置会话清理 + IoT 广播
6. 初始化规则引擎
7. 配置 Gin 路由 → 启动 HTTP 服务
8. 启动提醒/简报调度器
#### 3.2.2 路由表
**公开路由**
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/health` | 健康检查 |
| POST | `/api/v1/auth/register` | 注册(限流) |
| POST | `/api/v1/auth/login` | 登录(限流) |
**需认证路由** (JWT + Rate Limit)
| 方法 | 路径 | Handler |
|------|------|---------|
| POST | `/api/v1/auth/refresh` | authHandler |
| POST | `/api/v1/sessions` | sessionHandler.Create |
| GET | `/api/v1/sessions` | sessionHandler.List |
| DELETE | `/api/v1/sessions` | sessionHandler.DeleteAll |
| GET | `/api/v1/sessions/:id` | sessionHandler.Get |
| DELETE | `/api/v1/sessions/:id` | sessionHandler.Delete |
| GET | `/api/v1/sessions/:id/messages` | sessionHandler.GetMessages |
| DELETE | `/api/v1/sessions/:id/messages` | sessionHandler.ClearMessages |
| GET | `/api/v1/sessions/:id/export` | sessionHandler.ExportSession |
| GET | `/api/v1/messages/search` | sessionHandler.SearchMessages |
| GET | `/api/v1/memory/search` | memoryHandler.Query |
| GET | `/api/v1/memory` | memoryHandler.List |
| POST | `/api/v1/memory` | memoryHandler.Add |
| DELETE | `/api/v1/memory` | memoryHandler.Delete |
| POST | `/api/v1/notifications/push` | notificationHandler.Push |
| GET/POST/PUT/DELETE | `/api/v1/reminders*` | reminderHandler |
| GET/POST | `/api/v1/briefings*` | briefingHandler |
| POST/GET | `/api/v1/voice/*` | voiceHandler |
| POST/GET/DELETE | `/api/v1/files/*` | fileHandler |
| GET/POST/PUT/DELETE | `/api/v1/automation/rules*` | automationHandler |
| GET/POST/PUT/DELETE | `/api/v1/automation/scenes*` | automationHandler |
| POST/GET/PUT/DELETE | `/api/v1/knowledge/*` | knowledgeHandler |
| POST/GET | `/api/v1/images/*` | imageHandler |
**WebSocket**
| 路径 | 说明 |
|------|------|
| `GET /ws/chat?token=xxx&session_id=xxx` | WebSocket 升级(仅限 admin 用户) |
**内部服务** (Internal Service Token)
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/internal/notify` | 内部通知推送 |
**Webhook** (Webhook Auth)
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/webhook/generic` | 通用 Webhook |
| POST | `/api/v1/webhook/discord` | Discord Webhook |
#### 3.2.3 WebSocket Hub
**文件**[hub.go](backend/gateway/internal/ws/hub.go)
核心数据结构:
- `clients map[*Client]bool` — 所有活跃连接
- `userClients map[string]map[*Client]bool` — 按用户索引
- `sessions map[string]*SessionState` — 会话状态追踪
- `conversationCache sync.Map` — 对话缓存(最多 50 条/session
功能:
- 客户端注册/注销(优雅清理,两阶段广播)
- 按用户/会话精准推送
- 闲置会话自动标记(超过 idleTimeout 无活动 → state="idle"
- IoT 设备状态广播:每 10 秒轮询 IoT Debug Service 并推送给所有客户端
- 对话缓存:`CacheMessage()` / `GetConversation()` / `GetSessionHistory()`
#### 3.2.4 消息持久化
**WebSocket handler** ([chat_handler.go](backend/gateway/internal/handler/chat_handler.go)) 的 `streamResponse()`
1. 用户消息**立即**持久化到 DB(在 WebSocket 发送前)
2. AI 回复**流式**接收 SSE,逐 delta 转发 WebSocket
3. `review_messages` 解析后每条独立持久化(role="action"/"assistant"
4. 最终完整文本也持久化一次
#### 3.2.5 Store 层
| Store | 文件 | 功能 |
|-------|------|------|
| `SessionStore` | `session_store.go` | 会话 CRUD、消息持久化、搜索 |
| `UserStore` | `user_store.go` | 用户注册/查询/列表/删除 |
| `ReminderStore` | `reminder_store.go` | 提醒 CRUD |
| `BriefingStore` | `briefing_store.go` | 每日简报 CRUD |
| `AutomationStore` | `automation_store.go` | 自动化规则/场景 CRUD |
| `FileStore` | `file_store.go` | 文件上传/管理 |
| `KnowledgeStore` | `knowledge_store.go` | 知识库/文档管理 |
#### 3.2.6 规则引擎
**文件**[rule_engine.go](backend/gateway/internal/engine/rule_engine.go)
- 事件驱动自动化:定时触发 / Webhook 触发
- 场景执行:一组规则的批量触发
- 通过 WebSocket 向用户推送通知
---
### 3.3 Memory-Service (:8091) — 记忆系统
**目录**`backend/memory-service/`
**入口**[cmd/main.go](backend/memory-service/cmd/main.go)
**文件数**6 个 Go 文件
#### 3.3.1 包结构
| 包 | 文件 | 职责 |
|----|------|------|
| `cmd/` | `main.go` | HTTP 服务入口 |
| `internal/config/` | `config.go` | 配置加载 |
| `internal/model/` | `memory.go` | 记忆数据模型 |
| `internal/store/` | `store.go` | PostgreSQL + pgvector 存储层 |
| `internal/service/` | `memory_service.go` | 业务逻辑层 |
| `internal/handler/` | `memory_handler.go` | HTTP 处理器 |
#### 3.3.2 记忆数据模型
```
MemoryEntry {
ID, UserID, Content, Category, Priority,
Embedding (pgvector 1536维),
CreatedAt, UpdatedAt, DecayFactor, AccessCount, LastAccessedAt
}
```
#### 3.3.3 核心功能
- **语义搜索**pgvector 余弦相似度检索 top-N 相关记忆
- **去重**:Jaccard 相似度检测,合并高相似度记忆
- **衰减机制**`DecayFactor` 随时间衰减,低权重记忆被清理
- **记忆巩固**:高访问频率记忆自动提升优先级
- **CRUD API**:创建/查询/列表/删除
#### 3.3.4 HTTP API
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/v1/memories` | POST | 创建记忆 |
| `/api/v1/memories?user_id=xxx` | GET | 列表 |
| `/api/v1/memories/:id` | GET | 获取 |
| `/api/v1/memories/:id` | DELETE | 删除 |
| `/api/v1/memories/search?user_id=xxx&q=xxx&limit=10` | GET | 语义搜索 |
| `/api/v1/thinking/logs` | POST | 记录思考日志 |
| `/api/v1/health` | GET | 健康检查 |
---
### 3.4 工具系统 (pkg/plugins + AI-Core 集成)
> **迁移说明:** tool-engine (8092) 已移除。工具注册与调用整合到 `pkg/plugins` 共享模块,由 AI-Core 直接管理。
**目录**`backend/pkg/plugins/`
**核心**[pkg/plugins/manager/registry.go](backend/pkg/plugins/manager/registry.go)
**插件数**11 个共享插件 + 5 个 AI-Core 专属工具
#### 3.4.1 16 个 LLM 可调用工具
**共享插件** (`pkg/plugins/`)
| 工具 | 目录 | 功能 |
|------|------|------|
| calculator | `calculator/` | 数学表达式求值 |
| datetime | `datetime/` | 日期时间计算/格式化 |
| text | `text/` | 文本处理(统计/转换/截断) |
| crypto | `crypto/` | 哈希/加解密 |
| random | `random/` | 随机数/字符串生成 |
| markdown | `markdown/` | Markdown 渲染 |
| json | `json/` | JSON 解析/查询/格式化 |
| file | `file/` | 文件读写/列表 |
| http | `http/` | HTTP 请求(GET/POST 等) |
| web_search | `web_search/` | SearXNG 网络搜索 (DuckDuckGo 兜底) |
| web_fetch | `web_fetch/` | 网页内容提取 |
**AI-Core 专属工具** (`ai-core/internal/tools/`)
| 工具 | 功能 |
|------|------|
| iot_query | IoT 设备状态查询 |
| iot_control | IoT 设备操控 |
| host_exec | 主机命令执行 (沙箱) |
| host_file | 主机文件操作 (沙箱) |
| host_system | 主机系统信息 |
| vision_analyze | 图片视觉分析/OCR |
| knowledge_search | 知识库 RAG 检索 |
| knowledge_ingest | 知识库文档导入 |
#### 3.4.2 ToolRegistry 调用审计
[registry.go](backend/pkg/plugins/manager/registry.go) — 内存环形缓冲区 (500 条) 记录工具调用的参数/结果/耗时/成功率。API 端点:
- `GET /api/v1/tools/calls` — 分页查询,支持按工具名过滤
- `GET /api/v1/tools/calls/stats` — 按工具聚合的成功率/平均耗时
#### 3.4.3 主聊天流程工具调用
`Synthesizer` 通过 `ChatWithTools` 向 LLM 传递工具定义,支持最多 5 轮工具调用循环。后台思考器使用相同机制。
---
### 3.5 IoT-Debug-Service (:8083) — 模拟设备
**目录**`backend/iot-debug-service/`
**单文件服务**[cmd/main.go](backend/iot-debug-service/cmd/main.go)626 行)
#### 3.5.1 模拟设备列表
| ID | 名称 | 类型 | 可操作性 |
|----|------|------|---------|
| `light-livingroom` | 客厅灯 | light | toggle, brightness, color |
| `light-bedroom` | 卧室灯 | light | toggle, brightness, color |
| `ac-livingroom` | 客厅空调 | ac | toggle, temperature, mode |
| `ac-bedroom` | 卧室空调 | ac | toggle, temperature, mode |
| `curtain-livingroom` | 客厅窗帘 | curtain | toggle, position |
| `sensor-temperature` | 温度传感器 | sensor | 只读 |
| `sensor-humidity` | 湿度传感器 | sensor | 只读 |
| `lock-door` | 智能门锁 | lock | toggle, battery |
#### 3.5.2 API
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/v1/devices` | GET | 列出所有设备(不含历史) |
| `/api/v1/devices/{id}` | GET | 获取单个设备(含最近 10 条历史) |
| `/api/v1/devices/{id}/toggle` | POST | 切换开关状态 |
| `/api/v1/devices/{id}/set` | POST | 设置属性 `{field, value}` |
| `/api/v1/devices/{id}/history` | GET | 获取操作历史 |
#### 3.5.3 特性
- **线程安全**`sync.RWMutex` 保护所有设备操作
- **传感器波动**:每 30 秒自动模拟温度 ±0.2°C / 湿度 ±1% 随机波动
- **属性设置**:声明式控制,支持中文值("开"/"关")和布尔值
- **操作历史**:记录每次变更的字段、旧值、新值、时间戳
---
### 3.6 Voice-Service (:8093) — 语音服务
**目录**`backend/voice-service/`
**入口**[cmd/main.go](backend/voice-service/cmd/main.go)
**文件数**6 个 Go 文件
#### 3.6.1 STT (语音识别)
- **引擎**whisper.cpp(本地运行)
- **前处理**ffmpeg 音频格式转换
- **支持语言**:中文(`zh`
#### 3.6.2 TTS (语音合成)
三级回退策略:
1. **edge-tts**(首选)— Microsoft Edge TTS API,音质最好
2. **espeak-ng**(回退)— 离线 TTS,无需网络
3. **静默 WAV 生成器**(最终回退)— 生成等长静音音频,保证用户体验不中断
---
### 3.7 Proto (占位)
`backend/proto/` — 目前仅包含 `.gitkeep`,为未来的 Protobuf/gRPC 通信预留。
---
## 四、前端详解
**目录**`frontend/web/`
**入口**[main.tsx](frontend/web/src/main.tsx) → [App.tsx](frontend/web/src/App.tsx)
### 4.1 技术栈
| 技术 | 版本 | 用途 |
|------|------|------|
| React | 18.3 | UI 框架 |
| TypeScript | 5.6 | 类型系统 |
| Vite | 6.0 | 构建工具 |
| Zustand | 4.5 | 状态管理 |
| Tailwind CSS | 3.4 | 样式 |
| 原生 WebSocket | — | 实时通信 |
| SpeechSynthesis API | — | TTS 朗读 |
| SpeechRecognition API | — | STT 语音输入 |
### 4.2 状态管理 (Zustand)
| Store | 文件 | 职责 |
|-------|------|------|
| `authStore` | `authStore.ts` | 用户认证状态、token 管理 |
| `chatStore` | `chatStore.ts` | 消息列表、流式状态、IoT 设备状态、后台思考状态、历史分页 |
| `sessionStore` | `sessionStore.ts` | 会话列表、当前会话、消息加载、导出 |
| `notificationStore` | `notificationStore.ts` | 站内通知列表、已读管理 |
| `personaStore` | `personaStore.ts` | 昔涟人格配置、心情状态 |
### 4.3 组件树
```
App
├── ErrorBoundary
└── AppLayout
├── Header (Logo, 连接状态, 搜索按钮)
├── Sidebar (会话列表, 新建对话)
├── ChatContainer
│ ├── MessageList
│ │ ├── MessageBubble (用户/助手消息)
│ │ │ ├── CyreneAvatar (昔涟头像)
│ │ │ ├── ImageLightbox (图片灯箱)
│ │ │ ├── AIMessageActions (朗读按钮)
│ │ │ └── ActionMessageBubble (动作消息: 居中/斜体/灰色)
│ │ └── TypingIndicator (输入中动画)
│ ├── IoTStatusBar (设备状态条)
│ └── ChatInput (输入框/附件/发送)
├── AutomationPanel (自动化规则管理)
├── BriefingPanel (每日简报)
├── FilePanel (文件管理)
├── KnowledgePanel (知识库管理)
├── ReminderPanel (提醒管理)
└── SearchModal (全局搜索)
```
### 4.4 自定义 Hooks
| Hook | 文件 | 功能 |
|------|------|------|
| `useWebSocket` | `useWebSocket.ts` | WebSocket 连接生命周期、指数退避自动重连、消息路由 |
| `useChat` | `useChat.ts` | 消息发送逻辑 |
| `useAuth` | `useAuth.ts` | 认证状态管理 |
| `useSession` | `useSession.ts` | 会话切换、历史加载 |
| `useSpeechRecognition` | `useSpeechRecognition.ts` | 浏览器语音识别封装 |
| `useSpeechSynthesis` | `useSpeechSynthesis.ts` | 浏览器 TTS 封装 |
| `usePWA` | `usePWA.ts` | PWA 安装/更新 |
#### 4.4.1 WebSocket 重连策略
- **指数退避**:初始 1s,每次翻倍,最大 30s
- **jitter**:在 `[delay/2, delay]` 范围内随机,避免惊群
- **最大重试**:10 次后放弃并提示用户刷新
- **会话恢复**:重连后自动发送 `history` 消息恢复后端上下文
### 4.5 API 模块
| 模块 | 文件 | 对应后端 |
|------|------|---------|
| `client` | `client.ts` | Axios 实例、JWT 拦截器 |
| `auth` | `auth.ts` | `/api/v1/auth/*` |
| `sessions` | `sessions.ts` | `/api/v1/sessions/*` |
| `memory` | `memory.ts` | `/api/v1/memory/*` |
| `reminders` | `reminders.ts` | `/api/v1/reminders/*` |
| `briefings` | `briefings.ts` | `/api/v1/briefings/*` |
| `voice` | `voice.ts` | `/api/v1/voice/*` |
| `files` | `files.ts` | `/api/v1/files/*` |
| `automation` | `automation.ts` | `/api/v1/automation/*` |
| `knowledge` | `knowledge.ts` | `/api/v1/knowledge/*` |
### 4.6 WebSocket 消息流
**客户端 → 服务端**
```typescript
{ type: "message", content: "你好", mode: "text", session_id: "xxx", timestamp: 123 }
{ type: "history", session_id: "xxx" }
{ type: "voice_input", audio_data: "base64..." }
```
**服务端 → 客户端**
| type | 触发时机 | 前端处理 |
|------|---------|---------|
| `stream_chunk` | AI 流式逐字输出 | `appendToLastMessage` 累积 |
| `stream_end` | 流式输出结束 | `finishStreaming` 标记完成 |
| `response` | 审查后的独立消息 (action/chat) | `addMessage` 添加独立气泡 |
| `history_response` | 会话恢复/历史请求 | 仅在无消息时加载 |
| `review` | 旧版审查消息 | 逐条 `addMessage` |
| `multi_message` | 多段消息 | 逐段 `addMessage` |
| `stream_segments` | 断句信息 | 逐段 `addMessage` |
| `device_update` | IoT 设备状态广播 (10s) | `setIoTDevices` 更新状态栏 |
| `background_thinking` | 后台思考状态变化 | `setBackgroundThinkingStatus` |
| `notification` | 系统通知/提醒 | `addNotification` + 浏览器桌面通知 |
| `error` | 服务端错误 | 系统消息气泡 |
---
## 五、ethend (:9090) — 调试工具
**目录**`ethend/`
**类型**Node.js Express 应用
### 5.1 服务管理
| 端点 | 方法 | 功能 |
|------|------|------|
| `/api/services` | GET | 列出所有服务状态 |
| `/api/services/:id/build` | POST | 编译服务 (go build -o main.exe) |
| `/api/services/:id/start` | POST | 启动服务 |
| `/api/services/:id/stop` | POST | 停止服务 |
| `/api/services/:id/restart` | POST | 重启服务 |
| `/api/services/:id/logs` | GET | 获取服务日志 |
| `/api/services/:id/memory` | GET | 获取进程内存 |
### 5.2 其他功能
- **IoT 管理**:设备列表/状态切换
- **记忆管理**:记忆搜索/CRUD
- **性能监控**:CPU/内存使用率
- **数据库管理**:表结构检查、迁移执行
- **WebSocket 状态**:连接数/会话列表
- **健康检查轮询**:每秒检测所有服务可达性
### 5.3 关键配置
**构建命令**[config.js](ethend/src/config.js)):
```javascript
buildCommand: 'go',
buildArgs: ['build', '-o', isWin ? 'main.exe' : 'main', './cmd/main.go'],
goBin: GO_BIN
```
所有 Go 服务统一编译为 `main.exe`Windows),ethend 通过 `./main` 启动。**自定义二进制名称不会被 ethend 识别。**
---
## 六、对话管线详解
### 6.1 完整请求时序
```
Browser (React) Gateway AI-Core
│ │ │
├─ WebSocket: {type:"message"} ──┤ │
│ ├─ POST /api/v1/chat ──────►│
│ │ (SSE streaming) │
│ │ ├─ 意图分析 (0-1.4s)
│ │ ├─ 并行子会话
│ │ │ ├─ memory (检索)
│ │ │ ├─ iot (查询/操控)
│ │ │ └─ general (意图)
│ │ ├─ LLM 合成 (3-4s)
│ │ ├─ parseReviewMessages
│ │ │
│ │◄── SSE: delta ────────────┤
│◄── WS: stream_chunk ──────────┤ │
│◄── WS: stream_chunk ... ──────┤ │
│ │◄── SSE: review_messages ──┤
│◄── WS: response (action) ─────┤ (200ms delay) │
│◄── WS: response (chat) ───────┤ │
│ │◄── SSE: [DONE] ──────────┤
│◄── WS: stream_end ────────────┤ │
```
### 6.2 性能数据
| 场景 | 总响应 | 意图分析 | 子会话 | LLM 合成 |
|------|--------|---------|--------|---------|
| "你好呀" | ~3.9s | 0s | 跳过 | 3.9s |
| "打开客厅灯" | ~2.6s | 0s | IoT+Memory | ~2.6s |
| "关掉客厅灯" | ~2.6s | 0s | IoT+Memory | ~2.6s |
| "打开卧室灯和卧室空调" | ~3.0s | 0s | IoT+Memory | ~3.0s |
| "看看设备状态" | ~5.3s | 1.4s | IoT+Memory | ~3.9s |
| "你还记得我喜欢什么吗?" | ~4-5s | LLM | Memory+General | ~3-4s |
LLM 合成(deepseek-v4-flash)是主要延迟来源,约占 60-80% 的总响应时间。
### 6.3 E2E 消息流示例
```
用户: "帮我把客厅灯打开"
→ Gateway WS → AI-Core SSE
→ Intent: iot_control (快速通道, 0s)
→ Dispatch: memory + general + iot + review (并行)
→ IoT: 查询 8 个设备 → 匹配客厅灯 → 已开状态
→ Synthesize: 综合上下文 → LLM 生成回复
→ parseReviewMessages:
action: "歪着头看你"
chat: "叶酱,客厅灯早就开着啦♪ 你是不是工作太累看花了眼呀?"
→ Gateway: 200ms 间隔逐条推送 WebSocket response
→ Frontend: ActionMessageBubble + MessageBubble 分别渲染
```
---
## 七、数据库设计
### 7.1 PostgreSQL 连接
所有服务共用 PostgreSQL 数据库 `cyrene_ai`,通过 `DB_URL` 环境变量配置:
```
postgres://cyrene:cyrene_pass@localhost:5432/cyrene_ai?sslmode=disable
```
### 7.2 主要表结构
| 表 | 管理方 | 用途 |
|----|--------|------|
| `users` | Gateway | 用户认证 |
| `sessions` | Gateway | 会话记录 |
| `messages` | Gateway | 对话消息(role, content, timestamp |
| `memories` | Memory-Service | 记忆存储(含 pgvector embedding |
| `reminders` | Gateway | 定时提醒 |
| `briefings` | Gateway | 每日简报 |
| `automation_rules` | Gateway | 自动化规则 |
| `automation_scenes` | Gateway | 自动化场景 |
| `files` | Gateway | 文件元数据 |
| `knowledge_bases` | Gateway | 知识库 |
| `knowledge_documents` | Gateway | 知识文档 |
| — (工具调用日志) | AI-Core (内存) | 环形缓冲区 500 条,无 DB 持久化 |
### 7.3 已知缺项
- `messages``msg_type` 通过后端 ServerMessage 自动填充(含 markdown/code),但数据库层面未持久化该字段,历史消息查询依赖 role 判断
---
## 八、安全性
| 层面 | 实现 |
|------|------|
| 认证 | JWT (HS256),默认永不过期 |
| 密码存储 | bcrypt 哈希 |
| 会话隔离 | 按 userID + sessionID 双重隔离 |
| 限流 | 认证端点:5次/分钟/IPAPI 端点:10 req/s + 突发 20 |
| CORS | 可配置的来源白名单 |
| HTTP 框架 | Gin 的生产模式(ReleaseMode+ Recovery 中间件 |
| WebSocket 安全 | JWT token 验证 + admin-only 主对话 |
| 内部服务 | Internal Service Token 认证 |
---
## 九、已知限制与改进方向
### 当前限制
1. **LLM 合成延迟**(3-4s):LLM 调用是主要瓶颈,合成阶段无法被快速通道绕过
2. **"开" 字歧义**:无法将单独的 "开" 加入快速通道("开心"/"开始" 产生误判),"开灯" 等短命令仍走 LLM
3. **嵌入 API 依赖**DeepSeek 等不提供 `/embeddings` 端点的 API 需额外配置 `EMBEDDING_API_URL` 或使用本地 n-gram 降级
4. **跨群记忆混合**:后台思考 (Thinker) 仍将各频道记忆混合生成统一摘要,尚未完全隔离
5. **流式审查**`parseReviewMessages()` 需要等 LLM 合成完成后才能执行,无法实时拆分
### 建议改进方向
1. **LLM 响应缓存**:对相似问候/常见 IoT 命令引入语义缓存
2. **Ollama 本地嵌入**:配置 `EMBEDDING_API_URL=http://localhost:11434/v1` + `EMBEDDING_MODEL=bge-m3` 启用真实向量检索
3. **跨群聊记忆隔离**:Thinker 按频道独立维护记忆上下文
4. **流式审查**:在 LLM 合成过程中实时识别并分段发送 action/chat
5. **本地工作助手插件**:承载代码/长文生成等聊天平台不适合的能力
6. **前端集成测试**Playwright/CDP 端到端测试
---
## 附:快速参考
### 服务端口一览
| 服务 | 端口 | 技术 |
|------|------|------|
| Gateway | 8080 | Gin + WebSocket |
| AI-Core | 8081 | net/http + SSE |
| IoT Debug | 8083 | net/http |
| Platform Bridge | 8095 | net/http + WebSocket (OBv11/Telegram适配器) |
| Memory | 8091 | net/http + pgvector |
| Voice | 8093 | net/http + whisper.cpp |
| Frontend (dev) | 5173 | Vite |
| ethend | 9090 | Express |
### 常用命令
```bash
# 构建所有 Go 服务
cd backend/ai-core && GOWORK=off go build -o main.exe ./cmd/main.go
cd backend/gateway && GOWORK=off go build -o main.exe ./cmd/main.go
# ... 其他服务同理
# 或通过 ethend API
curl -X POST http://localhost:9090/api/services/ai-core/build
curl -X POST http://localhost:9090/api/services/gateway/build
# 启动前端开发服务器
cd frontend/web && pnpm dev
# E2E 测试
node test/test_final_e2e.mjs
```
### 环境变量 (.env)
位于 `.env`,基础设施与 LLM 回退配置:
```
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=cyrene
POSTGRES_PASSWORD=cyrene_pass
POSTGRES_DB=cyrene_ai
JWT_SECRET=xxx
ADMIN_USERNAME=admin
ADMIN_PASSWORD=xxx
# Phase 6: 以下 LLM 变量作为 models.json 不存在时的回退
LLM_API_URL=https://api.deepseek.com/v1
LLM_API_KEY=sk-xxx
LLM_MODEL=deepseek-v4-flash
LLM_FALLBACK_MODEL=deepseek-v4-flash
```
### 模型配置 (models.json)
Phase 6 新增,位于 `backend/models.json``.gitignore` 已排除)。格式:
```json
{
"version": "1.0",
"providers": {
"deepseek": {
"name": "deepseek",
"base_url": "https://api.deepseek.com",
"api_key": "sk-xxx",
"timeout_sec": 120,
"max_retries": 3
}
},
"models": {
"primary_chat": {
"id": "primary_chat",
"name": "deepseek-v4-flash",
"provider": "deepseek",
"description": "主对话模型",
"tags": ["chat", "fast"],
"params": { "temperature": 0.8, "max_tokens": 2048 },
"enabled": true
}
},
"routing": {
"chat": {
"purpose": "chat",
"fallback_chain": ["primary_chat", "fallback_chat"],
"required": false
}
}
}
```
**配置管理 API**Gateway adminethend 代理提供 UI):
| Method | Path | Description |
|--------|------|-------------|
| GET/POST/DELETE | `/api/v1/admin/models/providers/:name` | Provider CRUD |
| GET/POST/DELETE | `/api/v1/admin/models/models/:id` | Model CRUD |
| GET/POST/DELETE | `/api/v1/admin/models/routing/:purpose` | Routing CRUD |
**向后兼容**:如果 `models.json` 不存在,`ModelSelector` 自动回退到 `.env` 的 4 个 LLM 变量,行为与 Phase 5 及之前完全一致。