sunpayus/app.py

368 lines
13 KiB
Python
Raw Permalink Normal View History

"""
Agent 决策工作坊应用
这是一个基于 Flask 的多 Agent 决策工作坊应用允许用户创建工作坊配置角色进行多角色辩论并使用 DeepSeek API 生成决策要点
主要功能
1. 创建新工作坊
2. 配置工作坊角色及其视角
3. 进行多角色辩论
4. 使用 AI 生成决策要点
5. 查看决策结果
"""
from flask import Flask, render_template, request, redirect, url_for, jsonify
from datetime import datetime
import requests
import json
from dotenv import load_dotenv
import os
# 加载环境变量(从 .env 文件中读取配置)
load_dotenv()
# 初始化 Flask 应用
app = Flask(__name__)
# DeepSeek API 配置
# 从环境变量中获取 API 密钥,如果没有则使用默认值
DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY', 'sk-137bb3c986b640ae838d2cd2bfbca1dc')
DEEPSEEK_API_URL = 'https://api.deepseek.com/v1/chat/completions' # DeepSeek API 端点
def generate_decision_points(workshop):
"""
使用 DeepSeek API 生成决策要点
Args:
workshop (dict): 工作坊对象包含名称目标和辩论内容
Returns:
str: 生成的决策要点文本
"""
# 构建辩论内容的摘要,用于发送给 AI
debate_summary = "辩论内容:\n"
for item in workshop['debate_content']:
debate_summary += f"{item['role']}: {item['opinion']}\n"
# 构建系统提示,指导 AI 如何生成决策要点
system_prompt = f"你是一个决策分析专家,需要基于以下辩论内容,为'{workshop['name']}'工作坊生成全面的决策要点。工作坊目标是:{workshop['goal']}。请从多个角度分析,提取关键决策点,并提供具体的建议。"
# 构建请求数据,符合 DeepSeek API 的格式要求
data = {
"model": "deepseek-chat", # 使用的模型
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": debate_summary}
],
"temperature": 0.7, # 控制生成文本的随机性
"max_tokens": 1000 # 最大生成 token 数
}
# 构建请求头,包含认证信息
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {DEEPSEEK_API_KEY}'
}
try:
# 发送请求到 DeepSeek API
response = requests.post(DEEPSEEK_API_URL, headers=headers, data=json.dumps(data))
response.raise_for_status() # 检查请求是否成功
result = response.json() # 解析响应数据
decision_points = result['choices'][0]['message']['content'] # 提取生成的决策要点
# 移除井字号和米字号,提高阅读质量
decision_points = decision_points.replace('#', '').replace('*', '')
return decision_points
except Exception as e:
# 错误处理,当 API 请求失败时,返回简单的决策要点
print(f"DeepSeek API 请求失败: {e}")
decision_points = []
for content in workshop['debate_content']:
decision_points.append(f"{content['role']}: {content['opinion'][:100]}...")
return "\n".join(decision_points)
# 存储工作坊数据的临时字典(应用重启后数据会丢失)
# 键: workshop_id (整数), 值: 工作坊对象 (字典)
workshops = {}
@app.route('/')
def index():
"""
首页路由显示所有工作坊列表
Returns:
str: 渲染后的首页 HTML
"""
# 获取排序参数,默认为按创建时间排序
sort_by = request.args.get('sort_by', 'time')
# 对工作坊进行排序
sorted_workshops = []
if sort_by == 'name':
# 按名称首字母排序
sorted_workshops = sorted(workshops.items(), key=lambda x: x[1]['name'])
elif sort_by == 'time_asc':
# 按创建时间升序排序
sorted_workshops = sorted(workshops.items(), key=lambda x: x[1].get('created_at', datetime.min))
else:
# 按创建时间降序排序(默认)
sorted_workshops = sorted(workshops.items(), key=lambda x: x[1].get('created_at', datetime.min), reverse=True)
# 将排序后的结果转换回字典格式
sorted_workshops_dict = {workshop_id: workshop for workshop_id, workshop in sorted_workshops}
return render_template('index.html', workshops=sorted_workshops_dict, sort_by=sort_by)
@app.route('/create', methods=['GET', 'POST'])
def create_workshop():
"""
创建新工作坊的路由
GET 请求: 显示创建工作坊表单
POST 请求: 处理表单提交创建新工作坊并跳转到角色配置页面
Returns:
str: 渲染后的创建工作坊表单或重定向响应
"""
if request.method == 'POST':
# 生成唯一的工作坊 ID
workshop_id = len(workshops) + 1
# 获取表单数据
workshop_name = request.form['workshop_name']
workshop_goal = request.form['workshop_goal']
# 创建新工作坊对象
workshops[workshop_id] = {
'name': workshop_name, # 工作坊名称
'goal': workshop_goal, # 工作坊目标
'roles': [], # 角色列表,初始为空
'debate_content': [], # 辩论内容,初始为空
'decision_points': [], # 决策要点,初始为空
'final_decision': '', # 最终决策,初始为空
'created_at': datetime.now() # 创建时间
}
# 重定向到角色配置页面
return redirect(url_for('configure_roles', workshop_id=workshop_id))
# GET 请求,显示创建工作坊表单
return render_template('create_workshop.html')
@app.route('/workshop/<int:workshop_id>/roles', methods=['GET', 'POST'])
def configure_roles(workshop_id):
"""
配置工作坊角色的路由
Args:
workshop_id (int): 工作坊 ID
GET 请求: 显示角色配置表单
POST 请求: 处理表单提交添加角色并决定是否继续添加
Returns:
str: 渲染后的角色配置表单或重定向响应
"""
# 检查工作坊是否存在
if workshop_id not in workshops:
return redirect(url_for('index'))
if request.method == 'POST':
# 检查是否要删除此角色
if 'remove_current' in request.form:
# 不添加新角色,直接重定向回当前页面
return redirect(url_for('configure_roles', workshop_id=workshop_id))
else:
# 获取表单数据
role_name = request.form.get('role_name')
role_perspective = request.form.get('role_perspective')
# 只有当角色名称和视角都存在时才添加新角色
if role_name and role_perspective:
# 添加新角色到工作坊
workshops[workshop_id]['roles'].append({
'name': role_name, # 角色名称
'perspective': role_perspective # 角色视角
})
# 检查用户是否要继续添加角色
if 'add_more' in request.form:
# 继续添加角色,重定向回当前页面
return redirect(url_for('configure_roles', workshop_id=workshop_id))
else:
# 角色配置完成,跳转到辩论页面
return redirect(url_for('start_debate', workshop_id=workshop_id))
# GET 请求,显示角色配置表单
return render_template('configure_roles.html', workshop=workshops[workshop_id], workshop_id=workshop_id)
@app.route('/workshop/<int:workshop_id>/debate', methods=['GET', 'POST'])
def start_debate(workshop_id):
"""
开始辩论的路由
Args:
workshop_id (int): 工作坊 ID
GET 请求: 显示辩论表单
POST 请求: 处理表单提交添加角色观点
Returns:
str: 渲染后的辩论表单或重定向响应
"""
# 检查工作坊是否存在
if workshop_id not in workshops:
return redirect(url_for('index'))
if request.method == 'POST':
# 获取表单数据
role_index = int(request.form['role_index'])
role_opinion = request.form['role_opinion']
# 添加角色观点到辩论内容
workshops[workshop_id]['debate_content'].append({
'role': workshops[workshop_id]['roles'][role_index]['name'], # 角色名称
'opinion': role_opinion # 角色观点
})
# 继续辩论,重定向回当前页面
return redirect(url_for('start_debate', workshop_id=workshop_id))
# GET 请求,显示辩论表单
return render_template('start_debate.html', workshop=workshops[workshop_id], workshop_id=workshop_id)
@app.route('/workshop/<int:workshop_id>/results')
def show_results(workshop_id):
"""
显示决策结果的路由
Args:
workshop_id (int): 工作坊 ID
Returns:
str: 渲染后的结果页面 HTML
"""
# 检查工作坊是否存在
if workshop_id not in workshops:
return redirect(url_for('index'))
# 只有当工作坊没有决策要点时,才生成新的结果
workshop = workshops[workshop_id]
if not workshop.get('decision_points') or workshop['decision_points'] == []:
decision_points = generate_decision_points(workshop)
workshop['decision_points'] = decision_points
# 显示结果页面
return render_template('results.html', workshop=workshop, workshop_id=workshop_id)
@app.route('/workshop/<int:workshop_id>/save_final_decision', methods=['POST'])
def save_final_decision(workshop_id):
"""
保存最终决策的路由
Args:
workshop_id (int): 工作坊 ID
Returns:
str: 重定向到结果页面
"""
# 检查工作坊是否存在
if workshop_id not in workshops:
return redirect(url_for('index'))
# 获取最终决策内容
final_decision = request.form.get('final_decision', '')
workshops[workshop_id]['final_decision'] = final_decision
workshops[workshop_id]['final_decision_time'] = datetime.now() # 最终决策保存时间
# 重定向到首页
return redirect(url_for('index'))
@app.route('/api/workshops')
def api_workshops():
"""
API路由返回排序后的工作坊数据
Returns:
str: JSON格式的工作坊数据
"""
# 获取排序参数,默认为按创建时间排序
sort_by = request.args.get('sort_by', 'time')
# 对工作坊进行排序
sorted_workshops = []
if sort_by == 'name':
# 按名称首字母排序
sorted_workshops = sorted(workshops.items(), key=lambda x: x[1]['name'])
else:
# 按创建时间排序
sorted_workshops = sorted(workshops.items(), key=lambda x: x[1].get('created_at', datetime.min), reverse=True)
# 转换为前端需要的格式
result = []
for workshop_id, workshop in sorted_workshops:
# 转换时间对象为字符串
workshop_data = workshop.copy()
if 'created_at' in workshop_data and workshop_data['created_at']:
workshop_data['created_at'] = workshop_data['created_at'].strftime('%Y-%m-%d %H:%M')
if 'final_decision_time' in workshop_data and workshop_data['final_decision_time']:
workshop_data['final_decision_time'] = workshop_data['final_decision_time'].strftime('%Y-%m-%d %H:%M')
workshop_data['id'] = workshop_id
result.append(workshop_data)
return jsonify(result)
@app.route('/api/workshop/<int:workshop_id>', methods=['DELETE'])
def delete_workshop(workshop_id):
"""
API路由删除单个工作坊
Args:
workshop_id (int): 工作坊 ID
Returns:
str: JSON格式的删除结果
"""
if workshop_id in workshops:
del workshops[workshop_id]
return jsonify({'success': True, 'message': '工作坊删除成功'})
else:
return jsonify({'success': False, 'message': '工作坊不存在'}), 404
@app.route('/api/workshops/batch_delete', methods=['POST'])
def batch_delete_workshops():
"""
API路由批量删除工作坊
Returns:
str: JSON格式的删除结果
"""
data = request.get_json()
workshop_ids = data.get('workshop_ids', [])
deleted_count = 0
for workshop_id in workshop_ids:
if workshop_id in workshops:
del workshops[workshop_id]
deleted_count += 1
return jsonify({'success': True, 'message': f'成功删除{deleted_count}个工作坊', 'deleted_count': deleted_count})
if __name__ == '__main__':
"""
应用入口点
启动 Flask 应用开启调试模式
"""
app.run(debug=True)