feat: 统一API密钥管理 - 将所有API密钥集中到config/api_keys.py进行统一管理,提供更安全便捷的密钥管理方式

This commit is contained in:
赵杰
2025-09-25 14:34:58 +01:00
parent aea5f6bf74
commit 81162475d0
7 changed files with 636 additions and 24 deletions

238
API_KEY_SETUP_GUIDE.md Normal file
View File

@@ -0,0 +1,238 @@
# API密钥配置指南
## 📋 概述
本项目已将所有API密钥统一管理到 `config/api_keys.py` 文件中,提供更安全、更便捷的密钥管理方式。
## 🔧 配置步骤
### 1. 创建环境变量文件
在项目根目录创建 `.env` 文件:
```bash
# 复制模板文件
cp .env.template .env
```
### 2. 配置API密钥
编辑 `.env` 文件填入您的API密钥
```env
# 千问大模型API密钥 (必需)
QWEN_API_KEY=sk-your-actual-qwen-api-key-here
# OpenAI API密钥 (可选)
OPENAI_API_KEY=sk-your-actual-openai-api-key-here
# Anthropic API密钥 (可选)
ANTHROPIC_API_KEY=sk-ant-your-actual-anthropic-api-key-here
```
### 3. 验证配置
运行以下命令验证API密钥配置
```bash
python -c "from config.api_keys import get_api_status_report; print(get_api_status_report())"
```
## 🏗️ 架构说明
### 统一API密钥管理
- **文件位置**: `config/api_keys.py`
- **管理类**: `APIKeyManager`
- **全局实例**: 通过 `get_api_key_manager()` 获取
### 支持的API提供商
| 提供商 | 环境变量 | 必需性 | 用途 |
|--------|----------|--------|------|
| 千问 | `QWEN_API_KEY` | ✅ 必需 | 主要AI分析服务 |
| OpenAI | `OPENAI_API_KEY` | ⚪ 可选 | 备用AI服务 |
| Anthropic | `ANTHROPIC_API_KEY` | ⚪ 可选 | 备用AI服务 |
| Google | `GOOGLE_API_KEY` | ⚪ 可选 | 扩展服务 |
| 百度 | `BAIDU_API_KEY` | ⚪ 可选 | 扩展服务 |
| 腾讯 | `TENCENT_API_KEY` | ⚪ 可选 | 扩展服务 |
## 🔒 安全特性
### 1. 密钥验证
- 自动验证API密钥格式
- 检查密钥是否为空或无效
- 提供详细的验证报告
### 2. 环境隔离
- 所有密钥存储在环境变量中
- `.env` 文件被 `.gitignore` 忽略
- 不会意外提交到版本控制
### 3. 运行时管理
- 支持运行时动态设置密钥
- 提供密钥状态监控
- 支持多提供商切换
## 📚 使用方法
### 基本用法
```python
from config.api_keys import get_qwen_key, get_openai_key
# 获取千问API密钥
qwen_key = get_qwen_key()
if qwen_key:
print("千问API密钥已配置")
else:
print("千问API密钥未配置")
# 获取OpenAI API密钥
openai_key = get_openai_key()
```
### 高级用法
```python
from config.api_keys import get_api_key_manager
# 获取管理器实例
manager = get_api_key_manager()
# 检查提供商可用性
if manager.is_available('qwen'):
print("千问服务可用")
# 获取所有可用提供商
available = manager.get_available_providers()
print(f"可用提供商: {available}")
# 获取状态报告
status = manager.get_status_report()
print(f"配置状态: {status}")
```
### 动态设置密钥
```python
from config.api_keys import get_api_key_manager
manager = get_api_key_manager()
# 运行时设置API密钥
manager.set_key('qwen', 'sk-your-new-api-key')
```
## 🛠️ 开发工具
### 创建环境变量模板
```python
from config.api_keys import create_env_template
# 创建 .env 文件模板
create_env_template()
```
### 获取配置建议
```python
from config.api_keys import get_api_status_report
status = get_api_status_report()
for recommendation in status['recommendations']:
print(f"💡 {recommendation}")
```
## 🔍 故障排除
### 常见问题
1. **API密钥未配置**
```
ValueError: 千问API密钥未配置请在.env文件中设置QWEN_API_KEY
```
**解决方案**: 检查 `.env` 文件是否存在且包含正确的API密钥
2. **密钥格式错误**
```
⚠️ 千问API密钥格式可能不正确
```
**解决方案**: 确保API密钥以 `sk-` 开头(千问)或 `sk-ant-` 开头Anthropic
3. **环境变量未加载**
```
⚠️ 千问API密钥未配置
```
**解决方案**: 确保 `.env` 文件在项目根目录,且格式正确
### 调试命令
```bash
# 检查环境变量
python -c "import os; print('QWEN_API_KEY:', os.getenv('QWEN_API_KEY', 'Not set'))"
# 测试API密钥管理
python config/api_keys.py
# 验证配置
python -c "from config.api_keys import get_api_status_report; import json; print(json.dumps(get_api_status_report(), indent=2, ensure_ascii=False))"
```
## 📈 最佳实践
1. **密钥安全**
- 永远不要将API密钥硬编码在代码中
- 使用环境变量或配置文件
- 定期轮换API密钥
2. **错误处理**
- 始终检查API密钥是否可用
- 提供友好的错误提示
- 实现降级策略
3. **配置管理**
- 使用统一的配置管理
- 提供配置验证
- 支持多环境配置
## 🔄 迁移指南
如果您之前在其他文件中硬编码了API密钥请按以下步骤迁移
1. **移除硬编码密钥**
```python
# 旧方式 ❌
api_key = "sk-hardcoded-key"
# 新方式 ✅
from config.api_keys import get_qwen_key
api_key = get_qwen_key()
```
2. **更新导入语句**
```python
# 添加统一导入
from config.api_keys import get_qwen_key, get_openai_key, get_anthropic_key
```
3. **添加错误处理**
```python
api_key = get_qwen_key()
if not api_key:
raise ValueError("千问API密钥未配置")
```
## 📞 支持
如果您在配置API密钥时遇到问题
1. 检查本指南的故障排除部分
2. 运行调试命令验证配置
3. 查看项目日志获取详细错误信息
4. 提交Issue描述具体问题
---
**注意**: 请妥善保管您的API密钥不要分享给他人或提交到公开仓库。

View File

@@ -121,8 +121,8 @@ diet_recommendation_app/
### 2. 安装依赖 ### 2. 安装依赖
```bash ```bash
# 克隆项目 # 克隆项目
git clone <repository-url> git clone https://github.com/random-jason/diet_recommendation.git
cd diet_recommendation_app cd diet_recommendation
# 安装基础依赖 # 安装基础依赖
pip install -r requirements.txt pip install -r requirements.txt
@@ -131,7 +131,22 @@ pip install -r requirements.txt
pip install pytesseract opencv-python paddleocr easyocr pip install pytesseract opencv-python paddleocr easyocr
``` ```
### 3. OCR引擎配置可选 ### 3. 配置API密钥
```bash
# 方法1: 使用自动生成的模板
python -c "from config.api_keys import create_env_template; create_env_template()"
# 方法2: 手动创建.env文件
echo "QWEN_API_KEY=your_qwen_api_key_here" > .env
```
### 4. 验证配置
```bash
# 检查API密钥配置状态
python -c "from config.api_keys import get_api_status_report; print(get_api_status_report())"
```
### 5. OCR引擎配置可选
#### Tesseract安装 #### Tesseract安装
- **Windows**: 下载 [Tesseract安装包](https://github.com/UB-Mannheim/tesseract/wiki) - **Windows**: 下载 [Tesseract安装包](https://github.com/UB-Mannheim/tesseract/wiki)
@@ -166,6 +181,37 @@ python main.py
5. 查看和编辑识别结果 5. 查看和编辑识别结果
6. 确认保存到餐食记录 6. 确认保存到餐食记录
## 🔐 API密钥管理
### 统一密钥管理
项目采用统一的API密钥管理系统所有API密钥都通过 `config/api_keys.py` 进行管理:
- **千问API密钥** (必需): 用于主要AI分析服务
- **OpenAI API密钥** (可选): 备用AI服务
- **Anthropic API密钥** (可选): 备用AI服务
- **其他API密钥** (可选): Google、百度、腾讯等
### 配置方法
```python
from config.api_keys import get_qwen_key, get_api_status_report
# 获取API密钥
qwen_key = get_qwen_key()
# 检查配置状态
status = get_api_status_report()
print(f"可用提供商: {status['available_list']}")
```
### 安全特性
- ✅ 环境变量隔离
- ✅ 密钥格式验证
- ✅ 运行时状态监控
- ✅ 多提供商支持
- ✅ 自动配置建议
详细配置指南请参考:[API_KEY_SETUP_GUIDE.md](API_KEY_SETUP_GUIDE.md)
## 🔄 工作流程 ## 🔄 工作流程
### 1. 智能数据采集5天 ### 1. 智能数据采集5天

318
config/api_keys.py Normal file
View File

@@ -0,0 +1,318 @@
"""
API密钥统一管理
所有API密钥的集中管理和配置
"""
import os
from typing import Optional, Dict, Any
from dataclasses import dataclass
import logging
# 尝试加载环境变量
try:
from dotenv import load_dotenv
load_dotenv()
except Exception:
# 如果没有.env文件或加载失败使用默认配置
pass
logger = logging.getLogger(__name__)
@dataclass
class APIKeys:
"""API密钥配置类"""
# 千问大模型API密钥
qwen_api_key: Optional[str] = None
# OpenAI API密钥
openai_api_key: Optional[str] = None
# Anthropic API密钥
anthropic_api_key: Optional[str] = None
# 其他API密钥可扩展
google_api_key: Optional[str] = None
baidu_api_key: Optional[str] = None
tencent_api_key: Optional[str] = None
class APIKeyManager:
"""API密钥管理器"""
def __init__(self):
self.keys = APIKeys()
self._load_from_env()
self._validate_keys()
def _load_from_env(self):
"""从环境变量加载API密钥"""
# 千问API密钥
self.keys.qwen_api_key = os.getenv('QWEN_API_KEY')
# OpenAI API密钥
self.keys.openai_api_key = os.getenv('OPENAI_API_KEY')
# Anthropic API密钥
self.keys.anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')
# 其他API密钥
self.keys.google_api_key = os.getenv('GOOGLE_API_KEY')
self.keys.baidu_api_key = os.getenv('BAIDU_API_KEY')
self.keys.tencent_api_key = os.getenv('TENCENT_API_KEY')
logger.info("API密钥已从环境变量加载")
def _validate_keys(self):
"""验证API密钥格式"""
validation_results = {}
# 验证千问API密钥
if self.keys.qwen_api_key:
if self.keys.qwen_api_key.startswith('sk-'):
validation_results['qwen'] = True
logger.info("✅ 千问API密钥格式正确")
else:
validation_results['qwen'] = False
logger.warning("⚠️ 千问API密钥格式可能不正确")
else:
validation_results['qwen'] = False
logger.warning("⚠️ 千问API密钥未配置")
# 验证OpenAI API密钥
if self.keys.openai_api_key:
if self.keys.openai_api_key.startswith('sk-'):
validation_results['openai'] = True
logger.info("✅ OpenAI API密钥格式正确")
else:
validation_results['openai'] = False
logger.warning("⚠️ OpenAI API密钥格式可能不正确")
else:
validation_results['openai'] = False
logger.warning("⚠️ OpenAI API密钥未配置")
# 验证Anthropic API密钥
if self.keys.anthropic_api_key:
if self.keys.anthropic_api_key.startswith('sk-ant-'):
validation_results['anthropic'] = True
logger.info("✅ Anthropic API密钥格式正确")
else:
validation_results['anthropic'] = False
logger.warning("⚠️ Anthropic API密钥格式可能不正确")
else:
validation_results['anthropic'] = False
logger.warning("⚠️ Anthropic API密钥未配置")
return validation_results
def get_qwen_key(self) -> Optional[str]:
"""获取千问API密钥"""
return self.keys.qwen_api_key
def get_openai_key(self) -> Optional[str]:
"""获取OpenAI API密钥"""
return self.keys.openai_api_key
def get_anthropic_key(self) -> Optional[str]:
"""获取Anthropic API密钥"""
return self.keys.anthropic_api_key
def get_key(self, provider: str) -> Optional[str]:
"""根据提供商获取API密钥"""
key_map = {
'qwen': self.keys.qwen_api_key,
'openai': self.keys.openai_api_key,
'anthropic': self.keys.anthropic_api_key,
'google': self.keys.google_api_key,
'baidu': self.keys.baidu_api_key,
'tencent': self.keys.tencent_api_key
}
return key_map.get(provider.lower())
def is_available(self, provider: str) -> bool:
"""检查指定提供商的API密钥是否可用"""
key = self.get_key(provider)
return key is not None and len(key.strip()) > 0
def get_available_providers(self) -> list:
"""获取所有可用的API提供商"""
providers = ['qwen', 'openai', 'anthropic', 'google', 'baidu', 'tencent']
available = []
for provider in providers:
if self.is_available(provider):
available.append(provider)
return available
def get_all_keys(self) -> Dict[str, Optional[str]]:
"""获取所有API密钥用于调试不包含实际密钥值"""
return {
'qwen': '***' if self.keys.qwen_api_key else None,
'openai': '***' if self.keys.openai_api_key else None,
'anthropic': '***' if self.keys.anthropic_api_key else None,
'google': '***' if self.keys.google_api_key else None,
'baidu': '***' if self.keys.baidu_api_key else None,
'tencent': '***' if self.keys.tencent_api_key else None
}
def set_key(self, provider: str, api_key: str):
"""设置API密钥运行时设置"""
provider = provider.lower()
if provider == 'qwen':
self.keys.qwen_api_key = api_key
elif provider == 'openai':
self.keys.openai_api_key = api_key
elif provider == 'anthropic':
self.keys.anthropic_api_key = api_key
elif provider == 'google':
self.keys.google_api_key = api_key
elif provider == 'baidu':
self.keys.baidu_api_key = api_key
elif provider == 'tencent':
self.keys.tencent_api_key = api_key
else:
raise ValueError(f"不支持的API提供商: {provider}")
logger.info(f"API密钥已设置: {provider}")
def create_env_file(self, file_path: str = ".env"):
"""创建.env文件模板"""
env_content = """# API密钥配置文件
# 请将下面的your-api-key-here替换为实际的API密钥
# 千问大模型API密钥
QWEN_API_KEY=your-qwen-api-key-here
# OpenAI API密钥可选
OPENAI_API_KEY=your-openai-api-key-here
# Anthropic API密钥可选
ANTHROPIC_API_KEY=your-anthropic-api-key-here
# Google API密钥可选
GOOGLE_API_KEY=your-google-api-key-here
# 百度API密钥可选
BAIDU_API_KEY=your-baidu-api-key-here
# 腾讯API密钥可选
TENCENT_API_KEY=your-tencent-api-key-here
# 其他配置
DEBUG=true
LOG_LEVEL=INFO
"""
with open(file_path, 'w', encoding='utf-8') as f:
f.write(env_content)
logger.info(f"环境变量文件已创建: {file_path}")
def get_status_report(self) -> Dict[str, Any]:
"""获取API密钥状态报告"""
available_providers = self.get_available_providers()
return {
"total_providers": 6,
"available_providers": len(available_providers),
"available_list": available_providers,
"unavailable_providers": [p for p in ['qwen', 'openai', 'anthropic', 'google', 'baidu', 'tencent']
if p not in available_providers],
"recommendations": self._get_recommendations()
}
def _get_recommendations(self) -> list:
"""获取配置建议"""
recommendations = []
if not self.is_available('qwen'):
recommendations.append("建议配置千问API密钥以获得最佳的中文AI分析体验")
if not self.is_available('openai'):
recommendations.append("可选配置OpenAI API密钥作为备用AI服务")
if not self.is_available('anthropic'):
recommendations.append("可选配置Anthropic API密钥作为备用AI服务")
return recommendations
# 全局API密钥管理器实例
_api_key_manager: Optional[APIKeyManager] = None
def get_api_key_manager() -> APIKeyManager:
"""获取全局API密钥管理器实例"""
global _api_key_manager
if _api_key_manager is None:
_api_key_manager = APIKeyManager()
return _api_key_manager
# 便捷函数
def get_qwen_key() -> Optional[str]:
"""获取千问API密钥"""
return get_api_key_manager().get_qwen_key()
def get_openai_key() -> Optional[str]:
"""获取OpenAI API密钥"""
return get_api_key_manager().get_openai_key()
def get_anthropic_key() -> Optional[str]:
"""获取Anthropic API密钥"""
return get_api_key_manager().get_anthropic_key()
def get_api_key(provider: str) -> Optional[str]:
"""根据提供商获取API密钥"""
return get_api_key_manager().get_key(provider)
def is_api_available(provider: str) -> bool:
"""检查指定提供商的API是否可用"""
return get_api_key_manager().is_available(provider)
def get_available_providers() -> list:
"""获取所有可用的API提供商"""
return get_api_key_manager().get_available_providers()
def create_env_template(file_path: str = ".env"):
"""创建环境变量文件模板"""
return get_api_key_manager().create_env_file(file_path)
def get_api_status_report() -> Dict[str, Any]:
"""获取API状态报告"""
return get_api_key_manager().get_status_report()
if __name__ == "__main__":
# 测试API密钥管理
print("=== API密钥管理测试 ===")
manager = get_api_key_manager()
# 显示状态报告
status = manager.get_status_report()
print(f"✅ 总提供商数量: {status['total_providers']}")
print(f"✅ 可用提供商数量: {status['available_providers']}")
print(f"✅ 可用提供商: {status['available_list']}")
print(f"✅ 不可用提供商: {status['unavailable_providers']}")
# 显示建议
if status['recommendations']:
print("\n💡 配置建议:")
for rec in status['recommendations']:
print(f" - {rec}")
# 创建环境变量文件模板
manager.create_env_file()
print("\n✅ API密钥管理测试完成")

View File

@@ -161,13 +161,16 @@ class UnifiedConfig:
if os.getenv('DATABASE_PATH'): if os.getenv('DATABASE_PATH'):
self.database.database_path = os.getenv('DATABASE_PATH') self.database.database_path = os.getenv('DATABASE_PATH')
# API配置 # API配置 - 使用统一API密钥管理
self.api.qwen_api_key = os.getenv('QWEN_API_KEY') from config.api_keys import get_api_key_manager
api_manager = get_api_key_manager()
self.api.qwen_api_key = api_manager.get_qwen_key()
self.api.qwen_base_url = os.getenv('QWEN_BASE_URL', self.api.qwen_base_url) self.api.qwen_base_url = os.getenv('QWEN_BASE_URL', self.api.qwen_base_url)
self.api.qwen_model = os.getenv('QWEN_MODEL', self.api.qwen_model) self.api.qwen_model = os.getenv('QWEN_MODEL', self.api.qwen_model)
self.api.openai_api_key = os.getenv('OPENAI_API_KEY') self.api.openai_api_key = api_manager.get_openai_key()
self.api.anthropic_api_key = os.getenv('ANTHROPIC_API_KEY') self.api.anthropic_api_key = api_manager.get_anthropic_key()
# 应用配置 # 应用配置
if os.getenv('DEBUG'): if os.getenv('DEBUG'):

View File

@@ -479,8 +479,20 @@ class AIAnalyzer(BaseEngine):
import openai import openai
import anthropic import anthropic
self.openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) from config.api_keys import get_openai_key, get_anthropic_key
self.anthropic_client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
openai_key = get_openai_key()
anthropic_key = get_anthropic_key()
if openai_key:
self.openai_client = openai.OpenAI(api_key=openai_key)
else:
self.logger.warning("OpenAI API密钥未配置")
if anthropic_key:
self.anthropic_client = anthropic.Anthropic(api_key=anthropic_key)
else:
self.logger.warning("Anthropic API密钥未配置")
self._initialized = True self._initialized = True
self.logger.info("AI分析器初始化成功") self.logger.info("AI分析器初始化成功")

View File

@@ -519,10 +519,15 @@ class QwenLLMClient:
} }
# 千问配置 - 从环境变量获取 # 千问配置 - 使用统一API密钥管理
def get_qwen_config() -> LLMConfig: def get_qwen_config() -> LLMConfig:
"""获取千问配置""" """获取千问配置"""
api_key = os.getenv('QWEN_API_KEY', 'sk-c0dbefa1718d46eaa897199135066f00') from config.api_keys import get_qwen_key
api_key = get_qwen_key()
if not api_key:
raise ValueError("千问API密钥未配置请在.env文件中设置QWEN_API_KEY")
base_url = os.getenv('QWEN_BASE_URL', 'https://dashscope.aliyuncs.com/compatible-mode/v1') base_url = os.getenv('QWEN_BASE_URL', 'https://dashscope.aliyuncs.com/compatible-mode/v1')
model = os.getenv('QWEN_MODEL', 'qwen-plus-latest') model = os.getenv('QWEN_MODEL', 'qwen-plus-latest')

16
main.py
View File

@@ -48,19 +48,9 @@ class DietRecommendationApp:
"""加载配置""" """加载配置"""
config = BaseConfig() config = BaseConfig()
# 从环境变量加载API密钥 # 使用统一API密钥管理
config.qwen_api_key = os.getenv('QWEN_API_KEY') from config.api_keys import get_qwen_key
config.qwen_api_key = get_qwen_key()
# 从.env文件加载配置
env_file = Path('.env')
if env_file.exists():
try:
from dotenv import load_dotenv
load_dotenv()
config.qwen_api_key = os.getenv('QWEN_API_KEY')
except Exception:
# 如果.env文件有编码问题跳过加载
pass
return config return config