Skip to content

Repository files navigation

Agent-Roro — 情感陪伴智能体

Roro 是一个实时情感对话智能体,具备持续记忆与人设演化的能力。LLM 在回复中内联情绪标签,由 TTS 模块合成带情感的语音,并通过 WebSocket 流式推送到浏览器/ESP32 设备即时播放。系统支持全双工语音打断(barge-in)、分层记忆、人设演化、声纹识别和 MCP 物联网设备控制,可同时接入 Web 前端与硬件设备,提供连贯、有温度的多模态陪伴体验。

架构

用户(浏览器/ESP32 设备)
    ↕ WebSocket (/ws/chat 或 /ws/xiaozhi)
    ↕ 文本 JSON / PCM / 裸 Opus 二进制帧
Orchestrator (FastAPI :8090)
    ├─ ASR Bridge         → FunASR 2-pass Paraformer (WebSocket :10095)
    ├─ LLM Client         → OpenAI-compatible API (SSE 流式, 动态 prompt, 工具调用)
    ├─ Emotion Parser     → 5 类 45 种内联标签解析 + 关键词兜底引擎
    ├─ Sentence Splitter  → 句子级流式切分 (标签保护, 三步阻断)
    ├─ TTS Bridge         → Higgs TTS App Server :8081 (SSE 流式)
    ├─ Opus Codec         → Opus 编解码 (opuslib / ffmpeg 双后端, 设备用)
    ├─ MCP Session        → JSON-RPC 2.0 设备控制 (音量/亮度/拍照等)
    ├─ Voiceprint         → CAM++ 声纹识别 (CPU, 可选)
    ├─ Persona System     → 大五人格 + 情绪指数衰减 + 关系演进
    ├─ Memory System      → Core + Working + Semantic (SQLite)
    ├─ Admin Module       → 记忆管理后台 (/api/admin/* + SPA 前端)
    └─ Xiaozhi Handler    → 小智 ESP32 设备协议适配 (Opus 音频帧)

数据流: 用户语音/文字 → ASR 语音识别(FunASR 2-pass) → 动态 prompt 组装(人设+情绪+记忆) → LLM 流式生成(含 <|emotion:NAME|> 标签) → 句子切分 → 标签解析 → TTS 流式合成 → WebSocket 推送音频 chunk → 浏览器/设备即时播放。支持全双工语音打断(barge-in),MCP 设备控制。每轮对话后异步提取记忆(事实/情绪/关系信号)。

项目结构

agent-roro/
├── orchestrator/                # 后端 Python 服务 (FastAPI :8090)
│   ├── main.py                  # 入口: WebSocket 编排, REST API, 三态状态机
│   ├── config.py                # 6 个 dataclass 配置组, 全部 from_env()
│   ├── llm_client.py            # LLM OpenAI SSE 流式 + 工具调用循环
│   ├── emotion_parser.py        # 情感/风格/韵律/音效/环境标签解析 + 兜底引擎
│   ├── sentence_splitter.py     # 流式句子切分 (标签感知, 未闭合保护)
│   ├── tts_bridge.py            # Higgs TTS SSE 桥接 (流式/非流式)
│   ├── asr_bridge.py            # FunASR 2-pass WebSocket 桥接
│   ├── opus_codec.py            # Opus 编解码 (opuslib / ffmpeg 双后端)
│   ├── memory.py                # 对话持久化 (JSON 文件, 原子写入)
│   ├── xiaozhi_handler.py       # 小智 ESP32 设备协议处理
│   ├── mcp_transport.py         # MCP JSON-RPC 2.0 传输层
│   ├── mcp_session.py           # MCP 会话管理 (握手/工具发现/执行)
│   ├── persona/                 # 人设系统
│   │   ├── character.py         # 大五人格 + 说话风格
│   │   ├── emotion.py           # 情绪状态 (valence/arousal + 指数衰减)
│   │   └── relationship.py      # 亲密度/信任度/熟悉度演进
│   ├── memory_system/           # 分层记忆
│   │   ├── core.py              # L5 核心记忆 (人设+情绪+关系, 始终在上下文)
│   │   ├── working.py           # L4 工作记忆 (滑动窗口 20 轮)
│   │   ├── semantic.py          # L2 语义记忆 (SQLite, 事实+置信度衰减)
│   │   └── extraction.py        # 异步 LLM 记忆提取 pipeline
│   ├── voiceprint/              # 声纹识别
│   │   ├── extractor.py         # CAM++ / Mock embedding 提取
│   │   └── service.py           # 注册 + 1:N 识别
│   ├── admin/                   # 管理后台 (已实现)
│   │   ├── auth.py              # Token 认证
│   │   ├── routes.py            # /api/admin/* CRUD
│   │   ├── services.py          # 业务逻辑层
│   │   └── static/              # 管理后台前端 SPA
│   └── requirements.txt
├── frontend/                    # 前端
│   ├── index.html               # 单文件 UI (约 76KB, HTML+CSS+JS 全内联)
│   └── assets/                  # 头像、表情包 PNG
├── asr-server/                  # FunASR Docker 部署
│   └── docker-compose.yml
├── docs/                        # 设计文档
│   ├── API.md                         # Orchestrator REST / WebSocket API
│   ├── EMOTION_TAGS_AGENT_PROMPT.md   # 情绪标签参考手册
│   ├── full-duplex-architecture.md    # 全双工 + 打断技术方案
│   ├── memory-system.md               # 分层记忆系统设计
│   └── admin-memory-system.md         # 记忆后台管理设计方案
├── scripts/                     # 工具脚本
│   ├── generate_roro_emotions.py    # 表情包批量生成
│   └── ws_chat_test.py              # 端到端对话测试 (连 /ws/chat 校验 LLM+TTS)
├── data/                        # 运行时数据 (SQLite, core_memory.json, gitignored)
├── memory/                      # 对话历史 JSON (运行时, gitignored)
├── logs/                        # 结构化日志 (运行时, gitignored)
├── Dockerfile                   # Docker 构建镜像 (python:3.10-slim)
├── docker-compose.yml           # Docker Compose 编排 (含健康检查)
├── .dockerignore                # Docker 构建忽略
├── .env.example                 # 环境变量模板 (含多 provider 示例)
├── restart.ps1                  # PowerShell 后台重启脚本
├── restart.bat                  # Windows 重启入口 (双击)
├── start.sh                     # Linux/macOS 前台启动
├── start.bat                    # Windows 前台启动
├── test_emotion_parser.py       # 情绪解析器单元测试 (15 用例, 全部通过)
├── test_sentence_splitter.py    # 句子切分器单元测试 (30 用例, 全部通过)
└── test_api_roro.py           # Higgs TTS API 集成测试脚本

快速开始

1. 环境要求

  • Python 3.10+
  • Higgs TTS App Server 运行在 :8081
  • FunASR 2-pass 服务运行在 :10095 (语音输入时需要)
  • LLM API (OpenAI-compatible, 如 DashScope/DeepSeek/Ollama)

2. 配置

复制 .env.example.env 并填入实际值:

# LLM (任何 OpenAI-compatible API)
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=sk-xxx
LLM_MODEL=qwen-plus

# TTS (Higgs App Server)
TTS_SERVER_URL=http://localhost:8081
TTS_API_KEY=higgs_xxx
TTS_DEFAULT_VOICE=roro-01

# ASR (FunASR 2-pass)
ASR_SERVER_URL=ws://localhost:10095
ASR_SAMPLE_RATE=16000
ASR_MODE=2pass

# Server
HOST=0.0.0.0
PORT=8090
SERVER_PUBLIC_HOST=<宿主机局域网 IP>

3. 启动 ASR 服务

cd asr-server
docker compose up -d

4. 启动 Orchestrator

# Windows (前台)
start.bat

# Windows (后台,推荐)
双击 restart.bat

# Linux/macOS
bash start.sh

# Docker (一键部署)
docker compose up -d

然后打开浏览器访问 http://localhost:8090

WebSocket 协议

Web 客户端 /ws/chat

客户端 → 服务端:

type 格式 说明
message {"type":"message","text":"...","voice":"roro-01","session_id":"...","images":[base64...]} 文本消息 (可含图片)
audio_start {"type":"audio_start","session_id":"..."} 开始语音输入
二进制 PCM 16kHz 16bit 单声道 语音数据帧
audio_end {"type":"audio_end","session_id":"...","voice":"roro-01"} 结束语音输入
barge_in {"type":"barge_in"} 打断当前播放
ping {"type":"ping"} 心跳

服务端 → 客户端:

type 格式 说明
text_delta {"type":"text_delta","content":"token"} LLM 流式 token
sentence {"type":"sentence","text":"...","emotion":"joy","index":0} 完整句子
audio {"type":"audio","data":"base64...","format":"wav","emotion":"joy","sentence_index":0,"chunk_index":0} 音频 chunk
asr_partial {"type":"asr_partial","text":"..."} ASR 实时部分结果
asr_final {"type":"asr_final","text":"..."} ASR 最终结果
interrupted {"type":"interrupted"} 打断确认通知
done {"type":"done","elapsed_ms":3200,"sentences":2} 回合完成
error {"type":"error","message":"..."} 错误
asr_error {"type":"asr_error","message":"..."} ASR 错误
pong {"type":"pong"} 心跳响应

小智设备 /ws/xiaozhi

握手: 设备发 {"type":"hello","version":1,"features":{"mcp":true}},服务端回 {"type":"hello","transport":"websocket","audio_params":{"format":"opus","sample_rate":24000,"frame_duration":60}}

音频帧: 裸 Opus 二进制(RFC 6716),24kHz/60ms mono,非 OGG 容器。

控制消息: listen/start|stop|detectabort(打断)、goodbye(断开)、mcp(JSON-RPC 透传)。

语音输入 (ASR)

采用 FunASR 2-pass Paraformer 流式识别方案,本地 Docker 部署。浏览器通过 ScriptProcessorNode 采集 48kHz → 16kHz 重采样后以 PCM 16bit 单声道发送。前端实现自适应 VAD (噪声底估计 + 动态阈值),~840ms 静音自动判定说话结束,支持最大录音时长保护。小智设备则使用 Opus 编码的音频帧 (24kHz/60ms),服务端解码后送 ASR。

全双工 & Barge-in

WebSocket 支持混合文本/二进制帧。三态状态机驱动 (IDLE/SPEAKING/LISTENING):

  • 非阻塞打断: interrupt()set event + cancel task不 await,主消息循环永不阻塞
  • ASR 结果兜底: 打断不由 VAD 决定,必须等 ASR 确认有真实文字后才执行
  • 音频降音: VAD 触发 ASR 录音时,roro 音频降到 20% (GainNode),ASR 验证后才决定停或恢复
  • 冷却防抖: 前端 3 帧 VAD 确认 + 1 秒打断冷却 (统一应用于文本和语音路径)

详见 docs/full-duplex-architecture.md

情感标签

LLM 通过 System Prompt 在每句话前嵌入 <|category:value|> 标签,解析后透传给 TTS:

类别 数量 示例
emotion 21 affection, amusement, elation, enthusiasm, fear, sadness, surprise ...
style 3 whispering, shouting, singing
prosody 10 pause, long_pause, speed_fast, speed_slow, expressive_high ...
sfx 9 laughter, sigh, humming, crying ...
env 2 music, noise

注:prosody 虽含 pitch_high/pitch_low,但 System Prompt 已提示 LLM 避免使用——它们会改变声音的性别特征导致"变声"。

当 LLM 未输出标签时,兜底引擎根据关键词自动标注(16 组关键词映射,默认 contentment)。兜底判定基于有效标签校验(无效标签不会误触发)。

图片理解

支持两种图片处理模式:

  1. 多模态主链路 (LLM_MULTIMODAL=true): 主 LLM 直接接收图片 (content array 格式),要求 LLM_MODEL 是多模态模型
  2. 视觉旁路 (LLM_MULTIMODAL=false, MCP_VISION_BYPASS=true): 用 VL 模型描述图片后注入纯文本主链路
  3. 禁用 (vision_bypass=false): 前端图片被丢弃

前端支持按钮选择、粘贴、拖拽上传图片,base64 data URL,预览条 + 气泡渲染,单张上限 10MB。

对话记忆

每轮对话自动保存到 memory/{YYYYMMDD_HHMMSS}_{session_id}.json。同一 session_id 重连时恢复历史上下文,支持多轮对话。

日志

结构化日志同时写入 logs/roro.log 和 stderr:

2026-06-14 20:36:45 | INFO  | roro.ws      | [sid/T1] Sentence 0: emotion=enthusiasm | tags=1 | text='嗨!'
2026-06-14 20:36:45 | INFO  | roro.tts     | [sid/T1] TTS sentence 0: 1 chunks, 0.6s
2026-06-14 20:36:45 | INFO  | roro.llm     | [sid/T1] LLM done: 8 tokens, 9.1s
2026-06-14 20:36:45 | INFO  | roro.ws      | [sid/T1] DONE: 2 sentences, 9.1s total, emotions=[enthusiasm]

六个 logger 分区: roro (通用) / roro.ws (WebSocket) / roro.llm (LLM) / roro.tts (TTS) / roro.asr (ASR) / roro.memory (记忆系统)。客户端断连记录为 INFO 级别。

人设系统

Roro 的人格不是固定 prompt,而是有层次、会演化的系统。大五人格特质(开放性、尽责性、外向性、宜人性、神经质)定义核心性格,交互风格和幽默风格从对话中学习。情绪状态持续存在并自然指数衰减,每轮对话后更新。关系亲密度、信任度和熟悉度通过每次互动缓慢积累,讨论私人话题会加速关系发展。实现与数据边界见 docs/memory-system.md

分层记忆

层级 名称 内容 存储
L5 Core Memory 人设+情绪+关系 data/core_memory.json (始终在上下文)
L4 Working Memory 最近 20 轮对话 内存滑动窗口 (~4000 tokens)
L3 Procedural Memory 交互习惯模式 预留
L2 Semantic Memory 用户事实/偏好 data/semantic_memory.db (SQLite, 置信度衰减)
L1 Episodic Memory 具体事件回忆 memory/*.json (预留 Qdrant 向量检索)

每轮对话后异步提取记忆: 事实→Semantic, 情绪→Core, 关系信号→Core。事实有置信度衰减机制(30天未确认自动衰减)。详见 docs/memory-system.md

声纹识别

CAM++ 模型(CPU, ~29MB)提取声纹 embedding,支持用户注册(3句语音取平均)和 1:N 识别(对话开始时自动识别身份)。使用 numpy cosine similarity,阈值 0.75 高置信/0.65 低置信。当前默认使用 Mock 提取器用于测试,生产环境安装 modelscope+torch 后自动切换 CAM++。

MCP 设备控制

通过 MCP (Model Context Protocol) JSON-RPC 2.0 协议控制物联网设备:

  • 音量控制: self.audio_speaker.set_volume
  • 屏幕亮度: self.display.set_brightness
  • 拍照: 调用设备摄像头拍照并传回图片
  • 状态查询: 设备电量、WiFi 信号等

LLM 工具调用循环自动决定何时调用设备工具。设备连接断开时自动清理所有 pending 请求。

管理后台

提供 Web 管理界面 (http://localhost:8090/admin) 管理各层记忆:

  • Dashboard: 实时监控活跃会话、记忆量统计、情感/关系仪表盘
  • 语义事实: 分页查看、编辑、增删、确认置信度
  • 核心记忆: 修改情感状态(valence/arousal)、关系参数、昵称
  • 对话历史: 按 session 浏览、全文搜索、导出/删除
  • 运维: 全量备份、重置、触发衰减

REST API

端点 说明
GET / 前端页面
GET /health 健康检查 (含 LLM/TTS/ASR 状态, 活跃会话数)
GET /api/voices 列出可用声音
GET /api/vision/explain 视觉旁路图片分析
GET /api/admin/* 管理后台 API (需 ADMIN_TOKEN 认证)
WS /ws/chat 浏览器对话 WebSocket
WS /ws/xiaozhi 小智 ESP32 设备 WebSocket

部署

Docker 一键部署

# 编辑 .env 配置
cp .env.example .env

# 启动全部服务
docker compose up -d

# 仅启动 Orchestrator (不包含 ASR)
docker compose up -d orchestrator

# 查看日志
docker compose logs -f orchestrator

ESP32/小智设备接入:Docker 部署时容器无法自动探测宿主机 LAN IP,需在 .env 显式设置 SERVER_PUBLIC_HOST(值为宿主机局域网 IP)。视觉回调、OTA 等需要设备回连的场景依赖此地址;非 Docker 直跑时留空可自动探测。

Windows 部署

# 前台启动
start.bat

# 后台重启 (推荐)
双击 restart.bat

Linux/macOS 部署

bash start.sh

详见 Dockerfiledocker-compose.yml

测试

# 情绪解析器 (15 用例)
python test_emotion_parser.py

# 句子切分器 (30 用例)
python test_sentence_splitter.py

# Higgs TTS API 集成测试
python test_api_roro.py --api-key xxx [--host localhost] [--port 8081]

# 端到端对话测试 (需服务已启动, 校验 LLM→情绪解析→TTS 全链路)
python scripts/ws_chat_test.py

实现状态

特性 状态
ASR 语音输入 (FunASR + Paraformer 2-pass) ✅ 已完成
全双工对话 (边听边说, 非阻塞打断) ✅ 已完成
人设系统 (大五人格 + 情绪衰减 + 关系演进) ✅ 已完成
分层记忆 (Core + Working + Semantic + LLM 提取) ✅ 已完成
声纹识别 (CAM++ / Mock, 注册 + 1:N 识别) ✅ 已完成
图片理解 (多模态 + 视觉旁路双方案) ✅ 已完成
MCP 设备控制 (音量/亮度/拍照/状态查询) ✅ 已完成
ESP32 小智设备协议适配 (Opus 帧) ✅ 已完成
管理后台 (记忆查看/编辑/备份/重置) ✅ 已完成
Docker 容器化部署 ✅ 已完成
机器人形象 + 口型同步 ⏳ 待规划
Episodic Memory (Qdrant 向量检索) ⏳ 待规划
Procedural Memory (交互模式学习) ⏳ 待规划

延迟优化

当前实现会在 LLM 输出完整句子后并行启动 TTS,并按句子顺序发送音频;实际延迟取决于已配置的 LLM、TTS、ASR 服务和网络状况。接口与流式行为见 docs/full-duplex-architecture.md

相关项目

  • Higgs TTS — Roro 依赖的上游 TTS 引擎(基于 Higgs Audio v3 / 4B 模型),提供带情感标签的内联语音合成(App Server + 模型)。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages