|
|
| from flask import Blueprint, request, jsonify
|
| import os
|
| import json
|
| import time
|
| import uuid
|
| import requests
|
| from config import STREAM_API_KEY, STREAM_BASE_URL, DEFAULT_MODEL
|
|
|
| agent_builder_bp = Blueprint('agent_builder', __name__)
|
|
|
|
|
| DATA_DIR = os.getenv("DATA_DIR", ".")
|
| AGENTS_DIR = os.path.join(DATA_DIR, "agents")
|
| os.makedirs(AGENTS_DIR, exist_ok=True)
|
|
|
| @agent_builder_bp.route('/create', methods=['POST'])
|
| def create_agent():
|
| """创建新的Agent"""
|
| try:
|
| data = request.json
|
| print(f"[DEBUG] 收到创建Agent请求,数据: {json.dumps(data, ensure_ascii=False)}")
|
|
|
| name = data.get('name')
|
| description = data.get('description', '')
|
| subject = data.get('subject', name)
|
| instructor = data.get('instructor', '教师')
|
| agent_type = data.get('type', 'general')
|
| plugins = data.get('plugins', [])
|
|
|
| knowledge_bases = data.get('knowledgeBases') or data.get('knowledge_bases', [])
|
| workflow = data.get('workflow')
|
|
|
| print(f"[DEBUG] 解析后参数 - name: {name}, type: {agent_type}, plugins: {plugins}, knowledge_bases: {knowledge_bases}")
|
|
|
| if not name:
|
| print("[DEBUG] 错误:Agent名称为空")
|
| return jsonify({
|
| "success": False,
|
| "message": "Agent名称不能为空"
|
| }), 400
|
|
|
|
|
| agent_id = f"agent_{uuid.uuid4().hex[:8]}_{int(time.time())}"
|
|
|
|
|
| agent_config = {
|
| "id": agent_id,
|
| "name": name,
|
| "description": description,
|
| "subject": subject,
|
| "instructor": instructor,
|
| "type": agent_type,
|
| "created_at": int(time.time()),
|
| "plugins": plugins,
|
| "knowledge_bases": knowledge_bases,
|
| "workflow": workflow or {
|
| "nodes": [],
|
| "edges": []
|
| },
|
| "distributions": [],
|
| "stats": {
|
| "usage_count": 0,
|
| "last_used": None
|
| }
|
| }
|
|
|
|
|
| with open(os.path.join(AGENTS_DIR, f"{agent_id}.json"), 'w', encoding='utf-8') as f:
|
| json.dump(agent_config, f, ensure_ascii=False, indent=2)
|
|
|
| return jsonify({
|
| "success": True,
|
| "agent_id": agent_id,
|
| "message": f"Agent '{name}' 创建成功"
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/list', methods=['GET'])
|
| def list_agents():
|
| """获取所有Agent列表"""
|
| try:
|
| agents = []
|
|
|
| for filename in os.listdir(AGENTS_DIR):
|
| if filename.endswith('.json'):
|
| with open(os.path.join(AGENTS_DIR, filename), 'r', encoding='utf-8') as f:
|
| agent_config = json.load(f)
|
|
|
|
|
| agents.append({
|
| "id": agent_config.get("id"),
|
| "name": agent_config.get("name"),
|
| "description": agent_config.get("description"),
|
| "subject": agent_config.get("subject", agent_config.get("name")),
|
| "instructor": agent_config.get("instructor", "教师"),
|
| "type": agent_config.get("type", "general"),
|
| "created_at": agent_config.get("created_at"),
|
| "plugins": agent_config.get("plugins", []),
|
| "knowledge_bases": agent_config.get("knowledge_bases", []),
|
| "usage_count": agent_config.get("stats", {}).get("usage_count", 0),
|
| "distribution_count": len(agent_config.get("distributions", []))
|
| })
|
|
|
|
|
| agents.sort(key=lambda x: x.get("created_at", 0), reverse=True)
|
|
|
| return jsonify({
|
| "success": True,
|
| "data": agents
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/<agent_id>', methods=['GET'])
|
| def get_agent(agent_id):
|
| """获取特定Agent的配置"""
|
| try:
|
| agent_path = os.path.join(AGENTS_DIR, f"{agent_id}.json")
|
|
|
| if not os.path.exists(agent_path):
|
| return jsonify({
|
| "success": False,
|
| "message": "Agent不存在"
|
| }), 404
|
|
|
| with open(agent_path, 'r', encoding='utf-8') as f:
|
| agent_config = json.load(f)
|
|
|
| return jsonify({
|
| "success": True,
|
| "data": agent_config
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/<agent_id>', methods=['PUT'])
|
| def update_agent(agent_id):
|
| """更新Agent配置"""
|
| try:
|
| data = request.json
|
| agent_path = os.path.join(AGENTS_DIR, f"{agent_id}.json")
|
|
|
| if not os.path.exists(agent_path):
|
| return jsonify({
|
| "success": False,
|
| "message": "Agent不存在"
|
| }), 404
|
|
|
|
|
| with open(agent_path, 'r', encoding='utf-8') as f:
|
| agent_config = json.load(f)
|
|
|
|
|
| if 'name' in data:
|
| agent_config['name'] = data['name']
|
|
|
| if 'description' in data:
|
| agent_config['description'] = data['description']
|
|
|
| if 'plugins' in data:
|
| agent_config['plugins'] = data['plugins']
|
|
|
| if 'knowledge_bases' in data:
|
| agent_config['knowledge_bases'] = data['knowledge_bases']
|
|
|
| if 'workflow' in data:
|
| agent_config['workflow'] = data['workflow']
|
|
|
|
|
| agent_config['updated_at'] = int(time.time())
|
|
|
|
|
| with open(agent_path, 'w', encoding='utf-8') as f:
|
| json.dump(agent_config, f, ensure_ascii=False, indent=2)
|
|
|
| return jsonify({
|
| "success": True,
|
| "message": "Agent更新成功"
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/<agent_id>', methods=['DELETE'])
|
| def delete_agent(agent_id):
|
| """删除Agent"""
|
| try:
|
| agent_path = os.path.join(AGENTS_DIR, f"{agent_id}.json")
|
|
|
| if not os.path.exists(agent_path):
|
| return jsonify({
|
| "success": False,
|
| "message": "Agent不存在"
|
| }), 404
|
|
|
|
|
| os.remove(agent_path)
|
|
|
| return jsonify({
|
| "success": True,
|
| "message": "Agent删除成功"
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/<agent_id>/distribute', methods=['POST'])
|
| def distribute_agent(agent_id):
|
| """为Agent创建分发链接"""
|
| try:
|
| data = request.json
|
| expires_in = data.get('expires_in', 0)
|
|
|
| agent_path = os.path.join(AGENTS_DIR, f"{agent_id}.json")
|
|
|
| if not os.path.exists(agent_path):
|
| return jsonify({
|
| "success": False,
|
| "message": "Agent不存在"
|
| }), 404
|
|
|
|
|
| with open(agent_path, 'r', encoding='utf-8') as f:
|
| agent_config = json.load(f)
|
|
|
|
|
| token = uuid.uuid4().hex
|
|
|
|
|
| expiry = int(time.time() + expires_in) if expires_in > 0 else 0
|
|
|
|
|
| distribution = {
|
| "id": f"dist_{uuid.uuid4().hex[:6]}",
|
| "created_at": int(time.time()),
|
| "token": token,
|
| "expires_at": expiry,
|
| "usage_count": 0
|
| }
|
|
|
|
|
| if "distributions" not in agent_config:
|
| agent_config["distributions"] = []
|
|
|
| agent_config["distributions"].append(distribution)
|
|
|
|
|
| with open(agent_path, 'w', encoding='utf-8') as f:
|
| json.dump(agent_config, f, ensure_ascii=False, indent=2)
|
|
|
|
|
| access_link = f"/student/{agent_id}?token={token}"
|
|
|
| return jsonify({
|
| "success": True,
|
| "distribution": {
|
| "id": distribution["id"],
|
| "link": access_link,
|
| "token": token,
|
| "expires_at": expiry
|
| },
|
| "message": "分发链接创建成功"
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/<agent_id>/distributions', methods=['GET'])
|
| def get_distributions(agent_id):
|
| """获取Agent的分发链接列表"""
|
| try:
|
| agent_path = os.path.join(AGENTS_DIR, f"{agent_id}.json")
|
| if not os.path.exists(agent_path):
|
| return jsonify({"success": False, "message": "Agent不存在"}), 404
|
|
|
| with open(agent_path, 'r', encoding='utf-8') as f:
|
| agent_config = json.load(f)
|
|
|
| distributions = agent_config.get('distributions', [])
|
|
|
| return jsonify({
|
| "success": True,
|
| "distributions": distributions
|
| })
|
|
|
| except Exception as e:
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| @agent_builder_bp.route('/ai-assist', methods=['POST'])
|
| def ai_assisted_workflow():
|
| """使用AI辅助Agent工作流编排"""
|
| try:
|
| data = request.json
|
| description = data.get('description', '')
|
| subject = data.get('subject', '通用学科')
|
|
|
| knowledge_bases = data.get('knowledgeBases') or data.get('knowledge_bases', [])
|
| plugins = data.get('plugins', [])
|
|
|
| if not description:
|
| return jsonify({
|
| "success": False,
|
| "message": "请提供Agent描述"
|
| }), 400
|
|
|
|
|
| available_knowledge_bases = []
|
| try:
|
|
|
| kb_response = requests.get("https://samlax12-agent.hf.space/api/knowledge")
|
| if kb_response.status_code == 200:
|
| kb_data = kb_response.json()
|
| if kb_data.get("success"):
|
| for kb in kb_data.get("data", []):
|
| available_knowledge_bases.append(kb["id"])
|
| except:
|
|
|
| pass
|
|
|
|
|
| available_plugins = ["code", "visualization", "mindmap"]
|
|
|
|
|
| system_prompt = """你是一个专业的AI工作流设计师。你需要根据用户描述,设计一个适合教育场景的Agent工作流。
|
| 你不仅要设计工作流结构,还要推荐合适的知识库和插件。请确保工作流逻辑合理,能够满足用户的需求。
|
|
|
| 工作流应包含以下类型的节点:
|
| 1. 意图识别:识别用户输入的意图
|
| 2. 知识库查询:从指定知识库中检索信息
|
| 3. 插件调用:调用特定插件(如代码执行、3D可视化或思维导图)
|
| 4. 回复生成:生成最终回复
|
|
|
| 用户当前选择的知识库:{knowledge_bases}
|
| 系统中可用的知识库有:{available_knowledge_bases}
|
|
|
| 用户当前选择的插件:{plugins}
|
| 系统中可用的插件有:{available_plugins}
|
|
|
| 这个Agent的主题领域是:{subject}
|
|
|
| 重要:根据Agent描述,推荐最合适的知识库和插件。在推荐时,只能使用系统中真实可用的知识库和插件。
|
|
|
| 请返回三部分内容:
|
| 1. 推荐的知识库列表(只能从可用知识库中选择)
|
| 2. 推荐的插件列表(只能从可用插件中选择)
|
| 3. 完整的工作流JSON结构
|
|
|
| JSON格式示例:
|
| {{
|
| "recommended_knowledge_bases": ["rag_knowledge1", "rag_knowledge2"],
|
| "recommended_plugins": ["code", "visualization"],
|
| "workflow": {{
|
| "nodes": [
|
| {{ "id": "node1", "type": "intent_recognition", "data": {{ "name": "意图识别" }} }},
|
| {{ "id": "node2", "type": "knowledge_query", "data": {{ "name": "知识库查询", "knowledge_base_id": "rag_knowledge1" }} }},
|
| {{ "id": "node3", "type": "plugin_call", "data": {{ "name": "调用代码执行插件", "plugin_id": "code" }} }},
|
| {{ "id": "node4", "type": "generate_response", "data": {{ "name": "生成回复" }} }}
|
| ],
|
| "edges": [
|
| {{ "id": "edge1", "source": "node1", "target": "node2", "condition": "需要知识" }},
|
| {{ "id": "edge2", "source": "node1", "target": "node3", "condition": "需要代码执行" }},
|
| {{ "id": "edge3", "source": "node2", "target": "node4" }},
|
| {{ "id": "edge4", "source": "node3", "target": "node4" }}
|
| ]
|
| }}
|
| }}
|
| """
|
|
|
| system_prompt = system_prompt.format(
|
| knowledge_bases=", ".join(knowledge_bases) if knowledge_bases else "无",
|
| available_knowledge_bases=", ".join(available_knowledge_bases) if available_knowledge_bases else "无可用知识库",
|
| plugins=", ".join(plugins) if plugins else "无",
|
| available_plugins=", ".join(available_plugins),
|
| subject=subject
|
| )
|
|
|
|
|
| try:
|
| headers = {
|
| "Authorization": f"Bearer {STREAM_API_KEY}",
|
| "Content-Type": "application/json"
|
| }
|
|
|
| response = requests.post(
|
| f"{STREAM_BASE_URL}/chat/completions",
|
| headers=headers,
|
| json={
|
| "model": DEFAULT_MODEL,
|
| "messages": [
|
| {"role": "system", "content": system_prompt},
|
| {"role": "user", "content": f"请为以下描述的教育Agent设计工作流并推荐知识库和插件:\n\n{description}"}
|
| ]
|
| }
|
| )
|
|
|
| if response.status_code != 200:
|
| return jsonify({
|
| "success": False,
|
| "message": f"Error code: {response.status_code} - {response.text}",
|
| "workflow": create_default_workflow(),
|
| "recommended_knowledge_bases": [],
|
| "recommended_plugins": []
|
| }), 200
|
|
|
| result = response.json()
|
| content = result['choices'][0]['message']['content']
|
|
|
| except Exception as api_error:
|
| return jsonify({
|
| "success": False,
|
| "message": f"无法连接到AI模型服务: {str(api_error)}",
|
| "workflow": create_default_workflow(),
|
| "recommended_knowledge_bases": [],
|
| "recommended_plugins": []
|
| }), 200
|
|
|
|
|
| import re
|
| json_match = re.search(r'```json\n([\s\S]*?)\n```', content)
|
|
|
| if json_match:
|
| workflow_json = json_match.group(1)
|
| else:
|
|
|
| workflow_json = content
|
|
|
|
|
| try:
|
| result_data = json.loads(workflow_json)
|
| except:
|
|
|
| workflow_json = re.sub(r'```json\n|\n```', '', content)
|
| try:
|
| result_data = json.loads(workflow_json)
|
| except:
|
|
|
| import re
|
| json_patterns = re.findall(r'\{[\s\S]*?\}', content)
|
| if json_patterns:
|
| try:
|
|
|
| longest_json = max(json_patterns, key=len)
|
| result_data = json.loads(longest_json)
|
| except:
|
|
|
| return jsonify({
|
| "success": True,
|
| "message": "使用默认工作流(AI生成的JSON无效)",
|
| "workflow": create_default_workflow(),
|
| "recommended_knowledge_bases": [],
|
| "recommended_plugins": []
|
| })
|
| else:
|
|
|
| return jsonify({
|
| "success": True,
|
| "message": "使用默认工作流(未找到JSON结构)",
|
| "workflow": create_default_workflow(),
|
| "recommended_knowledge_bases": [],
|
| "recommended_plugins": []
|
| })
|
|
|
|
|
| recommended_knowledge_bases = result_data.get("recommended_knowledge_bases", [])
|
| recommended_plugins = result_data.get("recommended_plugins", [])
|
| workflow = result_data.get("workflow", create_default_workflow())
|
|
|
|
|
| valid_knowledge_bases = []
|
| for kb in recommended_knowledge_bases:
|
| if kb in available_knowledge_bases:
|
| valid_knowledge_bases.append(kb)
|
|
|
|
|
| valid_plugins = []
|
| for plugin in recommended_plugins:
|
| if plugin in available_plugins:
|
| valid_plugins.append(plugin)
|
|
|
| return jsonify({
|
| "success": True,
|
| "workflow": workflow,
|
| "recommended_knowledge_bases": valid_knowledge_bases,
|
| "recommended_plugins": valid_plugins,
|
| "message": "已成功创建工作流"
|
| })
|
|
|
| except Exception as e:
|
| import traceback
|
| traceback.print_exc()
|
| return jsonify({
|
| "success": False,
|
| "message": str(e)
|
| }), 500
|
|
|
| def create_default_workflow():
|
| """创建一个默认的空工作流"""
|
| return {
|
| "nodes": [],
|
| "edges": []
|
| } |