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
This commit is contained in:
yumoqing 2026-07-21 17:00:53 +08:00
parent 98d977d848
commit 254bf8e9b2
20 changed files with 633 additions and 1 deletions

3
.env Normal file
View File

@ -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

20
.gitignore vendored Normal file
View File

@ -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

View File

@ -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` — 用量日志

24
app/ragserver.py Normal file
View File

@ -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)

86
build.sh Executable file
View File

@ -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 ==="

54
conf/config.json Normal file
View File

@ -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
}

25
json/documents.json Normal file
View File

@ -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}}"
}
}
}

26
json/engine_configs.json Normal file
View File

@ -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"
}
}

26
json/knowledge_bases.json Normal file
View File

@ -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}}"
}
}
}

28
json/subscriptions.json Normal file
View File

@ -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"
}
}

View File

@ -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"]}
]
}

37
models/documents.json Normal file
View File

@ -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"]}
]
}

View File

@ -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"]}
]
}

35
models/entities.json Normal file
View File

@ -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"]}
]
}

View File

@ -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"]}
]
}

View File

@ -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"]}
]
}

34
models/subscriptions.json Normal file
View File

@ -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"]}
]
}

30
models/usage_logs.json Normal file
View File

@ -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"]}
]
}

7
start.sh Executable file
View File

@ -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))"

8
stop.sh Executable file
View File

@ -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