feat: 更新 Docker 配置,添加 Milvus 令牌支持,优化健康检查命令,移除 PostgreSQL 配置,调整环境变量,增强系统稳定性

This commit is contained in:
Wenjie Zhang 2025-07-18 11:29:12 +08:00
parent 9a162d5197
commit 5ed016298c
13 changed files with 103 additions and 556 deletions

View File

@ -25,32 +25,25 @@ services:
- NEO4J_USERNAME=${NEO4J_USERNAME:-neo4j}
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-0123456789}
- MILVUS_URI=${MILVUS_URI:-http://milvus:19530}
- MILVUS_TOKEN=${MILVUS_TOKEN:-}
- MINERU_OCR_URI=${MINERU_OCR_URI:-http://mineru:30000}
- PADDLEX_URI=${PADDLEX_URI:-http://paddlex-ocr:8080}
- MINIO_URI=${MINIO_URI:-http://milvus-minio:9000}
# PostgreSQL 配置
- POSTGRES_URI=postgresql://${POSTGRES_USER:-lightrag}:${POSTGRES_PASSWORD:-lightrag}@postgres-lightrag:5432/${POSTGRES_DATABASE:-lightrag}
- POSTGRES_HOST=${POSTGRES_HOST:-postgres-lightrag}
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
- POSTGRES_DATABASE=${POSTGRES_DATABASE:-lightrag}
- POSTGRES_USER=${POSTGRES_USER:-lightrag}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-lightrag}
# 连接池配置
- POSTGRES_MAX_CONNECTIONS=50
- POSTGRES_MIN_CONNECTIONS=5
- MODEL_DIR=/models
- RUNNING_IN_DOCKER=true
- NO_PROXY=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio
- no_proxy=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio
command: uv run uvicorn server.main:app --host 0.0.0.0 --port 5050 --reload
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:5050/health || exit 1"]
test: ["CMD-SHELL", "curl -f http://localhost:5050/api/health || exit 1"]
interval: 30s
timeout: 15s
retries: 8
start_period: 180s
depends_on:
postgres:
condition: service_healthy
# postgres:
# condition: service_healthy
milvus:
condition: service_healthy
minio:
@ -80,31 +73,31 @@ services:
# PostgreSQL 服务 - 基于官方推荐的 shangor/postgres-for-rag 镜像
# 提供 KV store, VectorDB (pgvector) 和 GraphDB (Apache AGE) 功能
postgres:
image: shangor/postgres-for-rag:v1.0
container_name: postgres-lightrag
environment:
- POSTGRES_DATABASE=${POSTGRES_DATABASE:-lightrag}
- POSTGRES_USER=${POSTGRES_USER:-lightrag}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-lightrag}
- POSTGRES_HOST_AUTH_METHOD=md5
# 启用必要的扩展
- POSTGRES_INITDB_ARGS=--auth-host=md5
volumes:
- ./docker/volumes/postgres/data:/var/lib/postgresql/data
- ./docker/init-postgres.sh:/init-postgres.sh
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "PGPASSWORD=${POSTGRES_PASSWORD:-lightrag} psql -h localhost -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c \"SELECT ag_catalog.create_graph('test_ready');\" || PGPASSWORD=${POSTGRES_PASSWORD:-lightrag} psql -h localhost -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c 'SELECT 1'"]
interval: 30s
timeout: 15s
retries: 15
start_period: 120s
networks:
- app-network
restart: unless-stopped
command: ["bash", "/init-postgres.sh"]
# postgres:
# image: shangor/postgres-for-rag:v1.0
# container_name: postgres-lightrag
# environment:
# - POSTGRES_DATABASE=${POSTGRES_DATABASE:-lightrag}
# - POSTGRES_USER=${POSTGRES_USER:-lightrag}
# - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-lightrag}
# - POSTGRES_HOST_AUTH_METHOD=md5
# # 启用必要的扩展
# - POSTGRES_INITDB_ARGS=--auth-host=md5
# volumes:
# - ./docker/volumes/postgres/data:/var/lib/postgresql/data
# - ./docker/init-postgres.sh:/init-postgres.sh
# ports:
# - "5432:5432"
# healthcheck:
# test: ["CMD-SHELL", "PGPASSWORD=${POSTGRES_PASSWORD:-lightrag} psql -h localhost -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c \"SELECT ag_catalog.create_graph('test_ready');\" || PGPASSWORD=${POSTGRES_PASSWORD:-lightrag} psql -h localhost -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c 'SELECT 1'"]
# interval: 30s
# timeout: 15s
# retries: 15
# start_period: 120s
# networks:
# - app-network
# restart: unless-stopped
# command: ["bash", "/init-postgres.sh"]
graph:
image: neo4j:5.26

View File

@ -29,6 +29,11 @@ COPY ../pyproject.toml /app/pyproject.toml
COPY ../.python-version /app/.python-version
# 安装依赖项
ENV HTTP_PROXY=http://172.19.13.5:7890 \
HTTPS_PROXY=http://172.19.13.5:7890 \
http_proxy=http://172.19.13.5:7890 \
https_proxy=http://172.19.13.5:7890
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-install-project
@ -38,4 +43,7 @@ COPY ../server /app/server
# 同步项目
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync
uv sync
# 取消代理
RUN unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy

View File

@ -1,358 +0,0 @@
#!/bin/bash
# PostgreSQL 初始化脚本
# 适用于 shangor/postgres-for-rag 镜像
set -e
echo "Starting PostgreSQL initialization..."
# 启动 PostgreSQL 服务
service postgresql start
sleep 10
echo "PostgreSQL service started, waiting for it to be ready..."
# 等待 PostgreSQL 完全启动
while ! su - postgres -c "pg_isready" > /dev/null 2>&1; do
echo "Waiting for PostgreSQL to be ready..."
sleep 5
done
# 额外等待以确保数据库完全启动
echo "PostgreSQL is ready, waiting additional time for full initialization..."
sleep 10
echo "PostgreSQL is ready, creating user and database..."
# 创建用户(如果不存在)
if ! su - postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='${POSTGRES_USER:-lightrag}'\"" | grep -q 1; then
echo "Creating user ${POSTGRES_USER:-lightrag}..."
su - postgres -c "createuser -s ${POSTGRES_USER:-lightrag}"
else
echo "User ${POSTGRES_USER:-lightrag} already exists"
fi
# 创建数据库(如果不存在)
if ! su - postgres -c "psql -tAc \"SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DATABASE:-lightrag}'\"" | grep -q 1; then
echo "Creating database ${POSTGRES_DATABASE:-lightrag}..."
su - postgres -c "createdb -O ${POSTGRES_USER:-lightrag} ${POSTGRES_DATABASE:-lightrag}"
else
echo "Database ${POSTGRES_DATABASE:-lightrag} already exists"
fi
# 设置密码
echo "Setting password for user ${POSTGRES_USER:-lightrag}..."
su - postgres -c "psql -c \"ALTER USER ${POSTGRES_USER:-lightrag} PASSWORD '${POSTGRES_PASSWORD:-lightrag}';\""
# 配置 pg_hba.conf 以允许密码认证
echo "Configuring PostgreSQL authentication..."
PG_HBA_CONF="/etc/postgresql/16/main/pg_hba.conf"
# 备份原始配置
cp $PG_HBA_CONF $PG_HBA_CONF.backup
# 创建新的 pg_hba.conf 文件,确保 lightrag 用户的规则在前面
cat > $PG_HBA_CONF << EOF
# PostgreSQL Client Authentication Configuration File
# ===================================================
# TYPE DATABASE USER ADDRESS METHOD
# LightRAG user authentication (must be first to take precedence)
local ${POSTGRES_DATABASE:-lightrag} ${POSTGRES_USER:-lightrag} md5
host ${POSTGRES_DATABASE:-lightrag} ${POSTGRES_USER:-lightrag} 127.0.0.1/32 md5
host ${POSTGRES_DATABASE:-lightrag} ${POSTGRES_USER:-lightrag} ::1/128 md5
host ${POSTGRES_DATABASE:-lightrag} ${POSTGRES_USER:-lightrag} 0.0.0.0/0 md5
# Default configurations
local all postgres peer
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
local replication all peer
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256
EOF
# 重启 PostgreSQL 以应用 pg_hba.conf 更改
echo "Restarting PostgreSQL to apply authentication changes..."
service postgresql restart
sleep 5
echo "Installing extensions..."
# 安装 vector 扩展
echo "Installing vector extension..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"CREATE EXTENSION IF NOT EXISTS vector;\""
# 安装 age 扩展
echo "Installing age extension..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"CREATE EXTENSION IF NOT EXISTS age;\""
echo "Configuring AGE extension..."
# 加载 AGE 扩展
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"LOAD 'age';\""
# 设置搜索路径
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"SET search_path = ag_catalog, public;\""
# 创建图(如果不存在)
echo "Creating LightRAG graph..."
if su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -tAc \"SELECT 1 FROM ag_catalog.ag_graph WHERE name='lightrag'\"" | grep -q 1; then
echo "Graph 'lightrag' already exists"
else
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"SELECT ag_catalog.create_graph('lightrag');\""
echo "Graph 'lightrag' created successfully"
fi
echo "Creating LightRAG application tables..."
# 创建 LightRAG 需要的基础表
echo "Creating lightrag_kv_store table..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
CREATE TABLE IF NOT EXISTS lightrag_kv_store (
key VARCHAR PRIMARY KEY,
value JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
\""
echo "Creating lightrag_doc_status table..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
CREATE TABLE IF NOT EXISTS lightrag_doc_status (
doc_id VARCHAR PRIMARY KEY,
status VARCHAR NOT NULL,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
\""
echo "Creating indexes for performance optimization..."
# 为应用表创建索引
echo "Creating KV store indexes..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_kv_store_updated_at ON lightrag_kv_store(updated_at);
\""
echo "Creating doc status indexes..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_doc_status_status ON lightrag_doc_status(status);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_doc_status_updated_at ON lightrag_doc_status(updated_at);
\""
# 等待图表创建完成后创建 AGE 相关索引
echo "Waiting for graph tables to be fully initialized..."
sleep 15
# 验证 AGE 扩展是否正确加载
echo "Verifying AGE extension is working..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"SELECT ag_catalog.create_graph('test_graph'); SELECT ag_catalog.drop_graph('test_graph', true);\"" || echo "AGE extension verification failed, but continuing..."
echo "Creating AGE performance indexes..."
# 检查并创建 Entity 表索引
echo "Creating Entity table indexes..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
DO \$\$
BEGIN
-- Entity 表索引
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'lightrag' AND table_name = 'Entity') THEN
PERFORM 1;
-- 主键索引通常自动创建,这里创建其他有用的索引
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = 'Entity' AND indexname = 'entity_node_id_gin_idx') THEN
CREATE INDEX CONCURRENTLY entity_node_id_gin_idx ON lightrag.\\\"Entity\\\" USING gin(properties);
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Entity table not ready yet, skipping indexes';
END
\$\$;
\""
# 检查并创建 DIRECTED 关系表索引
echo "Creating DIRECTED relationship indexes..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
DO \$\$
BEGIN
-- DIRECTED 关系表索引
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'lightrag' AND table_name = 'DIRECTED') THEN
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = 'DIRECTED' AND indexname = 'directed_eid_idx') THEN
CREATE INDEX CONCURRENTLY directed_eid_idx ON lightrag.\\\"DIRECTED\\\" (end_id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = 'DIRECTED' AND indexname = 'directed_sid_idx') THEN
CREATE INDEX CONCURRENTLY directed_sid_idx ON lightrag.\\\"DIRECTED\\\" (start_id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = 'DIRECTED' AND indexname = 'directed_seid_idx') THEN
CREATE INDEX CONCURRENTLY directed_seid_idx ON lightrag.\\\"DIRECTED\\\" (start_id, end_id);
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'DIRECTED table not ready yet, skipping indexes';
END
\$\$;
\""
# 检查并创建 Edge 表索引
echo "Creating Edge table indexes..."
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
DO \$\$
BEGIN
-- Edge 表索引
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'lightrag' AND table_name = '_ag_label_edge') THEN
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = '_ag_label_edge' AND indexname = 'edge_sid_idx') THEN
CREATE INDEX CONCURRENTLY edge_sid_idx ON lightrag.\\\"_ag_label_edge\\\" (start_id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = '_ag_label_edge' AND indexname = 'edge_eid_idx') THEN
CREATE INDEX CONCURRENTLY edge_eid_idx ON lightrag.\\\"_ag_label_edge\\\" (end_id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'lightrag' AND tablename = '_ag_label_edge' AND indexname = 'edge_seid_idx') THEN
CREATE INDEX CONCURRENTLY edge_seid_idx ON lightrag.\\\"_ag_label_edge\\\" (start_id, end_id);
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Edge table not ready yet, skipping indexes';
END
\$\$;
\""
echo "Setting up permissions..."
# 授权给 lightrag 用户
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${POSTGRES_USER:-lightrag};
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${POSTGRES_USER:-lightrag};
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ${POSTGRES_USER:-lightrag};
GRANT USAGE ON SCHEMA public TO ${POSTGRES_USER:-lightrag};
\""
# 为 AGE 图授权
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
GRANT ALL PRIVILEGES ON SCHEMA lightrag TO ${POSTGRES_USER:-lightrag};
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA lightrag TO ${POSTGRES_USER:-lightrag};
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA lightrag TO ${POSTGRES_USER:-lightrag};
GRANT USAGE ON SCHEMA lightrag TO ${POSTGRES_USER:-lightrag};
\""
echo "Applying PostgreSQL performance configuration..."
# 应用性能配置
su - postgres -c "psql -c \"ALTER SYSTEM SET shared_preload_libraries = 'age';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET max_connections = 200;\""
su - postgres -c "psql -c \"ALTER SYSTEM SET shared_buffers = '256MB';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET effective_cache_size = '1GB';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET maintenance_work_mem = '64MB';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET random_page_cost = 1.1;\""
su - postgres -c "psql -c \"ALTER SYSTEM SET effective_io_concurrency = 200;\""
su - postgres -c "psql -c \"ALTER SYSTEM SET work_mem = '8MB';\""
# 连接和查询超时配置
su - postgres -c "psql -c \"ALTER SYSTEM SET statement_timeout = '300000';\"" # 5分钟查询超时
su - postgres -c "psql -c \"ALTER SYSTEM SET idle_in_transaction_session_timeout = '600000';\"" # 10分钟事务超时
su - postgres -c "psql -c \"ALTER SYSTEM SET tcp_keepalives_idle = '600';\"" # TCP keepalive 设置
su - postgres -c "psql -c \"ALTER SYSTEM SET tcp_keepalives_interval = '30';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET tcp_keepalives_count = '3';\""
# 查询优化配置
su - postgres -c "psql -c \"ALTER SYSTEM SET max_parallel_workers_per_gather = '2';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET max_parallel_workers = '4';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET enable_parallel_hash = 'on';\""
# 内存和查询限制优化
su - postgres -c "psql -c \"ALTER SYSTEM SET temp_buffers = '16MB';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET max_stack_depth = '2MB';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET huge_pages = 'try';\""
# 启用查询计划缓存和日志记录
su - postgres -c "psql -c \"ALTER SYSTEM SET plan_cache_mode = 'auto';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET log_statement = 'all';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET log_duration = 'on';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET log_min_duration_statement = '1000';\"" # 记录超过1秒的查询
su - postgres -c "psql -c \"ALTER SYSTEM SET log_disconnections = 'on';\""
su - postgres -c "psql -c \"ALTER SYSTEM SET log_connections = 'on';\""
# 重新加载配置
su - postgres -c "psql -c \"SELECT pg_reload_conf();\""
echo "Restarting PostgreSQL to apply configuration changes..."
service postgresql restart
sleep 5
echo "Verifying setup..."
# 验证扩展
echo "Installed extensions:"
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"\\dx\""
# 验证图
echo "Available graphs:"
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"SELECT name FROM ag_catalog.ag_graph;\""
# 验证表
echo "Created tables:"
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
SELECT schemaname, tablename
FROM pg_tables
WHERE schemaname IN ('public', 'lightrag')
ORDER BY schemaname, tablename;
\""
# 验证索引
echo "Created indexes:"
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
SELECT schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE schemaname IN ('public', 'lightrag')
ORDER BY schemaname, tablename, indexname;
\""
# 测试连接
echo "Testing database connection..."
export PGPASSWORD="${POSTGRES_PASSWORD:-lightrag}"
if PGPASSWORD="${POSTGRES_PASSWORD:-lightrag}" psql -h localhost -p 5432 -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c "SELECT version();" > /dev/null 2>&1; then
echo "✅ Database connection test successful!"
else
echo "❌ Database connection test failed!"
echo "Checking pg_hba.conf..."
cat $PG_HBA_CONF | head -20
echo "Checking PostgreSQL logs..."
tail -20 /var/log/postgresql/postgresql-16-main.log
fi
echo "✅ PostgreSQL setup completed successfully!"
echo "Database: ${POSTGRES_DATABASE:-lightrag}"
echo "User: ${POSTGRES_USER:-lightrag}"
echo "Extensions: vector, age"
echo "Graph: lightrag"
echo "Tables: lightrag_kv_store, lightrag_doc_status + AGE graph tables"
echo "Connection: postgresql://${POSTGRES_USER:-lightrag}:***@localhost:5432/${POSTGRES_DATABASE:-lightrag}"
# 最终验证数据库完全准备就绪
echo "Performing final readiness check..."
sleep 5
if PGPASSWORD="${POSTGRES_PASSWORD:-lightrag}" psql -h localhost -p 5432 -U ${POSTGRES_USER:-lightrag} -d ${POSTGRES_DATABASE:-lightrag} -c "SELECT ag_catalog.create_graph('final_test'); SELECT ag_catalog.drop_graph('final_test', true);" > /dev/null 2>&1; then
echo "✅ Database is fully ready for LightRAG operations!"
else
echo "⚠️ Database basic operations work, but AGE may need more time to be fully ready"
fi
# 显示数据库统计信息
echo "Database statistics:"
su - postgres -c "psql -d ${POSTGRES_DATABASE:-lightrag} -c \"
SELECT
schemaname,
COUNT(*) as table_count
FROM pg_tables
WHERE schemaname IN ('public', 'lightrag')
GROUP BY schemaname;
\""
# 保持容器运行并显示日志
echo "PostgreSQL is ready for connections on port 5432"
tail -f /var/log/postgresql/postgresql-16-main.log

View File

@ -1,72 +0,0 @@
-- LightRAG PostgreSQL 初始化脚本
-- 基于官方文档建议: https://github.com/HKUDS/LightRAG
-- 创建必要的扩展
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS age;
-- 加载 AGE 扩展
LOAD 'age';
-- 设置搜索路径
SET search_path = ag_catalog, "$user", public;
-- 创建 LightRAG 图(如果不存在)
-- 注意:这里使用 'lightrag' 作为图名,可以根据需要修改
SELECT create_graph('lightrag');
-- 为 AGE 创建索引以提高性能
-- 替换下面的 'lightrag' 为您的实际图名
-- Entity 表索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS entity_p_idx ON lightrag."Entity" (id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS entity_idx_node_id ON lightrag."Entity" (ag_catalog.agtype_access_operator(properties, '"node_id"'::agtype));
CREATE INDEX CONCURRENTLY IF NOT EXISTS entity_node_id_gin_idx ON lightrag."Entity" using gin(properties);
-- Vertex 表索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS vertex_p_idx ON lightrag."_ag_label_vertex" (id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS vertex_idx_node_id ON lightrag."_ag_label_vertex" (ag_catalog.agtype_access_operator(properties, '"node_id"'::agtype));
-- DIRECTED 关系表索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS directed_p_idx ON lightrag."DIRECTED" (id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS directed_eid_idx ON lightrag."DIRECTED" (end_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS directed_sid_idx ON lightrag."DIRECTED" (start_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS directed_seid_idx ON lightrag."DIRECTED" (start_id,end_id);
-- Edge 表索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS edge_p_idx ON lightrag."_ag_label_edge" (id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS edge_sid_idx ON lightrag."_ag_label_edge" (start_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS edge_eid_idx ON lightrag."_ag_label_edge" (end_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS edge_seid_idx ON lightrag."_ag_label_edge" (start_id,end_id);
-- 集群表以提高性能
ALTER TABLE lightrag."DIRECTED" CLUSTER ON directed_sid_idx;
-- 创建 LightRAG 需要的基础表(如果使用 PostgreSQL 作为 KV store
CREATE TABLE IF NOT EXISTS lightrag_kv_store (
key VARCHAR PRIMARY KEY,
value JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 创建索引
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_lightrag_kv_store_updated_at ON lightrag_kv_store(updated_at);
-- 创建文档状态表
CREATE TABLE IF NOT EXISTS lightrag_doc_status (
doc_id VARCHAR PRIMARY KEY,
status VARCHAR NOT NULL,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 授权给 lightrag 用户
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO lightrag;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO lightrag;
GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO lightrag;
-- 为 AGE 图授权
GRANT ALL PRIVILEGES ON SCHEMA lightrag TO lightrag;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA lightrag TO lightrag;

View File

@ -10,7 +10,6 @@ dependencies = [
"dashscope>=1.23.2",
"docx2txt>=0.9",
"fastapi>=0.115.12",
"graspologic>=3.3.0",
"langchain-community>=0.3.22",
"langchain-deepseek>=0.1.3",
"langchain-huggingface>=0.2.0",
@ -42,7 +41,6 @@ dependencies = [
"tavily-python>=0.7.0",
"unstructured>=0.17.2",
"uvicorn[standard]>=0.34.2",
"zhipuai>=2.1.5.20250421",
]
[tool.ruff]
line-length = 210 # 代码最大行宽
@ -59,11 +57,3 @@ dev = [
"ruff>=0.12.1",
"vllm>=0.8.5.post1",
]
# uv 配置 - 使用清华大学镜像源
[tool.uv]
index-url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
extra-index-url = [
"https://mirrors.aliyun.com/pypi/simple/",
"https://pypi.douban.com/simple/"
]

View File

@ -1,8 +1,4 @@
import json
import os
import asyncio
import traceback
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, Query, HTTPException, Depends
from server.utils.auth_middleware import get_admin_user
from server.models.user_model import User
@ -23,13 +19,13 @@ async def get_subgraph(
):
"""
使用 LightRAG 原生方法获取知识图谱子图
Args:
db_id: LightRAG 数据库实例ID
node_label: 节点标签用于查找起始节点使用 "*" 获取全图
max_depth: 子图的最大深度
max_nodes: 返回的最大节点数量
Returns:
包含节点和边的知识图谱数据
"""
@ -95,10 +91,10 @@ async def get_graph_labels(
):
"""
获取知识图谱中的所有标签
Args:
db_id: LightRAG 数据库实例ID
Returns:
图谱中所有可用的标签列表
"""
@ -132,7 +128,7 @@ async def get_available_databases(
):
"""
获取所有可用的 LightRAG 数据库
Returns:
可用的数据库列表
"""

View File

@ -7,7 +7,7 @@ from neo4j import GraphDatabase as GD
from neo4j import Query
from src import config
from src.models.embedding import get_embedding_model
from src.models import select_embedding_model
from src.utils import logger
warnings.filterwarnings("ignore", category=UserWarning)
@ -22,7 +22,7 @@ class GraphDatabase:
self.status = "closed"
self.kgdb_name = "neo4j"
self.embed_model_name = os.getenv("GRAPH_EMBED_MODEL_NAME") or "siliconflow/BAAI/bge-m3"
self.embed_model = get_embedding_model(self.embed_model_name)
self.embed_model = select_embedding_model(self.embed_model_name)
self.work_dir = os.path.join(config.save_dir, "knowledge_graph", self.kgdb_name)
os.makedirs(self.work_dir, exist_ok=True)
@ -590,9 +590,11 @@ class GraphDatabase:
source_id = source.element_id
target_id = target.element_id
assert node_dict is not None, "node_dict is required"
source_name = node_dict[source_id]["name"] if source_name is None else source_name
target_name = node_dict[target_id]["name"] if target_name is None else target_name
# 如果没有提供 source_name 或 target_name则需要 node_dict
if source_name is None or target_name is None:
assert node_dict is not None, "node_dict is required when source_name or target_name is None"
source_name = node_dict[source_id]["name"] if source_name is None else source_name
target_name = node_dict[target_id]["name"] if target_name is None else target_name
relationship_type = relationship._properties.get("type", "unknown")
if relationship_type == "unknown":

View File

@ -92,11 +92,12 @@ class LightRagBasedKB:
# 使用配置的 LLM 和 embedding 函数
rag = LightRAG(
working_dir=working_dir,
workspace=db_id,
llm_model_func=self._get_llm_func(llm_info),
embedding_func=self._get_embedding_func(embed_info),
vector_storage="MilvusVectorDBStorage",
kv_storage="JsonKVStorage",
graph_storage="PGGraphStorage",
graph_storage="Neo4JStorage",
doc_status_storage="JsonDocStatusStorage",
log_file_path=os.path.join(self.work_dir, db_id, "lightrag.log"),
)
@ -127,16 +128,16 @@ class LightRagBasedKB:
# provider_info = config.model_names[llm_info.get("provider")]
# api_key = os.getenv(provider_info.get("env")[0] or "OPENAI_API_KEY") or "no_api_key"
# base_url = get_docker_safe_url(provider_info.get("base_url", "http://localhost:8081/v1"))
from src.models import get_custom_model
llm_info = get_custom_model("qwen3:32b-RFnC")
from src.models import select_model
model = select_model("dashscope", "qwen-max-latest")
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
return await openai_complete_if_cache(
llm_info.get("name", "qwen3-1.7b"),
prompt,
model=model.model_name,
prompt=prompt,
system_prompt=system_prompt,
history_messages=history_messages,
api_key=llm_info.get("api_key"),
base_url=get_docker_safe_url(llm_info.get("api_base")),
api_key=model.api_key,
base_url=model.base_url,
extra_body={"enable_thinking": False},
**kwargs,
)
@ -144,16 +145,16 @@ class LightRagBasedKB:
def _get_embedding_func(self, embed_info: dict):
"""获取 embedding 函数"""
api_key = os.getenv(embed_info.get("api_key", "OPENAI_API_KEY")) or "no_api_key"
base_url = embed_info.get("base_url", "http://localhost:8081/v1").replace("/embeddings", "")
from src.models import select_embedding_model
model = select_embedding_model("siliconflow/BAAI/bge-m3")
return EmbeddingFunc(
embedding_dim=embed_info.get("dimension") or 1024,
embedding_dim=model.dimension,
max_token_size=4096,
func=lambda texts: openai_embed(
texts=texts,
model=embed_info.get("model_name") or "Qwen3-Embedding-0.6B",
api_key=api_key,
base_url=get_docker_safe_url(base_url)
model=model.model,
api_key=model.api_key,
base_url=model.base_url.replace("/embeddings", ""),
),
)
@ -335,7 +336,8 @@ class LightRagBasedKB:
# 根据内容类型处理内容
if content_type == "file":
markdown_content = await self._process_file_to_markdown(item, params=params)
logger.info(f"Markdown content: {markdown_content[:100].replace('\n', ' ')}...")
markdown_content_lines = markdown_content[:100].replace('\n', ' ')
logger.info(f"Markdown content: {markdown_content_lines}...")
else: # URL
markdown_content = await self._process_url_to_markdown(item, params=params)

View File

@ -4,6 +4,7 @@ import traceback
from src import config
from src.utils.logging_config import logger
from src.models.chat_model import OpenAIBase
from src.models.embedding import OllamaEmbedding, OtherEmbedding
def select_model(model_provider, model_name=None):
"""根据模型提供者选择模型"""
@ -38,6 +39,24 @@ def select_model(model_provider, model_name=None):
except Exception as e:
raise ValueError(f"Model provider {model_provider} load failed, {e} \n {traceback.format_exc()}")
def select_embedding_model(model_id):
provider, model_name = model_id.split('/', 1) if model_id else ("", "")
support_embed_models = config.embed_model_names.keys()
assert model_id in support_embed_models, f"Unsupported embed model: {model_id}, only support {support_embed_models}"
logger.debug(f"Loading embedding model {model_id}")
if provider == "local":
raise ValueError("Local embedding model is not supported, please use other embedding models")
elif provider == "ollama":
model = OllamaEmbedding(model_id)
else:
model = OtherEmbedding(model_id)
return model
def get_custom_model(model_id):
"""return model_info"""
assert config.custom_models is not None, "custom_models is not set"

View File

@ -3,7 +3,6 @@ import json
import requests
import asyncio
from abc import abstractmethod
from zhipuai import ZhipuAI
from langchain_huggingface import HuggingFaceEmbeddings
from src import config
@ -19,6 +18,7 @@ class BaseEmbeddingModel:
self.model = self.info["name"]
self.dimension = self.info.get("dimension", None)
self.url = get_docker_safe_url(self.info["base_url"])
self.base_url = get_docker_safe_url(self.info["base_url"])
self.api_key = os.getenv(self.info["api_key"], self.info["api_key"])
@abstractmethod
@ -110,19 +110,3 @@ class OtherEmbedding(BaseEmbeddingModel):
"model": self.model,
"input": message,
}
def get_embedding_model(model_id):
provider, model_name = model_id.split('/', 1) if model_id else ("", "")
support_embed_models = config.embed_model_names.keys()
assert model_id in support_embed_models, f"Unsupported embed model: {model_id}, only support {support_embed_models}"
logger.debug(f"Loading embedding model {model_id}")
if provider == "local":
raise ValueError("Local embedding model is not supported, please use other embedding models")
elif provider == "ollama":
model = OllamaEmbedding(model_id)
else:
model = OtherEmbedding(model_id)
return model

View File

@ -47,19 +47,19 @@ export const useGraphStore = defineStore('graph', {
// 首先尝试通过dynamicId匹配
let foundEdge = state.rawGraph.edges.find(edge => edge.dynamicId === state.selectedEdge)
if (foundEdge) return foundEdge
// 如果没找到尝试通过原始ID匹配
foundEdge = state.rawGraph.edges.find(edge => edge.id === state.selectedEdge)
if (foundEdge) return foundEdge
// 最后尝试通过source->target格式匹配
const [source, target] = state.selectedEdge.split('->')
if (source && target) {
foundEdge = state.rawGraph.edges.find(edge =>
foundEdge = state.rawGraph.edges.find(edge =>
edge.source === source.trim() && edge.target === target.trim()
)
}
return foundEdge || null
},
@ -200,7 +200,7 @@ export const useGraphStore = defineStore('graph', {
const nodeId = String(node.id)
const labels = node.labels || [node.entity_type || 'unknown']
const entityType = node.entity_type || labels[0] || 'unknown'
const processedNode = {
id: nodeId,
labels: Array.isArray(labels) ? labels.map(String) : [String(labels)],
@ -246,10 +246,10 @@ export const useGraphStore = defineStore('graph', {
const sourceId = String(edge.source)
const targetId = String(edge.target)
const dynamicId = `${sourceId}-${targetId}-${index}`
// 适配新的LightRAG API格式
const weight = Number(edge.properties?.weight || edge.weight || 1.0)
const processedEdge = {
id: String(edge.id),
source: sourceId,
@ -345,7 +345,7 @@ export const useGraphStore = defineStore('graph', {
try {
// 使用动态ID作为Sigma边ID避免重复
const sigmaEdgeId = edge.dynamicId || `${edge.source}->${edge.target}`
// 检查是否已存在相同的边
if (!sigmaGraph.hasEdge(sigmaEdgeId)) {
sigmaGraph.addEdgeWithKey(sigmaEdgeId, String(edge.source), String(edge.target), edgeAttributes)

View File

@ -289,20 +289,6 @@
<a-input-number size="small" v-model:value="meta.top_k" :min="1" :max="100" />
</div>
</div>
<div class="params-group">
<div class="params-item">
<p>片段最大Token数</p>
<a-input-number size="small" v-model:value="meta.max_token_for_text_unit" :min="1" :max="4000" />
</div>
<div class="params-item">
<p>关系描述最大Token数</p>
<a-input-number size="small" v-model:value="meta.max_token_for_global_context" :min="1" :max="4000" />
</div>
<div class="params-item">
<p>实体描述最大Token数</p>
<a-input-number size="small" v-model:value="meta.max_token_for_local_context" :min="1" :max="4000" />
</div>
</div>
</div>
</div>
<div class="sider-bottom">
@ -314,8 +300,8 @@
<a-tab-pane key="knowledge-graph" force-render>
<template #tab><span><Waypoints size="14" class="mr-3 bn-1px" />知识图谱开发中</span></template>
<div class="knowledge-graph-container db-tab-container">
<KnowledgeGraphViewer
:initial-database-id="databaseId"
<KnowledgeGraphViewer
:initial-database-id="databaseId"
:hide-db-selector="true"
/>
</div>
@ -396,9 +382,6 @@ const meta = reactive({
only_need_context: true,
only_need_prompt: false,
top_k: 10,
max_token_for_text_unit: 4000,
max_token_for_global_context: 4000,
max_token_for_local_context: 4000,
});
const enable_ocr_options = ref([
@ -1541,10 +1524,10 @@ const toggleAutoRefresh = (checked) => {
.knowledge-graph-container {
height: calc(100vh - 200px);
:deep(.knowledge-graph-viewer) {
height: 100%;
.sigma-container {
height: calc(100% - 80px);
}

View File

@ -231,9 +231,9 @@ const onSearch = () => {
// return
// }
if (!confirm(`构建图数据库时向量模型为 ${graphInfo?.value?.embed_model_name},当前向量模型为 ${cur_embed_model.value},是否继续查询?`)) {
return
}
// if (!confirm(` ${graphInfo?.value?.embed_model_name} ${cur_embed_model.value}`)) {
// return
// }
}
if (!state.searchInput) {