Merge branch 'dev2' into dev
# Conflicts: # yj_room_agent/settings.py # yj_room_agent/urls.py # yj_room_agent/views.py
This commit is contained in:
3
.env
Normal file
3
.env
Normal file
@@ -0,0 +1,3 @@
|
||||
MODEL_API_KEY=sk-9nng4v6qsdiw42r6d3re4nym15hiti29
|
||||
MODEL_BASE_URL=https://gateout.yced.com.cn/v1
|
||||
MODEL_NAME=deepseek
|
||||
@@ -1,3 +1,3 @@
|
||||
Django==5.2.1
|
||||
Django==2.32.3
|
||||
dotenv==0.9.9
|
||||
requests==2.32.3
|
||||
python-decouple==3.8
|
||||
177
yj_room_agent/LLM/ai_service.py
Normal file
177
yj_room_agent/LLM/ai_service.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from threading import Lock
|
||||
from datetime import datetime
|
||||
import requests, json
|
||||
from .openai_client import call_openai_api
|
||||
from decouple import config
|
||||
import threading, re
|
||||
|
||||
MODEL_NAME = config('MODEL_NAME', default="")
|
||||
BASE_URL = config('MODEL_BASE_URL', default="")
|
||||
|
||||
|
||||
def is_json(myjson):
|
||||
try:
|
||||
json_object = json.loads(myjson)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def query_room_info() -> str:
|
||||
resp = requests.get('http://127.0.0.1:8000/myapi/room/')
|
||||
return resp.text
|
||||
|
||||
|
||||
def book_room(data: dict) -> str:
|
||||
resp = requests.post('http://127.0.0.1:8000/myapi/room/', data)
|
||||
return resp.text
|
||||
|
||||
|
||||
def check_and_process_think(content: str) -> str:
|
||||
filtered_text = re.sub(r"<think\b[^>]*>.*?</think>", '', content, flags=re.DOTALL)
|
||||
return filtered_text
|
||||
|
||||
|
||||
def build_prompt():
|
||||
"""构建增强提示词"""
|
||||
# 获取可用会议室信息
|
||||
room_info = query_room_info()
|
||||
for_mart_str = '''
|
||||
{
|
||||
"room_id":"11", //会议室ID
|
||||
"capacity":20,
|
||||
"start_time":"2025-06-04 09:30:10",
|
||||
"end_time":"2025-06-04 12:30:10",
|
||||
"user_confirm":1 //用户是否确认
|
||||
}
|
||||
'''
|
||||
|
||||
time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
# 增强提示词模板
|
||||
template = f"""你是一个专业的OA会议预订助手,请根据以下信息提供服务:
|
||||
现在时间是 :{time_now}
|
||||
可用会议室信息:
|
||||
{room_info}
|
||||
请按以下步骤处理:
|
||||
1. 解析用户预订需求(时间、人数、设备要求等)
|
||||
2. 根据可用会议室列表推荐合适选项,推荐选项时不需要提取用户预订信息,待用户确认是再提取。按照正常自然语言对话返回
|
||||
3. 如果用户确定要预订某间会议室,而不是在询问合适会议室时,请帮根据上下文我提取用户预订信息,预订时间等信息并返回,结果请只返回json格式得预订信息且不需要包含多余的描述内容以及<think>标签等,输出结果示例如下:
|
||||
{for_mart_str}
|
||||
4. 当用户再次发起预订会议室时,不要直接提取用户的预订信息而是请重新查看现有会议室的最新情况,基于用户需求给用户推荐合理的会议室,推荐选项时不需要提取用户预订信息,不要返回json数据,待用户确认是再提取。按照正常自然语言对话返回
|
||||
5. 如果用户需要解析调用预订会议室返回的结果,请解析用户提供的相应结果信息,并给予自然语言反馈,不需要返回json数据
|
||||
6. 用户其他需求,请按照自然语言对话返回
|
||||
|
||||
"""
|
||||
return template
|
||||
|
||||
|
||||
class DialogManager:
|
||||
def __init__(self):
|
||||
self.dialogs = {}
|
||||
self.lock = Lock()
|
||||
|
||||
def get_history(self, session_id: str) -> list:
|
||||
return self.dialogs.get(session_id, [])
|
||||
|
||||
def add_message(self, session_id, role, content):
|
||||
with self.lock:
|
||||
if session_id not in self.dialogs:
|
||||
self.dialogs[session_id] = []
|
||||
# 自动维护对话历史
|
||||
self.dialogs[session_id].append({
|
||||
"role": role,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"content": content
|
||||
})
|
||||
# 上下文压缩(超长对话处理)
|
||||
if len(self.dialogs[session_id]) > 50:
|
||||
self.compress_context(session_id)
|
||||
|
||||
def compress_context(self, session_id):
|
||||
"""对话历史压缩算法"""
|
||||
history = self.dialogs[session_id]
|
||||
# 保留最近3条完整记录
|
||||
recent = history[-5:]
|
||||
# 摘要生成中间对话内容
|
||||
summary = self.generate_summary(history[3:-3])
|
||||
# 重组会话历史
|
||||
self.dialogs[session_id] = [
|
||||
{"role": "system", "content": f"对话摘要:{summary}"},
|
||||
*recent
|
||||
]
|
||||
|
||||
def generate_summary(self, messages):
|
||||
"""生成对话摘要(调用本地模型)"""
|
||||
text = "\n".join([f"{m['role']}: {m['content']}" for m in messages])
|
||||
headers = {}
|
||||
messages = [
|
||||
{"role": "system",
|
||||
"content": "你现在是一个对话总结助手,基于用户与模型智能体的对话,生成对话总结摘要,要求简明且保留关键信息"},
|
||||
{"role": "user", "content": f"请生成以下对话的简明摘要(保留关键信息):\n\n{text}"}
|
||||
]
|
||||
payload = {
|
||||
"model": MODEL_NAME,
|
||||
"messages": messages,
|
||||
}
|
||||
|
||||
headers["Authorization"] = f"Bearer {config('MODEL_API_KEY')}"
|
||||
response = requests.post(f"{BASE_URL}/chat/completions", data=json.dumps(payload, ensure_ascii=False),
|
||||
timeout=1000, headers=headers, verify=False)
|
||||
response.raise_for_status()
|
||||
print(response.text)
|
||||
return response.json().get("response", "摘要生成失败")
|
||||
|
||||
|
||||
dialog_manager = DialogManager()
|
||||
|
||||
|
||||
def add_message_async(manager: DialogManager, session_id: str, role: str, content: str):
|
||||
thread = threading.Thread(target=manager.add_message, args=(session_id, role, content))
|
||||
thread.start()
|
||||
|
||||
|
||||
def process_chat(user_id: str, user_input: str):
|
||||
history = []
|
||||
query_history = dialog_manager.get_history(user_id)
|
||||
history.extend(query_history)
|
||||
prompt = ''
|
||||
if history is None or len(history) == 0:
|
||||
prompt = build_prompt()
|
||||
add_message_async(dialog_manager, user_id, 'system', prompt)
|
||||
add_message_async(dialog_manager, user_id, 'user', user_input)
|
||||
resp = call_openai_api(model=MODEL_NAME, system_prompt=prompt, user_query=user_input,
|
||||
api_key=config('MODEL_API_KEY'),
|
||||
history=history)
|
||||
content = resp["choices"][0]["message"]["content"]
|
||||
reasoning_content = resp["choices"][0]["message"]["reasoning_content"]
|
||||
print(content)
|
||||
new_content = check_and_process_think(content=content)
|
||||
if 'json' in new_content or is_json(new_content):
|
||||
new_content = new_content.replace("json", '')
|
||||
new_content = new_content.replace("`", '')
|
||||
data = json.loads(new_content)
|
||||
# 触发预订函数------
|
||||
result = book_room(data=data)
|
||||
print(result)
|
||||
book_promot = f'''
|
||||
系统调用API预订该会议室的结果如下:
|
||||
{result}
|
||||
请帮用户解析预订会议室的结果,并根据结果给予用户相应自然语言反馈
|
||||
'''
|
||||
new_history = []
|
||||
query_history = dialog_manager.get_history(user_id)
|
||||
new_history.extend(query_history)
|
||||
resp = call_openai_api(model=MODEL_NAME, user_query=book_promot,
|
||||
api_key=config('MODEL_API_KEY'),
|
||||
history=new_history,
|
||||
system_prompt='',
|
||||
)
|
||||
content = resp["choices"][0]["message"]["content"]
|
||||
add_message_async(dialog_manager, user_id, 'assistant', content)
|
||||
return {'response': resp}
|
||||
else:
|
||||
|
||||
add_message_async(dialog_manager, user_id, 'assistant', content)
|
||||
if reasoning_content:
|
||||
add_message_async(dialog_manager, user_id, 'assistant', reasoning_content)
|
||||
return {'response': resp}
|
||||
241
yj_room_agent/LLM/openai_client.py
Normal file
241
yj_room_agent/LLM/openai_client.py
Normal file
File diff suppressed because one or more lines are too long
@@ -12,19 +12,20 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from decouple import config
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-i(fm5c2v*=vgfwmgdl^qi7iezv(xfwovbqu=+^=vm72e$gnx&l'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ["10.212.26.178",'127.0.0.1']
|
||||
ALLOWED_HOSTS = ["192.168.237.130", '127.0.0.1','10.212.27.4']
|
||||
|
||||
# Application definition
|
||||
|
||||
@@ -42,7 +43,7 @@ MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
#'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
|
||||
@@ -16,7 +16,7 @@ Including another URLconf
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from yj_room_agent import views
|
||||
from yj_room_agent import views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import json
|
||||
from django.http import StreamingHttpResponse, JsonResponse, FileResponse
|
||||
from yj_room_agent.LLM.ai_service import process_chat
|
||||
from django.views.decorators.http import require_POST
|
||||
import json
|
||||
|
||||
from django.http import StreamingHttpResponse, JsonResponse, FileResponse, HttpResponseNotAllowed
|
||||
import requests
|
||||
|
||||
Reference in New Issue
Block a user