Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4c00a75
部署流水线: 新增 dev 分支测试机自动部署工作流
Jul 24, 2026
a478c7b
Merge pull request #56 from fulai-tech/qlj/dev/0724-deploy-dev-workflow
dwqnidq Jul 24, 2026
0b28a7e
ES同步: 改为先清空索引再按 Mongo 全量重建写入 (#57)
dwqnidq Jul 28, 2026
a74a68d
日志滚动: 支持按日命名并在超大小时归档续写 (#58)
dwqnidq Aug 7, 2026
50589d7
检索出参: 返回素材 cover_url 字段 (#59)
dwqnidq Aug 7, 2026
b703fd8
去 BioNode: 手板与量产分目录 gRPC 并直连 Mongo
Aug 21, 2026
5c8fc4a
Merge pull request #60 from fulai-tech/qlj/dev/0821-uburnode-grpc-server
dwqnidq Aug 21, 2026
55de4ba
gRPC 端口: 手板改为 50050、量产改为 50051
Aug 24, 2026
bd4598c
Merge pull request #61 from fulai-tech/qlj/dev/0824-grpc-ports-50055-…
dwqnidq Aug 24, 2026
1b2836e
gRPC 端口: 量产改为 50055
Aug 24, 2026
4aa5efd
Merge pull request #62 from fulai-tech/qlj/dev/0824-grpc-ports-50055-…
dwqnidq Aug 24, 2026
6699550
gRPC 端口: 手板改为 50065、量产改为 50064
Aug 24, 2026
ff159ac
Merge pull request #63 from fulai-tech/qlj/dev/0824-mass-prod-port-50064
dwqnidq Aug 24, 2026
b1eb822
量产报告服务: 新增 ReportService 五个查询 RPC 空实现
Aug 24, 2026
26bd4da
Merge pull request #64 from fulai-tech/qlj/dev/0824-somni-report-service
dwqnidq Aug 24, 2026
41011f7
量产接口: 完善问卷答案与音频目录查询
Aug 25, 2026
d87ac58
Merge pull request #65 from fulai-tech/qlj/dev/0824-somni-audio-service
dwqnidq Aug 25, 2026
722a84d
问卷答卷: GetAnswer 的 answers 改为整段 JSON 字符串
Aug 26, 2026
9adc02f
Merge pull request #66 from fulai-tech/qlj/dev/0826-getanswer-answers…
dwqnidq Aug 26, 2026
cfca3db
量产音频: 实现 GetHot 并调整 GetAudio/GetAudioTag 契约与独立 Redis
Aug 26, 2026
a097ec4
Merge pull request #67 from fulai-tech/qlj/dev/0826-somni-audio-getho…
dwqnidq Aug 26, 2026
192d707
量产报告服务: 实现五个查询 RPC 的 Mongo 聚合与 typed 出参
Aug 26, 2026
4883349
Merge pull request #68 from fulai-tech/qlj/dev/0826-somni-report-impl
dwqnidq Aug 26, 2026
dbb5bc4
fix: 优化唤醒缓存与向量相似度计算
chentaiyan-fullive Aug 27, 2026
312e282
Merge pull request #69 from fulai-tech/fix/somni-wake-cache-vectorize…
chentaiyan-fullive Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,41 @@ APP_HOST=0.0.0.0
APP_PORT=8080
APP_DEBUG=false

# Elasticsearch
# 功能手板 gRPC(与 HTTP 同进程;明文无鉴权,生产勿对公网裸暴露)
GRPC_HOST=0.0.0.0
GRPC_PORT=50065
GRPC_ENABLED=true

# 量产 gRPC
SOMNI_GRPC_PORT=50064
SOMNI_GRPC_ENABLED=true

# Elasticsearch(功能手板)
ES_NODE=http://localhost:9200
ES_AUDIO_INDEX=somni_audio_materials
ES_TAG_VECTORS_INDEX=somni_audio_tag_dictionary

# MongoDB(Somni 数据同步源)
# 量产 ES(与手板隔离;可填另一节点)
SOMNI_ES_NODE=http://localhost:9201
SOMNI_ES_AUDIO_INDEX=somni_audio_materials
SOMNI_ES_TAG_VECTORS_INDEX=somni_audio_tag_dictionary
SOMNI_ES_SEARCH_EVENTS_INDEX=somni_audio_search_events

# MongoDB(功能手板 Fullive)
MONGO_URI=mongodb://user:password@host:27017/Fullive
MONGO_DB=Fullive

# comm-service gRPC
COMM_GRPC_HOST=bionode-test.fulai.tech
COMM_GRPC_PORT=443
COMM_GRPC_USE_TLS=true
# MongoDB(量产 Somni)
SOMNI_MONGO_URI=mongodb://user:password@host:27017/Somni
SOMNI_MONGO_DB=Somni
# 答卷集合(对齐 BioNode quiz_answers 结构)
SOMNI_MONGO_ANSWERS_COLLECTION=somni_quiz_answers
# 量产报告相关集合
SOMNI_MONGO_DEVICES_COLLECTION=somni_devices
SOMNI_MONGO_TELEMETRY_COLLECTION=somni_telemetry
SOMNI_MONGO_RECORDS_COLLECTION=somni_records
SOMNI_MONGO_SLEEP_REPORTS_COLLECTION=somni_sleep_reports
SOMNI_MONGO_EVENTS_COLLECTION=somni_events

# 检索参数
SIM_THRESHOLD=0.7
Expand Down Expand Up @@ -53,8 +75,18 @@ LOG_LEVEL=INFO
LOG_DIR=logs
LOG_RETENTION=7 days

# 音频检索缓存(空 REDIS_URL 表示关闭;TTL 自写入起算,命中不续期)
# 功能手板 / HTTP 检索缓存(空 REDIS_URL 表示关闭;TTL 自写入起算,命中不续期)
REDIS_URL=redis://127.0.0.1:6379/0
# 量产 Redis(独立实例;空则 GetHot 关闭,不回退 REDIS_URL)
# 本地可另起:redis-server --port 6380 --save "" --appendonly no
SOMNI_REDIS_URL=redis://127.0.0.1:6380/0
# GetHot 热点排行(Redis ZSET + ES 搜索事件索引)
SOMNI_HOT_ENABLED=true
SOMNI_HOT_TOP_N=10
SOMNI_HOT_REDIS_KEY=somni:audio:hot:v1
SOMNI_REDIS_MAX_CONNECTIONS=128
SOMNI_REDIS_CONNECT_TIMEOUT_SEC=2
SOMNI_REDIS_SOCKET_TIMEOUT_SEC=2
# 连接池大小:需 ≥ HTTP 并发峰值,过小会 Too many connections
REDIS_MAX_CONNECTIONS=512
SEARCH_CACHE_MAX_SIZE=2048
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Deploy UburNode (dev)

on:
workflow_dispatch:
push:
branches:
- dev

permissions:
contents: read

env:
# 测试机大盘路径(根分区仅 8G,Docker 与代码均放 localssd)
DEPLOY_DIR: /mnt/localssd/uburnode
# 服务器侧用 SSH 拉代码(需已配置 ~/.ssh/config 或默认可用的 GitHub 部署钥)
GIT_REPO: git@github.com:fulai-tech/UburPython.git
DEPLOY_BRANCH: dev

jobs:
deploy:
runs-on: ubuntu-latest
concurrency:
group: uburnode-deploy-dev
cancel-in-progress: true

steps:
- name: Validate required secrets
run: |
test -n "${{ secrets.SSH_HOST_DEV }}" || (echo "Missing SSH_HOST_DEV" && exit 1)
test -n "${{ secrets.SSH_USER_DEV }}" || (echo "Missing SSH_USER_DEV" && exit 1)
test -n "${{ secrets.SSH_PRIVATE_KEY_DEV }}" || (echo "Missing SSH_PRIVATE_KEY_DEV" && exit 1)

- name: Deploy on server
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST_DEV }}
username: ${{ secrets.SSH_USER_DEV }}
key: ${{ secrets.SSH_PRIVATE_KEY_DEV }}
port: ${{ secrets.SSH_PORT_DEV || 22 }}
command_timeout: 90m
script: |
set -euo pipefail
DEPLOY_DIR="${{ secrets.DEPLOY_PATH_DEV || env.DEPLOY_DIR }}"
GIT_REPO="${{ env.GIT_REPO }}"
DEPLOY_BRANCH="${{ env.DEPLOY_BRANCH }}"

if [ ! -d "$DEPLOY_DIR/.git" ]; then
if [ -d "$DEPLOY_DIR" ] && [ -n "$(ls -A "$DEPLOY_DIR" 2>/dev/null)" ]; then
echo "错误: $DEPLOY_DIR 已存在但不是 git 仓库。"
echo "请备份 .env 后: rm -rf $DEPLOY_DIR && git clone -b $DEPLOY_BRANCH $GIT_REPO $DEPLOY_DIR"
exit 1
fi
mkdir -p "$DEPLOY_DIR"
git clone -b "$DEPLOY_BRANCH" "$GIT_REPO" "$DEPLOY_DIR"
fi

cd "$DEPLOY_DIR"
# 统一为 SSH remote;本地改动一律丢弃,强制对齐远程 DEPLOY_BRANCH
git remote set-url origin "$GIT_REPO"
git fetch --all --prune
# -f 必须:否则工作区有改动(如 docker-compose.yml)时 checkout 会直接失败
git checkout -f -B "$DEPLOY_BRANCH" "origin/$DEPLOY_BRANCH"
git reset --hard "origin/$DEPLOY_BRANCH"
# 清理会挡住检出的未跟踪文件;保留 .env / logs / models(本地配置、日志、已上传 ONNX)
git clean -fd -e .env -e .env.* -e logs -e logs/ -e models -e models/

if [ ! -f .env ]; then
echo "缺少 $DEPLOY_DIR/.env,请从 .env.example 复制并填写后重试"
exit 1
fi

# 测试机用预置 ONNX(Dockerfile.server),避免构建期导出占满磁盘/耗时
docker compose -f docker-compose.yml -f docker-compose.prebuilt.yml up -d --build --remove-orphans
docker image prune -f
docker compose -f docker-compose.yml -f docker-compose.prebuilt.yml ps
80 changes: 32 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,48 @@
# UburPython

BioNode 体系中的 **Somni 音频检索服务**:以三维度检索为核心,从 MongoDB 同步 Somni 原料与标签词典至 Elasticsearch,对外提供 HTTP 检索 API
**Somni / 功能手板音频检索服务**:三维度检索为核心;HTTP 保留;同进程再对外暴露功能手板与量产两套 gRPC

- **核心**:三维度音频检索(`somni_audio_materials` ES 召回 + 标签词典向量 + 四步精排流水线)
- **数据源**:MongoDB `Fullive` 库(`somni_audio_materials`、`somni_audio_tag_dictionary`)
- **索引**:Elasticsearch `somni_audio_materials`、`somni_audio_tag_dictionary`(字段含义见 mapping `meta.description`)
- **写路径**:`POST/PUT /api/audio` 写 Mongo `somni_audio_materials`(Somni 文档体);有 `audio_url` 时同步 upsert ES;`DELETE` 仍经 comm-service
- **核心**:三维度音频检索(ES 召回 + 标签词典向量 + 精排)
- **功能手板**:`MONGO_*` + `ES_NODE` + `REDIS_URL`;HTTP `:8080` + gRPC `:50065`
- **量产**:`SOMNI_MONGO_*` + `SOMNI_ES_*` + `SOMNI_REDIS_URL`(独立 Redis 实例);gRPC `:50064`
- **写路径**:直连 Mongo,再同步本侧 ES(不再调用 BioNode)
- **接口文档**:`docs/功能手板接口文档.md`、`docs/量产接口文档.md`

## 架构

```text
算法端 / 调用方
对外 HTTP (FastAPI + Pydantic)
├──读(检索)──► somni_audio_materials (ES)
│ + somni_audio_tag_dictionary (ES 向量)
│ + 进程内 Embedding (bge-small-zh-v1.5)
├──写(创建/更新)──► Mongo somni_audio_materials ──► ES upsert(有 audio_url)
├──写(删除)──► comm-service (gRPC) + ES delete
└──同步(定时/手动)──► MongoDB Somni 集合 ──► Elasticsearch
├── HTTP :8080 ──► app/api/audio ──► server/handboard(Fullive)
├── gRPC :50065 ─► uburnode.v1(手板 audio/quiz)
└── gRPC :50064 ─► uburnode.somni.v1(量产 ListTags/ListAudios/Search/GetAnswer)
```

## 数据流

| 环节 | 来源 | 目标 | 模块 |
|------|------|------|------|
| Mongo → ES 同步 | `somni_audio_materials`、`somni_audio_tag_dictionary` | 同名 ES 索引 | `scripts/sync_es_from_comm.py` |
| 标签向量 | 词典 `name` / `name_en` | `name_vector` / `name_en_vector` | 同步脚本 + `app/embedding/` |
| HTTP 检索 | ES 原料文档 | `data.results[].materials[]` 原样返回 | `app/services/retrieval.py` |
| HTTP 创建/更新 | Somni 文档体(仅创建强制 `audio_name`) | Mongo + ES | `app/services/audio.py` |
| HTTP 删除(遗留) | material_id | comm + ES delete | `app/services/audio.py` |

字段命名全链路 **snake_case**。Somni 表结构详见仓库内 `音频表结构.md`。

## 目录结构

```text
UburPython/
├── app/
│ ├── main.py # FastAPI 入口 + lifespan
│ ├── core/ # 配置、日志、标签转换
│ ├── api/audio.py # 4 个 HTTP 端点
│ ├── schemas/audio.py # Pydantic 模型
│ ├── services/ # AudioService、RetrievalService
│ ├── mongo/materials.py # Mongo somni_audio_materials 读写
│ ├── es/
│ │ ├── search.py # EsSearch 读路径
│ │ ├── sync.py # EsSync(Somni upsert + 删除)
│ │ ├── somni_docs.py # description_text 等文档转换
│ │ └── index_mappings.py # ES 索引 mapping + 字段注释
│ ├── embedding/encoder.py # bge-small-zh-v1.5 向量编码
│ └── bionode_grpc_clients/ # comm-service gRPC 客户端
│ ├── main.py # FastAPI + lifespan(双 gRPC)
│ ├── api/audio.py # HTTP /api/audio
│ ├── server/
│ │ ├── bootstrap.py # 启停手板/量产 gRPC
│ │ ├── handboard/ # 功能手板 audio|quiz
│ │ └── somni/ # 量产 audio|quiz
│ ├── uburnode_grpc/grpc_gen/ # proto 生成 stub
│ ├── core/ # 配置、日志、bson 工具
│ ├── schemas/
│ ├── services/retrieval.py # 检索流水线
│ ├── es/ # ES 读/写同步
│ ├── embedding/
│ ├── cache/
│ └── middleware/
├── scripts/
│ ├── sync_es_from_comm.py # Mongo → ES 差异同步
│ └── gen_proto.sh # 生成 gRPC stub
├── proto/ # bionode_comm.proto
│ └── gen_uburnode_proto.sh # 生成对外 gRPC stub
├── proto/
│ ├── uburnode.proto
│ └── uburnode_somni.proto
├── tests/
├── pyproject.toml
└── .env.example
Expand All @@ -70,22 +54,22 @@ UburPython/
# 1. 安装依赖(推荐 uv)
uv sync --extra dev

# 2. 生成 comm gRPC stub(CUD 接口需要)
chmod +x scripts/gen_proto.sh
./scripts/gen_proto.sh
# 2. 生成对外 gRPC stub
chmod +x scripts/gen_uburnode_proto.sh
./scripts/gen_uburnode_proto.sh

# 3. 本地 Elasticsearch
docker compose -f docker-compose.es.yml up -d
curl -s http://localhost:9200

# 4. 配置环境变量
cp .env.example .env
# 编辑 ES_NODE、MONGO_URI、EMBEDDING_ONNX_DIR、COMM_GRPC_* 等
# 编辑 ES_NODE、MONGO_URI、SOMNI_MONGO_URI、SOMNI_ES_NODE、EMBEDDING_* 等

# 5. 导出 ONNX 模型(若 models/ 目录尚无模型)
# 见 scripts/export_onnx_model.py

# 6. Mongo → ES 全量同步
# 6. Mongo → ES 同步(手板库)
uv run python scripts/sync_es_from_comm.py --dry-run
uv run python scripts/sync_es_from_comm.py

Expand Down
2 changes: 1 addition & 1 deletion app/api/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
UpdateAudioRequest,
)
from app.schemas.response import ApiResponse, success
from app.services.audio import AudioService
from app.server.handboard.audio.service import AudioService

router = APIRouter(prefix="/audio", tags=["audio"])

Expand Down
5 changes: 0 additions & 5 deletions app/bionode_grpc_clients/__init__.py

This file was deleted.

8 changes: 0 additions & 8 deletions app/bionode_grpc_clients/comm/__init__.py

This file was deleted.

Loading