调用api进行对话
This commit is contained in:
parent
00deeb711b
commit
c0bba14ee8
@ -14,7 +14,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# API 配置
|
# API 配置
|
||||||
API_BASE_URL = 'http://81.69.223.133:48329'
|
API_BASE_URL = 'http://180.163.88.62:30331'
|
||||||
|
|
||||||
DEPARTMENT_GROUPS = {
|
DEPARTMENT_GROUPS = {
|
||||||
"技术部": ["开发组", "测试组", "运维组"],
|
"技术部": ["开发组", "测试组", "运维组"],
|
||||||
|
@ -40,6 +40,8 @@ from django.utils.decorators import method_decorator
|
|||||||
import uuid
|
import uuid
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
import traceback
|
import traceback
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -178,55 +180,145 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
|
|||||||
"""创建聊天记录"""
|
"""创建聊天记录"""
|
||||||
try:
|
try:
|
||||||
data = request.data
|
data = request.data
|
||||||
required_fields = ['dataset_id', 'dataset_name', 'question', 'answer']
|
|
||||||
|
|
||||||
# 检查必填字段
|
# 检查必填字段 - 支持单知识库或多知识库模式
|
||||||
for field in required_fields:
|
if 'question' not in data:
|
||||||
if field not in data:
|
|
||||||
return Response({
|
return Response({
|
||||||
'code': 400,
|
'code': 400,
|
||||||
'message': f'缺少必填字段: {field}',
|
'message': '缺少必填字段: question',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 检查知识库ID:支持dataset_id或dataset_id_list格式
|
||||||
|
dataset_ids = []
|
||||||
|
if 'dataset_id' in data:
|
||||||
|
dataset_ids.append(data['dataset_id'])
|
||||||
|
elif 'dataset_id_list' in data and isinstance(data['dataset_id_list'], list):
|
||||||
|
dataset_ids = data['dataset_id_list']
|
||||||
|
else:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '缺少必填字段: dataset_id 或 dataset_id_list',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not dataset_ids:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '至少需要提供一个知识库ID',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# 验证所有知识库并收集external_ids
|
||||||
|
external_id_list = []
|
||||||
|
user = request.user
|
||||||
|
primary_knowledge_base = None # 主知识库,用于关联聊天记录
|
||||||
|
|
||||||
|
for idx, kb_id in enumerate(dataset_ids):
|
||||||
|
try:
|
||||||
|
knowledge_base = KnowledgeBase.objects.filter(id=kb_id).first()
|
||||||
|
if not knowledge_base:
|
||||||
|
return Response({
|
||||||
|
'code': 404,
|
||||||
|
'message': f'知识库不存在: {kb_id}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
# 保存第一个知识库作为主知识库
|
||||||
|
if idx == 0:
|
||||||
|
primary_knowledge_base = knowledge_base
|
||||||
|
|
||||||
|
# 检查知识库权限
|
||||||
|
can_read = False
|
||||||
|
|
||||||
|
# 检查权限表
|
||||||
|
permission = KBPermissionModel.objects.filter(
|
||||||
|
knowledge_base=knowledge_base,
|
||||||
|
user=user,
|
||||||
|
can_read=True,
|
||||||
|
status='active'
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if permission:
|
||||||
|
can_read = True
|
||||||
|
else:
|
||||||
|
# 使用_can_read方法判断
|
||||||
|
can_read = self._can_read(
|
||||||
|
type=knowledge_base.type,
|
||||||
|
user=user,
|
||||||
|
department=knowledge_base.department,
|
||||||
|
group=knowledge_base.group,
|
||||||
|
creator_id=knowledge_base.user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not can_read:
|
||||||
|
return Response({
|
||||||
|
'code': 403,
|
||||||
|
'message': f'无权访问知识库: {knowledge_base.name}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
# 添加知识库的external_id到列表
|
||||||
|
if knowledge_base.external_id:
|
||||||
|
external_id_list.append(knowledge_base.external_id)
|
||||||
|
else:
|
||||||
|
logger.warning(f"知识库 {knowledge_base.id} ({knowledge_base.name}) 没有external_id")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': f'处理知识库ID出错: {str(e)}',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not external_id_list:
|
||||||
|
return Response({
|
||||||
|
'code': 400,
|
||||||
|
'message': '没有有效的知识库external_id',
|
||||||
'data': None
|
'data': None
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# 获取或创建对话ID
|
# 获取或创建对话ID
|
||||||
conversation_id = data.get('conversation_id', str(uuid.uuid4()))
|
conversation_id = data.get('conversation_id', str(uuid.uuid4()))
|
||||||
|
|
||||||
# 获取知识库 - 不进行 UUID 转换
|
# 调用外部API获取答案 (传递多个knowledge base的external_id)
|
||||||
try:
|
answer = self._get_answer_from_external_api(
|
||||||
knowledge_base = KnowledgeBase.objects.filter(id=data['dataset_id']).first()
|
dataset_external_id_list=external_id_list,
|
||||||
if not knowledge_base:
|
question=data['question']
|
||||||
return Response({
|
)
|
||||||
'code': 404,
|
|
||||||
'message': '知识库不存在',
|
|
||||||
'data': None
|
|
||||||
}, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
except Exception as e:
|
|
||||||
return Response({
|
|
||||||
'code': 400,
|
|
||||||
'message': f'无效的知识库ID: {str(e)}',
|
|
||||||
'data': None
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# 创建用户问题记录
|
if not answer:
|
||||||
|
return Response({
|
||||||
|
'code': 500,
|
||||||
|
'message': '获取AI回答失败',
|
||||||
|
'data': None
|
||||||
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
# 创建用户问题记录 (关联到主知识库)
|
||||||
question_record = ChatHistory.objects.create(
|
question_record = ChatHistory.objects.create(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
knowledge_base=knowledge_base,
|
knowledge_base=primary_knowledge_base,
|
||||||
conversation_id=conversation_id,
|
conversation_id=conversation_id,
|
||||||
role='user',
|
role='user',
|
||||||
content=data['question'],
|
content=data['question'],
|
||||||
metadata={'model_name': data.get('model_name', 'default')}
|
metadata={
|
||||||
|
'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'),
|
||||||
|
'dataset_id_list': dataset_ids
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 创建AI回答记录
|
# 创建AI回答记录
|
||||||
answer_record = ChatHistory.objects.create(
|
answer_record = ChatHistory.objects.create(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
knowledge_base=knowledge_base,
|
knowledge_base=primary_knowledge_base,
|
||||||
conversation_id=conversation_id,
|
conversation_id=conversation_id,
|
||||||
parent_id=str(question_record.id),
|
parent_id=str(question_record.id),
|
||||||
role='assistant',
|
role='assistant',
|
||||||
content=data['answer'],
|
content=answer,
|
||||||
metadata={'model_name': data.get('model_name', 'default')}
|
metadata={
|
||||||
|
'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'),
|
||||||
|
'dataset_id_list': dataset_ids
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return Response({
|
return Response({
|
||||||
@ -235,7 +327,8 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
|
|||||||
'data': {
|
'data': {
|
||||||
'id': answer_record.id,
|
'id': answer_record.id,
|
||||||
'conversation_id': conversation_id,
|
'conversation_id': conversation_id,
|
||||||
'dataset_id': str(knowledge_base.id),
|
'dataset_id': str(primary_knowledge_base.id),
|
||||||
|
'dataset_name': primary_knowledge_base.name,
|
||||||
'role': 'assistant',
|
'role': 'assistant',
|
||||||
'content': answer_record.content,
|
'content': answer_record.content,
|
||||||
'created_at': answer_record.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
'created_at': answer_record.created_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
@ -251,6 +344,126 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
|
|||||||
'data': None
|
'data': None
|
||||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
def _get_answer_from_external_api(self, dataset_external_id_list, question):
|
||||||
|
"""调用外部API获取AI回答"""
|
||||||
|
try:
|
||||||
|
# 确保所有ID都是字符串
|
||||||
|
dataset_external_ids = [str(id) if isinstance(id, uuid.UUID) else id for id in dataset_external_id_list]
|
||||||
|
|
||||||
|
logger.info(f"准备调用外部API,知识库ID列表: {dataset_external_ids}")
|
||||||
|
|
||||||
|
# 第一个API调用创建聊天
|
||||||
|
chat_request_data = {
|
||||||
|
"id": "d333dee2-b3c2-11ef-af2c-a4bb6dafa942",
|
||||||
|
"model_id": "58c5deb4-f2e2-11ef-9a1b-0242ac120009",
|
||||||
|
"dataset_id_list": dataset_external_ids,
|
||||||
|
"multiple_rounds_dialogue": False,
|
||||||
|
"dataset_setting": {
|
||||||
|
"top_n": 10,
|
||||||
|
"similarity": "0.3",
|
||||||
|
"max_paragraph_char_number": 10000,
|
||||||
|
"search_mode": "blend",
|
||||||
|
"no_references_setting": {
|
||||||
|
"value": "{question}",
|
||||||
|
"status": "ai_questioning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model_setting": {
|
||||||
|
"prompt": "**相关文档内容**:{data} **回答要求**:如果相关文档内容中没有可用信息,请回答\"没有在知识库中查找到相关信息,建议咨询相关技术支持或参考官方文档进行操作\"。请根据相关文档内容回答用户问题。不要输出与用户问题无关的内容。请使用中文回答客户问题。**用户问题**:{question}"
|
||||||
|
},
|
||||||
|
"problem_optimization": False
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"发送创建聊天请求:{settings.API_BASE_URL}/api/application/chat/open")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 测试JSON序列化,提前捕获可能的错误
|
||||||
|
json_data = json.dumps(chat_request_data)
|
||||||
|
logger.debug(f"请求数据序列化成功,长度: {len(json_data)}")
|
||||||
|
except TypeError as e:
|
||||||
|
logger.error(f"JSON序列化失败: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
chat_response = requests.post(
|
||||||
|
url=f"{settings.API_BASE_URL}/api/application/chat/open",
|
||||||
|
json=chat_request_data,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"API响应状态码: {chat_response.status_code}")
|
||||||
|
|
||||||
|
if chat_response.status_code != 200:
|
||||||
|
logger.error(f"外部API调用失败: {chat_response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
chat_data = chat_response.json()
|
||||||
|
logger.debug(f"API响应数据: {chat_data}")
|
||||||
|
|
||||||
|
if chat_data.get('code') != 200 or not chat_data.get('data'):
|
||||||
|
logger.error(f"外部API返回错误: {chat_data}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
chat_id = chat_data['data']
|
||||||
|
logger.info(f"聊天创建成功,chat_id: {chat_id}")
|
||||||
|
|
||||||
|
# 第二个API调用发送消息
|
||||||
|
message_request_data = {
|
||||||
|
"message": question,
|
||||||
|
"re_chat": False,
|
||||||
|
"stream": True
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(f"发送聊天消息请求: {settings.API_BASE_URL}/api/application/chat_message/{chat_id}")
|
||||||
|
message_response = requests.post(
|
||||||
|
url=f"{settings.API_BASE_URL}/api/application/chat_message/{chat_id}",
|
||||||
|
json=message_request_data,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
stream=True,
|
||||||
|
timeout=60
|
||||||
|
)
|
||||||
|
|
||||||
|
if message_response.status_code != 200:
|
||||||
|
logger.error(f"外部API聊天消息调用失败: {message_response.status_code}, {message_response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 拼接流式响应 - 修复SSE格式解析
|
||||||
|
full_content = ""
|
||||||
|
try:
|
||||||
|
for line in message_response.iter_lines():
|
||||||
|
if line:
|
||||||
|
line_text = line.decode('utf-8')
|
||||||
|
# 处理SSE格式 (data: {...})
|
||||||
|
if line_text.startswith('data: '):
|
||||||
|
json_str = line_text[6:] # 去掉 "data: " 前缀
|
||||||
|
logger.debug(f"处理SSE数据: {json_str}")
|
||||||
|
try:
|
||||||
|
chunk = json.loads(json_str)
|
||||||
|
if 'content' in chunk:
|
||||||
|
content_part = chunk['content']
|
||||||
|
full_content += content_part
|
||||||
|
logger.debug(f"追加内容: '{content_part}'")
|
||||||
|
if chunk.get('is_end', False):
|
||||||
|
logger.debug("收到结束标记")
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error(f"JSON解析错误: {str(e)}, 原始数据: {json_str}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"收到非SSE格式数据: {line_text}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理流式响应出错: {str(e)}")
|
||||||
|
if full_content:
|
||||||
|
logger.info(f"已接收部分内容: {len(full_content)} 字符")
|
||||||
|
return full_content.strip()
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.info(f"聊天回答拼接完成,总长度: {len(full_content)}")
|
||||||
|
return full_content.strip() if full_content else "未能获取到有效回答"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"调用外部API获取回答失败: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return None
|
||||||
|
|
||||||
def update(self, request, pk=None):
|
def update(self, request, pk=None):
|
||||||
"""更新聊天记录"""
|
"""更新聊天记录"""
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user