diff --git a/.env b/.env index 27e6193..9cf28ac 100644 --- a/.env +++ b/.env @@ -5,9 +5,9 @@ MODEL_BASE_URL=https://api.deepseek.com #模型名称 MODEL_NAME=deepseek-chat #放行host地址 -DJANGO_ALLOWED_HOSTS=192.168.237.130,127.0.0.1,10.212.27.4 +DJANGO_ALLOWED_HOSTS=192.168.237.130,127.0.0.1,172.20.10.10 #是否开启debug模式 -DJANGO_DEBUG_ENABLE=False +DJANGO_DEBUG_ENABLE=True DEFAULT_AUTH_URL = 'https://oa-dev.yced.com.cn/iuap-api-auth' DEFAULT_GATEWAY_URL = 'https://oa-dev.yced.com.cn/iuap-api-gateway ' @@ -25,4 +25,4 @@ QUERY_MEETING_ROOM = ['GET', '/yonbip/uspace/external/access/getMeetingRoom'] QUERY_MEETINGBOOKING_INFO = ['GET', '/yonbip/uspace/external/access/getDestineInformation'] BOOK_MEETING_ROOM = ['POST', '/yonbip/uspace/destine/Meeting'] CANCEL_MEETING = ['POST', '/yonbip/uspace/cancel/Meeting'] -BOOKMEETING = ['POST', '/yonbip/uspace/external/access/book'] \ No newline at end of file +BOOKMEETING = ['POST', '/yonbip/uspace/external/access/book'] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d8ca093 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM docker-0.unsee.tech/python:3.12-slim +WORKDIR /app +COPY . /app +ENV TZ=Asia/Shanghai \ + LANG=C.UTF-8 +RUN apt-get update && pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ \ + && pip install yonyouopenapisdk-1.1.1-py3-none-any.whl -i https://mirrors.aliyun.com/pypi/simple/ +RUN mkdir -p /app/logs && touch /app/logs/yj_room_agent.log +EXPOSE 9000 +CMD ["gunicorn","-w 3","-b 0.0.0.0:9000", "yj_room_agent.wsgi"] diff --git a/requirements.txt b/requirements.txt index a005d57..548ee01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ Django==5.2.1 requests==2.32.3 -python-decouple==3.8 \ No newline at end of file +python-decouple==3.8 +gunicorn==23.0.0 +APScheduler==3.11.0 \ No newline at end of file diff --git a/yj_room_agent/LLM/ai_service.py b/yj_room_agent/LLM/ai_service.py index caa5ba2..b50db3b 100644 --- a/yj_room_agent/LLM/ai_service.py +++ b/yj_room_agent/LLM/ai_service.py @@ -1,6 +1,9 @@ from threading import Lock from datetime import datetime import requests, json +from django.db.models.expressions import result + +from apscheduler.schedulers.background import BackgroundScheduler from .openai_client import call_openai_api from decouple import config import threading, re @@ -9,10 +12,23 @@ from ..tools import getinfo, params_filter MODEL_NAME = config('MODEL_NAME', default="") BASE_URL = config('MODEL_BASE_URL', default="") +''' +解析时间,转化为时间戳 +''' + def parse_time(time_str: str) -> int: - timestamp = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S").timestamp() - return int(timestamp) + try: + timestamp = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S").timestamp() + return int(timestamp) + except Exception as ee: + print("解析时间错误:" + str(ee.with_traceback())) + return int(datetime.now().timestamp()) + + +''' +判断是否为json格式 +''' def is_json(myjson): @@ -41,7 +57,9 @@ def query_room_type(params: dict) ->str: content = json.loads(json.dumps(room_type)) return str(content) -# 查询会议室信息 +''' +查询会议室信息 +''' def query_room_info(params: dict) -> str: # resp = requests.get('http://127.0.0.1:8000/myapi/room/') """ @@ -141,20 +159,33 @@ def check_and_process_think(content: str) -> str: return filtered_text +''' +初始提示词构建 +''' + + def build_prompt(params): """构建增强提示词""" # 获取可用会议室信息 room_info = query_room_info(params) - for_mart_str = ''' + book_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 //用户是否确认 + "user_confirm":1 //用户是否确认, + "func_name":"book_room", } ''' + qry_book_info_for_mart_str = ''' + { + + "func_name":"query_booking_info", + } + ''' + time_now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 增强提示词模板 template = f"""你是一个专业的OA会议预订助手,请根据以下信息提供服务: @@ -165,15 +196,22 @@ def build_prompt(params): 1. 解析用户预订需求(时间、人数、设备要求等) 2. 根据可用会议室列表推荐合适选项,推荐选项时,没有用户明确需要预订哪间会议室时,不需要提取用户预订信息,直接返回推荐会议室列表,按照自然语言返回即可 3. 只有当用户确定要预订某间会议室,而不是在询问合适会议室时,请根据上下文提取用户预订信息,预订时间等信息并返回而不是直接提示预订成功,结果请只返回json格式得预订信息且不需要包含多余的描述内容以及标签等,输出结果示例如下: - {for_mart_str} - 4. 如果当用户再次请求预订会议室时,请不要按照json格式直接提取用户的预订信息,而是请重新查看现有会议室的最新情况,基于用户需求给用户推荐合理的会议室,推荐选项时不需要提取用户预订信息按照json格式返回,只需要重新给用户推荐选项即可,按照正常自然语言对话返回 - 5. 如果用户需要解析调用预订会议室返回的结果,请解析用户提供的相应结果信息,并给予自然语言反馈,不需要返回json数据 - 6. 用户其他需求,请按照自然语言对话返回 + {book_for_mart_str} + 4. 用户需要查询已有的会议室预订情况或者预订情况时,结果请按照以下的json示例返回,不需要返回其他的多余数据,返回示例如下: + {qry_book_info_for_mart_str} + 5. 如果当用户再次请求预订会议室时,请不要按照json格式直接提取用户的预订信息,而是请重新查看现有会议室的最新情况,基于用户需求给用户推荐合理的会议室,推荐选项时不需要提取用户预订信息按照json格式返回,只需要重新给用户推荐选项即可,按照正常自然语言对话返回 + 6. 如果用户需要解析调用API返回的结果,请解析用户提供的相应结果信息,并给予自然语言反馈,不需要返回json数据 + 7. 用户其他需求,请按照自然语言对话返回 """ return template +''' +消息历史维护 +''' + + class DialogManager: def __init__(self): self.dialogs = {} @@ -182,6 +220,10 @@ class DialogManager: def get_history(self, session_id: str) -> list: return self.dialogs.get(session_id, []) + def clear_history(self): + print('开始定时执行清理历史消息') + self.dialogs.clear() + def add_message(self, session_id, role, content): with self.lock: if session_id not in self.dialogs: @@ -232,6 +274,13 @@ class DialogManager: dialog_manager = DialogManager() +scheduler = BackgroundScheduler() +# 每天凌晨1点执行任务,清理缓存中的历史消息 +scheduler.add_job(dialog_manager.clear_history, 'cron', hour=1) +scheduler.start() +''' +异步添加上下文处理信息 +''' def add_message_async(manager: DialogManager, session_id: str, role: str, content: str): @@ -239,6 +288,49 @@ def add_message_async(manager: DialogManager, session_id: str, role: str, conten thread.start() +''' +函数调用,处理预订会议室 +''' + + +def process_book_room(data, params) -> str: + result = book_room(data=data, params=params) + book_promot = f''' + 系统调用API预订该会议室的结果如下: + {result} + 请帮用户解析预订会议室的结果,并根据结果给予用户相应自然语言反馈 + ''' + return book_promot + + +''' +函数调用,查询已经预订会议室详情 +''' + + +def process_query_book_room(**kwargs) -> str: + result = query_booking_info(params=kwargs['params']) + book_promot = f''' + 系统调用API查询当前租户下已经预订的会议室的结果如下: + {result} + 请帮用户解析已有的预订会议室的结果,并根据结果给予用户相应自然语言反馈 + ''' + return book_promot + + +''' +函数调用map维护 +''' +func_tion_call_map = { + "book_room": process_book_room, + "query_booking_info": process_query_book_room +} + +''' +消息对话入口函数 +''' + + def process_chat(user_id: str, user_input: str, params: dict): history = [] query_history = dialog_manager.get_history(user_id) @@ -260,14 +352,27 @@ def process_chat(user_id: str, user_input: str, params: dict): new_content = new_content.replace("json", '') new_content = new_content.replace("`", '') data = json.loads(new_content) - # 触发预订函数------ - result = book_room(data=data, params=params) - print(result) - book_promot = f''' - 系统调用API预订该会议室的结果如下: - {result} - 请帮用户解析预订会议室的结果,并根据结果给予用户相应自然语言反馈 - ''' + fun_name = data.get("func_name", None) + book_promot = '' + result_false = {'result': False} + if fun_name: + func = func_tion_call_map.get(fun_name, None) + if func: + # 触发函数调用------ + book_promot = func(data=data, params=params) + + else: + book_promot = f''' + 未查询到相应的执行函数,结果为: + {result_false} + 请帮用户解析结果,并根据结果给予用户相应自然语言反馈 + ''' + else: + book_promot = f''' + 提取用户的函数调用参数失败,结果为: + {result_false} + 请帮用户解析结果,并根据结果给予用户相应自然语言反馈 + ''' new_history = [] query_history = dialog_manager.get_history(user_id) new_history.extend(query_history) diff --git a/yonyouopenapisdk-1.1.1-py3-none-any.whl b/yonyouopenapisdk-1.1.1-py3-none-any.whl new file mode 100644 index 0000000..c4d19af Binary files /dev/null and b/yonyouopenapisdk-1.1.1-py3-none-any.whl differ