- Go 服务 godotenv.Load("../.env") → godotenv.Load("../../.env")
- ethend.sh/config.js 读取路径改为根目录 .env
- 删除 .docker.env.example 和 backend/.env.example,统一为 .env.example
- Docker compose 默认读取根 .env,无需 --env-file
- 同步更新全部文档
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
35 KiB
Cyrene AI 项目架构与功能分析文档
日期:2026-05-23
分支:dev
项目根目录:d:\Project\Code\Uni\Cyrene
目录
一、项目概览
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
包数量:11 个内部包,约 40 个 .go 文件
3.1.1 启动流程
- 加载
.env环境变量 - 初始化人格加载器(
persona.NewLoader)——从internal/persona/目录读取 YAML 配置 - 初始化 LLM 适配器:优先加载
models.json→ModelSelector;无配置文件时回退到.env - 初始化记忆系统(
memory.NewStore+NewRetriever+NewExtractor)——PostgreSQL 持久化 - 初始化会话历史存储(
context.NewConversationStore)——内存缓存,上限 50 条 - 初始化 IoT 客户端(
tools.NewIoTClient)——连接 IoT Debug Service - 注册 13 个 LLM 可调用工具(
tools.NewRegistry) - 启动后台思考引擎(
background.NewThinker) - 构建编排器 v2.0(
orchestrator.NewOrchestrator) - 注册 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
快速通道关键词:
controlWords = ["打开", "关闭", "关掉", "关上", "调到", "设置", "开关", "调节", "调高", "调低", "开一下", "关一下"]
deviceWords = ["灯", "空调", "窗帘", "电视", "风扇", "加湿器", "插座", "门锁", "传感器"]
意图类型:iot_control, iot_query, greeting, chat, story, memory_trigger, knowledge, task
3.1.5 IoT Provider (子会话)
Execute():收集所有 device-action 对 → 批量执行- 上下文窗口:±30 字节 + 全文回退逻辑
- 操作检测:
hasOpen/hasClose布尔值判断 - 通过
personaDir字段加载 IoT 回复人格配置
3.1.6 后台思考引擎
- 事件驱动:
TriggerPostChatThink()在每次对话后触发 - 沉默触发:用户长时间不活动
- 思考内容可持久化到 memory-service
3.2 Gateway (:8080) — API 网关
目录:backend/gateway/
入口:cmd/main.go
包数量:8 个内部包,约 30 个 .go 文件
3.2.1 启动流程
- 加载配置 (
config.Load) - 确保上传目录
./uploads/存在 - 初始化 7 个持久化 Store(降级:连接失败不影响启动)
- 种子数据:自动创建 admin 用户 + 清理旧 admin 用户
- 初始化 WebSocket Hub + 闲置会话清理 + IoT 广播
- 初始化规则引擎
- 配置 Gin 路由 → 启动 HTTP 服务
- 启动提醒/简报调度器
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
核心数据结构:
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) 的 streamResponse():
- 用户消息立即持久化到 DB(在 WebSocket 发送前)
- AI 回复流式接收 SSE,逐 delta 转发 WebSocket
review_messages解析后每条独立持久化(role="action"/"assistant")- 最终完整文本也持久化一次
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 规则引擎
- 事件驱动自动化:定时触发 / Webhook 触发
- 场景执行:一组规则的批量触发
- 通过 WebSocket 向用户推送通知
3.3 Memory-Service (:8091) — 记忆系统
目录: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
插件数: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 — 内存环形缓冲区 (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(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
文件数:6 个 Go 文件
3.6.1 STT (语音识别)
- 引擎:whisper.cpp(本地运行)
- 前处理:ffmpeg 音频格式转换
- 支持语言:中文(
zh)
3.6.2 TTS (语音合成)
三级回退策略:
- edge-tts(首选)— Microsoft Edge TTS API,音质最好
- espeak-ng(回退)— 离线 TTS,无需网络
- 静默 WAV 生成器(最终回退)— 生成等长静音音频,保证用户体验不中断
3.7 Proto (占位)
backend/proto/ — 目前仅包含 .gitkeep,为未来的 Protobuf/gRPC 通信预留。
四、前端详解
目录:frontend/web/
入口:main.tsx → 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 消息流
客户端 → 服务端:
{ 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):
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次/分钟/IP;API 端点:10 req/s + 突发 20 |
| CORS | 可配置的来源白名单 |
| HTTP 框架 | Gin 的生产模式(ReleaseMode)+ Recovery 中间件 |
| WebSocket 安全 | JWT token 验证 + admin-only 主对话 |
| 内部服务 | Internal Service Token 认证 |
九、已知限制与改进方向
当前限制
- LLM 合成延迟(3-4s):deepseek-v4-flash 调用是主要瓶颈,合成阶段无法被快速通道绕过
- "开" 字歧义:无法将单独的 "开" 加入快速通道("开心"/"开始" 产生误判),"开灯" 等短命令仍走 LLM
- msg_type 数据库持久化:后端 ServerMessage 已自动填充 msg_type(含 markdown/code),但数据库 messages 表未存储该字段
- Node.js v24 Windows 原生 WebSocket bug:频繁建立连接可能触发 libuv
UV_HANDLE_CLOSING断言 - 流式审查:
parseReviewMessages()需要等 LLM 合成完成后才能执行,无法实时拆分 frontend/packages/shared/和backend/proto/为空占位
建议改进方向
- LLM 响应缓存:对相似问候/常见 IoT 命令引入语义缓存
- 数据库迁移:为 messages 表添加
msg_type列以持久化消息类型(当前仅 ServerMessage 传输层携带) - 流式审查:在 LLM 合成过程中实时识别并分段发送 action/chat
- Protobuf 通信:填充
backend/proto/目录,服务间改用 gRPC - WebSocket 兼容:生产环境使用
wsnpm 包替代原生 WebSocket - 前端集成测试:Playwright/CDP 端到端测试
附:快速参考
服务端口一览
| 服务 | 端口 | 技术 |
|---|---|---|
| Gateway | 8080 | Gin + WebSocket |
| AI-Core | 8081 | net/http + SSE |
| IoT Debug | 8083 | net/http |
| Memory | 8091 | net/http + pgvector |
| Voice | 8093 | net/http + whisper.cpp |
| Frontend (dev) | 5173 | Vite |
| ethend | 9090 | Express |
常用命令
# 构建所有 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 已排除)。格式:
{
"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 admin,ethend 代理提供 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 及之前完全一致。