From 254bf8e9b2482bde513e5d70d6075adcbe9e7627 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Tue, 21 Jul 2026 17:00:53 +0800 Subject: [PATCH] feat: initial ragserver - web app skeleton with data models, CRUD, and build scripts - ahserver web app on port 9190 - DB: rag (test/test123, utf8mb4) - 7 data models: knowledge_bases, documents, document_chunks, entities, entity_relations, subscriptions, engine_configs, usage_logs - 4 CRUD definitions: knowledge_bases, documents, subscriptions, engine_configs - build.sh clones all foundation modules + rag-pipeline + services - independent venv, bricks frontend, RBAC support --- .env | 3 ++ .gitignore | 20 +++++++++ README.md | 67 +++++++++++++++++++++++++++- app/ragserver.py | 24 ++++++++++ build.sh | 86 ++++++++++++++++++++++++++++++++++++ conf/config.json | 54 ++++++++++++++++++++++ json/documents.json | 25 +++++++++++ json/engine_configs.json | 26 +++++++++++ json/knowledge_bases.json | 26 +++++++++++ json/subscriptions.json | 28 ++++++++++++ models/document_chunks.json | 30 +++++++++++++ models/documents.json | 37 ++++++++++++++++ models/engine_configs.json | 33 ++++++++++++++ models/entities.json | 35 +++++++++++++++ models/entity_relations.json | 29 ++++++++++++ models/knowledge_bases.json | 32 ++++++++++++++ models/subscriptions.json | 34 ++++++++++++++ models/usage_logs.json | 30 +++++++++++++ start.sh | 7 +++ stop.sh | 8 ++++ 20 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 .env create mode 100644 .gitignore create mode 100644 app/ragserver.py create mode 100755 build.sh create mode 100644 conf/config.json create mode 100644 json/documents.json create mode 100644 json/engine_configs.json create mode 100644 json/knowledge_bases.json create mode 100644 json/subscriptions.json create mode 100644 models/document_chunks.json create mode 100644 models/documents.json create mode 100644 models/engine_configs.json create mode 100644 models/entities.json create mode 100644 models/entity_relations.json create mode 100644 models/knowledge_bases.json create mode 100644 models/subscriptions.json create mode 100644 models/usage_logs.json create mode 100755 start.sh create mode 100755 stop.sh diff --git a/.env b/.env new file mode 100644 index 0000000..746b18f --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +# RAG Server environment +SAGE_RBAC_DB=rag +PYTHONPATH=$PWD:$PWD/pkgs/apppublic:$PWD/pkgs/sqlor:$PWD/pkgs/ahserver:$PWD/pkgs/rbac:$PWD/pkgs/rag-pipeline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a73bb5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Runtime +py3/ +*.pid +logs/ +files/ +pkgs/ +__pycache__/ +*.pyc + +# Build artifacts +wwwroot/bricks/ +wwwroot/*_list/ +mysql.ddl.sql +models/mysql.ddl.sql + +# IDE +.idea/ +.vscode/ +*.swp +*.swo diff --git a/README.md b/README.md index 73102a4..cfedac2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,67 @@ -# ragserver +# RAG Server — 多媒体知识库 SaaS 平台 +## 概述 + +RagServer 是一个独立的多媒体 RAG (检索增强生成) SaaS Web 应用。支持文本、图片、音频、视频文件的拖拽上传,自动向量化入库,人脸识别,声纹识别,知识图谱构建,以及统一的混合检索。 + +## 架构 + +``` +┌─────────────────────────────────────────────────────┐ +│ RagServer (:9190) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ +│ │ RBAC 鉴权 │ │ 订阅容量 │ │ 拖拽上传 UI │ │ +│ │ 多租户隔离 │ │ quota 管控│ │ 知识库/文档 CRUD │ │ +│ └──────────┘ └──────────┘ └──────────────────┘ │ +│ │ │ +│ ┌──────────┴──────────┐ │ +│ │ rag-pipeline 引擎 │ │ +│ │ ingest / search │ │ +│ └──────────┬──────────┘ │ +└─────────────────────────┼───────────────────────────┘ + │ + ┌─────────┬───────────┼───────────┬─────────┐ + ▼ ▼ ▼ ▼ ▼ +┌───────┐ ┌───────┐ ┌─────────┐ ┌───────┐ ┌──────────┐ +│ CLIP │ │ VDB │ │ Graph │ │Rerank │ │ Face │ +│ 9086 │ │ 8886 │ │ 9092 │ │ 9090 │ │ 9091 │ +└───────┘ └───────┘ └─────────┘ └───────┘ └──────────┘ +``` + +## 数据库 + +- 数据库名: `rag` +- 用户: `test` / 密码: `test123` +- 字符集: `utf8mb4` + +## 模块结构 + +| 模块 | 说明 | 仓库 | +|------|------|------| +| ragserver | 主 Web 应用 | yumoqing/ragserver | +| rag-pipeline | RAG 管线引擎 | yumoqing/rag-pipeline | +| clip_embedding | 多模态向量化 (CLIP ViT-H-14) | yumoqing/clip_embedding | +| vdb | 向量数据库 (Milvus Lite) | yumoqing/vdb | +| bge-reranker | 文本重排 (BGE Reranker v2-m3) | yumoqing/bge-reranker | +| face-service | 人脸识别 (InsightFace) | yumoqing/face-service | +| graph-service | 图数据库 (NetworkX) | yumoqing/graph-service | +| ner-service | 命名实体识别 (GLiNER) | yumoqing/ner-service | +| speaker-id | 声纹识别 (ECAPA-TDNN) | yumoqing/speaker-id | + +## 部署 + +```bash +# 测试服务器 +ssh apitest@tokentest.opencomputing.cn +cd ~/ragserver +git pull && bash build.sh && ./stop.sh && ./start.sh +``` + +## 数据模型 + +- `knowledge_bases` — 知识库 +- `documents` — 文档 (文本/图片/音频/视频) +- `document_chunks` — 文档分片 +- `subscriptions` — 租户订阅 (磁盘容量/文档数/API调用量) +- `engine_configs` — 引擎配置 (支持租户级覆盖) +- `usage_logs` — 用量日志 diff --git a/app/ragserver.py b/app/ragserver.py new file mode 100644 index 0000000..1a5146c --- /dev/null +++ b/app/ragserver.py @@ -0,0 +1,24 @@ +# -*- coding:utf-8 -*- +from ahserver.webapp import webapp +from ahserver.serverenv import ServerEnv + + +def get_module_dbname(m): + return 'rag' + + +def password_encode(s): + if s is None: + return '' + from ahserver.globalEnv import password_encode as _orig + return _orig(s) + + +def init(): + env = ServerEnv() + env.get_module_dbname = get_module_dbname + env.password_encode = password_encode + + +if __name__ == '__main__': + webapp(init) diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..e477858 --- /dev/null +++ b/build.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -e +cdir=$(cd "$(dirname "$0")"; pwd) + +# Environment +export SAGE_RBAC_DB=rag + +# Virtualenv +if [ ! -d py3 ]; then + python3 -m venv py3 +fi +source py3/bin/activate + +# Install build tools +pip install -q xls2ddl + +# pkgs directory +mkdir -p pkgs logs files + +# Clone and install foundation modules +for repo in apppublic sqlor ahserver rbac appbase bricks-for-python; do + [ -d pkgs/$repo ] || git clone git@git.opencomputing.cn:yumoqing/$repo.git pkgs/$repo + pip install -q -e pkgs/$repo +done + +# Clone and install rag-pipeline (core engine) +[ -d pkgs/rag-pipeline ] || git clone git@git.opencomputing.cn:yumoqing/rag-pipeline.git pkgs/rag-pipeline +pip install -q -e pkgs/rag-pipeline + +# Clone service modules (for reference, not imported) +for svc in clip_embedding vdb bge-reranker face-service graph-service ner-service speaker-id; do + [ -d pkgs/$svc ] || git clone git@git.opencomputing.cn:yumoqing/$svc.git pkgs/$svc +done + +# Build bricks frontend +if [ ! -d pkgs/bricks ]; then + git clone git@git.opencomputing.cn:yumoqing/bricks.git pkgs/bricks +fi +mkdir -p pkgs/bricks/dist/i18n +cd pkgs/bricks +bash build.sh +cd $cdir + +# Symlink bricks +ln -sf pkgs/bricks/dist wwwroot/bricks 2>/dev/null || true + +# Install appbase and rbac +pip install -q -e pkgs/appbase +pip install -q -e pkgs/rbac + +# Generate DDL from models +if ls models/*.json >/dev/null 2>&1; then + cd models + json2ddl mysql . > mysql.ddl.sql + mysql -h db -utest -ptest123 rag < mysql.ddl.sql 2>/dev/null || echo "DDL already applied or mysql not available" + cd $cdir +fi + +# Generate CRUD from json/ +if ls json/*.json >/dev/null 2>&1; then + xls2ui -m models -o wwwroot ragserver json/*.json 2>/dev/null || true +fi + +# Symlink module wwwroots +for m in rag-pipeline; do + [ -d pkgs/$m/wwwroot ] && ln -sf ../pkgs/$m/wwwroot wwwroot/$m 2>/dev/null || true +done + +# Fix created_by in CRUD dspy files +if [ -d wwwroot/knowledge_bases_list ]; then + find wwwroot -name '*.dspy' -exec sed -i "s/ns\['created_by'\] = ''/ns['created_by'] = userorgid/" {} \; 2>/dev/null || true +fi + +# Sync password_key from Sage if available +if [ -f /d/apitest/sage/conf/config.json ]; then + sage_key=$(python3 -c "import json; print(json.load(open('/d/apitest/sage/conf/config.json'))['password_key'])") + python3 -c " +import json +c = json.load(open('$cdir/conf/config.json')) +c['password_key'] = '$sage_key' +json.dump(c, open('$cdir/conf/config.json', 'w'), indent=4) +" + echo "password_key synced from Sage" +fi + +echo "=== RAG Server build complete ===" diff --git a/conf/config.json b/conf/config.json new file mode 100644 index 0000000..a1159c1 --- /dev/null +++ b/conf/config.json @@ -0,0 +1,54 @@ +{ + "password_key": "RagServer2026Key", + "databases": { + "rag": { + "driver": "mysql+aiomysql", + "kwargs": { + "host": "db", + "port": 3306, + "user": "test", + "password": "test123", + "db": "rag", + "charset": "utf8mb4", + "minsize": 2, + "maxsize": 10 + } + } + }, + "SAGE_RBAC_DB": "rag", + "session_redis": { + "host": "127.0.0.1", + "port": 6379, + "db": 1 + }, + "logger": { + "name": "ragserver", + "levelname": "info", + "logfile": "$[workdir]$/logs/ragserver.log" + }, + "filesroot": "$[workdir]$/files", + "website": { + "paths": [ + ["$[workdir]$/wwwroot", ""] + ], + "client_max_size": 524288000, + "host": "0.0.0.0", + "port": 9190, + "coding": "utf-8", + "indexes": [ + "index.ui", + "index.html" + ], + "processors": [ + [".wss", "ws"], + [".ws", "ws"], + [".ui", "bui"], + [".dspy", "dspy"], + [".md", "md"] + ], + "startswiths": [ + {"leading": "/idfile", "registerfunction": "idfile"} + ] + }, + "hot_reload": true +} diff --git a/json/documents.json b/json/documents.json new file mode 100644 index 0000000..937f7d1 --- /dev/null +++ b/json/documents.json @@ -0,0 +1,25 @@ +{ + "tblname": "documents", + "params": { + "title": "文档管理", + "browserfields": [ + {"field": "file_name", "title": "文件名", "width": "25%"}, + {"field": "file_type", "title": "类型", "width": "10%"}, + {"field": "file_size", "title": "大小", "width": "10%"}, + {"field": "chunk_count", "title": "分片数", "width": "8%"}, + {"field": "status", "title": "状态", "width": "10%"}, + {"field": "kb_id", "title": "知识库", "width": "17%"}, + {"field": "created_at", "title": "上传时间", "width": "20%"} + ], + "editfields": [ + {"field": "file_name", "uitype": "Text", "required": true}, + {"field": "kb_id", "uitype": "Text", "required": true}, + {"field": "file_type", "uitype": "Text"} + ], + "searchfields": ["file_name"], + "sort": "created_at desc", + "data_filter": { + "org_id": "{{userorgid}}" + } + } +} diff --git a/json/engine_configs.json b/json/engine_configs.json new file mode 100644 index 0000000..c338fb2 --- /dev/null +++ b/json/engine_configs.json @@ -0,0 +1,26 @@ +{ + "tblname": "engine_configs", + "params": { + "title": "引擎配置", + "browserfields": [ + {"field": "engine_type", "title": "引擎类型", "width": "15%"}, + {"field": "engine_name", "title": "名称", "width": "15%"}, + {"field": "endpoint_url", "title": "服务地址", "width": "25%"}, + {"field": "model_name", "title": "模型", "width": "15%"}, + {"field": "is_default", "title": "默认", "width": "8%"}, + {"field": "status", "title": "状态", "width": "10%"}, + {"field": "org_id", "title": "机构", "width": "12%"} + ], + "editfields": [ + {"field": "engine_type", "uitype": "Text", "required": true}, + {"field": "engine_name", "uitype": "Text", "required": true}, + {"field": "endpoint_url", "uitype": "Text"}, + {"field": "api_key", "uitype": "Text"}, + {"field": "model_name", "uitype": "Text"}, + {"field": "is_default", "uitype": "Text"}, + {"field": "config_json", "uitype": "Text"} + ], + "searchfields": ["engine_type", "engine_name", "endpoint_url"], + "sort": "engine_type, priority desc" + } +} diff --git a/json/knowledge_bases.json b/json/knowledge_bases.json new file mode 100644 index 0000000..99482d5 --- /dev/null +++ b/json/knowledge_bases.json @@ -0,0 +1,26 @@ +{ + "tblname": "knowledge_bases", + "params": { + "title": "知识库管理", + "browserfields": [ + {"field": "name", "title": "名称", "width": "20%"}, + {"field": "description", "title": "描述", "width": "30%"}, + {"field": "doc_count", "title": "文档数", "width": "10%"}, + {"field": "total_size", "title": "大小", "width": "10%"}, + {"field": "embedding_engine", "title": "向量引擎", "width": "15%"}, + {"field": "created_at", "title": "创建时间", "width": "15%"} + ], + "editfields": [ + {"field": "name", "uitype": "Text", "required": true}, + {"field": "description", "uitype": "Text"}, + {"field": "embedding_engine", "uitype": "Text", "default": "clip-vith14"}, + {"field": "vdb_collection", "uitype": "Text"}, + {"field": "graph_name", "uitype": "Text"} + ], + "searchfields": ["name", "description"], + "sort": "created_at desc", + "data_filter": { + "org_id": "{{userorgid}}" + } + } +} diff --git a/json/subscriptions.json b/json/subscriptions.json new file mode 100644 index 0000000..53f7ae5 --- /dev/null +++ b/json/subscriptions.json @@ -0,0 +1,28 @@ +{ + "tblname": "subscriptions", + "params": { + "title": "订阅管理", + "browserfields": [ + {"field": "org_id", "title": "机构", "width": "15%"}, + {"field": "plan_name", "title": "套餐", "width": "15%"}, + {"field": "disk_quota_bytes", "title": "磁盘配额", "width": "12%"}, + {"field": "disk_used_bytes", "title": "已用磁盘", "width": "12%"}, + {"field": "doc_used", "title": "文档数", "width": "10%"}, + {"field": "start_date", "title": "开始", "width": "12%"}, + {"field": "end_date", "title": "结束", "width": "12%"}, + {"field": "status", "title": "状态", "width": "12%"} + ], + "editfields": [ + {"field": "org_id", "uitype": "Text", "required": true}, + {"field": "plan_name", "uitype": "Text", "required": true}, + {"field": "disk_quota_bytes", "uitype": "Text", "default": "1073741824"}, + {"field": "doc_quota", "uitype": "Text"}, + {"field": "kb_quota", "uitype": "Text"}, + {"field": "api_call_quota", "uitype": "Text"}, + {"field": "start_date", "uitype": "Text"}, + {"field": "end_date", "uitype": "Text"} + ], + "searchfields": ["org_id", "plan_name"], + "sort": "created_at desc" + } +} diff --git a/models/document_chunks.json b/models/document_chunks.json new file mode 100644 index 0000000..9037e1a --- /dev/null +++ b/models/document_chunks.json @@ -0,0 +1,30 @@ +{ + "summary": [ + { + "name": "document_chunks", + "title": "文档分片", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "doc_id", "title": "文档ID", "type": "str", "length": 32}, + {"name": "kb_id", "title": "知识库ID", "type": "str", "length": 32}, + {"name": "chunk_index", "title": "分片序号", "type": "int", "default": 0}, + {"name": "chunk_type", "title": "分片类型", "type": "str", "length": 32}, + {"name": "content", "title": "文本内容", "type": "text", "length": 65535}, + {"name": "description", "title": "描述", "type": "text", "length": 65535}, + {"name": "vector_id", "title": "向量库ID", "type": "str", "length": 128}, + {"name": "start_offset", "title": "起始偏移", "type": "int", "default": 0}, + {"name": "end_offset", "title": "结束偏移", "type": "int", "default": 0}, + {"name": "time_start", "title": "时间起始(秒)", "type": "float", "dec": 3}, + {"name": "time_end", "title": "时间结束(秒)", "type": "float", "dec": 3}, + {"name": "metadata", "title": "元数据JSON", "type": "text", "length": 2000}, + {"name": "created_at", "title": "创建时间", "type": "datetime"} + ], + "indexes": [ + {"name": "idx_chunk_doc", "idxtype": "index", "idxfields": ["doc_id"]}, + {"name": "idx_chunk_kb", "idxtype": "index", "idxfields": ["kb_id"]}, + {"name": "idx_chunk_vector", "idxtype": "index", "idxfields": ["vector_id"]} + ] +} diff --git a/models/documents.json b/models/documents.json new file mode 100644 index 0000000..ff04880 --- /dev/null +++ b/models/documents.json @@ -0,0 +1,37 @@ +{ + "summary": [ + { + "name": "documents", + "title": "文档", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "kb_id", "title": "知识库ID", "type": "str", "length": 32}, + {"name": "file_name", "title": "文件名", "type": "str", "length": 255}, + {"name": "file_type", "title": "文件类型", "type": "str", "length": 32}, + {"name": "file_size", "title": "文件大小", "type": "int", "default": 0}, + {"name": "file_path", "title": "文件路径", "type": "str", "length": 500}, + {"name": "mime_type", "title": "MIME类型", "type": "str", "length": 128}, + {"name": "chunk_count", "title": "分片数", "type": "int", "default": 0}, + {"name": "embed_dim", "title": "向量维度", "type": "int", "default": 1024}, + {"name": "metadata", "title": "元数据JSON", "type": "text", "length": 4000}, + {"name": "status", "title": "状态", "type": "str", "length": 16, "default": "pending"}, + {"name": "error_msg", "title": "错误信息", "type": "text", "length": 2000}, + {"name": "org_id", "title": "所属机构", "type": "str", "length": 32}, + {"name": "created_by", "title": "创建人", "type": "str", "length": 32}, + {"name": "created_at", "title": "创建时间", "type": "datetime"}, + {"name": "updated_at", "title": "更新时间", "type": "datetime"} + ], + "codes": [ + {"field": "file_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='file_type'"}, + {"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='doc_status'"}, + {"field": "kb_id", "table": "knowledge_bases", "valuefield": "id", "textfield": "name"} + ], + "indexes": [ + {"name": "idx_doc_kb", "idxtype": "index", "idxfields": ["kb_id"]}, + {"name": "idx_doc_org", "idxtype": "index", "idxfields": ["org_id"]}, + {"name": "idx_doc_status", "idxtype": "index", "idxfields": ["status"]} + ] +} diff --git a/models/engine_configs.json b/models/engine_configs.json new file mode 100644 index 0000000..be5dfe7 --- /dev/null +++ b/models/engine_configs.json @@ -0,0 +1,33 @@ +{ + "summary": [ + { + "name": "engine_configs", + "title": "引擎配置", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "org_id", "title": "机构ID", "type": "str", "length": 32, "nullable": "yes"}, + {"name": "engine_type", "title": "引擎类型", "type": "str", "length": 32}, + {"name": "engine_name", "title": "引擎名称", "type": "str", "length": 64}, + {"name": "endpoint_url", "title": "服务地址", "type": "str", "length": 255}, + {"name": "api_key", "title": "API密钥", "type": "str", "length": 255}, + {"name": "model_name", "title": "模型名称", "type": "str", "length": 128}, + {"name": "config_json", "title": "扩展配置JSON", "type": "text", "length": 4000}, + {"name": "is_default", "title": "是否默认", "type": "int", "default": 0}, + {"name": "priority", "title": "优先级", "type": "int", "default": 0}, + {"name": "status", "title": "状态", "type": "str", "length": 16, "default": "active"}, + {"name": "health_check_at", "title": "最近健康检查", "type": "datetime"}, + {"name": "created_at", "title": "创建时间", "type": "datetime"}, + {"name": "updated_at", "title": "更新时间", "type": "datetime"} + ], + "codes": [ + {"field": "engine_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='engine_type'"}, + {"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='engine_status'"} + ], + "indexes": [ + {"name": "idx_eng_type", "idxtype": "index", "idxfields": ["engine_type", "is_default"]}, + {"name": "idx_eng_org", "idxtype": "index", "idxfields": ["org_id", "engine_type"]} + ] +} diff --git a/models/entities.json b/models/entities.json new file mode 100644 index 0000000..68b896a --- /dev/null +++ b/models/entities.json @@ -0,0 +1,35 @@ +{ + "summary": [ + { + "name": "entities", + "title": "实体", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "kb_id", "title": "知识库ID", "type": "str", "length": 32}, + {"name": "name", "title": "实体名", "type": "str", "length": 255}, + {"name": "entity_type", "title": "实体类型", "type": "str", "length": 64}, + {"name": "description", "title": "描述", "type": "text", "length": 2000}, + {"name": "graph_node_id", "title": "图节点ID", "type": "str", "length": 128}, + {"name": "source_doc_ids", "title": "来源文档ID列表", "type": "text", "length": 2000}, + {"name": "face_embedding_id", "title": "人脸特征ID", "type": "str", "length": 128}, + {"name": "voice_embedding_id", "title": "声纹特征ID", "type": "str", "length": 128}, + {"name": "metadata", "title": "元数据JSON", "type": "text", "length": 2000}, + {"name": "org_id", "title": "所属机构", "type": "str", "length": 32}, + {"name": "created_at", "title": "创建时间", "type": "datetime"}, + {"name": "updated_at", "title": "更新时间", "type": "datetime"} + ], + "codes": [ + {"field": "entity_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='entity_type'"}, + {"field": "kb_id", "table": "knowledge_bases", "valuefield": "id", "textfield": "name"} + ], + "indexes": [ + {"name": "idx_entity_kb", "idxtype": "index", "idxfields": ["kb_id"]}, + {"name": "idx_entity_name", "idxtype": "index", "idxfields": ["kb_id", "name"]}, + {"name": "idx_entity_type", "idxtype": "index", "idxfields": ["kb_id", "entity_type"]}, + {"name": "idx_entity_face", "idxtype": "index", "idxfields": ["face_embedding_id"]}, + {"name": "idx_entity_voice", "idxtype": "index", "idxfields": ["voice_embedding_id"]} + ] +} diff --git a/models/entity_relations.json b/models/entity_relations.json new file mode 100644 index 0000000..243b31e --- /dev/null +++ b/models/entity_relations.json @@ -0,0 +1,29 @@ +{ + "summary": [ + { + "name": "entity_relations", + "title": "实体关系", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "kb_id", "title": "知识库ID", "type": "str", "length": 32}, + {"name": "subject_id", "title": "主体实体ID", "type": "str", "length": 32}, + {"name": "object_id", "title": "客体实体ID", "type": "str", "length": 32}, + {"name": "relation_type", "title": "关系类型", "type": "str", "length": 64}, + {"name": "description", "title": "关系描述", "type": "text", "length": 2000}, + {"name": "graph_edge_id", "title": "图边ID", "type": "str", "length": 128}, + {"name": "source_chunk_ids", "title": "来源分片ID列表", "type": "text", "length": 2000}, + {"name": "org_id", "title": "所属机构", "type": "str", "length": 32}, + {"name": "created_at", "title": "创建时间", "type": "datetime"} + ], + "codes": [ + {"field": "kb_id", "table": "knowledge_bases", "valuefield": "id", "textfield": "name"} + ], + "indexes": [ + {"name": "idx_rel_kb", "idxtype": "index", "idxfields": ["kb_id"]}, + {"name": "idx_rel_subject", "idxtype": "index", "idxfields": ["subject_id"]}, + {"name": "idx_rel_object", "idxtype": "index", "idxfields": ["object_id"]} + ] +} diff --git a/models/knowledge_bases.json b/models/knowledge_bases.json new file mode 100644 index 0000000..0ea0967 --- /dev/null +++ b/models/knowledge_bases.json @@ -0,0 +1,32 @@ +{ + "summary": [ + { + "name": "knowledge_bases", + "title": "知识库", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "name", "title": "名称", "type": "str", "length": 100}, + {"name": "description", "title": "描述", "type": "text", "length": 2000}, + {"name": "org_id", "title": "所属机构", "type": "str", "length": 32}, + {"name": "embedding_engine", "title": "向量化引擎", "type": "str", "length": 64, "default": "clip-vith14"}, + {"name": "vdb_collection", "title": "向量库集合名", "type": "str", "length": 128}, + {"name": "graph_name", "title": "图名称", "type": "str", "length": 128}, + {"name": "doc_count", "title": "文档数", "type": "int", "default": 0}, + {"name": "chunk_count", "title": "分片数", "type": "int", "default": 0}, + {"name": "total_size", "title": "总大小(字节)", "type": "int", "default": 0}, + {"name": "status", "title": "状态", "type": "str", "length": 16, "default": "active"}, + {"name": "created_by", "title": "创建人", "type": "str", "length": 32}, + {"name": "created_at", "title": "创建时间", "type": "datetime"}, + {"name": "updated_at", "title": "更新时间", "type": "datetime"} + ], + "codes": [ + {"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='kb_status'"} + ], + "indexes": [ + {"name": "idx_kb_org", "idxtype": "index", "idxfields": ["org_id"]}, + {"name": "idx_kb_name", "idxtype": "unique", "idxfields": ["org_id", "name"]} + ] +} diff --git a/models/subscriptions.json b/models/subscriptions.json new file mode 100644 index 0000000..bec6ac9 --- /dev/null +++ b/models/subscriptions.json @@ -0,0 +1,34 @@ +{ + "summary": [ + { + "name": "subscriptions", + "title": "租户订阅", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "org_id", "title": "机构ID", "type": "str", "length": 32}, + {"name": "plan_name", "title": "套餐名称", "type": "str", "length": 64}, + {"name": "disk_quota_bytes", "title": "磁盘配额(字节)", "type": "int", "default": 1073741824}, + {"name": "disk_used_bytes", "title": "已用磁盘(字节)", "type": "int", "default": 0}, + {"name": "doc_quota", "title": "文档数配额", "type": "int", "default": 1000}, + {"name": "doc_used", "title": "已用文档数", "type": "int", "default": 0}, + {"name": "kb_quota", "title": "知识库数配额", "type": "int", "default": 10}, + {"name": "kb_used", "title": "已用知识库数", "type": "int", "default": 0}, + {"name": "api_call_quota", "title": "API调用配额(月)", "type": "int", "default": 10000}, + {"name": "api_call_used", "title": "已用API调用(月)", "type": "int", "default": 0}, + {"name": "start_date", "title": "开始日期", "type": "date"}, + {"name": "end_date", "title": "结束日期", "type": "date"}, + {"name": "status", "title": "状态", "type": "str", "length": 16, "default": "active"}, + {"name": "created_at", "title": "创建时间", "type": "datetime"}, + {"name": "updated_at", "title": "更新时间", "type": "datetime"} + ], + "codes": [ + {"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sub_status'"} + ], + "indexes": [ + {"name": "idx_sub_org", "idxtype": "unique", "idxfields": ["org_id"]}, + {"name": "idx_sub_status", "idxtype": "index", "idxfields": ["status"]} + ] +} diff --git a/models/usage_logs.json b/models/usage_logs.json new file mode 100644 index 0000000..6bb25f0 --- /dev/null +++ b/models/usage_logs.json @@ -0,0 +1,30 @@ +{ + "summary": [ + { + "name": "usage_logs", + "title": "用量日志", + "primary": ["id"] + } + ], + "fields": [ + {"name": "id", "title": "ID", "type": "str", "length": 32}, + {"name": "org_id", "title": "机构ID", "type": "str", "length": 32}, + {"name": "kb_id", "title": "知识库ID", "type": "str", "length": 32}, + {"name": "operation", "title": "操作类型", "type": "str", "length": 32}, + {"name": "bytes_delta", "title": "磁盘变化(字节)", "type": "int", "default": 0}, + {"name": "bytes_total", "title": "累计磁盘", "type": "int", "default": 0}, + {"name": "api_calls", "title": "API调用数", "type": "int", "default": 1}, + {"name": "engine_type", "title": "引擎类型", "type": "str", "length": 32}, + {"name": "tokens_used", "title": "Token消耗", "type": "int", "default": 0}, + {"name": "cost_estimate", "title": "预估费用", "type": "float", "dec": 4}, + {"name": "detail", "title": "详情JSON", "type": "text", "length": 2000}, + {"name": "created_at", "title": "创建时间", "type": "datetime"} + ], + "codes": [ + {"field": "operation", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='usage_op'"} + ], + "indexes": [ + {"name": "idx_usage_org", "idxtype": "index", "idxfields": ["org_id", "created_at"]}, + {"name": "idx_usage_kb", "idxtype": "index", "idxfields": ["kb_id"]} + ] +} diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..02b42f1 --- /dev/null +++ b/start.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cdir=$(cd "$(dirname "$0")"; pwd) +source py3/bin/activate +cd $cdir +nohup py3/bin/python app/ragserver.py > logs/ragserver_stdout.log 2>&1 & +echo $! > ragserver.pid +echo "RAG Server started (pid $(cat ragserver.pid))" diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..90a75b6 --- /dev/null +++ b/stop.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cdir=$(cd "$(dirname "$0")"; pwd) +if [ -f ragserver.pid ]; then + kill $(cat ragserver.pid) 2>/dev/null && echo "RAG Server stopped" || echo "Server not running" + rm -f ragserver.pid +else + echo "No PID file found" +fi