docs: update README and CLAUDE.md to v2.2.0

- Added documentation for audit tracking (IP address, invocation method).
- Updated database model descriptions for enhanced WorkOrder and Conversation fields.
- Documented the new UnifiedConfig system.
- Reflected enhanced logging transparency for knowledge base parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhaojie
2026-02-11 00:08:09 +08:00
parent 2026007045
commit c3560b43fd
218 changed files with 3354 additions and 5096 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -1,71 +0,0 @@
import os
from typing import Dict, Any
class Config:
"""系统配置类"""
# 阿里云千问API配置
ALIBABA_API_KEY = "sk-c0dbefa1718d46eaa897199135066f00"
ALIBABA_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
ALIBABA_MODEL_NAME = "qwen-plus-latest"
# 数据库配置
DATABASE_URL = "mysql+pymysql://tsp_assistant:123456@jeason.online/tsp_assistant?charset=utf8mb4"
# DATABASE_URL = "sqlite:///local_test.db" # 本地测试数据库
# 知识库配置
KNOWLEDGE_BASE_PATH = "data/knowledge_base"
VECTOR_DB_PATH = "data/vector_db"
# 对话配置
MAX_HISTORY_LENGTH = 10
RESPONSE_TIMEOUT = 30
# 分析配置
ANALYTICS_UPDATE_INTERVAL = 3600 # 1小时
ALERT_THRESHOLD = 0.8 # 预警阈值
# 日志配置
LOG_LEVEL = "INFO"
LOG_FILE = "logs/tsp_assistant.log"
# 系统监控配置
SYSTEM_MONITORING = True # 是否启用系统监控
MONITORING_INTERVAL = 60 # 监控间隔(秒)
@classmethod
def get_api_config(cls) -> Dict[str, Any]:
"""获取API配置"""
return {
"api_key": cls.ALIBABA_API_KEY,
"base_url": cls.ALIBABA_BASE_URL,
"model_name": cls.ALIBABA_MODEL_NAME
}
@classmethod
def get_database_config(cls) -> Dict[str, Any]:
"""获取数据库配置"""
return {
"url": cls.DATABASE_URL,
"echo": False
}
@classmethod
def get_knowledge_config(cls) -> Dict[str, Any]:
"""获取知识库配置"""
return {
"base_path": cls.KNOWLEDGE_BASE_PATH,
"vector_db_path": cls.VECTOR_DB_PATH
}
@classmethod
def get_config(cls) -> Dict[str, Any]:
"""获取完整配置"""
return {
"system_monitoring": cls.SYSTEM_MONITORING,
"monitoring_interval": cls.MONITORING_INTERVAL,
"log_level": cls.LOG_LEVEL,
"log_file": cls.LOG_FILE,
"analytics_update_interval": cls.ANALYTICS_UPDATE_INTERVAL,
"alert_threshold": cls.ALERT_THRESHOLD
}

View File

@@ -1,36 +1,42 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
统一配置管理模块
整合所有配置,提供统一的配置接口
从环境变量加载所有配置,提供统一的配置接口
"""
import os
import json
import logging
from typing import Dict, Any, Optional
from dataclasses import dataclass, asdict
from pathlib import Path
from dotenv import load_dotenv
# 在模块加载时,自动从.env文件加载环境变量
# 这使得所有后续的os.getenv调用都能获取到.env中定义的值
load_dotenv()
logger = logging.getLogger(__name__)
# --- 数据类定义 ---
# 这些类定义了配置的结构,但不包含敏感的默认值。
# 默认值只用于那些不敏感或在大多数环境中都相同的值。
@dataclass
class DatabaseConfig:
"""数据库配置"""
url: str = "mysql+pymysql://tsp_assistant:password@jeason.online/tsp_assistant?charset=utf8mb4"
url: str
pool_size: int = 10
max_overflow: int = 20
pool_timeout: int = 30
pool_recycle: int = 3600
pool_recycle: int = 600 # 改为 10 分钟回收连接,避免连接超时
@dataclass
class LLMConfig:
"""LLM配置"""
provider: str = "qwen"
api_key: str = "sk-c0dbefa1718d46eaa897199135066f00"
base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
model: str = "qwen-plus-latest"
provider: str
api_key: str
model: str
base_url: Optional[str] = None
temperature: float = 0.7
max_tokens: int = 2000
timeout: int = 30
@@ -47,13 +53,12 @@ class ServerConfig:
@dataclass
class FeishuConfig:
"""飞书配置"""
app_id: str = ""
app_secret: str = ""
app_token: str = ""
table_id: str = ""
status: str = "active"
sync_limit: int = 10
auto_sync_interval: int = 0
app_id: Optional[str] = None
app_secret: Optional[str] = None
verification_token: Optional[str] = None
encrypt_key: Optional[str] = None
table_id: Optional[str] = None
@dataclass
class AIAccuracyConfig:
@@ -63,234 +68,120 @@ class AIAccuracyConfig:
manual_review_threshold: float = 0.80
ai_suggestion_confidence: float = 0.95
human_resolution_confidence: float = 0.90
prefer_human_when_low_accuracy: bool = True
enable_auto_approval: bool = True
enable_human_fallback: bool = True
@dataclass
class SystemConfig:
"""系统配置"""
backup_enabled: bool = True
backup_interval: int = 24 # 小时
max_backup_files: int = 7
cache_enabled: bool = True
cache_ttl: int = 3600 # 秒
monitoring_enabled: bool = True
# --- 统一配置管理器 ---
class UnifiedConfig:
"""统一配置管理器"""
def __init__(self, config_dir: str = "config"):
self.config_dir = Path(config_dir)
self.config_file = self.config_dir / "unified_config.json"
# 默认配置 - 从config/llm_config.py加载默认LLM配置
self.database = DatabaseConfig()
self.llm = self._load_default_llm_config()
self.server = ServerConfig()
self.feishu = FeishuConfig()
self.ai_accuracy = AIAccuracyConfig()
self.system = SystemConfig()
# 加载配置
self.load_config()
def _load_default_llm_config(self) -> LLMConfig:
"""加载默认LLM配置"""
try:
from config.llm_config import DEFAULT_CONFIG
# 将config/llm_config.py中的配置转换为统一配置的格式
return LLMConfig(
provider=DEFAULT_CONFIG.provider,
api_key=DEFAULT_CONFIG.api_key,
base_url=DEFAULT_CONFIG.base_url,
model=DEFAULT_CONFIG.model,
temperature=DEFAULT_CONFIG.temperature,
max_tokens=DEFAULT_CONFIG.max_tokens
)
except Exception as e:
logger.warning(f"无法加载默认LLM配置使用内置默认值: {e}")
return LLMConfig()
def load_config(self):
"""加载配置文件"""
try:
if self.config_file.exists():
with open(self.config_file, 'r', encoding='utf-8') as f:
config_data = json.load(f)
# 更新配置
if 'database' in config_data:
self.database = DatabaseConfig(**config_data['database'])
if 'llm' in config_data:
self.llm = LLMConfig(**config_data['llm'])
if 'server' in config_data:
self.server = ServerConfig(**config_data['server'])
if 'feishu' in config_data:
self.feishu = FeishuConfig(**config_data['feishu'])
if 'ai_accuracy' in config_data:
self.ai_accuracy = AIAccuracyConfig(**config_data['ai_accuracy'])
if 'system' in config_data:
self.system = SystemConfig(**config_data['system'])
logger.info("配置文件加载成功")
else:
logger.info("配置文件不存在,使用默认配置")
self.save_config()
except Exception as e:
logger.error(f"加载配置文件失败: {e}")
def save_config(self):
"""保存配置文件"""
try:
self.config_dir.mkdir(exist_ok=True)
config_data = {
'database': asdict(self.database),
'llm': asdict(self.llm),
'server': asdict(self.server),
'feishu': asdict(self.feishu),
'ai_accuracy': asdict(self.ai_accuracy),
'system': asdict(self.system)
}
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config_data, f, indent=2, ensure_ascii=False)
logger.info("配置文件保存成功")
except Exception as e:
logger.error(f"保存配置文件失败: {e}")
def load_from_env(self):
"""从环境变量加载配置"""
# 数据库配置
if os.getenv('DATABASE_URL'):
self.database.url = os.getenv('DATABASE_URL')
# LLM配置
if os.getenv('LLM_PROVIDER'):
self.llm.provider = os.getenv('LLM_PROVIDER')
if os.getenv('LLM_API_KEY'):
self.llm.api_key = os.getenv('LLM_API_KEY')
if os.getenv('LLM_MODEL'):
self.llm.model = os.getenv('LLM_MODEL')
# 服务器配置
if os.getenv('SERVER_PORT'):
self.server.port = int(os.getenv('SERVER_PORT'))
if os.getenv('LOG_LEVEL'):
self.server.log_level = os.getenv('LOG_LEVEL')
# 飞书配置
if os.getenv('FEISHU_APP_ID'):
self.feishu.app_id = os.getenv('FEISHU_APP_ID')
if os.getenv('FEISHU_APP_SECRET'):
self.feishu.app_secret = os.getenv('FEISHU_APP_SECRET')
if os.getenv('FEISHU_APP_TOKEN'):
self.feishu.app_token = os.getenv('FEISHU_APP_TOKEN')
if os.getenv('FEISHU_TABLE_ID'):
self.feishu.table_id = os.getenv('FEISHU_TABLE_ID')
def get_database_url(self) -> str:
"""获取数据库连接URL"""
return self.database.url
def get_llm_config(self) -> Dict[str, Any]:
"""获取LLM配置"""
return asdict(self.llm)
def get_server_config(self) -> Dict[str, Any]:
"""获取服务器配置"""
return asdict(self.server)
def get_feishu_config(self) -> Dict[str, Any]:
"""获取飞书配置"""
return asdict(self.feishu)
def get_ai_accuracy_config(self) -> Dict[str, Any]:
"""获取AI准确率配置"""
return asdict(self.ai_accuracy)
def get_system_config(self) -> Dict[str, Any]:
"""获取系统配置"""
return asdict(self.system)
def update_config(self, section: str, config_data: Dict[str, Any]):
"""更新配置"""
try:
if section == 'database':
self.database = DatabaseConfig(**config_data)
elif section == 'llm':
self.llm = LLMConfig(**config_data)
elif section == 'server':
self.server = ServerConfig(**config_data)
elif section == 'feishu':
self.feishu = FeishuConfig(**config_data)
elif section == 'ai_accuracy':
self.ai_accuracy = AIAccuracyConfig(**config_data)
elif section == 'system':
self.system = SystemConfig(**config_data)
else:
raise ValueError(f"未知的配置节: {section}")
self.save_config()
logger.info(f"配置节 {section} 更新成功")
except Exception as e:
logger.error(f"更新配置失败: {e}")
raise
def validate_config(self) -> bool:
"""验证配置有效性"""
try:
# 验证数据库配置
if not self.database.url:
logger.error("数据库URL未配置")
return False
# 验证LLM配置
if not self.llm.api_key:
logger.warning("LLM API密钥未配置")
# 验证飞书配置
if self.feishu.status == "active":
if not all([self.feishu.app_id, self.feishu.app_secret,
self.feishu.app_token, self.feishu.table_id]):
logger.warning("飞书配置不完整")
logger.info("配置验证通过")
return True
except Exception as e:
logger.error(f"配置验证失败: {e}")
return False
"""
统一配置管理器
在实例化时,从环境变量中加载所有配置。
"""
def __init__(self):
logger.info("Initializing unified configuration from environment variables...")
self.database = self._load_database_from_env()
self.llm = self._load_llm_from_env()
self.server = self._load_server_from_env()
self.feishu = self._load_feishu_from_env()
self.ai_accuracy = self._load_ai_accuracy_from_env()
self.validate_config()
def _load_database_from_env(self) -> DatabaseConfig:
db_url = os.getenv("DATABASE_URL")
if not db_url:
raise ValueError("FATAL: DATABASE_URL environment variable is not set.")
logger.info("Database config loaded.")
return DatabaseConfig(url=db_url)
def _load_llm_from_env(self) -> LLMConfig:
api_key = os.getenv("LLM_API_KEY")
if not api_key:
logger.warning("LLM_API_KEY is not set. LLM functionality will be disabled or fail.")
config = LLMConfig(
provider=os.getenv("LLM_PROVIDER", "qwen"),
api_key=api_key,
model=os.getenv("LLM_MODEL", "qwen-plus-latest"),
base_url=os.getenv("LLM_BASE_URL"),
temperature=float(os.getenv("LLM_TEMPERATURE", 0.7)),
max_tokens=int(os.getenv("LLM_MAX_TOKENS", 2000)),
timeout=int(os.getenv("LLM_TIMEOUT", 30))
)
logger.info("LLM config loaded.")
return config
def _load_server_from_env(self) -> ServerConfig:
config = ServerConfig(
host=os.getenv("SERVER_HOST", "0.0.0.0"),
port=int(os.getenv("SERVER_PORT", 5000)),
websocket_port=int(os.getenv("WEBSOCKET_PORT", 8765)),
debug=os.getenv("DEBUG_MODE", "False").lower() in ('true', '1', 't'),
log_level=os.getenv("LOG_LEVEL", "INFO").upper()
)
logger.info("Server config loaded.")
return config
def _load_feishu_from_env(self) -> FeishuConfig:
config = FeishuConfig(
app_id=os.getenv("FEISHU_APP_ID"),
app_secret=os.getenv("FEISHU_APP_SECRET"),
verification_token=os.getenv("FEISHU_VERIFICATION_TOKEN"),
encrypt_key=os.getenv("FEISHU_ENCRYPT_KEY"),
table_id=os.getenv("FEISHU_TABLE_ID")
)
logger.info("Feishu config loaded.")
return config
def _load_ai_accuracy_from_env(self) -> AIAccuracyConfig:
config = AIAccuracyConfig(
auto_approve_threshold=float(os.getenv("AI_AUTO_APPROVE_THRESHOLD", 0.95)),
use_human_resolution_threshold=float(os.getenv("AI_USE_HUMAN_RESOLUTION_THRESHOLD", 0.90)),
manual_review_threshold=float(os.getenv("AI_MANUAL_REVIEW_THRESHOLD", 0.80)),
ai_suggestion_confidence=float(os.getenv("AI_SUGGESTION_CONFIDENCE", 0.95)),
human_resolution_confidence=float(os.getenv("AI_HUMAN_RESOLUTION_CONFIDENCE", 0.90))
)
logger.info("AI Accuracy config loaded.")
return config
def validate_config(self):
"""在启动时验证关键配置"""
if not self.database.url:
raise ValueError("Database URL is missing.")
if not self.llm.api_key:
logger.warning("LLM API key is not configured. AI features may fail.")
if self.feishu.app_id and not self.feishu.app_secret:
logger.warning("FEISHU_APP_ID is set, but FEISHU_APP_SECRET is missing.")
logger.info("Configuration validation passed (warnings may exist).")
# --- Public Getters ---
def get_all_config(self) -> Dict[str, Any]:
"""获取所有配置"""
"""获取所有配置的字典表示"""
return {
'database': asdict(self.database),
'llm': asdict(self.llm),
'server': asdict(self.server),
'feishu': asdict(self.feishu),
'ai_accuracy': asdict(self.ai_accuracy),
'system': asdict(self.system)
}
# 全局配置实例
_config_instance = None
# --- 全局单例模式 ---
_config_instance: Optional[UnifiedConfig] = None
def get_config() -> UnifiedConfig:
"""获取全局配置实例"""
"""
获取全局统一配置实例。
第一次调用时,它会创建并加载配置。后续调用将返回缓存的实例。
"""
global _config_instance
if _config_instance is None:
_config_instance = UnifiedConfig()
_config_instance.load_from_env()
return _config_instance
def reload_config():
"""重新加载配置"""
def reload_config() -> UnifiedConfig:
"""强制重新加载配置"""
global _config_instance
_config_instance = None
return get_config()