""" 多 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//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//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//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//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/', 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)