更新环境配置,支持多种AI服务提供商(华为和硅基流动),增强API调用的灵活性和可配置性,同时更新文档以反映新的配置选项和使用说明。
This commit is contained in:
parent
cd27bb4bd0
commit
11be119ffc
58
.env
58
.env
@ -1,18 +1,68 @@
|
||||
# 华为大模型API配置(必需)
|
||||
# ========== AI服务提供商配置 ==========
|
||||
# 选择使用的AI服务提供商
|
||||
# 可选值: 'huawei' 或 'siliconflow'
|
||||
# 默认值: 'siliconflow'
|
||||
AI_PROVIDER=siliconflow
|
||||
|
||||
# ========== 华为大模型API配置 ==========
|
||||
# 当 AI_PROVIDER=huawei 时使用以下配置
|
||||
|
||||
# API端点地址
|
||||
HUAWEI_API_ENDPOINT=http://10.100.31.26:3001/v1/chat/completions
|
||||
|
||||
# API密钥
|
||||
HUAWEI_API_KEY=sk-PoeiV3qwyTIRqcVc84E8E24cD2904872859a87922e0d9186
|
||||
|
||||
HUAWEI_API_TIMEOUT=900
|
||||
|
||||
# 模型名称
|
||||
HUAWEI_MODEL=DeepSeek-R1-Distill-Llama-70B
|
||||
|
||||
# 数据库配置
|
||||
# API超时配置(秒)
|
||||
# 开启思考模式时,响应时间会显著增加,需要更长的超时时间
|
||||
# 默认180秒(3分钟)
|
||||
HUAWEI_API_TIMEOUT=180
|
||||
|
||||
# API最大token数配置
|
||||
# 开启思考模式时,模型可能生成更长的响应,需要更多的token
|
||||
# 默认12000
|
||||
HUAWEI_API_MAX_TOKENS=12000
|
||||
|
||||
# ========== 硅基流动API配置 ==========
|
||||
# 当 AI_PROVIDER=siliconflow 时使用以下配置
|
||||
|
||||
# API端点地址(默认值,通常不需要修改)
|
||||
SILICONFLOW_URL=https://api.siliconflow.cn/v1/chat/completions
|
||||
|
||||
# API密钥(必需)
|
||||
SILICONFLOW_API_KEY=sk-pgujibohpenkomkwlufexmqzyckglgogdiubfplgqxkfqgfu
|
||||
|
||||
# 模型名称(默认值,通常不需要修改)
|
||||
SILICONFLOW_MODEL=Qwen/Qwen2.5-72B-Instruct
|
||||
|
||||
# API超时配置(秒)
|
||||
# 默认120秒
|
||||
SILICONFLOW_API_TIMEOUT=120
|
||||
|
||||
# API最大token数配置
|
||||
# 默认2000
|
||||
SILICONFLOW_API_MAX_TOKENS=2000
|
||||
|
||||
# ========== 数据库配置 ==========
|
||||
DB_HOST=152.136.177.240
|
||||
DB_PORT=5012
|
||||
DB_USER=finyx
|
||||
DB_PASSWORD=6QsGK6MpePZDE57Z
|
||||
DB_NAME=finyx
|
||||
|
||||
# ========== MinIO配置(可选,文档生成功能需要) ==========
|
||||
MINIO_ENDPOINT=minio.datacubeworld.com:9000
|
||||
MINIO_ACCESS_KEY=JOLXFXny3avFSzB0uRA5
|
||||
MINIO_SECRET_KEY=G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I
|
||||
MINIO_BUCKET=finyx
|
||||
MINIO_SECURE=true
|
||||
|
||||
# ========== 服务配置 ==========
|
||||
# 服务端口
|
||||
PORT=7500
|
||||
|
||||
# 调试模式(true/false)
|
||||
DEBUG=False
|
||||
|
||||
56
.env.example
56
.env.example
@ -1,4 +1,12 @@
|
||||
# 华为大模型API配置(必需)
|
||||
# ========== AI服务提供商配置 ==========
|
||||
# 选择使用的AI服务提供商
|
||||
# 可选值: 'huawei' 或 'siliconflow'
|
||||
# 默认值: 'siliconflow'
|
||||
AI_PROVIDER=siliconflow
|
||||
|
||||
# ========== 华为大模型API配置 ==========
|
||||
# 当 AI_PROVIDER=huawei 时使用以下配置
|
||||
|
||||
# API端点地址
|
||||
HUAWEI_API_ENDPOINT=http://10.100.31.26:3001/v1/chat/completions
|
||||
|
||||
@ -8,9 +16,53 @@ HUAWEI_API_KEY=sk-PoeiV3qwyTIRqcVc84E8E24cD2904872859a87922e0d9186
|
||||
# 模型名称
|
||||
HUAWEI_MODEL=DeepSeek-R1-Distill-Llama-70B
|
||||
|
||||
# 数据库配置
|
||||
# API超时配置(秒)
|
||||
# 开启思考模式时,响应时间会显著增加,需要更长的超时时间
|
||||
# 默认180秒(3分钟)
|
||||
HUAWEI_API_TIMEOUT=180
|
||||
|
||||
# API最大token数配置
|
||||
# 开启思考模式时,模型可能生成更长的响应,需要更多的token
|
||||
# 默认12000
|
||||
HUAWEI_API_MAX_TOKENS=12000
|
||||
|
||||
# ========== 硅基流动API配置 ==========
|
||||
# 当 AI_PROVIDER=siliconflow 时使用以下配置
|
||||
|
||||
# API端点地址(默认值,通常不需要修改)
|
||||
SILICONFLOW_URL=https://api.siliconflow.cn/v1/chat/completions
|
||||
|
||||
# API密钥(必需)
|
||||
SILICONFLOW_API_KEY=sk-pgujibohpenkomkwlufexmqzyckglgogdiubfplgqxkfqgfu
|
||||
|
||||
# 模型名称(默认值,通常不需要修改)
|
||||
SILICONFLOW_MODEL=Qwen/Qwen2.5-72B-Instruct
|
||||
|
||||
# API超时配置(秒)
|
||||
# 默认120秒
|
||||
SILICONFLOW_API_TIMEOUT=120
|
||||
|
||||
# API最大token数配置
|
||||
# 默认2000
|
||||
SILICONFLOW_API_MAX_TOKENS=2000
|
||||
|
||||
# ========== 数据库配置 ==========
|
||||
DB_HOST=152.136.177.240
|
||||
DB_PORT=5012
|
||||
DB_USER=finyx
|
||||
DB_PASSWORD=6QsGK6MpePZDE57Z
|
||||
DB_NAME=finyx
|
||||
|
||||
# ========== MinIO配置(可选,文档生成功能需要) ==========
|
||||
MINIO_ENDPOINT=minio.datacubeworld.com:9000
|
||||
MINIO_ACCESS_KEY=JOLXFXny3avFSzB0uRA5
|
||||
MINIO_SECRET_KEY=G1BR8jStNfovkfH5ou39EmPl34E4l7dGrnd3Cz0I
|
||||
MINIO_BUCKET=finyx
|
||||
MINIO_SECURE=true
|
||||
|
||||
# ========== 服务配置 ==========
|
||||
# 服务端口
|
||||
PORT=7500
|
||||
|
||||
# 调试模式(true/false)
|
||||
DEBUG=False
|
||||
|
||||
38
README.md
38
README.md
@ -6,7 +6,10 @@
|
||||
|
||||
- ✅ AI解析接口 (`/api/ai/extract`) - 从输入文本中提取结构化字段
|
||||
- ✅ 字段配置管理 - 从数据库读取字段配置
|
||||
- ✅ 支持华为大模型(DeepSeek-R1-Distill-Llama-70B)
|
||||
- ✅ 支持多种AI服务提供商:
|
||||
- 华为大模型(DeepSeek-R1-Distill-Llama-70B)
|
||||
- 硅基流动(DeepSeek-V3.2-Exp)
|
||||
- ✅ 可通过配置灵活切换AI服务提供商
|
||||
- ✅ Web测试界面 - 可视化测试解析功能
|
||||
|
||||
## 项目结构
|
||||
@ -69,20 +72,30 @@ copy .env.example .env
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
编辑 `.env` 文件,填入你的API密钥:
|
||||
编辑 `.env` 文件,填入你的配置:
|
||||
|
||||
```env
|
||||
# 华为大模型API配置(必需,已内置默认值,如需修改可调整)
|
||||
# API端点地址
|
||||
# ========== AI服务提供商配置 ==========
|
||||
# 选择使用的AI服务提供商
|
||||
# 可选值: 'huawei' 或 'siliconflow'
|
||||
# 默认值: 'siliconflow'
|
||||
AI_PROVIDER=siliconflow
|
||||
|
||||
# ========== 华为大模型API配置(当 AI_PROVIDER=huawei 时使用) ==========
|
||||
HUAWEI_API_ENDPOINT=http://10.100.31.26:3001/v1/chat/completions
|
||||
|
||||
# API密钥
|
||||
HUAWEI_API_KEY=sk-PoeiV3qwyTIRqcVc84E8E24cD2904872859a87922e0d9186
|
||||
|
||||
# 模型名称
|
||||
HUAWEI_MODEL=DeepSeek-R1-Distill-Llama-70B
|
||||
HUAWEI_API_TIMEOUT=180
|
||||
HUAWEI_API_MAX_TOKENS=12000
|
||||
|
||||
# 数据库配置(已默认配置,如需修改可调整)
|
||||
# ========== 硅基流动API配置(当 AI_PROVIDER=siliconflow 时使用) ==========
|
||||
SILICONFLOW_URL=https://api.siliconflow.cn/v1/chat/completions
|
||||
SILICONFLOW_API_KEY=your_siliconflow_api_key_here
|
||||
SILICONFLOW_MODEL=deepseek-ai/DeepSeek-V3.2-Exp
|
||||
SILICONFLOW_API_TIMEOUT=120
|
||||
SILICONFLOW_API_MAX_TOKENS=2000
|
||||
|
||||
# ========== 数据库配置 ==========
|
||||
DB_HOST=152.136.177.240
|
||||
DB_PORT=5012
|
||||
DB_USER=finyx
|
||||
@ -90,6 +103,13 @@ DB_PASSWORD=6QsGK6MpePZDE57Z
|
||||
DB_NAME=finyx
|
||||
```
|
||||
|
||||
**AI服务提供商选择说明:**
|
||||
|
||||
- **华为大模型**:设置 `AI_PROVIDER=huawei`,并配置 `HUAWEI_API_KEY` 和 `HUAWEI_API_ENDPOINT`
|
||||
- **硅基流动**:设置 `AI_PROVIDER=siliconflow`(默认值),并配置 `SILICONFLOW_API_KEY`
|
||||
|
||||
如果配置的AI服务不完整,系统会自动尝试使用另一个可用的服务。
|
||||
|
||||
**华为大模型API调用示例:**
|
||||
|
||||
```bash
|
||||
|
||||
@ -31,7 +31,12 @@ class AIService:
|
||||
"""AI服务类"""
|
||||
|
||||
def __init__(self):
|
||||
# 华为大模型配置(必需)
|
||||
# ========== AI服务提供商选择 ==========
|
||||
# 通过环境变量 AI_PROVIDER 选择使用的AI服务
|
||||
# 可选值: 'huawei' 或 'siliconflow',默认为 'siliconflow'
|
||||
self.ai_provider_config = os.getenv('AI_PROVIDER', 'siliconflow').lower()
|
||||
|
||||
# ========== 华为大模型配置 ==========
|
||||
self.huawei_api_endpoint = os.getenv('HUAWEI_API_ENDPOINT', 'http://10.100.31.26:3001/v1/chat/completions')
|
||||
self.huawei_api_key = os.getenv('HUAWEI_API_KEY', 'sk-PoeiV3qwyTIRqcVc84E8E24cD2904872859a87922e0d9186')
|
||||
self.huawei_model = os.getenv('HUAWEI_MODEL', 'DeepSeek-R1-Distill-Llama-70B')
|
||||
@ -39,16 +44,39 @@ class AIService:
|
||||
# API超时配置(秒)
|
||||
# 开启思考模式时,响应时间会显著增加,需要更长的超时时间
|
||||
# 可以通过环境变量 HUAWEI_API_TIMEOUT 自定义,默认180秒(3分钟)
|
||||
self.api_timeout = int(os.getenv('HUAWEI_API_TIMEOUT', '180'))
|
||||
self.huawei_api_timeout = int(os.getenv('HUAWEI_API_TIMEOUT', '180'))
|
||||
|
||||
# API最大token数配置
|
||||
# 开启思考模式时,模型可能生成更长的响应,需要更多的token
|
||||
# 可以通过环境变量 HUAWEI_API_MAX_TOKENS 自定义,默认12000
|
||||
self.api_max_tokens = int(os.getenv('HUAWEI_API_MAX_TOKENS', '12000'))
|
||||
self.huawei_api_max_tokens = int(os.getenv('HUAWEI_API_MAX_TOKENS', '12000'))
|
||||
|
||||
# ========== 硅基流动配置 ==========
|
||||
self.siliconflow_url = os.getenv('SILICONFLOW_URL', 'https://api.siliconflow.cn/v1/chat/completions')
|
||||
self.siliconflow_api_key = os.getenv('SILICONFLOW_API_KEY', '')
|
||||
self.siliconflow_model = os.getenv('SILICONFLOW_MODEL', 'deepseek-ai/DeepSeek-V3.2-Exp')
|
||||
|
||||
# API超时配置(秒)
|
||||
self.siliconflow_api_timeout = int(os.getenv('SILICONFLOW_API_TIMEOUT', '120'))
|
||||
|
||||
# API最大token数配置
|
||||
self.siliconflow_api_max_tokens = int(os.getenv('SILICONFLOW_API_MAX_TOKENS', '2000'))
|
||||
|
||||
# 确定使用的AI服务
|
||||
self.ai_provider = self._determine_ai_provider()
|
||||
|
||||
# 根据选择的提供商设置当前使用的超时和token配置
|
||||
if self.ai_provider == 'huawei':
|
||||
self.api_timeout = self.huawei_api_timeout
|
||||
self.api_max_tokens = self.huawei_api_max_tokens
|
||||
elif self.ai_provider == 'siliconflow':
|
||||
self.api_timeout = self.siliconflow_api_timeout
|
||||
self.api_max_tokens = self.siliconflow_api_max_tokens
|
||||
else:
|
||||
# 默认值
|
||||
self.api_timeout = 120
|
||||
self.api_max_tokens = 2000
|
||||
|
||||
# 初始化AI日志记录器
|
||||
if AI_LOGGER_AVAILABLE:
|
||||
try:
|
||||
@ -60,8 +88,45 @@ class AIService:
|
||||
self.ai_logger = None
|
||||
|
||||
def _determine_ai_provider(self) -> str:
|
||||
"""确定使用的AI服务提供商(仅支持华为大模型)"""
|
||||
"""
|
||||
确定使用的AI服务提供商
|
||||
根据 AI_PROVIDER 环境变量配置选择,支持 'huawei' 或 'siliconflow'
|
||||
"""
|
||||
# 根据配置选择服务提供商
|
||||
if self.ai_provider_config == 'huawei':
|
||||
# 检查华为大模型配置是否完整
|
||||
if self.huawei_api_endpoint and self.huawei_api_key:
|
||||
print(f"[AI服务] 使用华为大模型: {self.huawei_model}")
|
||||
return 'huawei'
|
||||
else:
|
||||
print(f"[AI服务] 警告: 配置为使用华为大模型,但配置不完整,尝试使用硅基流动")
|
||||
# 如果华为配置不完整,尝试使用硅基流动
|
||||
if self.siliconflow_api_key and self.siliconflow_url:
|
||||
print(f"[AI服务] 使用硅基流动: {self.siliconflow_model}")
|
||||
return 'siliconflow'
|
||||
else:
|
||||
return 'none'
|
||||
elif self.ai_provider_config == 'siliconflow':
|
||||
# 检查硅基流动配置是否完整
|
||||
if self.siliconflow_api_key and self.siliconflow_url:
|
||||
print(f"[AI服务] 使用硅基流动: {self.siliconflow_model}")
|
||||
return 'siliconflow'
|
||||
else:
|
||||
print(f"[AI服务] 警告: 配置为使用硅基流动,但配置不完整,尝试使用华为大模型")
|
||||
# 如果硅基流动配置不完整,尝试使用华为大模型
|
||||
if self.huawei_api_endpoint and self.huawei_api_key:
|
||||
print(f"[AI服务] 使用华为大模型: {self.huawei_model}")
|
||||
return 'huawei'
|
||||
else:
|
||||
return 'none'
|
||||
else:
|
||||
# 未知的配置值,尝试自动检测
|
||||
print(f"[AI服务] 警告: 未知的AI_PROVIDER配置值 '{self.ai_provider_config}',尝试自动检测")
|
||||
if self.siliconflow_api_key and self.siliconflow_url:
|
||||
print(f"[AI服务] 自动选择硅基流动: {self.siliconflow_model}")
|
||||
return 'siliconflow'
|
||||
elif self.huawei_api_endpoint and self.huawei_api_key:
|
||||
print(f"[AI服务] 自动选择华为大模型: {self.huawei_model}")
|
||||
return 'huawei'
|
||||
else:
|
||||
return 'none'
|
||||
@ -78,33 +143,36 @@ class AIService:
|
||||
提取的字段字典,格式: {field_code: field_value}
|
||||
"""
|
||||
if self.ai_provider == 'none':
|
||||
raise Exception("未配置华为大模型服务,请设置 HUAWEI_API_KEY 和 HUAWEI_API_ENDPOINT")
|
||||
raise Exception("未配置AI服务,请设置 AI_PROVIDER 环境变量('huawei' 或 'siliconflow'),并配置相应的API密钥")
|
||||
|
||||
if self.ai_provider == 'huawei':
|
||||
return self._extract_with_huawei(prompt, output_fields)
|
||||
elif self.ai_provider == 'siliconflow':
|
||||
return self._extract_with_siliconflow(prompt, output_fields)
|
||||
else:
|
||||
raise Exception(f"未知的AI服务提供商: {self.ai_provider}")
|
||||
|
||||
def _extract_with_siliconflow(self, prompt: str, output_fields: List[Dict]) -> Optional[Dict]:
|
||||
"""
|
||||
使用硅基流动API提取字段(已不再使用,仅保留用于参考)
|
||||
系统仅支持华为大模型,不再支持自动回退
|
||||
使用硅基流动API提取字段(临时启用)
|
||||
"""
|
||||
try:
|
||||
# 生成会话ID(用于关联同一次调用的请求和响应)
|
||||
session_id = f"session_{int(time.time() * 1000)}"
|
||||
|
||||
payload = {
|
||||
"model": self.siliconflow_model,
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "你是一个专业的数据提取助手,能够从文本中准确提取结构化信息。请严格按照JSON格式返回结果。"
|
||||
"content": "你是一个专业的数据提取助手。请从输入文本中提取结构化信息,并严格按照JSON格式返回结果。\n\n⚠️ 核心要求(必须严格遵守):\n\n1. 字段提取要求:\n - 如果文本中明确提到信息(如性别、年龄、职务、职级、线索来源等),必须提取,绝对不能设为空字符串\n - 性别字段(target_gender):如果文本中出现\"男\"、\"女\"、\"男性\"、\"女性\"、\"先生\"、\"女士\"等任何表示性别的词汇,必须提取并转换为\"男\"或\"女\",不能为空\n - 职级字段(target_professional_rank):如果文本中提到\"正处级\"、\"副处级\"、\"正科级\"等,必须提取,不能为空\n - 线索来源字段(clue_source):如果文本中提到\"举报\"、\"群众举报\"、\"来信\"等,必须提取,不能为空\n\n2. 字段名格式要求(严格禁止错误):\n - 必须使用\"target_professional_rank\",禁止使用\"_professional_rank\"或任何下划线前缀\n - 必须使用\"clue_source\",禁止使用\"_source\"、\"source\"或任何下划线前缀\n - 必须使用\"target_organization\",禁止使用\"target_organisation\"(英式拼写)\n - 所有字段名必须严格按照JSON示例中的字段编码,不能随意修改\n\n3. JSON格式要求:\n - 只返回JSON对象,不要包含任何其他文字、思考过程、markdown代码块标记或```json标记\n - 所有字段名必须使用双引号\n - 必须返回所有要求的字段,即使值为空字符串也要包含在JSON中\n - JSON格式必须完整且有效,不能有语法错误\n\n4. 提取逻辑:\n - 逐字逐句仔细阅读输入文本,不要遗漏任何信息\n - 对于性别、职级、线索来源等关键字段,请特别仔细查找\n - 如果文本中明确提到某个信息,必须提取出来,不能设为空"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
}
|
||||
],
|
||||
"temperature": 0.3,
|
||||
"max_tokens": 2000
|
||||
"temperature": 0.2,
|
||||
"max_tokens": self.siliconflow_api_max_tokens
|
||||
}
|
||||
|
||||
headers = {
|
||||
@ -112,46 +180,170 @@ class AIService:
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 记录请求信息(发送请求前)
|
||||
api_request_info = {
|
||||
"endpoint": self.siliconflow_url,
|
||||
"model": self.siliconflow_model,
|
||||
"messages": payload["messages"],
|
||||
"temperature": payload.get("temperature"),
|
||||
"max_tokens": payload.get("max_tokens"),
|
||||
}
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_request_only(prompt, api_request_info, session_id)
|
||||
|
||||
print(f"[AI服务] 正在调用硅基流动API...")
|
||||
|
||||
extracted_data = None
|
||||
error_message = None
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
self.siliconflow_url,
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=30
|
||||
timeout=self.siliconflow_api_timeout
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"API调用失败: {response.status_code} - {response.text}")
|
||||
error_message = f"API调用失败: {response.status_code} - {response.text}"
|
||||
# 记录错误
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=None,
|
||||
extracted_data=None,
|
||||
error=error_message,
|
||||
session_id=session_id
|
||||
)
|
||||
raise Exception(error_message)
|
||||
|
||||
result = response.json()
|
||||
|
||||
# 提取AI返回的内容
|
||||
if 'choices' in result and len(result['choices']) > 0:
|
||||
content = result['choices'][0]['message']['content']
|
||||
raw_content = result['choices'][0]['message']['content']
|
||||
|
||||
# 尝试解析JSON
|
||||
try:
|
||||
# 如果返回的是代码块,提取JSON部分
|
||||
if '```json' in content:
|
||||
json_start = content.find('```json') + 7
|
||||
json_end = content.find('```', json_start)
|
||||
content = content[json_start:json_end].strip()
|
||||
elif '```' in content:
|
||||
json_start = content.find('```') + 3
|
||||
json_end = content.find('```', json_start)
|
||||
content = content[json_start:json_end].strip()
|
||||
# 调试:打印原始返回内容(前500字符)
|
||||
print(f"[AI服务] API返回的原始内容(前500字符): {raw_content[:500]}")
|
||||
|
||||
extracted_data = json.loads(content)
|
||||
return extracted_data
|
||||
except json.JSONDecodeError:
|
||||
# 如果不是JSON,尝试从文本中提取
|
||||
return self._parse_text_response(content, output_fields)
|
||||
# 清理内容
|
||||
content = raw_content
|
||||
|
||||
# 尝试解析JSON(使用增强的修复机制)
|
||||
extracted_data = self._extract_json_from_text(content)
|
||||
if extracted_data:
|
||||
print(f"[AI服务] JSON解析成功,提取到 {len(extracted_data)} 个字段")
|
||||
print(f"[AI服务] 原始字段名: {list(extracted_data.keys())}")
|
||||
# 规范化字段名并映射到正确的字段编码
|
||||
normalized_data = self._normalize_field_names(extracted_data, output_fields)
|
||||
print(f"[AI服务] 规范化后的字段名: {list(normalized_data.keys())}")
|
||||
# 打印关键字段的值用于调试
|
||||
for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth']:
|
||||
if key in normalized_data:
|
||||
print(f"[AI服务] 规范化后 {key} = '{normalized_data[key]}'")
|
||||
# 规范化日期格式
|
||||
normalized_data = self._normalize_date_formats(normalized_data, output_fields)
|
||||
# 后处理:从已有信息推断缺失字段(传入原始prompt以便从输入文本中提取)
|
||||
normalized_data = self._post_process_inferred_fields(normalized_data, output_fields, prompt)
|
||||
# 打印后处理后的关键字段
|
||||
for key in ['target_name', 'target_gender', 'target_age', 'target_date_of_birth', 'target_organization', 'target_position']:
|
||||
if key in normalized_data:
|
||||
print(f"[AI服务] 后处理后 {key} = '{normalized_data[key]}'")
|
||||
# 即使提取的字段不完整,也返回结果(更宽容的处理)
|
||||
if any(v for v in normalized_data.values() if v): # 至少有一个非空字段
|
||||
print(f"[AI服务] 返回提取的数据(包含 {sum(1 for v in normalized_data.values() if v)} 个非空字段)")
|
||||
# 记录成功的对话
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=result,
|
||||
extracted_data=normalized_data,
|
||||
error=None,
|
||||
session_id=session_id
|
||||
)
|
||||
return normalized_data
|
||||
else:
|
||||
raise Exception("API返回格式异常")
|
||||
print(f"[AI服务] 警告:提取的数据全部为空,但继续返回(允许部分字段为空)")
|
||||
# 记录对话(即使数据为空)
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=result,
|
||||
extracted_data=normalized_data,
|
||||
error="提取的数据全部为空",
|
||||
session_id=session_id
|
||||
)
|
||||
return normalized_data
|
||||
|
||||
# 如果无法提取JSON,记录错误但尝试更宽容的处理
|
||||
print(f"[AI服务] 警告:无法从内容中提取完整JSON,尝试备用解析方法")
|
||||
print(f"[AI服务] 清理后的内容(前500字符): {content[:500]}")
|
||||
|
||||
# 尝试从文本中提取
|
||||
parsed_data = self._parse_text_response(content, output_fields)
|
||||
if parsed_data and any(v for v in parsed_data.values() if v): # 至少有一个非空字段
|
||||
print(f"[AI服务] 使用备用方法解析成功,提取到 {len(parsed_data)} 个字段")
|
||||
# 记录对话
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=result,
|
||||
extracted_data=parsed_data,
|
||||
error=None,
|
||||
session_id=session_id
|
||||
)
|
||||
return parsed_data
|
||||
|
||||
# 如果所有方法都失败,返回空字典而不是抛出异常(更宽容)
|
||||
error_msg = f"无法从API返回内容中提取JSON数据。原始内容长度: {len(raw_content)}, 清理后内容长度: {len(content)}"
|
||||
print(f"[AI服务] 警告:{error_msg}")
|
||||
print(f"[AI服务] 完整内容: {content}")
|
||||
# 返回一个包含所有输出字段的空字典,而不是抛出异常
|
||||
empty_result = {field['field_code']: '' for field in output_fields}
|
||||
print(f"[AI服务] 返回空结果(包含 {len(empty_result)} 个字段,全部为空)")
|
||||
# 记录失败的对话
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=result,
|
||||
extracted_data=empty_result,
|
||||
error=error_msg,
|
||||
session_id=session_id
|
||||
)
|
||||
return empty_result
|
||||
else:
|
||||
error_msg = "API返回格式异常:未找到choices字段或choices为空"
|
||||
# 记录错误
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=result,
|
||||
extracted_data=None,
|
||||
error=error_msg,
|
||||
session_id=session_id
|
||||
)
|
||||
raise Exception(error_msg)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
raise Exception("AI服务调用超时")
|
||||
except Exception as e:
|
||||
raise Exception(f"AI服务调用失败: {str(e)}")
|
||||
# 如果发生异常,记录错误日志
|
||||
error_msg = str(e)
|
||||
if self.ai_logger:
|
||||
self.ai_logger.log_conversation(
|
||||
prompt=prompt,
|
||||
api_request=api_request_info,
|
||||
api_response=None,
|
||||
extracted_data=None,
|
||||
error=error_msg,
|
||||
session_id=session_id
|
||||
)
|
||||
# 重新抛出异常,让上层处理
|
||||
raise
|
||||
|
||||
def _extract_with_huawei(self, prompt: str, output_fields: List[Dict]) -> Optional[Dict]:
|
||||
"""
|
||||
@ -249,7 +441,7 @@ class AIService:
|
||||
"top_p": 0.9, # 降低top_p,更聚焦
|
||||
"top_k": 40, # 增加top_k,允许更多选择
|
||||
"seed": 1,
|
||||
"max_tokens": self.api_max_tokens,
|
||||
"max_tokens": self.huawei_api_max_tokens,
|
||||
"n": 1,
|
||||
"enable_thinking": False # 关闭思考模式以提高JSON生成稳定性
|
||||
}
|
||||
@ -276,11 +468,11 @@ class AIService:
|
||||
enable_thinking = payload.get('enable_thinking', False)
|
||||
if enable_thinking:
|
||||
# 思考模式:使用配置的超时时间(默认180秒)
|
||||
timeout = self.api_timeout
|
||||
timeout = self.huawei_api_timeout
|
||||
print(f"[AI服务] 思考模式已开启,使用超时时间: {timeout}秒")
|
||||
else:
|
||||
# 非思考模式:使用较短的超时时间
|
||||
timeout = min(self.api_timeout, 120) # 最多120秒
|
||||
timeout = min(self.huawei_api_timeout, 120) # 最多120秒
|
||||
print(f"[AI服务] 思考模式未开启,使用超时时间: {timeout}秒")
|
||||
|
||||
extracted_data = None
|
||||
|
||||
@ -15,17 +15,50 @@ class TemplateAIHelper:
|
||||
"""模板AI辅助类,用于智能分析文档内容"""
|
||||
|
||||
def __init__(self):
|
||||
# 华为大模型配置(必需)
|
||||
# ========== AI服务提供商选择 ==========
|
||||
# 通过环境变量 AI_PROVIDER 选择使用的AI服务
|
||||
# 可选值: 'huawei' 或 'siliconflow',默认为 'siliconflow'
|
||||
ai_provider = os.getenv('AI_PROVIDER', 'siliconflow').lower()
|
||||
|
||||
# ========== 华为大模型配置 ==========
|
||||
huawei_key = os.getenv('HUAWEI_API_KEY', 'sk-PoeiV3qwyTIRqcVc84E8E24cD2904872859a87922e0d9186')
|
||||
huawei_endpoint = os.getenv('HUAWEI_API_ENDPOINT', 'http://10.100.31.26:3001/v1/chat/completions')
|
||||
huawei_model = os.getenv('HUAWEI_MODEL', 'DeepSeek-R1-Distill-Llama-70B')
|
||||
|
||||
# ========== 硅基流动配置 ==========
|
||||
siliconflow_key = os.getenv('SILICONFLOW_API_KEY', '')
|
||||
siliconflow_url = os.getenv('SILICONFLOW_URL', 'https://api.siliconflow.cn/v1/chat/completions')
|
||||
siliconflow_model = os.getenv('SILICONFLOW_MODEL', 'deepseek-ai/DeepSeek-V3.2-Exp')
|
||||
|
||||
# 根据配置选择服务提供商
|
||||
if ai_provider == 'huawei':
|
||||
if not huawei_key or not huawei_endpoint:
|
||||
raise Exception("未配置华为大模型服务,请设置 HUAWEI_API_KEY 和 HUAWEI_API_ENDPOINT")
|
||||
|
||||
# 使用华为大模型
|
||||
raise Exception("未配置华为大模型服务,请设置 HUAWEI_API_KEY 和 HUAWEI_API_ENDPOINT,或设置 AI_PROVIDER=siliconflow 使用硅基流动")
|
||||
self.api_key = huawei_key
|
||||
self.model = os.getenv('HUAWEI_MODEL', 'DeepSeek-R1-Distill-Llama-70B')
|
||||
self.model = huawei_model
|
||||
self.api_url = huawei_endpoint
|
||||
print(f"[模板AI助手] 使用华为大模型: {huawei_model}")
|
||||
elif ai_provider == 'siliconflow':
|
||||
if not siliconflow_key:
|
||||
raise Exception("未配置硅基流动服务,请设置 SILICONFLOW_API_KEY,或设置 AI_PROVIDER=huawei 使用华为大模型")
|
||||
self.api_key = siliconflow_key
|
||||
self.model = siliconflow_model
|
||||
self.api_url = siliconflow_url
|
||||
print(f"[模板AI助手] 使用硅基流动: {siliconflow_model}")
|
||||
else:
|
||||
# 自动检测:优先使用硅基流动,如果未配置则使用华为大模型
|
||||
if siliconflow_key and siliconflow_url:
|
||||
self.api_key = siliconflow_key
|
||||
self.model = siliconflow_model
|
||||
self.api_url = siliconflow_url
|
||||
print(f"[模板AI助手] 自动选择硅基流动: {siliconflow_model}")
|
||||
elif huawei_key and huawei_endpoint:
|
||||
self.api_key = huawei_key
|
||||
self.model = huawei_model
|
||||
self.api_url = huawei_endpoint
|
||||
print(f"[模板AI助手] 自动选择华为大模型: {huawei_model}")
|
||||
else:
|
||||
raise Exception("未配置AI服务,请设置 AI_PROVIDER 环境变量('huawei' 或 'siliconflow'),并配置相应的API密钥")
|
||||
|
||||
def test_api_connection(self) -> bool:
|
||||
"""
|
||||
@ -35,9 +68,9 @@ class TemplateAIHelper:
|
||||
是否连接成功
|
||||
"""
|
||||
try:
|
||||
print(f" [测试] 正在测试华为大模型API连接...")
|
||||
print(f" [测试] 正在测试API连接...")
|
||||
|
||||
# 华为大模型payload
|
||||
# 测试payload
|
||||
test_payload = {
|
||||
"model": self.model,
|
||||
"messages": [
|
||||
@ -46,11 +79,14 @@ class TemplateAIHelper:
|
||||
"content": "测试"
|
||||
}
|
||||
],
|
||||
"stream": False,
|
||||
"temperature": 0.5,
|
||||
"max_tokens": 10
|
||||
}
|
||||
|
||||
# 如果是华为大模型,添加额外的参数
|
||||
if 'huawei' in self.api_url.lower() or '10.100.31.26' in self.api_url:
|
||||
test_payload["stream"] = False
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
@ -146,7 +182,7 @@ class TemplateAIHelper:
|
||||
|
||||
只返回JSON,不要其他说明文字。"""
|
||||
|
||||
# 调用AI API(华为大模型)
|
||||
# 调用AI API
|
||||
payload = {
|
||||
"model": self.model,
|
||||
"messages": [
|
||||
@ -159,18 +195,21 @@ class TemplateAIHelper:
|
||||
"content": prompt
|
||||
}
|
||||
],
|
||||
"stream": False,
|
||||
"presence_penalty": 1.03,
|
||||
"frequency_penalty": 1.0,
|
||||
"repetition_penalty": 1.0,
|
||||
"temperature": 0.5,
|
||||
"top_p": 0.95,
|
||||
"top_k": 1,
|
||||
"seed": 1,
|
||||
"max_tokens": 8192,
|
||||
"n": 1
|
||||
"max_tokens": 8192
|
||||
}
|
||||
|
||||
# 如果是华为大模型,添加额外的参数
|
||||
if 'huawei' in self.api_url.lower() or '10.100.31.26' in self.api_url:
|
||||
payload["stream"] = False
|
||||
payload["presence_penalty"] = 1.03
|
||||
payload["frequency_penalty"] = 1.0
|
||||
payload["repetition_penalty"] = 1.0
|
||||
payload["top_p"] = 0.95
|
||||
payload["top_k"] = 1
|
||||
payload["seed"] = 1
|
||||
payload["n"] = 1
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user