2026-06-14 16:49:18 +08:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# 企微IT智能服务台 — 配置管理模块
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# 说明:使用 pydantic-settings 从环境变量读取所有配置项
|
|
|
|
|
|
# 优先级:环境变量 > .env 文件 > 默认值
|
|
|
|
|
|
# 所有配置项集中管理,避免散落在代码各处
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
2026-07-18 01:13:33 +08:00
|
|
|
|
import logging
|
2026-07-09 13:46:15 +08:00
|
|
|
|
import os
|
2026-06-14 16:49:18 +08:00
|
|
|
|
from typing import List
|
|
|
|
|
|
|
|
|
|
|
|
import redis.asyncio as aioredis
|
|
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
|
2026-07-18 01:13:33 +08:00
|
|
|
|
# v4.0 P1-7 修复:get_automation_thresholds 异常分支使用 logger 但未定义
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
|
|
|
|
"""应用配置类。
|
|
|
|
|
|
|
|
|
|
|
|
使用 pydantic-settings 自动从环境变量读取配置值。
|
|
|
|
|
|
支持 .env 文件自动加载(开发环境便利)。
|
|
|
|
|
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
|
wecom_corp_id: 企业微信企业ID
|
|
|
|
|
|
wecom_agent_id: 企业微信应用AgentId
|
|
|
|
|
|
wecom_secret: 企业微信应用Secret
|
|
|
|
|
|
wecom_token: 企业微信回调Token
|
|
|
|
|
|
wecom_encoding_aes_key: 企业微信回调EncodingAESKey(43位)
|
|
|
|
|
|
database_url: PostgreSQL 数据库连接地址
|
|
|
|
|
|
redis_url: Redis 连接地址
|
|
|
|
|
|
backend_host: 后端监听地址
|
|
|
|
|
|
backend_port: 后端监听端口
|
|
|
|
|
|
cors_origins: CORS 允许的源地址(逗号分隔)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 企微自建应用配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 企业ID(在企微管理后台 > 我的企业 > 企业信息 中查看)
|
|
|
|
|
|
wecom_corp_id: str = "ww1234567890abcdef"
|
|
|
|
|
|
# 应用AgentId(在企微管理后台 > 应用管理 > 自建应用 中查看)
|
|
|
|
|
|
wecom_agent_id: str = "1000002"
|
|
|
|
|
|
# 应用Secret(在企微管理后台 > 应用管理 > 自建应用 中查看)
|
|
|
|
|
|
wecom_secret: str = "your-agent-secret"
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# 企微通讯录同步 Secret(管理工具 → 通讯录同步 → 开启接口同步)
|
|
|
|
|
|
# 与应用 Secret 不同,此 Secret 有完整的通讯录读取权限
|
|
|
|
|
|
# 如果配置了,get_department_list / get_department_members 将使用此 Secret 获取 access_token
|
|
|
|
|
|
wecom_contact_secret: str = ""
|
2026-07-05 17:03:36 +08:00
|
|
|
|
# 审批应用Secret(在企微管理后台 > 应用管理 > 审批 > 查看Secret)
|
|
|
|
|
|
wecom_approval_secret: str = ""
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# 会议室API专用Secret(在企微管理后台 > 应用管理 > 会议室 > 可调用接口的应用 中配置)
|
|
|
|
|
|
# 与应用Secret和通讯录Secret不同,用于获取会议室API的独立access_token
|
|
|
|
|
|
wecom_meetingroom_secret: str = ""
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# 回调Token(在企微管理后台 > 应用管理 > 接收消息 中设置)
|
|
|
|
|
|
wecom_token: str = "your-callback-token"
|
2026-07-11 23:13:10 +08:00
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 小鱼易联终端管理配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 小鱼易联企业ID
|
|
|
|
|
|
xylink_enterprise_id: str = ""
|
|
|
|
|
|
# SDK客户端ID
|
|
|
|
|
|
xylink_client_id: str = ""
|
|
|
|
|
|
# SDK客户端密钥
|
|
|
|
|
|
xylink_client_secret: str = ""
|
|
|
|
|
|
# 企业ID(扩展ID)
|
|
|
|
|
|
xylink_ext_id: str = ""
|
|
|
|
|
|
# API基础URL(小鱼易联开放平台)
|
|
|
|
|
|
xylink_api_base: str = "https://sdk.xylink.com/api/rest/external/v1/"
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# 回调EncodingAESKey(43位字符串,用于消息加解密)
|
|
|
|
|
|
wecom_encoding_aes_key: str = "your-aes-key-43-characters-long-encoding-key"
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 数据库配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# PostgreSQL 连接地址
|
|
|
|
|
|
# Docker 环境使用容器名 postgres,本地开发使用 localhost
|
|
|
|
|
|
database_url: str = "postgresql://wecom:wecom_secret@localhost:5432/wecom_it_desk"
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Redis 配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Redis 连接地址
|
|
|
|
|
|
# Docker 环境使用容器名 redis,本地开发使用 localhost
|
2026-07-05 17:03:36 +08:00
|
|
|
|
# 从环境变量 REDIS_URL 读取,格式: redis://:password@host:port/db
|
|
|
|
|
|
redis_url: str = "" # 默认为空,由环境变量 REDIS_URL 提供
|
2026-06-14 16:49:18 +08:00
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 服务配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 后端监听地址(0.0.0.0 表示监听所有网卡)
|
|
|
|
|
|
backend_host: str = "0.0.0.0"
|
|
|
|
|
|
# 后端监听端口
|
|
|
|
|
|
backend_port: int = 8000
|
|
|
|
|
|
# CORS 允许的源地址(逗号分隔的字符串)
|
|
|
|
|
|
cors_origins: str = "http://localhost:5173,http://localhost:5174,http://localhost:5175"
|
|
|
|
|
|
|
2026-07-09 13:46:15 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 运行期日志目录(供"运行期日志"管理页面读取)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 后端运行期日志(JSON 格式,由 utils.logging_config.setup_logging 写入)
|
|
|
|
|
|
# 所在目录。容器内默认 /app/logs,可通过环境变量 RUNTIME_LOG_DIR 覆盖;
|
|
|
|
|
|
# 宿主机需将真实日志目录挂载到该路径(见 docker-compose.yml backend 卷)。
|
|
|
|
|
|
RUNTIME_LOG_DIR: str = os.getenv("RUNTIME_LOG_DIR", "/app/logs")
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# AI 服务配置(Dify)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Dify API 端点(兼容 OpenAI Chat Completions 格式)
|
|
|
|
|
|
# 必须通过环境变量 DIFY_API_URL 配置,不设置默认值(防止凭据泄露)
|
|
|
|
|
|
dify_api_url: str = ""
|
|
|
|
|
|
# Dify API Key(格式:base_url|app_id|app_name)
|
|
|
|
|
|
# 必须通过环境变量 DIFY_API_KEY 配置,不设置默认值(防止凭据泄露)
|
|
|
|
|
|
dify_api_key: str = ""
|
|
|
|
|
|
# Dify API 请求超时(秒),在网络慢时可调大
|
|
|
|
|
|
dify_timeout: int = 30
|
|
|
|
|
|
|
2026-07-18 00:28:15 +08:00
|
|
|
|
# v4.0 P0-6: Dify 双路径超时预算切分(修复 proxy 兜底不可达)
|
|
|
|
|
|
# 为什么:httpx 30s = wait_for 30s,native→proxy 串行最坏 60s,
|
|
|
|
|
|
# wait_for 必先触发,proxy 兜底数学上不可达。
|
|
|
|
|
|
# 预算:native 12s + proxy 12s + 开销 < 30s,wait_for 保持为最后防线。
|
|
|
|
|
|
dify_native_timeout: int = 12 # Dify 原生直连超时(秒)
|
|
|
|
|
|
dify_proxy_timeout: int = 12 # Dify 代理路径超时(秒)
|
|
|
|
|
|
|
2026-07-13 02:17:03 +08:00
|
|
|
|
# Dify 原生 API 配置(绕过 dify2openai 代理,直连 Dify /v1/chat-messages)
|
|
|
|
|
|
# 为什么:dify2openai 代理存在 [object Object] 序列化 bug,
|
|
|
|
|
|
# 直连 Dify 原生 API 可绕过此问题,且响应格式更简单(answer 字段直接返回内容)
|
|
|
|
|
|
# 用法:配置后 get_structured_reply() 优先使用原生 API,未配置则回退到代理
|
|
|
|
|
|
dify_native_base_url: str = "" # 如 http://yw-dify.dc.servyou-it.com
|
|
|
|
|
|
dify_native_api_key: str = "" # 如 app-7jkRkAzvX4QM9v9SM3P8mMEO(仅 app key,不含管道分隔格式)
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# AI Wingman 服务配置(Dify Agent 2 — 坐席端辅助)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 坐席端 Wingman 专用 Dify API 端点(与员工端 Agent 分开)
|
|
|
|
|
|
# 留空则禁用 Wingman 功能(不影响主流程)
|
|
|
|
|
|
dify_wingman_api_url: str = ""
|
|
|
|
|
|
# 坐席端 Wingman Dify API Key(需要新建 Agent 后填入,留空则禁用)
|
|
|
|
|
|
# 格式:base_url|app_id|app_name(与 dify_api_key 相同格式)
|
|
|
|
|
|
dify_wingman_api_key: str = ""
|
|
|
|
|
|
# Wingman API 请求超时(秒)
|
|
|
|
|
|
dify_wingman_timeout: int = 30
|
|
|
|
|
|
|
2026-07-13 02:17:03 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# AI 分诊服务配置(Dify Agent 3 — 独立分诊应用)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Dify 分诊应用 OpenAI 兼容接口地址(共用 dify2openai 代理,通过不同 API Key 区分)
|
|
|
|
|
|
dify_triage_api_url: str = ""
|
|
|
|
|
|
# Dify 分诊应用 API Key(格式:base_url|app_id|app_name)
|
|
|
|
|
|
dify_triage_api_key: str = ""
|
|
|
|
|
|
# Dify 分诊请求超时(秒),超时自动转人工
|
|
|
|
|
|
dify_triage_timeout: int = 5
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Mock 登录配置(测试阶段使用,跳过企微 OAuth2)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 是否启用 Mock 登录(默认 false,生产环境必须关闭)
|
|
|
|
|
|
mock_login_enabled: bool = False
|
|
|
|
|
|
|
2026-07-13 02:17:03 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 终端页面配置(小鱼易联会议室终端)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 终端页面基础URL(用于生成NE2005等不支持H5的终端的二维码)
|
|
|
|
|
|
# 格式:https://域名/itterminal/
|
|
|
|
|
|
terminal_base_url: str = "https://itsupport.servyou.com.cn/itterminal/"
|
|
|
|
|
|
|
2026-06-16 14:28:51 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 开发模式配置(本地 docker-compose.dev.yml 用)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 是否启用开发模式(本地开发环境,启用后挂载 /api/dev/* Mock OAuth 路由)
|
|
|
|
|
|
# ⚠️ 生产环境必须为 false / 不设置
|
|
|
|
|
|
# 启用的副作用:
|
|
|
|
|
|
# 1. 后端启动时挂载 /api/dev/login /users /health 三个 Mock 端点
|
|
|
|
|
|
# 2. /api/dev/login 跳过企微 OAuth 直接生成 token
|
|
|
|
|
|
# 3. 启动日志会大声警告 "🧪 DEV_MODE enabled"
|
|
|
|
|
|
dev_mode: bool = False
|
|
|
|
|
|
# 开发模式默认 userid(本地前端兜底用,实际由前端 /api/dev/login 传入)
|
|
|
|
|
|
dev_default_userid: str = "dev-user-001"
|
|
|
|
|
|
# 开发模式默认姓名
|
|
|
|
|
|
dev_default_name: str = "开发测试用户"
|
|
|
|
|
|
# 开发模式默认部门
|
|
|
|
|
|
dev_default_dept: str = "信息技术部"
|
|
|
|
|
|
|
2026-07-07 21:52:11 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 运行环境 & 管理后台 IP 白名单(三端认证重构 AUTH-01)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 应用运行环境:dev / test / production
|
|
|
|
|
|
# 控制 UA 校验 / IP 白名单 / 真实企微 OAuth 的启用(仅 production 启用)
|
|
|
|
|
|
# 通过环境变量 APP_ENV 控制(默认 dev,避免本地误触发强校验)
|
|
|
|
|
|
app_env: str = "dev"
|
|
|
|
|
|
# 管理后台登录 IP 白名单(逗号分隔,支持 CIDR,如 10.240.0.0/16)
|
|
|
|
|
|
# 仅允许白名单内的 IP 访问管理后台登录;其余 IP 返回 4004(无权限)
|
|
|
|
|
|
# 通过环境变量 ADMIN_ALLOWED_IPS 覆盖
|
|
|
|
|
|
admin_allowed_ips: str = "117.147.35.138,218.75.34.87,10.240.0.0/16"
|
|
|
|
|
|
|
2026-06-15 14:14:58 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 审批模板配置(企微审批应用)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 资源申请审批模板ID(在企微审批应用设置中获取)
|
|
|
|
|
|
approval_template_resource: str = ""
|
|
|
|
|
|
# 设备申请审批模板ID(在企微审批应用设置中获取)
|
|
|
|
|
|
approval_template_device: str = ""
|
|
|
|
|
|
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# ITSM 运维平台配置(一站式运维平台 OpenAPI)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# ITSM OpenAPI app_id(向ITSM平台方申请,用于签名认证)
|
|
|
|
|
|
itsm_app_id: str = ""
|
|
|
|
|
|
# ITSM OpenAPI app_secret(向ITSM平台方申请,用于签名认证)
|
|
|
|
|
|
itsm_app_secret: str = ""
|
|
|
|
|
|
# ITSM 生产环境基址
|
|
|
|
|
|
itsm_base_url: str = "https://devops.dc.servyou-it.com/itsm"
|
|
|
|
|
|
# ITSM 测试环境基址
|
|
|
|
|
|
itsm_test_base_url: str = "https://test-devops.dc.servyou-it.com/itsm"
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 审批意图识别 Dify 应用配置(独立于主 AI 对话和自动化引擎)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Dify 审批意图识别应用基址(OpenAI 兼容代理,如 http://yw-dify.dc.servyou-it.com/dify2openai)
|
|
|
|
|
|
approval_dify_base_url: str = ""
|
|
|
|
|
|
# Dify 审批意图识别应用 API Key
|
|
|
|
|
|
approval_dify_api_key: str = ""
|
|
|
|
|
|
# Dify 审批意图识别请求超时(秒)
|
2026-07-18 02:31:46 +08:00
|
|
|
|
approval_dify_timeout: int = 8 # v4.0 批次3 D1 止血:15s→8s(路由检测与主Dify串行叠加 15+30=45s → 8+30=38s)
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# 审批意图置信度阈值(≥ 此值才触发审批卡片)
|
|
|
|
|
|
approval_confidence_threshold: float = 0.7
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# IT 资产升级审批推送配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Excel 资产清单文件路径(12个月度 sheet,含固定资产编码、开始使用日期等)
|
|
|
|
|
|
# 通过环境变量 ASSET_EXCEL_PATH 覆盖
|
|
|
|
|
|
asset_excel_path: str = "D:/资料/00-工作文件/03-资产管理/固定资产清单/资产记录/2025资产/2025资产1~12.xlsx"
|
|
|
|
|
|
# 资产更换年限阈值(≥ 此年限视为符合更换条件)
|
|
|
|
|
|
# 通过环境变量 ASSET_REPLACEMENT_THRESHOLD_YEARS 覆盖
|
|
|
|
|
|
asset_replacement_threshold_years: int = 5
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 业务路由推荐配置(路由推荐功能)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 路由置信度阈值:Dify 返回的 routing_confidence ≥ 此值才触发名片推荐
|
|
|
|
|
|
# 低于此值走正常 AI 回复流程,避免误路由
|
|
|
|
|
|
routing_confidence_threshold: float = 0.7
|
|
|
|
|
|
|
2026-06-22 17:38:47 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# v0.7.1 企微 SSO 入口配置 (task #85)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 是否启用企微 SSO(true = 优先用企微 OAuth2 静默授权,失败时降级扫码)
|
|
|
|
|
|
# 通过环境变量 WECOM_SSO_ENABLED 控制(默认 false,避免老用户被打扰)
|
|
|
|
|
|
wecom_sso_enabled: bool = False
|
|
|
|
|
|
# SSO OAuth 回调 base URL(企微要求 redirect_uri 必须用可信域名)
|
|
|
|
|
|
# 生产: https://itsupport.servyou.com.cn 开发: http://localhost:5176
|
|
|
|
|
|
wecom_sso_callback_base: str = ""
|
|
|
|
|
|
|
2026-06-16 10:07:42 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# v0.5.4 应急页身份检测配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# IT支持-咨询坐席 通讯录标签 ID(在企微管理后台 > 通讯录管理 > 标签管理 中查看)
|
|
|
|
|
|
# 配置后,应急页会通过此标签判断当前用户是否为坐席
|
|
|
|
|
|
# 留空则降级到下面的硬编码名单
|
|
|
|
|
|
wecom_agent_tag_id: str = ""
|
|
|
|
|
|
# 硬编码坐席 userid 列表(逗号分隔),作为标签检测的降级方案
|
|
|
|
|
|
# 例:"zhangsan,lisi,wangwu"(生产环境建议用标签方案)
|
|
|
|
|
|
wecom_agent_userids: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# v0.6.0 内容审核报警配置(占位,后续完善)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 合规通知企微群机器人 webhook
|
|
|
|
|
|
content_audit_webhook: str = ""
|
|
|
|
|
|
# 主管接收报警的 userid(多个用逗号分隔)
|
|
|
|
|
|
content_audit_supervisor_userids: str = ""
|
|
|
|
|
|
|
2026-07-07 21:52:11 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 阶段5 自动化闭环配置(环境变量前缀 AUTOMATION_*)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 说明:自动化引擎连接的外部系统基址与密钥占位。
|
|
|
|
|
|
# 优先级:环境变量 AUTOMATION_* > 阶段1-4 既有的 system_configs 集成配置
|
|
|
|
|
|
# (huorong/lianruan/ragflow 在 app/integrations/*/config.py 中已有 getter)
|
|
|
|
|
|
# 注意:密钥均为占位,生产环境必须通过环境变量注入,切勿硬编码真实密钥。
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Dify(意图识别 / AI 编排)
|
|
|
|
|
|
automation_dify_base_url: str = ""
|
|
|
|
|
|
automation_dify_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# RAGFlow(知识库检索,默认内网 :9380)
|
|
|
|
|
|
automation_ragflow_base_url: str = "http://10.80.0.85:9380"
|
|
|
|
|
|
automation_ragflow_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 火绒终端安全(HRESS HMAC-SHA1 签名)
|
|
|
|
|
|
automation_huorong_base_url: str = ""
|
|
|
|
|
|
automation_huorong_access_key_id: str = ""
|
|
|
|
|
|
automation_huorong_access_key_secret: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 联软 LV7000(三层认证:IP白名单 + 账号密码 + Token)
|
|
|
|
|
|
automation_lianruan_base_url: str = ""
|
|
|
|
|
|
automation_lianruan_api_account: str = ""
|
|
|
|
|
|
automation_lianruan_api_password: str = ""
|
|
|
|
|
|
automation_lianruan_validate_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 北森 EHR(静态映射兜底)
|
|
|
|
|
|
automation_ehr_base_url: str = ""
|
|
|
|
|
|
automation_ehr_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 自动化阈值(JSON 字符串):置信度下限 / 超时秒 / 连续未解决次数 / 高危必转
|
|
|
|
|
|
# 管理后台可配(见 ScenarioConfig + 全局阈值),此处为默认值。
|
|
|
|
|
|
automation_thresholds: str = '{"confidence_min":0.6,"timeout_seconds":60,"unresolved_threshold":2,"high_risk_force_handoff":true}'
|
|
|
|
|
|
|
2026-07-09 11:47:16 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Neo4j 图数据库配置(知识图谱存储 — Tier0 / T01)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Neo4j bolt 协议连接地址(默认本地开发容器)
|
|
|
|
|
|
neo4j_uri: str = "bolt://localhost:7687"
|
|
|
|
|
|
# Neo4j 用户名
|
|
|
|
|
|
neo4j_user: str = "neo4j"
|
|
|
|
|
|
# Neo4j 密码(⚠️ 仅从环境变量注入,不设默认值)
|
|
|
|
|
|
neo4j_password: str = ""
|
|
|
|
|
|
# Neo4j 默认数据库名
|
|
|
|
|
|
neo4j_database: str = "neo4j"
|
|
|
|
|
|
# Neo4j 连接最大存活时间(秒)
|
|
|
|
|
|
neo4j_max_connection_lifetime: int = 3600
|
|
|
|
|
|
# Neo4j 连接池上限
|
|
|
|
|
|
neo4j_max_connection_pool_size: int = 50
|
|
|
|
|
|
# Neo4j 连接获取超时(秒)
|
|
|
|
|
|
neo4j_connection_acquisition_timeout: int = 30
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 置信门控配置(D3 — 全局置信阈值)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# AI 回复置信度低于此阈值时,前端渲染"转人工"入口
|
|
|
|
|
|
# 可通过环境变量 CONFIDENCE_GATE_THRESHOLD 覆盖
|
|
|
|
|
|
confidence_gate_threshold: float = 0.7
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# RAGFlow Ingestion 开关(通道 C — 文档→KB)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 是否启用 RAGFlow 文档 ingestion 功能(默认关闭,需部署 RAGFlow 服务后开启)
|
|
|
|
|
|
ragflow_ingestion_enabled: bool = False
|
|
|
|
|
|
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 百度语音识别(ASR)配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 百度智能云语音识别 AppID(在百度智能云控制台 > 语音技术 中查看)
|
|
|
|
|
|
baidu_asr_app_id: str = ""
|
|
|
|
|
|
# 百度 ASR API Key
|
|
|
|
|
|
baidu_asr_api_key: str = ""
|
|
|
|
|
|
# 百度 ASR Secret Key
|
|
|
|
|
|
baidu_asr_secret_key: str = ""
|
|
|
|
|
|
|
2026-07-09 11:47:16 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Qwen-VL 视觉理解配置(D5 — 截图理解)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 本地 Qwen-VL 模型名称(Dify vision workflow 中配置的模型标识)
|
|
|
|
|
|
qwen_vl_model: str = "Qwen3-VL-8B-Instruct"
|
|
|
|
|
|
# Dify Vision Workflow API 端点(独立于 Wingman Agent)
|
|
|
|
|
|
dify_vision_api_url: str = ""
|
|
|
|
|
|
# Dify Vision Workflow API Key
|
|
|
|
|
|
dify_vision_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# 阶段5 自动化闭环外部系统配置(简化命名,供管理后台展示)
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Dify(意图识别 / AI 编排)
|
|
|
|
|
|
dify_base_url: str = ""
|
|
|
|
|
|
dify_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# RAGFlow(知识库检索)
|
|
|
|
|
|
ragflow_base_url: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 火绒终端安全(HRESS HMAC-SHA1 签名)
|
|
|
|
|
|
huorong_base_url: str = ""
|
|
|
|
|
|
huorong_key: str = ""
|
|
|
|
|
|
huorong_secret: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 联软 LV7000(三层认证:IP白名单 + 账号密码 + Token)
|
|
|
|
|
|
lianruan_base_url: str = ""
|
|
|
|
|
|
lianruan_username: str = ""
|
|
|
|
|
|
lianruan_password: str = ""
|
|
|
|
|
|
lianruan_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
|
|
# 北森 EHR(静态映射兜底)
|
|
|
|
|
|
ehr_base_url: str = ""
|
|
|
|
|
|
|
2026-07-11 23:13:10 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# P2 上下文压缩配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
context_compress_threshold: int = 6000 # 压缩触发阈值(token数)
|
|
|
|
|
|
context_compress_timeout: int = 30 # LLM摘要超时(秒)
|
|
|
|
|
|
context_max_compress_level: int = 3 # 渐进式压缩最大级别
|
|
|
|
|
|
context_keep_recent_turns: int = 4 # 压缩后保留最近对话轮数
|
|
|
|
|
|
context_compress_ratio_warn: float = 0.3 # 压缩率警告阈值
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# P3 多轮纠错配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
max_undo_count: int = 5 # 最大撤销更正次数
|
|
|
|
|
|
|
2026-07-07 21:52:11 +08:00
|
|
|
|
def get_automation_thresholds(self) -> dict:
|
|
|
|
|
|
"""解析自动化阈值配置,返回带默认值的字典。
|
|
|
|
|
|
|
|
|
|
|
|
为什么单独成方法:阈值是 JSON 字符串(便于通过环境变量整体注入),
|
|
|
|
|
|
解析失败时回退到代码内默认值,避免单点配置错误导致引擎不可用。
|
|
|
|
|
|
"""
|
|
|
|
|
|
default = {
|
|
|
|
|
|
"confidence_min": 0.6,
|
|
|
|
|
|
"timeout_seconds": 60,
|
|
|
|
|
|
"unresolved_threshold": 2,
|
|
|
|
|
|
"high_risk_force_handoff": True,
|
|
|
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
|
|
|
import json as _json
|
|
|
|
|
|
if self.automation_thresholds:
|
|
|
|
|
|
parsed = _json.loads(self.automation_thresholds)
|
|
|
|
|
|
if isinstance(parsed, dict):
|
|
|
|
|
|
default.update(parsed)
|
|
|
|
|
|
except Exception as e: # 解析失败仅记日志,不中断启动
|
|
|
|
|
|
logger.warning(f"自动化阈值解析失败,使用默认值: {e}")
|
|
|
|
|
|
return default
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
# Pydantic-settings 配置
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
model_config = SettingsConfigDict(
|
|
|
|
|
|
# 自动从 .env 文件加载环境变量
|
|
|
|
|
|
env_file=".env",
|
|
|
|
|
|
# .env 文件编码
|
|
|
|
|
|
env_file_encoding="utf-8",
|
|
|
|
|
|
# 环境变量名大小写不敏感
|
|
|
|
|
|
case_sensitive=False,
|
|
|
|
|
|
# 额外字段不允许(防止拼写错误的配置被忽略)
|
|
|
|
|
|
extra="ignore",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
def cors_origins_list(self) -> List[str]:
|
|
|
|
|
|
"""将 CORS 源地址字符串解析为列表。
|
|
|
|
|
|
|
|
|
|
|
|
将逗号分隔的字符串(如 "http://a,http://b")
|
|
|
|
|
|
转换为列表(如 ["http://a", "http://b"]),
|
|
|
|
|
|
方便 FastAPI 的 CORSMiddleware 使用。
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
List[str]: CORS 允许的源地址列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 去除每项的前后空格,过滤空字符串
|
|
|
|
|
|
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
|
|
|
|
|
|
|
|
|
|
|
|
def create_redis_client(self) -> aioredis.Redis:
|
|
|
|
|
|
"""创建 Redis 异步客户端实例。
|
|
|
|
|
|
|
2026-07-07 21:52:11 +08:00
|
|
|
|
使用单独的 host/port/password 参数,避免 URL 解析问题
|
|
|
|
|
|
(特别是密码中包含特殊字符 ! @ # 时)。
|
|
|
|
|
|
|
2026-06-14 16:49:18 +08:00
|
|
|
|
自动附加 protocol=2 参数,强制使用 RESP2 协议。
|
|
|
|
|
|
原因:Windows 版 Redis 3.x 不支持 RESP3 协议(HELLO 命令),
|
|
|
|
|
|
而 redis-py 8.0+ 默认使用 RESP3,会导致连接失败。
|
|
|
|
|
|
全项目统一使用此方法创建 Redis 客户端,避免协议不匹配。
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
aioredis.Redis: 配置好的 Redis 异步客户端
|
|
|
|
|
|
"""
|
2026-07-07 21:52:11 +08:00
|
|
|
|
# 连接超时保护:防止 Redis 不可达时请求无限挂起
|
|
|
|
|
|
# (历史事故:REDIS_URL 密码含 @ # 导致 urlparse 解析到错误 host,
|
|
|
|
|
|
# 连接一直挂起,最终表现为登录接口超时 / 502 / 浏览器"网络连接失败")
|
|
|
|
|
|
socket_connect_timeout = 5
|
|
|
|
|
|
socket_timeout = 5
|
|
|
|
|
|
|
2026-07-05 17:03:36 +08:00
|
|
|
|
# 如果 redis_url 为空,使用默认值
|
2026-07-07 21:52:11 +08:00
|
|
|
|
if not self.redis_url:
|
|
|
|
|
|
# 默认值:本地 Redis
|
|
|
|
|
|
return aioredis.Redis(
|
|
|
|
|
|
host="localhost",
|
|
|
|
|
|
port=6379,
|
|
|
|
|
|
protocol=2,
|
|
|
|
|
|
decode_responses=True,
|
|
|
|
|
|
socket_connect_timeout=socket_connect_timeout,
|
|
|
|
|
|
socket_timeout=socket_timeout,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 解析 REDIS_URL 提取连接参数
|
|
|
|
|
|
# 格式: redis://:password@host:port/db
|
|
|
|
|
|
# ⚠️ 密码可能含 URL 保留字符(@ # ! 等),部署时必须用 URL-encode:
|
|
|
|
|
|
# @ → %40, # → %23, ! → %21
|
|
|
|
|
|
# 例: R3d!s@2026#Secure → R3d%21s%402026%23Secure
|
|
|
|
|
|
# urlparse 不会自动解码百分号编码,这里用 unquote 还原真实密码/主机
|
|
|
|
|
|
from urllib.parse import urlparse, unquote
|
|
|
|
|
|
parsed = urlparse(self.redis_url)
|
|
|
|
|
|
|
|
|
|
|
|
# 提取密码(先尝试标准 urlparse 字段,失败则从 netloc 兜底)
|
|
|
|
|
|
password = parsed.password
|
|
|
|
|
|
if not password:
|
|
|
|
|
|
# 尝试从 netloc 中提取(格式 :password@host)
|
|
|
|
|
|
netloc = parsed.netloc
|
|
|
|
|
|
if "@" in netloc:
|
|
|
|
|
|
password = netloc.split("@")[0].split(":")[-1]
|
|
|
|
|
|
if password:
|
|
|
|
|
|
password = unquote(password)
|
|
|
|
|
|
|
|
|
|
|
|
hostname = unquote(parsed.hostname) if parsed.hostname else "localhost"
|
|
|
|
|
|
port = parsed.port or 6379
|
|
|
|
|
|
db = parsed.path and int(parsed.path.lstrip("/")) or 0
|
|
|
|
|
|
|
|
|
|
|
|
return aioredis.Redis(
|
|
|
|
|
|
host=hostname,
|
|
|
|
|
|
port=port,
|
|
|
|
|
|
password=password,
|
|
|
|
|
|
db=db,
|
|
|
|
|
|
protocol=2,
|
|
|
|
|
|
decode_responses=True,
|
|
|
|
|
|
socket_connect_timeout=socket_connect_timeout,
|
|
|
|
|
|
socket_timeout=socket_timeout,
|
|
|
|
|
|
)
|
2026-06-14 16:49:18 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 创建全局配置实例
|
|
|
|
|
|
# 整个应用通过 from app.config import settings 使用同一个实例
|
|
|
|
|
|
settings = Settings()
|