Files
nonye/back/blueprints/aiask.py
2025-07-17 23:13:04 +08:00

265 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from flask import Blueprint, request, jsonify, current_app
import json
import datetime
import threading
import time
from werkzeug.exceptions import BadRequest, InternalServerError
from ai_processor import process_ai_message
import uuid
bp = Blueprint('aiask', __name__)
# 使用字典存储对话历史和状态
conversation_data = {}
data_lock = threading.Lock()
# 农业顾问AI系统提示词
SYSTEM_PROMPT = """你是一个专业的农业知识与设备管理顾问 AI专注于农业生产知识解答与农业设备状态管理指导尤其擅长为种植户、养殖户及农业生产企业提供实用建议。
你的任务是:
1. 解答农业生产相关的知识疑问,包括但不限于作物种植、畜禽养殖、病虫害防治、土壤改良、农资使用等内容。
2. 提供农业设备(如播种机、收割机、灌溉设备、养殖设备等)的状态监测、日常维护、常见故障排查及管理建议。
3. 鼓励用户采用科学的农业生产方式和规范的设备管理流程,提升生产效率与安全性。
你需要遵循的原则:
- 始终保持专业、严谨、科学的态度,基于农业技术规范和设备管理标准提供建议。
- 不提供未经证实的农业技术或设备操作方法,对于复杂的设备故障或特殊农业问题,建议用户咨询专业技术人员或农业机构。
- 只返回相关建议,不要返回其他无关内容"""
def get_conversation_data(user_id):
"""获取用户的对话数据"""
with data_lock:
if user_id not in conversation_data:
conversation_data[user_id] = {
'history': [],
'processing': False,
'last_active': time.time(),
'request_id': None
}
return conversation_data[user_id]
def cleanup_inactive_sessions():
"""清理超过1小时不活跃的会话"""
with data_lock:
current_time = time.time()
inactive_users = [
user_id for user_id, data in conversation_data.items()
if current_time - data['last_active'] > 3600 # 1小时
]
for user_id in inactive_users:
del conversation_data[user_id]
def add_to_history(user_id, role, content):
"""添加消息到对话历史,确保内容不为空"""
data = get_conversation_data(user_id)
data['history'].append({
"role": role,
"content": content or "(空回复)",
"timestamp": datetime.datetime.now().isoformat()
})
data['last_active'] = time.time()
# 限制历史记录长度仅保留最近10条交互
if len(data['history']) > 10:
data['history'] = data['history'][-10:]
def format_question(history):
"""格式化问题,包含系统提示和历史对话"""
# 获取最近的历史记录最多10条交互
recent_history = history[-10:] if len(history) > 10 else history
# 构建包含系统提示的完整对话上下文
formatted_context = [
{"role": "system", "content": SYSTEM_PROMPT}
]
# 添加用户与助手的历史对话
formatted_context.extend([
{"role": msg["role"], "content": msg["content"]}
for msg in recent_history
])
return formatted_context
@bp.route('/ask', methods=['POST'])
def ask():
"""向AI提问改进异步处理流程"""
data = request.json
question = data.get('question')
user_id = data.get('user_id', 'anonymous')
if not question:
return jsonify({"code": 400, "message": "问题不能为空"}), 400
# 获取用户数据
user_data = get_conversation_data(user_id)
# 检查是否已有处理中的请求
if user_data['processing']:
return jsonify({
"code": 429,
"message": "已有处理中的请求,请稍后再试",
"request_id": user_data['request_id']
}), 429
# 添加用户问题到历史
add_to_history(user_id, "user", question)
# 格式化问题(包含系统提示和历史对话)
full_question = format_question(user_data['history'])
# 生成唯一请求ID
request_id = f"req_{uuid.uuid4().hex[:8]}"
user_data['request_id'] = request_id
user_data['processing'] = True
# 调用AI回答函数异步处理
try:
appid = current_app.config.get('APPID')
api_key = current_app.config.get('API_KEY')
api_secret = current_app.config.get('API_SECRET')
spark_url = current_app.config.get('SPARK_URL')
domain = current_app.config.get('DOMAIN', 'x1')
app = current_app._get_current_object()
def process_ai_request():
try:
with app.app_context():
start_time = time.time()
current_app.logger.info(f"开始处理AI请求 {request_id}")
ai_answer = process_ai_message(
appid, api_key, api_secret, spark_url, domain, full_question
)
# 记录处理时间
process_time = time.time() - start_time
current_app.logger.info(
f"AI请求 {request_id} 处理完成,耗时 {process_time:.2f}"
)
add_to_history(user_id, "assistant", ai_answer)
except Exception as e:
with app.app_context():
current_app.logger.error(f"AI请求 {request_id} 处理错误: {str(e)}")
add_to_history(user_id, "assistant", f"处理请求时出错: {str(e)}")
finally:
# 更新处理状态
user_data['processing'] = False
user_data['request_id'] = None
cleanup_inactive_sessions()
# 启动处理线程
threading.Thread(target=process_ai_request, daemon=True).start()
return jsonify({
"code": 200,
"message": "请求已接收,正在处理中",
"request_id": request_id
})
except Exception as e:
user_data['processing'] = False
user_data['request_id'] = None
current_app.logger.error(f"AI请求初始化错误: {str(e)}")
return jsonify({
"code": 500,
"message": "服务器内部错误",
"request_id": request_id
}), 500
@bp.route('/history', methods=['GET'])
def get_history():
"""获取对话历史,优化性能"""
user_id = request.args.get('user_id')
if not user_id:
return jsonify({
"code": 400,
"message": "用户ID不能为空",
"history": []
}), 400
try:
user_data = get_conversation_data(user_id)
history = user_data.get('history', [])
current_app.logger.info(
f"获取历史记录用户ID: {user_id},记录数量: {len(history)}"
)
return jsonify({
"code": 200,
"message": "获取历史记录成功",
"history": history,
"processing": user_data['processing'],
"request_id": user_data['request_id']
})
except Exception as e:
current_app.logger.error(f"获取历史记录错误: {str(e)}")
return jsonify({
"code": 500,
"message": "获取历史记录失败",
"history": []
}), 500
@bp.route('/clear', methods=['POST'])
def clear_history():
"""清除对话历史"""
user_id = request.json.get('user_id', 'anonymous')
with data_lock:
if user_id in conversation_data:
conversation_data[user_id]['history'] = []
return jsonify({
"code": 200,
"message": "对话历史已清除"
})
@bp.route('/status', methods=['GET'])
def check_status():
"""检查AI处理状态优化准确性"""
user_id = request.args.get('user_id')
if not user_id:
return jsonify({
"code": 400,
"message": "用户ID不能为空"
}), 400
try:
user_data = get_conversation_data(user_id)
return jsonify({
"code": 200,
"processing": user_data['processing'],
"request_id": user_data['request_id'],
"last_active": user_data['last_active']
})
except Exception as e:
current_app.logger.error(f"状态检查错误: {str(e)}")
return jsonify({
"code": 500,
"message": "状态检查失败"
}), 500
# 定时清理不活跃会话
def start_cleanup_scheduler():
def cleanup_task():
while True:
time.sleep(3600) # 每小时清理一次
cleanup_inactive_sessions()
thread = threading.Thread(target=cleanup_task, daemon=True)
thread.start()
# 启动时开始清理任务
start_cleanup_scheduler()