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_USERNAME=${NEO4J_USERNAME:-neo4j}
- NEO4J_PASSWORD=${NEO4J_PASSWORD:-0123456789} - NEO4J_PASSWORD=${NEO4J_PASSWORD:-0123456789}
- MILVUS_URI=${MILVUS_URI:-http://milvus:19530} - MILVUS_URI=${MILVUS_URI:-http://milvus:19530}
- MILVUS_TOKEN=${MILVUS_TOKEN:-}
- MINERU_OCR_URI=${MINERU_OCR_URI:-http://mineru:30000} - MINERU_OCR_URI=${MINERU_OCR_URI:-http://mineru:30000}
- PADDLEX_URI=${PADDLEX_URI:-http://paddlex-ocr:8080} - PADDLEX_URI=${PADDLEX_URI:-http://paddlex-ocr:8080}
- MINIO_URI=${MINIO_URI:-http://milvus-minio:9000} - 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 - MODEL_DIR=/models
- RUNNING_IN_DOCKER=true - 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 command: uv run uvicorn server.main:app --host 0.0.0.0 --port 5050 --reload
restart: unless-stopped restart: unless-stopped
healthcheck: 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 interval: 30s
timeout: 15s timeout: 15s
retries: 8 retries: 8
start_period: 180s start_period: 180s
depends_on: depends_on:
postgres: # postgres:
condition: service_healthy # condition: service_healthy
milvus: milvus:
condition: service_healthy condition: service_healthy
minio: minio:
@ -80,31 +73,31 @@ services:
# PostgreSQL 服务 - 基于官方推荐的 shangor/postgres-for-rag 镜像 # PostgreSQL 服务 - 基于官方推荐的 shangor/postgres-for-rag 镜像
# 提供 KV store, VectorDB (pgvector) 和 GraphDB (Apache AGE) 功能 # 提供 KV store, VectorDB (pgvector) 和 GraphDB (Apache AGE) 功能
postgres: # postgres:
image: shangor/postgres-for-rag:v1.0 # image: shangor/postgres-for-rag:v1.0
container_name: postgres-lightrag # container_name: postgres-lightrag
environment: # environment:
- POSTGRES_DATABASE=${POSTGRES_DATABASE:-lightrag} # - POSTGRES_DATABASE=${POSTGRES_DATABASE:-lightrag}
- POSTGRES_USER=${POSTGRES_USER:-lightrag} # - POSTGRES_USER=${POSTGRES_USER:-lightrag}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-lightrag} # - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-lightrag}
- POSTGRES_HOST_AUTH_METHOD=md5 # - POSTGRES_HOST_AUTH_METHOD=md5
# 启用必要的扩展 # # 启用必要的扩展
- POSTGRES_INITDB_ARGS=--auth-host=md5 # - POSTGRES_INITDB_ARGS=--auth-host=md5
volumes: # volumes:
- ./docker/volumes/postgres/data:/var/lib/postgresql/data # - ./docker/volumes/postgres/data:/var/lib/postgresql/data
- ./docker/init-postgres.sh:/init-postgres.sh # - ./docker/init-postgres.sh:/init-postgres.sh
ports: # ports:
- "5432:5432" # - "5432:5432"
healthcheck: # 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'"] # 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 # interval: 30s
timeout: 15s # timeout: 15s
retries: 15 # retries: 15
start_period: 120s # start_period: 120s
networks: # networks:
- app-network # - app-network
restart: unless-stopped # restart: unless-stopped
command: ["bash", "/init-postgres.sh"] # command: ["bash", "/init-postgres.sh"]
graph: graph:
image: neo4j:5.26 image: neo4j:5.26

View File

@ -29,6 +29,11 @@ COPY ../pyproject.toml /app/pyproject.toml
COPY ../.python-version /app/.python-version 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 \ RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-install-project uv sync --no-install-project
@ -39,3 +44,6 @@ COPY ../server /app/server
# 同步项目 # 同步项目
RUN --mount=type=cache,target=/root/.cache/uv \ 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", "dashscope>=1.23.2",
"docx2txt>=0.9", "docx2txt>=0.9",
"fastapi>=0.115.12", "fastapi>=0.115.12",
"graspologic>=3.3.0",
"langchain-community>=0.3.22", "langchain-community>=0.3.22",
"langchain-deepseek>=0.1.3", "langchain-deepseek>=0.1.3",
"langchain-huggingface>=0.2.0", "langchain-huggingface>=0.2.0",
@ -42,7 +41,6 @@ dependencies = [
"tavily-python>=0.7.0", "tavily-python>=0.7.0",
"unstructured>=0.17.2", "unstructured>=0.17.2",
"uvicorn[standard]>=0.34.2", "uvicorn[standard]>=0.34.2",
"zhipuai>=2.1.5.20250421",
] ]
[tool.ruff] [tool.ruff]
line-length = 210 # 代码最大行宽 line-length = 210 # 代码最大行宽
@ -59,11 +57,3 @@ dev = [
"ruff>=0.12.1", "ruff>=0.12.1",
"vllm>=0.8.5.post1", "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 import traceback
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, Query, HTTPException, Depends from fastapi import APIRouter, Query, HTTPException, Depends
from server.utils.auth_middleware import get_admin_user from server.utils.auth_middleware import get_admin_user
from server.models.user_model import User from server.models.user_model import User

View File

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

View File

@ -92,11 +92,12 @@ class LightRagBasedKB:
# 使用配置的 LLM 和 embedding 函数 # 使用配置的 LLM 和 embedding 函数
rag = LightRAG( rag = LightRAG(
working_dir=working_dir, working_dir=working_dir,
workspace=db_id,
llm_model_func=self._get_llm_func(llm_info), llm_model_func=self._get_llm_func(llm_info),
embedding_func=self._get_embedding_func(embed_info), embedding_func=self._get_embedding_func(embed_info),
vector_storage="MilvusVectorDBStorage", vector_storage="MilvusVectorDBStorage",
kv_storage="JsonKVStorage", kv_storage="JsonKVStorage",
graph_storage="PGGraphStorage", graph_storage="Neo4JStorage",
doc_status_storage="JsonDocStatusStorage", doc_status_storage="JsonDocStatusStorage",
log_file_path=os.path.join(self.work_dir, db_id, "lightrag.log"), 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")] # 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" # 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")) # base_url = get_docker_safe_url(provider_info.get("base_url", "http://localhost:8081/v1"))
from src.models import get_custom_model from src.models import select_model
llm_info = get_custom_model("qwen3:32b-RFnC") model = select_model("dashscope", "qwen-max-latest")
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs): async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
return await openai_complete_if_cache( return await openai_complete_if_cache(
llm_info.get("name", "qwen3-1.7b"), model=model.model_name,
prompt, prompt=prompt,
system_prompt=system_prompt, system_prompt=system_prompt,
history_messages=history_messages, history_messages=history_messages,
api_key=llm_info.get("api_key"), api_key=model.api_key,
base_url=get_docker_safe_url(llm_info.get("api_base")), base_url=model.base_url,
extra_body={"enable_thinking": False}, extra_body={"enable_thinking": False},
**kwargs, **kwargs,
) )
@ -144,16 +145,16 @@ class LightRagBasedKB:
def _get_embedding_func(self, embed_info: dict): def _get_embedding_func(self, embed_info: dict):
"""获取 embedding 函数""" """获取 embedding 函数"""
api_key = os.getenv(embed_info.get("api_key", "OPENAI_API_KEY")) or "no_api_key" from src.models import select_embedding_model
base_url = embed_info.get("base_url", "http://localhost:8081/v1").replace("/embeddings", "") model = select_embedding_model("siliconflow/BAAI/bge-m3")
return EmbeddingFunc( return EmbeddingFunc(
embedding_dim=embed_info.get("dimension") or 1024, embedding_dim=model.dimension,
max_token_size=4096, max_token_size=4096,
func=lambda texts: openai_embed( func=lambda texts: openai_embed(
texts=texts, texts=texts,
model=embed_info.get("model_name") or "Qwen3-Embedding-0.6B", model=model.model,
api_key=api_key, api_key=model.api_key,
base_url=get_docker_safe_url(base_url) base_url=model.base_url.replace("/embeddings", ""),
), ),
) )
@ -335,7 +336,8 @@ class LightRagBasedKB:
# 根据内容类型处理内容 # 根据内容类型处理内容
if content_type == "file": if content_type == "file":
markdown_content = await self._process_file_to_markdown(item, params=params) 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 else: # URL
markdown_content = await self._process_url_to_markdown(item, params=params) 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 import config
from src.utils.logging_config import logger from src.utils.logging_config import logger
from src.models.chat_model import OpenAIBase from src.models.chat_model import OpenAIBase
from src.models.embedding import OllamaEmbedding, OtherEmbedding
def select_model(model_provider, model_name=None): def select_model(model_provider, model_name=None):
"""根据模型提供者选择模型""" """根据模型提供者选择模型"""
@ -38,6 +39,24 @@ def select_model(model_provider, model_name=None):
except Exception as e: except Exception as e:
raise ValueError(f"Model provider {model_provider} load failed, {e} \n {traceback.format_exc()}") 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): def get_custom_model(model_id):
"""return model_info""" """return model_info"""
assert config.custom_models is not None, "custom_models is not set" assert config.custom_models is not None, "custom_models is not set"

View File

@ -3,7 +3,6 @@ import json
import requests import requests
import asyncio import asyncio
from abc import abstractmethod from abc import abstractmethod
from zhipuai import ZhipuAI
from langchain_huggingface import HuggingFaceEmbeddings from langchain_huggingface import HuggingFaceEmbeddings
from src import config from src import config
@ -19,6 +18,7 @@ class BaseEmbeddingModel:
self.model = self.info["name"] self.model = self.info["name"]
self.dimension = self.info.get("dimension", None) self.dimension = self.info.get("dimension", None)
self.url = get_docker_safe_url(self.info["base_url"]) 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"]) self.api_key = os.getenv(self.info["api_key"], self.info["api_key"])
@abstractmethod @abstractmethod
@ -110,19 +110,3 @@ class OtherEmbedding(BaseEmbeddingModel):
"model": self.model, "model": self.model,
"input": message, "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

@ -289,20 +289,6 @@
<a-input-number size="small" v-model:value="meta.top_k" :min="1" :max="100" /> <a-input-number size="small" v-model:value="meta.top_k" :min="1" :max="100" />
</div> </div>
</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> </div>
<div class="sider-bottom"> <div class="sider-bottom">
@ -396,9 +382,6 @@ const meta = reactive({
only_need_context: true, only_need_context: true,
only_need_prompt: false, only_need_prompt: false,
top_k: 10, 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([ const enable_ocr_options = ref([

View File

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