所有运行时配置通过 src/config.py 的 Settings 加载,源头是 .env 文件。本文按域解读 .env.example 中的配置项,标注必填与典型值。
不要硬编码密钥,不要把真实
.env提交到仓库。
| 分组 | 关键变量前缀 | 何时关心 |
|---|---|---|
| 应用 | APP_*, LOG_LEVEL, LOG_* |
始终 |
| 数据库 | DB_* |
始终 |
| 缓存 | REDIS_* |
始终 |
| 安全 | API_KEY_ENCRYPTION_SECRET |
始终(必须与 Java 管理端一致) |
| 系统级 LLM | SYSTEM_LLM_* |
始终(兜底 LLM 调用) |
| Markdown 增强 | MARKDOWN_PARSER_* |
调整解析增强行为时 |
| 分块策略 | CHUNKING_* |
调整分块参数时 |
| 向量存储 | VECTOR_STORE_TYPE, QDRANT_*, ES_*, CHUNK_INDEX_*, SPARSE_VECTOR_* |
始终(选择 Qdrant 或 ES,并配置稀疏向量) |
| 对象存储 | STORAGE_TYPE, MINIO_*, LOCAL_DOCS_PATH |
始终 |
| 解析临时目录 | PARSE_TEMP_DIR |
始终(流式下载落盘目录) |
| PDF 解析 | PDF_PARSER_*, MINERU_*, DOCLING_* |
处理 PDF 时 |
| MQ | MQ_VENDOR, KAFKA_*, RABBITMQ_*, *_TOPIC |
始终 |
| CORS | CORS_ORIGINS |
前端跨域时 |
启动前必须设置以下项(无默认或默认值不可用):
| 变量 | 说明 |
|---|---|
DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME |
MySQL 连接 |
REDIS_HOST / REDIS_PORT |
Redis 连接 |
API_KEY_ENCRYPTION_SECRET |
API Key 加密 Secret,必须与 Java 管理端一致;64 位 hex,解码后 32 字节,用于 AES-256-GCM |
SYSTEM_LLM_PROVIDER / SYSTEM_LLM_API_KEY / SYSTEM_LLM_API_BASE |
系统级兜底 LLM |
KAFKA_BOOTSTRAP_SERVERS 等(若 MQ_VENDOR=kafka) |
Kafka 接入信息 |
MINIO_*(若 STORAGE_TYPE=minio) |
对象存储凭据 |
QDRANT_HOST 或 ES_HOST(取决于 VECTOR_STORE_TYPE) |
向量存储 |
| 开关 | 默认 | 含义 |
|---|---|---|
MQ_VENDOR |
kafka |
切换 Kafka / RabbitMQ |
VECTOR_STORE_TYPE |
qdrant |
切换 Qdrant / Elasticsearch |
SPARSE_VECTOR_ENABLED |
true |
是否在向量化阶段同步生成 BGE-M3 稀疏向量;关闭后保持旧 dense-only 语义 |
STORAGE_TYPE |
minio |
切换 MinIO / 本地存储 |
PARSE_TEMP_DIR |
/tmp/tolink-rag-parse |
解析任务源文件临时落盘目录。流式下载在此创建临时文件;解析为 markdown 后立即清理;worker 启动时清空兜底。不预设最小容量,沿用部署机系统盘大小;写满会归类为 TEMP_DISK_FULL 错误码。扩消费者时容量需要 ≥ 单文件上限 × 并发数 |
PDF_PARSER_BACKEND |
mineru |
PDF 解析后端:auto / mineru / opendataloader / naive |
PDF_PARSER_FALLBACKS |
空 | 逗号分隔回退链,空表示不回退 |
PDF_IMAGE_UPLOAD_ASYNC |
true |
PDF 图片是否异步上传,关闭后主链路同步等待 |
INIT_KAFKA_TOPICS_ON_STARTUP |
false |
应用启动时是否自动建 topic,生产建议保持 false |
TOLINK_RUN_REAL_VECTOR_STORAGE_TESTS |
false |
是否运行真实 MySQL+Qdrant 集成测试 |
MARKDOWN_PARSER_ENABLE_TABLE_ENHANCEMENT |
true |
是否启用表格 LLM 增强 |
MARKDOWN_PARSER_ENABLE_IMAGE_ENHANCEMENT |
true |
是否启用图片 LLM 增强 |
MARKDOWN_PARSER_VISION_CONCURRENCY |
24 |
图片视觉增强最大并发数,可降为 16 / 8 / 1 控制限流风险 |
CHUNKING_ENABLE_ADVANCED_PIPELINE |
true |
是否启用进阶分块流水线 |
注:ES 入库失败即终态,无 ES 内部自动重试配置。原
ES_INDEXING_MAX_RETRY已移除(用户侧重试由document_parse_pipeline.retry_count记录,触发路径待后续需求接线)。
日志系统基于 Loguru,统一在 src/utils/logger.py 配置。运行时始终输出到 stdout(容器 / 本地通用);开启文件落盘后,额外按 Java 端约定写入按日期归档的本地文件。
| 变量 | 默认 | 含义 |
|---|---|---|
LOG_LEVEL |
INFO |
控制台与全量日志文件的级别下限;ERROR 文件固定只收 ERROR 及以上,不受此项影响 |
LOG_FILE_ENABLED |
true |
是否写本地文件。纯容器环境若靠 docker logs 采集,可设 false 只保留 stdout |
LOG_DIR |
logs |
日志根目录 |
LOG_SERVICE_NAME |
tolink-service |
日志文件名前缀 |
LOG_RETENTION_DAYS |
7 |
日志保留天数,超过自动清理旧日期目录 |
落盘结构(每天 0 点切分,按日期目录归档,对齐 Java 端):
logs/
├── 2026-06-07/
│ ├── tolink-service-<pid>.log # 当天全量(>= LOG_LEVEL)
│ └── tolink-service-error-<pid>.log # 当天 ERROR 及以上
├── 2026-06-08/
│ ├── tolink-service-<pid>.log
│ └── tolink-service-error-<pid>.log
└── ...
实现要点:文件名中的日期由 Loguru 在创建新文件时求值,配合 rotation="00:00" 每天 0 点切分,自然落入新的日期目录;写入开启 enqueue 队列,异步刷盘不阻塞业务。
保留清理按 日期目录整体删除:删除 <LOG_DIR>/<YYYY-MM-DD>/ 中日期早于 LOG_RETENTION_DAYS(当前 7 天)的目录,在进程启动时与每天 0 点切分时各执行一次。之所以不用 Loguru 自带 retention:日志文件名带 PID,Loguru 的清理 glob 会带上字面 PID,只能清掉当前进程写的文件,进程重启(部署 / 崩溃 / 扩缩容)后旧 PID 的日期目录无人回收、会无限堆积。按日期目录清理与 PID 无关,重启与多 worker 场景都能正确回收(非 YYYY-MM-DD 命名的目录不受影响)。
文件名带 PID 后缀(<pid> 为进程号):多 worker(gunicorn)部署时各进程写各自文件,避免多进程共写同一文件导致的写入交错与 0 点切分/清理竞争;单进程部署同样安全,仅文件名多一段 PID。每行日志同时携带进程号(控制台格式的 {process} 字段),多 worker 共写 stdout 时也能区分来源进程。
注意:PID 在
setup_logger()调用时求值。gunicorn 若启用--preload,需在post_fork钩子里重新调用setup_logger(),否则各 worker 会复用 master 的 PID 写到同一文件。
项目自身代码统一用 Loguru(from src.utils.logger import logger),但 uvicorn、SQLAlchemy、kafka、transformers 等第三方库以及少数遗留模块仍走 Python 标准库 logging。src/utils/logger.py 通过 InterceptHandler 把标准库 logging 全量桥接进 Loguru,使运行时只有一条输出管道:所有日志(无论来自 Loguru 还是标准库)都进同一份日期文件、同一种格式、由 LOG_LEVEL 统一过滤。
要点:
- 日志在 src/main.py 顶部显式初始化(
setup_logger()),不依赖 import 副作用;放在其余模块导入之前,确保导入期日志也被捕获。 uvicorn/uvicorn.access/uvicorn.error/gunicorn等自带 handler 的 logger 会被显式接管(清空其 handler、打开 propagate),其访问日志与未捕获异常的 500 堆栈因此也进入日期文件。uvicorn.run传log_config=None,不再安装 uvicorn 自己的日志配置。- 异常堆栈开启
backtrace、关闭diagnose:保留完整调用栈,但不展开局部变量值,避免在生产日志里泄露密钥 / PII。 - 全局未捕获异常由 src/main.py 的
Exceptionhandler 兜底:带请求方法 / 路径记录完整堆栈,再返回统一 500 错误体{code, message, data}。 - 应用关闭(lifespan shutdown)时
await logger.complete(),等待enqueue队列里的日志全部落盘,避免退出丢尾部日志。 - 约定:应用代码新增日志一律用 Loguru;遗留的标准库 logging 会被自动桥接,无需改写,但不要再新增标准库 logging 用法。
消费框架对业务回调异常做有限退避重试 + 死信兜底;详细行为见 mq.md §4.1。
| 变量 | 默认 | 含义 |
|---|---|---|
MQ_MAX_RETRIES |
3 |
业务回调抛 RetriableError 子类时最多重试次数;超限后进死信 |
MQ_RETRY_BACKOFF_SECONDS |
1.0 |
重试之间固定退避秒数;单条消息最长阻塞 ≈ 此值 × MQ_MAX_RETRIES |
MQ_DLQ_SUFFIX |
.DLT |
死信目标命名后缀(原 topic / queue + 后缀) |
死信兜底恒启用,不提供关闭开关。死信目标在应用启动时由
ensure_topics()(Kafka)或RabbitMQReceiver.start()(RabbitMQ)幂等创建。
应用启动时需要这些 topic 存在或被自动创建(见 mq_integration.md):
| 变量 | 默认值 | 用途 |
|---|---|---|
PARSE_TASK_TOPIC |
tolink-document-pares |
解析任务入队 |
PARSE_RESULT_TOPIC |
tolink.rag.parse_result |
解析终态通知 |
注意默认值中的
pares(非parse)是历史遗留,业务方对接时以实际配置为准。
| 变量 | 默认 | 调整方向 |
|---|---|---|
CHUNKING_MIN_CANDIDATE_CHUNK_TOKENS |
128 | 第一阶段候选边界粗分片软下限,调大可减少短 chunk |
CHUNKING_MIN_CHUNK_TOKENS |
150 | 短文档可减小 |
CHUNKING_MAX_CHUNK_TOKENS |
512 | 长上下文模型可加大 |
CHUNKING_OVERLAP_TOKENS |
64 | overlap token 数,范围 0..64;0 表示关闭 |
CHUNKING_HEADING_BREAK_LEVEL |
3 | 提升结构敏感性时减小 |
CHUNKING_SEMANTIC_PERCENTILE |
95 | 调整语义边界严格度 |
CHUNKING_SEMANTIC_UNIT |
sentence |
语义相似度计算粒度:sentence / paragraph |
CHUNKING_EMBED_BATCH_SIZE |
32 | 受向量服务并发上限约束 |
详细分块策略见 chunking.md。
稀疏向量首期使用本地 BAAI/bge-m3,与稠密向量在同一个 chunk 向量化阶段执行。模型输入是 chunk 原文,不使用 ES 分词结果。
| 变量 | 默认 | 说明 |
|---|---|---|
SPARSE_VECTOR_ENABLED |
true |
是否启用稀疏向量;关闭后只执行旧稠密向量流程 |
SPARSE_VECTOR_PROVIDER |
bge_m3 |
稀疏向量提供方;首期仅支持 bge_m3 |
SPARSE_VECTOR_MODEL_NAME |
BAAI/bge-m3 |
Hugging Face 模型名或本地模型目录 |
SPARSE_VECTOR_MODEL_CACHE_DIR |
空 | 模型缓存目录,空值使用默认 Hugging Face 缓存 |
SPARSE_VECTOR_LOCAL_FILES_ONLY |
false |
是否只使用本地已有模型文件 |
SPARSE_VECTOR_DEVICE |
auto |
推理设备:auto / cpu / cuda / cuda:n;CPU 固定 fp32,CUDA 固定 fp16 |
SPARSE_VECTOR_BATCH_SIZE |
12 |
BGE-M3 稀疏编码批大小 |
SPARSE_VECTOR_MAX_LENGTH |
8192 |
输入文本最大 token 长度 |
SPARSE_VECTOR_QDRANT_VECTOR_NAME |
sparse_text |
Qdrant named sparse vector 名称 |
SPARSE_VECTOR_TOP_K |
256 |
每条稀疏向量最多保留的非零 token 数;0 表示不截断 |
SPARSE_VECTOR_MIN_WEIGHT |
0.0 |
过滤低权重 token 的阈值 |
TOLINK_RUN_REAL_SPARSE_VECTOR_TESTS |
false |
是否运行真实 BGE-M3 smoke 测试 |
不再提供 SPARSE_VECTOR_USE_FP16 配置。推理精度只由 SPARSE_VECTOR_DEVICE 决定:CPU 使用 fp32,CUDA 使用 fp16。
稀疏向量首期使用本地 BAAI/bge-m3,与稠密向量在同一个 chunk 向量化阶段执行。模型输入是 chunk 原文,不使用 ES 分词结果。
| 变量 | 默认 | 说明 |
|---|---|---|
SPARSE_VECTOR_ENABLED |
true |
是否启用稀疏向量;关闭后只执行旧稠密向量流程 |
SPARSE_VECTOR_PROVIDER |
bge_m3 |
稀疏向量提供方;首期仅支持 bge_m3 |
SPARSE_VECTOR_MODEL_NAME |
BAAI/bge-m3 |
Hugging Face 模型名或本地模型目录 |
SPARSE_VECTOR_MODEL_CACHE_DIR |
空 | 模型缓存目录,空值使用默认 Hugging Face 缓存 |
SPARSE_VECTOR_LOCAL_FILES_ONLY |
false |
是否只使用本地已有模型文件 |
SPARSE_VECTOR_DEVICE |
auto |
推理设备:auto / cpu / cuda / cuda:n;CPU 固定 fp32,CUDA 固定 fp16 |
SPARSE_VECTOR_BATCH_SIZE |
12 |
BGE-M3 稀疏编码批大小 |
SPARSE_VECTOR_MAX_LENGTH |
8192 |
输入文本最大 token 长度 |
SPARSE_VECTOR_QDRANT_VECTOR_NAME |
sparse_text |
Qdrant named sparse vector 名称 |
SPARSE_VECTOR_TOP_K |
256 |
每条稀疏向量最多保留的非零 token 数;0 表示不截断 |
SPARSE_VECTOR_MIN_WEIGHT |
0.0 |
过滤低权重 token 的阈值 |
TOLINK_RUN_REAL_SPARSE_VECTOR_TESTS |
false |
是否运行真实 BGE-M3 smoke 测试 |
不再提供 SPARSE_VECTOR_USE_FP16 配置。推理精度只由 SPARSE_VECTOR_DEVICE 决定:CPU 使用 fp32,CUDA 使用 fp16。
内部多路召回 SSE 接口 POST /api/v1/internal/recall/stream 的配置。详见
docs/internals/recall_http_api.md。
| 变量 | 默认 | 说明 |
|---|---|---|
RECALL_INTERNAL_AUTH_ENABLED |
true |
是否启用内部 JWT 校验;生产必须为 true |
RECALL_INTERNAL_JWT_ISSUER |
tolink-java |
期望的 JWT iss |
RECALL_INTERNAL_JWT_AUDIENCE |
tolink-rag |
期望的 JWT aud |
RECALL_INTERNAL_JWT_SCOPE |
recall:execute |
期望的 JWT scope |
RECALL_INTERNAL_JWT_SECRET |
本地联调占位值 | HS256 共享密钥;Java 签发端与 Python 验签端必须一致,生产务必用环境变量覆盖为强随机值 |
RECALL_STREAM_TIMEOUT_MS |
60000 |
单次召回最大执行时间(毫秒);超时以 SSE error RECALL_TIMEOUT 终止 |
RECALL_STRICT_DEFAULT |
false |
pipeline 严格模式默认;false=宽松,允许单路失败降级 |
RECALL_RESULT_LIMIT |
20 |
服务端固定返回候选上限(同时作为各路执行期 top_k) |
RECALL_ENABLED_SOURCES |
bm25,sparse,dense |
启用的召回路(逗号分隔)。本期默认开启三路;运维侧可显式 set bm25,sparse 暂时回退到 dev 旧行为;未登记的 source 出现在配置中装配期 ValueError |
SPARSE_RETRIEVAL_TOP_K |
10 |
sparse 召回 facade 直调时的兜底 top_k;pipeline 路径下被 RECALL_RESULT_LIMIT 覆盖 |
SPARSE_RETRIEVAL_SCORE_THRESHOLD |
0.0 |
sparse 召回默认 score 阈值(0.0 = 不过滤;详见 vectorization.md §9.4) |
DENSE_RETRIEVAL_TOP_K |
10 |
dense 召回 facade 直调时的兜底 top_k;pipeline 路径下被 RECALL_RESULT_LIMIT 覆盖 |
DENSE_RETRIEVAL_SCORE_THRESHOLD |
0.0 |
dense 召回默认 score 阈值(cosine 上界 [0, 1],0.0 = 不过滤;facade 入口校验 > 1.0 早死) |
RECALL_GENERATION_CONTEXT_TOKEN_BUDGET |
4000 |
召回后 LLM 生成拼装上下文的 token 预算上限;命中片段按融合分数从高到低纳入,累计超预算即截断尾部低分片段(仅对外直连端点的生成阶段生效) |
对外直连召回 SSE 接口 POST /api/v1/recall/stream 的配置。前端凭 Java 签发的短期
session token 直连,独立密钥与内部端点隔离。详见
recall_http_api.md。
| 变量 | 默认 | 说明 |
|---|---|---|
RECALL_SESSION_AUTH_ENABLED |
true |
是否启用 session token 验签;生产必须为 true |
RECALL_SESSION_JWT_ISSUER |
tolink-java |
期望的 session JWT iss |
RECALL_SESSION_JWT_AUDIENCE |
tolink-rag-frontend |
期望的 session JWT aud(与内部端点 tolink-rag 区分) |
RECALL_SESSION_JWT_SCOPE |
recall:stream |
期望的 session JWT scope |
RECALL_SESSION_JWT_SECRET |
本地联调占位值 | 独立 HS256 密钥,与 RECALL_INTERNAL_JWT_SECRET 物理隔离、可单独轮转;生产务必覆盖 |
RECALL_SESSION_MAX_CONCURRENT |
3 |
单用户最大并发召回流数;token 短期可复用,此为资源滥用主闸门,超限返回 429 |
CORS_ORIGINS |
["*"] |
生产对外环境必须收敛为前端可信域名清单(不可用 *,否则带 Authorization 头的跨域预检失败) |
token 短期可复用:Python 只校验
exp(建议 Java 签发 30s,仅够建连),不做一次性 / 防重放 / 撤销;连上后流的存活由RECALL_STREAM_TIMEOUT_MS控制。并发计数依赖 Redis, Redis 不可用时 fail-open(放行,因限流是资源保护非鉴权)。
SPARSE_VECTOR_PROVIDER 除已有 bge_m3(本地)/ bge_m3_http(早期 bge-m3-server)
外,新增 remote_bge_m3:对接独立部署的 bge-m3-service,单次 /encode 同时
拿到 dense(1024 维)+ sparse lexical weights,并在客户端做超时 + 重试。详见
docs/internals/vectorization.md §6.6。
| 变量 | 默认 | 说明 |
|---|---|---|
BGE_M3_SERVICE_URL |
空 | bge-m3-service 根地址(如 http://127.0.0.1:7997),尾部 / 会被忽略;provider=remote_bge_m3 时必填 |
BGE_M3_TIMEOUT_SECONDS |
30.0 |
单次 /encode 请求超时(秒) |
BGE_M3_MAX_RETRIES |
3 |
网络错误 / 5xx 的重试次数(不含首次请求;0 = 不重试;4xx 直接抛错不重试) |
.env由 src/config.py 通过Settings(pydantic-settings)加载。- 运行时环境变量优先级高于
.env(部署时通过容器环境变量注入即可覆盖)。 - 新增配置必须在
Settings中声明,并在 .env.example 补充示例值。
- 部署步骤:deployment.md
- MQ 集成:mq_integration.md