175 lines
6.3 KiB
Python
175 lines
6.3 KiB
Python
"""
|
||
生成集成模块
|
||
"""
|
||
|
||
import logging
|
||
import os
|
||
import time
|
||
from typing import List
|
||
|
||
from openai import OpenAI
|
||
from langchain_core.documents import Document
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class GenerationIntegrationModule:
|
||
"""生成集成模块 - 负责答案生成"""
|
||
|
||
def __init__(self, model_name: str = "kimi-k2-0711-preview", temperature: float = 0.1, max_tokens: int = 2048):
|
||
"""
|
||
初始化生成集成模块
|
||
"""
|
||
self.model_name = model_name
|
||
self.temperature = temperature
|
||
self.max_tokens = max_tokens
|
||
|
||
# 初始化OpenAI客户端(使用Moonshot API)
|
||
api_key = os.getenv("MOONSHOT_API_KEY")
|
||
if not api_key:
|
||
raise ValueError("请设置 MOONSHOT_API_KEY 环境变量")
|
||
|
||
self.client = OpenAI(
|
||
api_key=api_key,
|
||
base_url="https://api.moonshot.cn/v1"
|
||
)
|
||
|
||
logger.info(f"生成模块初始化完成,模型: {model_name}")
|
||
|
||
def generate_adaptive_answer(self, question: str, documents: List[Document]) -> str:
|
||
"""
|
||
智能统一答案生成
|
||
自动适应不同类型的查询,无需预先分类
|
||
"""
|
||
# 构建上下文
|
||
context_parts = []
|
||
|
||
for doc in documents:
|
||
content = doc.page_content.strip()
|
||
if content:
|
||
# 添加检索层级信息(如果有的话)
|
||
level = doc.metadata.get('retrieval_level', '')
|
||
if level:
|
||
context_parts.append(f"[{level.upper()}] {content}")
|
||
else:
|
||
context_parts.append(content)
|
||
|
||
context = "\n\n".join(context_parts)
|
||
|
||
# LightRAG风格的统一提示词
|
||
prompt = f"""
|
||
作为一位专业的烹饪助手,请基于以下信息回答用户的问题。
|
||
|
||
检索到的相关信息:
|
||
{context}
|
||
|
||
用户问题:{question}
|
||
|
||
请提供准确、实用的回答。根据问题的性质:
|
||
- 如果是询问多个菜品,请提供清晰的列表
|
||
- 如果是询问具体制作方法,请提供详细步骤
|
||
- 如果是一般性咨询,请提供综合性回答
|
||
|
||
回答:
|
||
"""
|
||
|
||
try:
|
||
response = self.client.chat.completions.create(
|
||
model=self.model_name,
|
||
messages=[
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=self.temperature,
|
||
max_tokens=self.max_tokens
|
||
)
|
||
|
||
return response.choices[0].message.content.strip()
|
||
|
||
except Exception as e:
|
||
logger.error(f"LightRAG答案生成失败: {e}")
|
||
return f"抱歉,生成回答时出现错误:{str(e)}"
|
||
|
||
def generate_adaptive_answer_stream(self, question: str, documents: List[Document], max_retries: int = 3):
|
||
"""
|
||
LightRAG风格的流式答案生成(带重试机制)
|
||
"""
|
||
# 构建上下文
|
||
context_parts = []
|
||
|
||
for doc in documents:
|
||
content = doc.page_content.strip()
|
||
if content:
|
||
level = doc.metadata.get('retrieval_level', '')
|
||
if level:
|
||
context_parts.append(f"[{level.upper()}] {content}")
|
||
else:
|
||
context_parts.append(content)
|
||
|
||
context = "\n\n".join(context_parts)
|
||
|
||
# LightRAG风格的统一提示词
|
||
prompt = f"""
|
||
作为一位专业的烹饪助手,请基于以下信息回答用户的问题。
|
||
|
||
检索到的相关信息:
|
||
{context}
|
||
|
||
用户问题:{question}
|
||
|
||
请提供准确、实用的回答。根据问题的性质:
|
||
- 如果是询问多个菜品,请提供清晰的列表
|
||
- 如果是询问具体制作方法,请提供详细步骤
|
||
- 如果是一般性咨询,请提供综合性回答
|
||
|
||
回答:
|
||
"""
|
||
|
||
for attempt in range(max_retries):
|
||
try:
|
||
response = self.client.chat.completions.create(
|
||
model=self.model_name,
|
||
messages=[
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
temperature=self.temperature,
|
||
max_tokens=self.max_tokens,
|
||
stream=True,
|
||
timeout=60 # 增加超时设置
|
||
)
|
||
|
||
if attempt == 0:
|
||
print("开始流式生成回答...\n")
|
||
else:
|
||
print(f"第{attempt + 1}次尝试流式生成...\n")
|
||
|
||
full_response = ""
|
||
for chunk in response:
|
||
if chunk.choices[0].delta.content:
|
||
content = chunk.choices[0].delta.content
|
||
full_response += content
|
||
yield content # 使用yield返回流式内容
|
||
|
||
# 如果成功完成,退出重试循环
|
||
return
|
||
|
||
except Exception as e:
|
||
logger.warning(f"流式生成第{attempt + 1}次尝试失败: {e}")
|
||
|
||
if attempt < max_retries - 1:
|
||
wait_time = (attempt + 1) * 2 # 递增等待时间
|
||
print(f"⚠️ 连接中断,{wait_time}秒后重试...")
|
||
time.sleep(wait_time)
|
||
continue
|
||
else:
|
||
# 所有重试都失败,使用非流式作为后备
|
||
logger.error(f"流式生成完全失败,尝试非流式后备方案")
|
||
print("⚠️ 流式生成失败,切换到标准模式...")
|
||
|
||
try:
|
||
fallback_response = self.generate_adaptive_answer(question, documents)
|
||
yield fallback_response
|
||
return
|
||
except Exception as fallback_error:
|
||
logger.error(f"后备生成也失败: {fallback_error}")
|
||
error_msg = f"抱歉,生成回答时出现网络错误,请稍后重试。错误信息:{str(e)}"
|
||
yield error_msg
|
||
return |