调用api进行对话

This commit is contained in:
wanjia 2025-03-20 13:48:36 +08:00
parent 00deeb711b
commit c0bba14ee8
2 changed files with 246 additions and 33 deletions

View File

@ -14,7 +14,7 @@ import os
from pathlib import Path
# API 配置
API_BASE_URL = 'http://81.69.223.133:48329'
API_BASE_URL = 'http://180.163.88.62:30331'
DEPARTMENT_GROUPS = {
"技术部": ["开发组", "测试组", "运维组"],

View File

@ -40,6 +40,8 @@ from django.utils.decorators import method_decorator
import uuid
from rest_framework import serializers
import traceback
import requests
import json
@ -178,55 +180,145 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
"""创建聊天记录"""
try:
data = request.data
required_fields = ['dataset_id', 'dataset_name', 'question', 'answer']
# 检查必填字段
for field in required_fields:
if field not in data:
return Response({
'code': 400,
'message': f'缺少必填字段: {field}',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
# 获取或创建对话ID
conversation_id = data.get('conversation_id', str(uuid.uuid4()))
# 获取知识库 - 不进行 UUID 转换
try:
knowledge_base = KnowledgeBase.objects.filter(id=data['dataset_id']).first()
if not knowledge_base:
return Response({
'code': 404,
'message': '知识库不存在',
'data': None
}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
# 检查必填字段 - 支持单知识库或多知识库模式
if 'question' not in data:
return Response({
'code': 400,
'message': f'无效的知识库ID: {str(e)}',
'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
}, status=status.HTTP_400_BAD_REQUEST)
# 创建用户问题记录
# 获取或创建对话ID
conversation_id = data.get('conversation_id', str(uuid.uuid4()))
# 调用外部API获取答案 (传递多个knowledge base的external_id)
answer = self._get_answer_from_external_api(
dataset_external_id_list=external_id_list,
question=data['question']
)
if not answer:
return Response({
'code': 500,
'message': '获取AI回答失败',
'data': None
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# 创建用户问题记录 (关联到主知识库)
question_record = ChatHistory.objects.create(
user=request.user,
knowledge_base=knowledge_base,
knowledge_base=primary_knowledge_base,
conversation_id=conversation_id,
role='user',
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回答记录
answer_record = ChatHistory.objects.create(
user=request.user,
knowledge_base=knowledge_base,
knowledge_base=primary_knowledge_base,
conversation_id=conversation_id,
parent_id=str(question_record.id),
role='assistant',
content=data['answer'],
metadata={'model_name': data.get('model_name', 'default')}
content=answer,
metadata={
'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'),
'dataset_id_list': dataset_ids
}
)
return Response({
@ -235,7 +327,8 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
'data': {
'id': answer_record.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',
'content': answer_record.content,
'created_at': answer_record.created_at.strftime('%Y-%m-%d %H:%M:%S')
@ -250,6 +343,126 @@ class ChatHistoryViewSet(viewsets.ModelViewSet):
'message': f'创建聊天记录失败: {str(e)}',
'data': None
}, 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):
"""更新聊天记录"""