工作流动画效果

This commit is contained in:
rjb
2026-01-19 17:52:29 +08:00
parent 6674060f2f
commit e4aa6cdb79
12 changed files with 2835 additions and 102 deletions

View File

@@ -331,3 +331,69 @@ async def stop_agent(
logger.info(f"用户 {current_user.username} 停止了Agent: {agent.name} ({agent_id})")
return agent
class AgentDuplicateRequest(BaseModel):
"""Agent复制请求模型"""
name: Optional[str] = None # 如果提供,使用此名称;否则自动生成
@router.post("/{agent_id}/duplicate", response_model=AgentResponse, status_code=status.HTTP_201_CREATED)
async def duplicate_agent(
agent_id: str,
duplicate_data: Optional[AgentDuplicateRequest] = None,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
复制Agent
创建一个新的Agent副本包含原Agent的所有配置工作流、描述等
新Agent的状态为draft版本号为1
"""
# 获取原Agent
original_agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not original_agent:
raise NotFoundError(f"Agent不存在: {agent_id}")
# 检查权限read权限需要能读取原Agent才能复制
if not check_agent_permission(db, current_user, original_agent, "read"):
raise HTTPException(status_code=403, detail="无权复制此Agent")
# 生成新名称
if duplicate_data and duplicate_data.name:
new_name = duplicate_data.name
else:
# 自动生成名称:原名称 + " (副本)"
base_name = original_agent.name
new_name = f"{base_name} (副本)"
# 如果名称已存在,添加序号
counter = 1
while db.query(Agent).filter(
Agent.name == new_name,
Agent.user_id == current_user.id
).first():
counter += 1
new_name = f"{base_name} (副本 {counter})"
# 深拷贝工作流配置(避免引用问题)
import copy
new_workflow_config = copy.deepcopy(original_agent.workflow_config)
# 创建新Agent
new_agent = Agent(
name=new_name,
description=original_agent.description,
workflow_config=new_workflow_config,
user_id=current_user.id,
status="draft", # 复制的Agent状态为草稿
version=1 # 版本号从1开始
)
db.add(new_agent)
db.commit()
db.refresh(new_agent)
logger.info(f"用户 {current_user.username} 复制了Agent: {original_agent.name} ({agent_id}) -> {new_agent.name} ({new_agent.id})")
return new_agent

View File

@@ -6,6 +6,7 @@ import asyncio
from collections import defaultdict, deque
import json
import logging
import re
from app.services.llm_service import llm_service
from app.services.condition_parser import condition_parser
from app.services.data_transformer import data_transformer
@@ -123,10 +124,89 @@ class WorkflowEngine:
else:
# 否则合并所有输入
if isinstance(source_output, dict):
input_data.update(source_output)
# 如果source_output包含output字段展开它
if 'output' in source_output and isinstance(source_output['output'], dict):
# 将output中的内容展开到顶层
input_data.update(source_output['output'])
# 保留其他字段如status
for key, value in source_output.items():
if key != 'output':
input_data[key] = value
else:
input_data.update(source_output)
else:
input_data['input'] = source_output
# 如果input_data中没有query字段尝试从所有已执行的节点中查找特别是start节点
if 'query' not in input_data:
# 优先查找start节点
for node_id_key in ['start-1', 'start']:
if node_id_key in node_outputs:
node_output = node_outputs[node_id_key]
if isinstance(node_output, dict):
# 检查顶层字段因为node_outputs存储的是output字段的内容
if 'query' in node_output:
input_data['query'] = node_output['query']
logger.debug(f"[rjb] 从节点 {node_id_key} 中获取query: {input_data['query']}")
break
# 检查output字段兼容性
elif 'output' in node_output and isinstance(node_output['output'], dict):
if 'query' in node_output['output']:
input_data['query'] = node_output['output']['query']
logger.debug(f"[rjb] 从节点 {node_id_key} 的output中获取query: {input_data['query']}")
break
# 如果还没找到,遍历所有节点
if 'query' not in input_data:
for node_id_key, node_output in node_outputs.items():
if isinstance(node_output, dict):
# 检查顶层字段
if 'query' in node_output:
input_data['query'] = node_output['query']
logger.debug(f"[rjb] 从节点 {node_id_key} 中获取query: {input_data['query']}")
break
# 检查output字段兼容性
elif 'output' in node_output and isinstance(node_output['output'], dict):
if 'query' in node_output['output']:
input_data['query'] = node_output['output']['query']
logger.debug(f"[rjb] 从节点 {node_id_key} 的output中获取query: {input_data['query']}")
break
# 如果input_data中没有requirement_analysis字段尝试从所有已执行的节点中查找
if 'requirement_analysis' not in input_data:
# 优先查找requirement-analysis节点
for node_id_key in ['llm-requirement-analysis', 'requirement-analysis']:
if node_id_key in node_outputs:
node_output = node_outputs[node_id_key]
if isinstance(node_output, dict):
# 检查顶层字段因为node_outputs存储的是output字段的内容
if 'requirement_analysis' in node_output:
input_data['requirement_analysis'] = node_output['requirement_analysis']
logger.debug(f"[rjb] 从节点 {node_id_key} 中获取requirement_analysis")
break
# 检查output字段兼容性
elif 'output' in node_output and isinstance(node_output['output'], dict):
if 'requirement_analysis' in node_output['output']:
input_data['requirement_analysis'] = node_output['output']['requirement_analysis']
logger.debug(f"[rjb] 从节点 {node_id_key} 的output中获取requirement_analysis")
break
# 如果还没找到,遍历所有节点
if 'requirement_analysis' not in input_data:
for node_id_key, node_output in node_outputs.items():
if isinstance(node_output, dict):
# 检查顶层字段
if 'requirement_analysis' in node_output:
input_data['requirement_analysis'] = node_output['requirement_analysis']
logger.debug(f"[rjb] 从节点 {node_id_key} 中获取requirement_analysis")
break
# 检查output字段兼容性
elif 'output' in node_output and isinstance(node_output['output'], dict):
if 'requirement_analysis' in node_output['output']:
input_data['requirement_analysis'] = node_output['output']['requirement_analysis']
logger.debug(f"[rjb] 从节点 {node_id_key} 的output中获取requirement_analysis")
break
logger.debug(f"[rjb] 节点输入结果: node_id={node_id}, input_data={input_data}")
return input_data
@@ -344,27 +424,50 @@ class WorkflowEngine:
try:
# 将input_data转换为字符串用于格式化
if isinstance(input_data, dict):
# 如果prompt中包含变量尝试格式化
if '{' in prompt and '}' in prompt:
# 尝试格式化所有input_data中的键
formatted_prompt = prompt
for key, value in input_data.items():
placeholder = f'{{{key}}}'
if placeholder in formatted_prompt:
formatted_prompt = formatted_prompt.replace(
placeholder,
json_module.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else str(value)
)
# 如果还有{input}占位符替换为整个input_data
if '{input}' in formatted_prompt:
# 支持两种格式的变量:{key} 和 {{key}}
formatted_prompt = prompt
has_unfilled_variables = False
# 首先处理 {{variable}} 格式(模板节点常用)
import re
double_brace_vars = re.findall(r'\{\{(\w+)\}\}', prompt)
for var_name in double_brace_vars:
if var_name in input_data:
# 替换 {{variable}} 为实际值
value = input_data[var_name]
replacement = json_module.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else str(value)
formatted_prompt = formatted_prompt.replace(f'{{{{{var_name}}}}}', replacement)
else:
has_unfilled_variables = True
# 然后处理 {key} 格式
for key, value in input_data.items():
placeholder = f'{{{key}}}'
if placeholder in formatted_prompt:
formatted_prompt = formatted_prompt.replace(
'{input}',
json_module.dumps(input_data, ensure_ascii=False)
placeholder,
json_module.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else str(value)
)
prompt = formatted_prompt
else:
# 如果没有占位符将input_data作为JSON附加到prompt
prompt = f"{prompt}\n\n输入数据:\n{json_module.dumps(input_data, ensure_ascii=False)}"
# 如果还有{input}占位符替换为整个input_data
if '{input}' in formatted_prompt:
formatted_prompt = formatted_prompt.replace(
'{input}',
json_module.dumps(input_data, ensure_ascii=False)
)
# 如果仍有未填充的变量({{variable}}格式),将用户输入作为上下文附加
if has_unfilled_variables or re.search(r'\{\{(\w+)\}\}', formatted_prompt):
# 提取用户的实际查询内容
user_query = input_data.get('query', input_data.get('input', input_data.get('text', '')))
if not user_query and isinstance(input_data, dict):
# 如果没有明确的query字段尝试从整个input_data中提取文本内容
user_query = json_module.dumps(input_data, ensure_ascii=False)
if user_query:
formatted_prompt = f"{formatted_prompt}\n\n用户需求:{user_query}\n\n请根据以上用户需求,忽略未填充的变量占位符(如{{{{variable}}}}),直接基于用户需求来完成任务。"
prompt = formatted_prompt
else:
# 如果input_data不是dict直接转换为字符串
if '{input}' in prompt:
@@ -373,6 +476,7 @@ class WorkflowEngine:
prompt = f"{prompt}\n\n输入:{str(input_data)}"
except Exception as e:
# 格式化失败使用原始prompt和input_data
logger.warning(f"[rjb] Prompt格式化失败: {str(e)}")
try:
prompt = f"{prompt}\n\n输入数据:\n{json_module.dumps(input_data, ensure_ascii=False)}"
except:
@@ -381,8 +485,27 @@ class WorkflowEngine:
# 获取LLM配置
provider = node_data.get('provider', 'openai')
model = node_data.get('model', 'gpt-3.5-turbo')
temperature = node_data.get('temperature', 0.7)
max_tokens = node_data.get('max_tokens')
# 确保temperature是浮点数节点模板中可能是字符串
temperature_raw = node_data.get('temperature', 0.7)
if isinstance(temperature_raw, str):
try:
temperature = float(temperature_raw)
except (ValueError, TypeError):
temperature = 0.7
else:
temperature = float(temperature_raw) if temperature_raw is not None else 0.7
# 确保max_tokens是整数节点模板中可能是字符串
max_tokens_raw = node_data.get('max_tokens')
if max_tokens_raw is not None:
if isinstance(max_tokens_raw, str):
try:
max_tokens = int(max_tokens_raw)
except (ValueError, TypeError):
max_tokens = None
else:
max_tokens = int(max_tokens_raw) if max_tokens_raw is not None else None
else:
max_tokens = None
# 不传递 api_key 和 base_url让 LLM 服务使用系统默认配置(与节点测试保持一致)
api_key = None
base_url = None
@@ -461,13 +584,68 @@ class WorkflowEngine:
mode = node_data.get('mode', 'mapping')
try:
result = data_transformer.transform_data(
input_data=input_data,
mapping=mapping,
filter_rules=filter_rules,
compute_rules=compute_rules,
mode=mode
)
# 处理mapping中的{{variable}}格式从input_data中提取值
# 首先,如果input_data包含output字段需要展开它
expanded_input = input_data.copy()
if 'output' in input_data and isinstance(input_data['output'], dict):
# 将output中的内容展开到顶层但保留output字段
expanded_input.update(input_data['output'])
processed_mapping = {}
import re
for target_key, source_expr in mapping.items():
if isinstance(source_expr, str):
# 支持{{variable}}格式
double_brace_vars = re.findall(r'\{\{(\w+)\}\}', source_expr)
if double_brace_vars:
# 从expanded_input中获取变量值
var_value = None
for var_name in double_brace_vars:
# 尝试从expanded_input中获取支持嵌套路径
var_value = self._get_nested_value(expanded_input, var_name)
if var_value is not None:
break
if var_value is not None:
# 如果只有一个变量,直接使用值;否则替换表达式
if len(double_brace_vars) == 1:
processed_mapping[target_key] = var_value
else:
# 多个变量,替换表达式
processed_expr = source_expr
for var_name in double_brace_vars:
var_val = self._get_nested_value(expanded_input, var_name)
if var_val is not None:
replacement = json_module.dumps(var_val, ensure_ascii=False) if isinstance(var_val, (dict, list)) else str(var_val)
processed_expr = processed_expr.replace(f'{{{{{var_name}}}}}', replacement)
processed_mapping[target_key] = processed_expr
else:
# 变量不存在,保持原表达式
processed_mapping[target_key] = source_expr
else:
# 不是{{variable}}格式,直接使用
processed_mapping[target_key] = source_expr
else:
# 不是字符串,直接使用
processed_mapping[target_key] = source_expr
# 如果mode是merge需要合并所有输入数据
if mode == 'merge':
# 合并所有上游节点的输出(使用展开后的数据)
result = expanded_input.copy()
# 添加mapping的结果
for key, value in processed_mapping.items():
result[key] = value
else:
# 使用处理后的mapping进行转换使用展开后的数据
result = data_transformer.transform_data(
input_data=expanded_input,
mapping=processed_mapping,
filter_rules=filter_rules,
compute_rules=compute_rules,
mode=mode
)
exec_result = {'output': result, 'status': 'success'}
if self.logger:
duration = int((time.time() - start_time) * 1000)
@@ -477,6 +655,7 @@ class WorkflowEngine:
if self.logger:
duration = int((time.time() - start_time) * 1000)
self.logger.log_node_error(node_id, node_type, e, duration)
logger.error(f"[rjb] Transform节点执行失败: {str(e)}", exc_info=True)
return {
'output': None,
'status': 'failed',
@@ -1430,45 +1609,120 @@ class WorkflowEngine:
elif node_type == 'output' or node_type == 'end':
# 输出节点:返回最终结果
# 对于人机交互场景End节点应该返回纯文本字符串而不是JSON
logger.debug(f"[rjb] End节点处理: node_id={node_id}, input_data={input_data}, input_data type={type(input_data)}")
# 读取节点配置中的输出格式设置
node_data = node.get('data', {})
output_format = node_data.get('output_format', 'text') # 默认纯文本
logger.debug(f"[rjb] End节点处理: node_id={node_id}, output_format={output_format}, input_data={input_data}, input_data type={type(input_data)}")
final_output = input_data
# 递归解包,提取实际的文本内容
if isinstance(input_data, dict):
# 如果只有一个 key 且是 'input',提取其值
if len(input_data) == 1 and 'input' in input_data:
final_output = input_data['input']
logger.debug(f"[rjb] End节点提取第一层: final_output={final_output}, type={type(final_output)}")
# 如果提取的值仍然是字典且只有一个 'input' key继续提取
if isinstance(final_output, dict) and len(final_output) == 1 and 'input' in final_output:
final_output = final_output['input']
logger.debug(f"[rjb] End节点提取第二层: final_output={final_output}, type={type(final_output)}")
# 确保最终输出是字符串(对于人机交互场景)
# 如果是字典,尝试转换为字符串;如果是其他类型,也转换为字符串
if not isinstance(final_output, str):
if isinstance(final_output, dict):
# 如果是字典尝试提取文本内容或转换为JSON字符串
# 优先查找常见的文本字段
if 'text' in final_output:
final_output = str(final_output['text'])
elif 'content' in final_output:
final_output = str(final_output['content'])
elif 'message' in final_output:
final_output = str(final_output['message'])
elif 'response' in final_output:
final_output = str(final_output['response'])
elif len(final_output) == 1:
# 如果只有一个key直接使用其值
final_output = str(list(final_output.values())[0])
else:
# 否则转换为JSON字符串
final_output = json_module.dumps(final_output, ensure_ascii=False)
# 如果配置为JSON格式直接返回原始数据或格式化的JSON
if output_format == 'json':
# 如果是字典直接返回JSON格式
if isinstance(input_data, dict):
final_output = json_module.dumps(input_data, ensure_ascii=False, indent=2)
elif isinstance(input_data, str):
# 尝试解析为JSON如果成功则格式化否则直接返回
try:
parsed = json_module.loads(input_data)
final_output = json_module.dumps(parsed, ensure_ascii=False, indent=2)
except:
final_output = input_data
else:
final_output = str(final_output)
final_output = json_module.dumps({'output': input_data}, ensure_ascii=False, indent=2)
else:
# 默认纯文本格式:递归解包,提取实际的文本内容
if isinstance(input_data, dict):
# 优先提取 'output' 字段LLM节点的标准输出格式
if 'output' in input_data and isinstance(input_data['output'], str):
final_output = input_data['output']
# 如果只有一个 key 且是 'input',提取其值
elif len(input_data) == 1 and 'input' in input_data:
final_output = input_data['input']
# 如果包含 'solution' 字段,提取其值
elif 'solution' in input_data and isinstance(input_data['solution'], str):
final_output = input_data['solution']
# 如果input_data是字符串类型的字典JSON字符串尝试解析
elif isinstance(input_data, str):
try:
parsed = json_module.loads(input_data)
if isinstance(parsed, dict) and 'output' in parsed:
final_output = parsed['output']
elif isinstance(parsed, str):
final_output = parsed
except:
final_output = input_data
logger.debug(f"[rjb] End节点提取第一层: final_output={final_output}, type={type(final_output)}")
# 如果提取的值仍然是字典且只有一个 'input' key继续提取
if isinstance(final_output, dict) and len(final_output) == 1 and 'input' in final_output:
final_output = final_output['input']
logger.debug(f"[rjb] End节点提取第二层: final_output={final_output}, type={type(final_output)}")
# 确保最终输出是字符串(对于人机交互场景)
# 如果是字典,尝试转换为字符串;如果是其他类型,也转换为字符串
if not isinstance(final_output, str):
if isinstance(final_output, dict):
# 如果是字典尝试提取文本内容或转换为JSON字符串
# 优先查找常见的文本字段
if 'text' in final_output:
final_output = str(final_output['text'])
elif 'content' in final_output:
final_output = str(final_output['content'])
elif 'message' in final_output:
final_output = str(final_output['message'])
elif 'response' in final_output:
final_output = str(final_output['response'])
elif len(final_output) == 1:
# 如果只有一个key直接使用其值
final_output = str(list(final_output.values())[0])
else:
# 否则转换为纯文本不是JSON
# 尝试提取所有文本字段并组合,但排除系统字段
text_parts = []
exclude_keys = {'status', 'error', 'timestamp', 'node_id', 'execution_time'}
for key, value in final_output.items():
if key in exclude_keys:
continue
if isinstance(value, str) and value.strip():
# 如果值本身已经包含 "key: " 格式,直接使用
if value.strip().startswith(f"{key}:"):
text_parts.append(value.strip())
else:
text_parts.append(value.strip())
elif isinstance(value, (int, float, bool)):
text_parts.append(f"{key}: {value}")
if text_parts:
final_output = "\n".join(text_parts)
else:
final_output = str(final_output)
else:
final_output = str(final_output)
# 清理输出文本:移除常见的字段前缀(如 "input: ", "query: " 等)
if isinstance(final_output, str):
import re
# 移除行首的 "input: ", "query: ", "output: " 等前缀
lines = final_output.split('\n')
cleaned_lines = []
for line in lines:
# 匹配行首的 "字段名: " 格式并移除
# 但保留内容本身
line = re.sub(r'^(input|query|output|result|response|message|content|text):\s*', '', line, flags=re.IGNORECASE)
if line.strip(): # 只保留非空行
cleaned_lines.append(line)
# 如果清理后还有内容,使用清理后的版本
if cleaned_lines:
final_output = '\n'.join(cleaned_lines)
# 如果清理后为空,使用原始输出(避免丢失所有内容)
elif final_output.strip():
# 如果原始输出不为空,但清理后为空,说明可能格式特殊,尝试更宽松的清理
# 只移除明显的 "input: " 和 "query: " 前缀,保留其他内容
final_output = re.sub(r'^(input|query):\s*', '', final_output, flags=re.IGNORECASE | re.MULTILINE)
if not final_output.strip():
final_output = str(input_data) # 如果还是空,使用原始输入
logger.debug(f"[rjb] End节点最终输出: final_output={final_output}, type={type(final_output)}")
logger.debug(f"[rjb] End节点最终输出: output_format={output_format}, final_output={final_output[:100] if isinstance(final_output, str) else type(final_output)}")
result = {'output': final_output, 'status': 'success'}
if self.logger:
duration = int((time.time() - start_time) * 1000)

View File

@@ -0,0 +1,309 @@
#!/usr/bin/env python3
"""
生成包含多个节点模板的复杂Agent
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.models.agent import Agent
from app.models.user import User
from app.models.node_template import NodeTemplate
from datetime import datetime
import uuid
def generate_complex_agent_with_templates(db: Session, username: str = "admin"):
"""生成包含多个节点模板的复杂Agent"""
print("=" * 60)
print("生成复杂Agent包含多个节点模板")
print("=" * 60)
print()
# 查找用户
user = db.query(User).filter(User.username == username).first()
if not user:
print(f"❌ 未找到用户 '{username}',请先创建该用户")
return
print(f"✅ 找到用户: {user.username} (ID: {user.id})")
print()
# 查找可用的节点模板(公开的或用户自己的)
templates = db.query(NodeTemplate).filter(
(NodeTemplate.is_public == True) | (NodeTemplate.user_id == user.id)
).limit(10).all()
if len(templates) < 3:
print(f"⚠️ 警告: 只找到 {len(templates)} 个节点模板建议至少3个")
if len(templates) == 0:
print("❌ 没有可用的节点模板,请先创建节点模板")
return
print(f"📋 找到 {len(templates)} 个可用节点模板:")
for i, template in enumerate(templates[:5], 1):
print(f" {i}. {template.name} (ID: {template.id})")
print()
# 选择3-5个模板用于创建复杂Agent
selected_templates = templates[:min(5, len(templates))]
template_ids = [t.id for t in selected_templates]
# 生成复杂的工作流配置
# 工作流结构:开始 -> 模板1 -> 条件判断 -> 模板2/模板3 -> 转换 -> 模板4 -> 结束
nodes = []
edges = []
# 开始节点
start_node = {
"id": "start-1",
"type": "start",
"position": {"x": 100, "y": 200},
"data": {"label": "开始"}
}
nodes.append(start_node)
# 第一个模板节点
if len(template_ids) > 0:
template1_node = {
"id": "template-1",
"type": "template",
"position": {"x": 300, "y": 200},
"data": {
"label": selected_templates[0].name,
"template_id": template_ids[0],
"provider": selected_templates[0].provider,
"model": selected_templates[0].model,
"temperature": selected_templates[0].temperature,
"max_tokens": selected_templates[0].max_tokens,
"prompt": selected_templates[0].prompt
}
}
nodes.append(template1_node)
edges.append({
"id": "e1",
"source": "start-1",
"target": "template-1"
})
# 条件判断节点
condition_node = {
"id": "condition-1",
"type": "condition",
"position": {"x": 500, "y": 200},
"data": {
"label": "判断处理结果",
"condition": "{{result}} contains '需要' or {{result}} contains '重要'"
}
}
nodes.append(condition_node)
if len(template_ids) > 0:
edges.append({
"id": "e2",
"source": "template-1",
"target": "condition-1"
})
# 条件分支如果满足条件使用模板2否则使用模板3
if len(template_ids) > 1:
template2_node = {
"id": "template-2",
"type": "template",
"position": {"x": 700, "y": 100},
"data": {
"label": selected_templates[1].name,
"template_id": template_ids[1],
"provider": selected_templates[1].provider,
"model": selected_templates[1].model,
"temperature": selected_templates[1].temperature,
"max_tokens": selected_templates[1].max_tokens,
"prompt": selected_templates[1].prompt
}
}
nodes.append(template2_node)
edges.append({
"id": "e3",
"source": "condition-1",
"target": "template-2",
"sourceHandle": "true"
})
if len(template_ids) > 2:
template3_node = {
"id": "template-3",
"type": "template",
"position": {"x": 700, "y": 300},
"data": {
"label": selected_templates[2].name,
"template_id": template_ids[2],
"provider": selected_templates[2].provider,
"model": selected_templates[2].model,
"temperature": selected_templates[2].temperature,
"max_tokens": selected_templates[2].max_tokens,
"prompt": selected_templates[2].prompt
}
}
nodes.append(template3_node)
edges.append({
"id": "e4",
"source": "condition-1",
"target": "template-3",
"sourceHandle": "false"
})
# 数据转换节点
transform_node = {
"id": "transform-1",
"type": "transform",
"position": {"x": 900, "y": 200},
"data": {
"label": "数据转换",
"mode": "mapping",
"mapping": {
"final_result": "{{result}}",
"processed": "true"
}
}
}
nodes.append(transform_node)
# 连接条件分支到转换节点
if len(template_ids) > 1:
edges.append({
"id": "e5",
"source": "template-2",
"target": "transform-1"
})
if len(template_ids) > 2:
edges.append({
"id": "e6",
"source": "template-3",
"target": "transform-1"
})
# 最后一个模板节点(如果还有模板)
if len(template_ids) > 3:
template4_node = {
"id": "template-4",
"type": "template",
"position": {"x": 1100, "y": 200},
"data": {
"label": selected_templates[3].name,
"template_id": template_ids[3],
"provider": selected_templates[3].provider,
"model": selected_templates[3].model,
"temperature": selected_templates[3].temperature,
"max_tokens": selected_templates[3].max_tokens,
"prompt": selected_templates[3].prompt
}
}
nodes.append(template4_node)
edges.append({
"id": "e7",
"source": "transform-1",
"target": "template-4"
})
# 结束节点连接到最后一个模板
end_node = {
"id": "end-1",
"type": "end",
"position": {"x": 1300, "y": 200},
"data": {"label": "结束"}
}
nodes.append(end_node)
edges.append({
"id": "e8",
"source": "template-4",
"target": "end-1"
})
else:
# 如果没有更多模板,转换节点直接连接到结束节点
end_node = {
"id": "end-1",
"type": "end",
"position": {"x": 1100, "y": 200},
"data": {"label": "结束"}
}
nodes.append(end_node)
edges.append({
"id": "e7",
"source": "transform-1",
"target": "end-1"
})
# 检查是否已存在同名Agent
agent_name = "复杂模板Agent多节点"
existing = db.query(Agent).filter(
Agent.name == agent_name,
Agent.user_id == user.id
).first()
if existing:
print(f"⚠️ Agent '{agent_name}' 已存在,将更新它...")
existing.workflow_config = {"nodes": nodes, "edges": edges}
existing.description = f"包含 {len(selected_templates)} 个节点模板的复杂Agent支持条件分支和数据转换"
existing.updated_at = datetime.now()
agent = existing
else:
# 创建Agent
agent = Agent(
name=agent_name,
description=f"包含 {len(selected_templates)} 个节点模板的复杂Agent支持条件分支和数据转换",
workflow_config={"nodes": nodes, "edges": edges},
status="published",
user_id=user.id,
version=1
)
db.add(agent)
try:
db.commit()
db.refresh(agent)
print()
print("=" * 60)
print(f"✅ 成功创建/更新复杂Agent: {agent.name}")
print(f" ID: {agent.id}")
print(f" 状态: {agent.status}")
print(f" 节点数量: {len(nodes)}")
print(f" 连接数量: {len(edges)}")
print(f" 使用的模板节点: {len(selected_templates)}")
print("=" * 60)
print()
print("工作流结构:")
print(" 开始 -> 模板1 -> 条件判断 -> 模板2/模板3 -> 数据转换 -> 模板4 -> 结束")
print()
return agent
except Exception as e:
db.rollback()
print()
print("=" * 60)
print(f"❌ 创建失败: {e}")
print("=" * 60)
import traceback
traceback.print_exc()
raise
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="生成包含多个节点模板的复杂Agent")
parser.add_argument(
"--username",
type=str,
default="admin",
help="创建Agent的用户名默认: admin"
)
args = parser.parse_args()
db = SessionLocal()
try:
generate_complex_agent_with_templates(db, username=args.username)
except Exception as e:
print(f"❌ 发生错误: {e}")
import traceback
traceback.print_exc()
finally:
db.close()

View File

@@ -0,0 +1,490 @@
#!/usr/bin/env python3
"""
生成内容生成助手Agent
这是一个专门用于内容生成的Agent能够根据用户需求生成各种类型的内容
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.models.agent import Agent
from app.models.user import User
from app.models.node_template import NodeTemplate
from datetime import datetime
import uuid
import json
def generate_content_agent(db: Session, username: str = "admin"):
"""生成内容生成助手Agent"""
print("=" * 60)
print("生成内容生成助手Agent")
print("=" * 60)
print()
# 查找用户
user = db.query(User).filter(User.username == username).first()
if not user:
print(f"❌ 未找到用户 '{username}',请先创建该用户")
return
print(f"✅ 找到用户: {user.username} (ID: {user.id})")
print()
# 生成内容生成工作流配置
# 工作流结构:
# 开始 -> 需求分析 -> 内容类型判断 -> [文章生成分支 | 文案生成分支 | 脚本生成分支] -> 内容优化 -> 格式检查 -> 结束
nodes = []
edges = []
# 1. 开始节点
start_node = {
"id": "start-1",
"type": "start",
"position": {"x": 50, "y": 300},
"data": {
"label": "开始",
"output_format": "json"
}
}
nodes.append(start_node)
# 2. 需求分析节点LLM节点
requirement_analysis_node = {
"id": "llm-requirement-analysis",
"type": "llm",
"position": {"x": 250, "y": 300},
"data": {
"label": "需求分析",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.7",
"max_tokens": "1500",
"prompt": """你是一位专业的内容需求分析师。请分析用户的内容生成需求,提取关键信息。
用户输入:{{query}}
请分析并提取以下信息:
1. **内容类型**:文章、博客、营销文案、视频脚本、产品描述、社交媒体内容等
2. **主题/话题**:用户想要生成什么主题的内容
3. **目标受众**:内容面向的读者群体
4. **内容风格**:正式、轻松、专业、幽默等
5. **字数要求**:大概的字数范围
6. **特殊要求**:格式要求、关键词、结构要求等
请以JSON格式输出分析结果
{
"content_type": "内容类型",
"topic": "主题",
"target_audience": "目标受众",
"style": "内容风格",
"word_count": "字数要求",
"special_requirements": "特殊要求",
"keywords": ["关键词1", "关键词2"]
}"""
}
}
nodes.append(requirement_analysis_node)
edges.append({
"id": "e1",
"source": "start-1",
"target": "llm-requirement-analysis"
})
# 3. 内容整合节点Transform节点- 用于传递数据
content_prepare_node = {
"id": "transform-prepare",
"type": "transform",
"position": {"x": 450, "y": 300},
"data": {
"label": "准备数据",
"mode": "merge",
"mapping": {
"requirement_analysis": "{{output}}",
"query": "{{query}}"
}
}
}
nodes.append(content_prepare_node)
edges.append({
"id": "e2",
"source": "llm-requirement-analysis",
"target": "transform-prepare"
})
# 4. 内容类型判断节点(条件节点)
# 简化直接根据需求分析结果判断使用LLM输出中的关键词
content_type_condition = {
"id": "condition-content-type",
"type": "condition",
"position": {"x": 650, "y": 300},
"data": {
"label": "内容类型判断",
"condition": "{requirement_analysis} contains '文章' or {requirement_analysis} contains '博客' or {requirement_analysis} contains 'article'"
}
}
nodes.append(content_type_condition)
edges.append({
"id": "e2-1",
"source": "transform-prepare",
"target": "condition-content-type"
})
# 4. 文章生成节点LLM节点
article_generation_node = {
"id": "llm-article-generation",
"type": "llm",
"position": {"x": 650, "y": 200},
"data": {
"label": "文章生成",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.8",
"max_tokens": "4000",
"prompt": """你是一位专业的内容创作专家。请根据用户需求生成一篇高质量的文章。
**用户原始需求**{{query}}
**需求分析结果**
- 主题:{{topic}}
- 目标受众:{{target_audience}}
- 内容风格:{{style}}
- 字数要求:{{word_count}}
- 特殊要求:{{special_requirements}}
- 关键词:{{keywords}}
请生成一篇结构完整、内容丰富的文章,要求:
1. **标题**:吸引人且准确反映内容
2. **引言**:引人入胜的开头,概述文章要点
3. **正文**
- 分多个段落,逻辑清晰
- 每个段落有明确的主题
- 使用适当的过渡词连接段落
- 包含具体例子、数据或案例(如适用)
4. **结论**:总结要点,给出行动建议或思考
5. **格式**使用Markdown格式包含标题、列表、加粗等
**重要**:确保内容完全符合用户需求,不要偏离主题。"""
}
}
nodes.append(article_generation_node)
# 6. 文案生成节点LLM节点
copywriting_node = {
"id": "llm-copywriting",
"type": "llm",
"position": {"x": 850, "y": 300},
"data": {
"label": "文案生成",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.9",
"max_tokens": "2000",
"prompt": """你是一位资深的营销文案专家。请根据用户需求生成吸引人的营销文案。
**用户原始需求**{{query}}
**需求分析结果**
- 主题:{{topic}}
- 目标受众:{{target_audience}}
- 内容风格:{{style}}
- 字数要求:{{word_count}}
- 特殊要求:{{special_requirements}}
- 关键词:{{keywords}}
请生成营销文案,要求:
1. **标题/口号**:简洁有力,吸引眼球
2. **核心卖点**:突出产品或服务的核心价值
3. **情感共鸣**:触动目标受众的情感
4. **行动号召**:明确的行动指引
5. **语言风格**:符合目标受众的阅读习惯
**重要**:确保文案具有说服力和吸引力,能够有效传达信息并促使行动。"""
}
}
nodes.append(copywriting_node)
# 7. 脚本生成节点LLM节点
script_generation_node = {
"id": "llm-script-generation",
"type": "llm",
"position": {"x": 850, "y": 400},
"data": {
"label": "脚本生成",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.8",
"max_tokens": "3000",
"prompt": """你是一位专业的视频/音频脚本创作专家。请根据用户需求生成专业的脚本。
**用户原始需求**{{query}}
**需求分析结果**
- 主题:{{topic}}
- 目标受众:{{target_audience}}
- 内容风格:{{style}}
- 字数要求:{{word_count}}
- 特殊要求:{{special_requirements}}
- 关键词:{{keywords}}
请生成脚本,要求:
1. **开场**:吸引注意力的开头
2. **主体内容**
- 分场景/段落
- 标注说话者(如适用)
- 包含动作提示(如适用)
- 时间控制建议
3. **结尾**:有力的总结或行动号召
4. **格式**:清晰标注场景、角色、动作等
**重要**:确保脚本结构清晰,适合视频或音频制作。"""
}
}
nodes.append(script_generation_node)
# 8. 通用内容生成节点(兜底)
general_generation_node = {
"id": "llm-general-generation",
"type": "llm",
"position": {"x": 850, "y": 500},
"data": {
"label": "通用内容生成",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.8",
"max_tokens": "3000",
"prompt": """你是一位专业的内容创作专家。请根据用户需求生成高质量的内容。
**用户原始需求**{{query}}
**需求分析结果**
- 内容类型:{{content_type}}
- 主题:{{topic}}
- 目标受众:{{target_audience}}
- 内容风格:{{style}}
- 字数要求:{{word_count}}
- 特殊要求:{{special_requirements}}
- 关键词:{{keywords}}
请生成符合要求的内容,确保:
1. 内容完整、结构清晰
2. 符合目标受众的阅读习惯
3. 风格与要求一致
4. 包含必要的关键词
5. 满足字数要求
**重要**:确保生成的内容完全符合用户需求。"""
}
}
nodes.append(general_generation_node)
# 连接条件节点到各个生成节点
edges.append({
"id": "e3",
"source": "condition-content-type",
"target": "llm-article-generation",
"sourceHandle": "true",
"targetHandle": "input",
"condition": "article"
})
edges.append({
"id": "e4",
"source": "condition-content-type",
"target": "llm-copywriting",
"sourceHandle": "true",
"targetHandle": "input",
"condition": "copywriting"
})
edges.append({
"id": "e5",
"source": "condition-content-type",
"target": "llm-script-generation",
"sourceHandle": "true",
"targetHandle": "input",
"condition": "script"
})
edges.append({
"id": "e6",
"source": "condition-content-type",
"target": "llm-general-generation",
"sourceHandle": "false",
"targetHandle": "input"
})
# 9. 内容整合节点Transform节点
content_integration_node = {
"id": "transform-integration",
"type": "transform",
"position": {"x": 1050, "y": 300},
"data": {
"label": "内容整合",
"mode": "merge",
"mapping": {
"generated_content": "{{output}}",
"original_query": "{{query}}",
"requirement_analysis": "{{requirement_analysis}}"
}
}
}
nodes.append(content_integration_node)
# 连接所有生成节点到整合节点
for gen_node_id in ["llm-article-generation", "llm-copywriting", "llm-script-generation", "llm-general-generation"]:
edges.append({
"id": f"e-{gen_node_id}-integration",
"source": gen_node_id,
"target": "transform-integration"
})
# 10. 内容优化节点LLM节点
content_optimization_node = {
"id": "llm-optimization",
"type": "llm",
"position": {"x": 1250, "y": 300},
"data": {
"label": "内容优化",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.7",
"max_tokens": "4000",
"prompt": """你是一位专业的内容编辑和优化专家。请对生成的内容进行优化和润色。
**用户原始需求**{{original_query}}
**需求分析结果**{{requirement_analysis}}
**生成的内容**{{generated_content}}
请对内容进行优化,确保:
1. **内容质量**
- 检查逻辑是否清晰
- 确保信息准确、完整
- 优化表达,使其更流畅
2. **格式规范**
- 检查Markdown格式是否正确
- 确保标题层级合理
- 优化段落结构
3. **风格一致性**
- 确保风格符合要求
- 统一用词和语调
4. **用户体验**
- 确保内容易读易懂
- 优化可读性
**输出要求**
- 直接输出优化后的完整内容纯文本Markdown格式
- 不要包含JSON格式或其他包装
- 确保内容完整、专业、易读"""
}
}
nodes.append(content_optimization_node)
edges.append({
"id": "e7",
"source": "transform-integration",
"target": "llm-optimization"
})
# 11. 结束节点
end_node = {
"id": "end-1",
"type": "end",
"position": {"x": 1450, "y": 300},
"data": {
"label": "结束",
"output_format": "text" # 默认纯文本格式,适合对话场景
}
}
nodes.append(end_node)
edges.append({
"id": "e8",
"source": "llm-optimization",
"target": "end-1"
})
# 创建或更新Agent
workflow_config = {
"nodes": nodes,
"edges": edges
}
agent = db.query(Agent).filter(
Agent.name == "内容生成助手",
Agent.user_id == user.id
).first()
if agent:
agent.workflow_config = workflow_config
agent.updated_at = datetime.now()
print("⚠️ Agent '内容生成助手' 已存在,将更新它...")
else:
agent = Agent(
id=str(uuid.uuid4()),
name="内容生成助手",
description="智能内容生成助手,能够根据用户需求生成各种类型的高质量内容,包括文章、博客、营销文案、视频脚本等。支持需求分析、内容生成、优化润色等完整流程。",
workflow_config=workflow_config,
status="published",
user_id=user.id,
version=1
)
db.add(agent)
try:
db.commit()
db.refresh(agent)
print()
print("=" * 60)
print(f"✅ 成功创建/更新内容生成助手Agent")
print(f" ID: {agent.id}")
print(f" 状态: {agent.status}")
print(f" 节点数量: {len(nodes)}")
print(f" 连接数量: {len(edges)}")
print("=" * 60)
print()
print("工作流结构:")
print(" 开始 -> 需求分析 -> 内容类型判断 -> [文章生成 | 文案生成 | 脚本生成 | 通用生成] -> 内容整合 -> 内容优化 -> 结束")
print()
print("节点说明:")
print(" 1. 开始节点:接收用户输入(主题、类型、要求等)")
print(" 2. 需求分析节点:分析用户需求,提取内容类型、主题、风格等关键信息")
print(" 3. 数据准备节点:整合需求分析结果和原始查询")
print(" 4. 内容类型判断节点:根据内容类型进行分支处理")
print(" 5. 文章生成节点:生成结构完整的文章内容")
print(" 6. 文案生成节点:生成营销文案、广告文案等")
print(" 7. 脚本生成节点:生成视频/音频脚本")
print(" 8. 通用内容生成节点:处理其他类型的内容生成需求")
print(" 9. 内容整合节点:整合生成的内容和需求信息")
print(" 10. 内容优化节点:优化和润色内容,确保质量")
print(" 11. 结束节点:返回最终优化后的内容")
print()
print("使用示例:")
print(" 输入:'帮我写一篇关于人工智能发展趋势的博客文章2000字左右风格轻松易懂'")
print(" 输入:'生成一个产品推广的营销文案,面向年轻白领群体'")
print(" 输入:'写一个5分钟的产品介绍视频脚本'")
print()
return agent
except Exception as e:
db.rollback()
print()
print("=" * 60)
print(f"❌ 创建失败: {e}")
print("=" * 60)
import traceback
traceback.print_exc()
raise
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="生成内容生成助手Agent")
parser.add_argument("--username", type=str, default="admin", help="用户名")
args = parser.parse_args()
db = SessionLocal()
try:
generate_content_agent(db, username=args.username)
finally:
db.close()

View File

@@ -0,0 +1,468 @@
#!/usr/bin/env python3
"""
生成智能需求分析与解决方案生成Agent
这是一个复杂的多节点Agent能够理解用户需求并生成专业的解决方案
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.models.agent import Agent
from app.models.user import User
from app.models.node_template import NodeTemplate
from datetime import datetime
import uuid
import json
def generate_smart_agent(db: Session, username: str = "admin"):
"""生成智能需求分析与解决方案生成Agent"""
print("=" * 60)
print("生成智能需求分析与解决方案生成Agent")
print("=" * 60)
print()
# 查找用户
user = db.query(User).filter(User.username == username).first()
if not user:
print(f"❌ 未找到用户 '{username}',请先创建该用户")
return
print(f"✅ 找到用户: {user.username} (ID: {user.id})")
print()
# 查找可用的节点模板(优先选择特定类型的模板)
# 优先查找工作流设计、API集成、数据分析、业务流程等类型的模板
preferred_categories = ["工作流设计", "API集成", "数据分析", "业务流程", "技术方案"]
templates = []
for category in preferred_categories:
category_templates = db.query(NodeTemplate).filter(
((NodeTemplate.is_public == True) | (NodeTemplate.user_id == user.id)) &
(NodeTemplate.category == category)
).limit(2).all()
templates.extend(category_templates)
# 如果没找到足够的模板,补充其他公开模板
if len(templates) < 3:
other_templates = db.query(NodeTemplate).filter(
(NodeTemplate.is_public == True) | (NodeTemplate.user_id == user.id)
).limit(5).all()
for t in other_templates:
if t not in templates:
templates.append(t)
if len(templates) >= 5:
break
if len(templates) < 2:
print(f"⚠️ 警告: 只找到 {len(templates)} 个节点模板将使用LLM节点代替")
use_templates = False
else:
use_templates = True
print(f"📋 找到 {len(templates)} 个可用节点模板")
for i, template in enumerate(templates[:5], 1):
print(f" {i}. {template.name} ({template.category})")
print()
# 生成智能工作流配置
# 工作流结构:
# 开始 -> 需求理解 -> 需求分类 -> [技术方案分支 | 业务流程分支 | 数据分析分支] -> 方案整合 -> 输出优化 -> 结束
nodes = []
edges = []
# 1. 开始节点
start_node = {
"id": "start-1",
"type": "start",
"position": {"x": 50, "y": 300},
"data": {
"label": "开始",
"output_format": "json"
}
}
nodes.append(start_node)
# 2. 需求理解节点LLM节点
requirement_analysis_node = {
"id": "llm-requirement-analysis",
"type": "llm",
"position": {"x": 250, "y": 300},
"data": {
"label": "需求理解与分析",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.7",
"max_tokens": "2000",
"prompt": """你是一位专业的需求分析师。请分析用户的需求,并提取关键信息。
用户需求:{input}
请按照以下格式输出JSON
{{
"requirement_type": "技术方案|业务流程|数据分析|工作流设计|其他",
"key_points": ["关键点1", "关键点2", ...],
"complexity": "简单|中等|复杂",
"domain": "领域(如:电商、金融、教育等)",
"summary": "需求摘要"
}}"""
}
}
nodes.append(requirement_analysis_node)
edges.append({
"id": "e1",
"source": "start-1",
"target": "llm-requirement-analysis"
})
# 3. 需求分类节点(条件节点)
classification_node = {
"id": "condition-classify",
"type": "condition",
"position": {"x": 450, "y": 300},
"data": {
"label": "需求分类",
"condition": "{{requirement_type}} == '技术方案' or {{requirement_type}} == 'API集成'"
}
}
nodes.append(classification_node)
edges.append({
"id": "e2",
"source": "llm-requirement-analysis",
"target": "condition-classify"
})
# 4. 技术方案分支(如果使用模板)
if use_templates and len(templates) > 0:
# 查找技术方案相关的模板
tech_template = None
for t in templates:
if "技术" in t.category or "API" in t.category or "集成" in t.name:
tech_template = t
break
if not tech_template:
tech_template = templates[0]
tech_solution_node = {
"id": "template-tech-solution",
"type": "template",
"position": {"x": 650, "y": 200},
"data": {
"label": tech_template.name,
"template_id": tech_template.id,
"provider": tech_template.provider or "deepseek",
"model": tech_template.model or "deepseek-chat",
"temperature": str(tech_template.temperature) if tech_template.temperature else "0.7",
"max_tokens": str(tech_template.max_tokens) if tech_template.max_tokens else "2000",
"prompt": tech_template.prompt
}
}
nodes.append(tech_solution_node)
edges.append({
"id": "e3",
"source": "condition-classify",
"target": "template-tech-solution",
"sourceHandle": "true"
})
else:
# 使用LLM节点代替
tech_solution_node = {
"id": "llm-tech-solution",
"type": "llm",
"position": {"x": 650, "y": 200},
"data": {
"label": "技术方案设计",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.7",
"max_tokens": "3000",
"prompt": """你是一位资深的技术架构师。根据需求分析结果,设计一个完整的技术方案。
需求分析结果:{{requirement_analysis}}
请提供:
1. 技术选型建议
2. 架构设计
3. 实施步骤
4. 风险评估
5. 最佳实践建议
输出格式结构化的Markdown文档"""
}
}
nodes.append(tech_solution_node)
edges.append({
"id": "e3",
"source": "condition-classify",
"target": "llm-tech-solution",
"sourceHandle": "true"
})
# 5. 业务流程分支优先使用LLM节点确保能理解用户需求
# 使用LLM节点而不是模板节点因为LLM节点可以更好地理解需求上下文
process_solution_node = {
"id": "llm-process-solution",
"type": "llm",
"position": {"x": 650, "y": 400},
"data": {
"label": "业务流程设计",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.7",
"max_tokens": "4000",
"prompt": """你是一位资深的业务流程设计专家。请根据用户的需求,设计一个完整、详细的业务流程方案。
**重要**:请仔细阅读用户原始需求,确保设计方案完全符合用户的具体需求。
用户原始需求:{{query}}
需求分析结果:{{requirement_analysis}}
请提供以下内容:
1. **流程概述**:整体流程的目标、范围和价值
2. **详细流程设计**
- 流程的各个阶段和环节
- 每个环节的输入、输出和处理逻辑
- 状态流转图用Mermaid或文字描述
3. **关键步骤说明**
- 每个步骤的具体操作
- 前置条件和后置条件
- 异常情况处理
4. **角色与职责**
- 涉及的角色/系统
- 每个角色的职责和权限
5. **数据流转**
- 关键数据在各环节的流转
- 数据格式和验证规则
6. **异常处理机制**
- 常见异常场景
- 异常处理流程
7. **流程优化建议**
- 性能优化点
- 用户体验优化
8. **实施路线图**
- 分阶段实施计划
- 关键里程碑
**重要**:请确保设计方案完全符合用户的需求,不要偏离主题。如果用户需求是电商订单流程,就设计电商订单流程;如果是其他业务,就设计对应的业务流程。
输出格式结构化的Markdown文档使用清晰的标题和列表"""
}
}
nodes.append(process_solution_node)
edges.append({
"id": "e4",
"source": "condition-classify",
"target": "llm-process-solution",
"sourceHandle": "false"
})
# 6. 方案整合节点Transform节点
integration_node = {
"id": "transform-integration",
"type": "transform",
"position": {"x": 850, "y": 300},
"data": {
"label": "方案整合",
"mode": "merge",
"mapping": {
"solution": "{{result}}",
"requirement_analysis": "{{requirement_analysis}}",
"query": "{{query}}",
"timestamp": "{{timestamp}}"
}
}
}
nodes.append(integration_node)
# 连接分支到整合节点
if use_templates and len(templates) > 0:
edges.append({
"id": "e5",
"source": "template-tech-solution" if use_templates else "llm-tech-solution",
"target": "transform-integration"
})
else:
edges.append({
"id": "e5",
"source": "llm-tech-solution",
"target": "transform-integration"
})
# 业务流程分支始终使用LLM节点
edges.append({
"id": "e6",
"source": "llm-process-solution",
"target": "transform-integration"
})
# 7. 输出优化节点LLM节点
optimization_node = {
"id": "llm-optimization",
"type": "llm",
"position": {"x": 1050, "y": 300},
"data": {
"label": "输出优化与格式化",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": "0.5",
"max_tokens": "4000",
"prompt": """你是一位专业的技术文档编辑。请对方案进行优化和格式化,确保方案完全符合用户需求。
用户原始需求:{{query}}
需求分析结果:{{requirement_analysis}}
整合后的方案:{{solution}}
**重要检查**
1. 确保方案内容与用户需求完全匹配
2. 如果方案偏离了用户需求,请重新生成符合需求的方案
3. 如果方案是关于数据清洗的,但用户需求是业务流程设计,请重新生成业务流程设计方案
请:
1. 检查方案是否完全符合用户需求
2. 优化文档结构,使其更清晰
3. 补充关键细节和实施建议
4. 确保格式统一、专业
5. 如果发现方案不符合需求,请重新生成正确的方案
**输出要求**
- 只输出纯文本的Markdown文档内容
- 不要包含任何JSON格式、代码块标记或其他格式包装
- 直接输出方案文档的正文内容
- 确保内容完整、专业、易读
输出格式直接输出完整的Markdown文档正文纯文本"""
}
}
nodes.append(optimization_node)
edges.append({
"id": "e7",
"source": "transform-integration",
"target": "llm-optimization"
})
# 8. 结束节点
end_node = {
"id": "end-1",
"type": "end",
"position": {"x": 1250, "y": 300},
"data": {
"label": "结束",
"output_format": "text" # 默认纯文本格式,适合对话场景
}
}
nodes.append(end_node)
edges.append({
"id": "e8",
"source": "llm-optimization",
"target": "end-1"
})
# 检查是否已存在同名Agent
agent_name = "智能需求分析与解决方案生成器"
existing = db.query(Agent).filter(
Agent.name == agent_name,
Agent.user_id == user.id
).first()
if existing:
print(f"⚠️ Agent '{agent_name}' 已存在,将更新它...")
existing.workflow_config = {"nodes": nodes, "edges": edges}
existing.description = """智能需求分析与解决方案生成Agent
功能特点:
1. 自动理解用户需求并提取关键信息
2. 根据需求类型智能分类(技术方案/业务流程/数据分析等)
3. 调用专业模板或LLM生成针对性解决方案
4. 整合并优化输出,生成专业文档
适用场景:
- 技术方案设计
- 业务流程优化
- 系统架构设计
- 问题分析与解决"""
existing.updated_at = datetime.now()
agent = existing
else:
# 创建Agent
agent = Agent(
name=agent_name,
description="""智能需求分析与解决方案生成Agent
功能特点:
1. 自动理解用户需求并提取关键信息
2. 根据需求类型智能分类(技术方案/业务流程/数据分析等)
3. 调用专业模板或LLM生成针对性解决方案
4. 整合并优化输出,生成专业文档
适用场景:
- 技术方案设计
- 业务流程优化
- 系统架构设计
- 问题分析与解决""",
workflow_config={"nodes": nodes, "edges": edges},
status="published", # 直接发布,可以直接使用
user_id=user.id,
version=1
)
db.add(agent)
try:
db.commit()
db.refresh(agent)
print()
print("=" * 60)
print(f"✅ 成功创建/更新智能Agent: {agent.name}")
print(f" ID: {agent.id}")
print(f" 状态: {agent.status}")
print(f" 节点数量: {len(nodes)}")
print(f" 连接数量: {len(edges)}")
print("=" * 60)
print()
print("工作流结构:")
print(" 开始 -> 需求理解 -> 需求分类 -> [技术方案分支 | 业务流程分支] -> 方案整合 -> 输出优化 -> 结束")
print()
print("节点说明:")
print(" 1. 开始节点:接收用户输入")
print(" 2. 需求理解节点:分析用户需求,提取关键信息")
print(" 3. 需求分类节点:根据需求类型进行分支")
print(" 4. 技术方案/业务流程节点:生成针对性解决方案")
print(" 5. 方案整合节点:整合各分支结果")
print(" 6. 输出优化节点:优化和格式化最终输出")
print(" 7. 结束节点:返回最终结果")
print()
return agent
except Exception as e:
db.rollback()
print()
print("=" * 60)
print(f"❌ 创建失败: {e}")
print("=" * 60)
import traceback
traceback.print_exc()
raise
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="生成智能需求分析与解决方案生成Agent")
parser.add_argument(
"--username",
type=str,
default="admin",
help="创建Agent的用户名默认: admin"
)
args = parser.parse_args()
db = SessionLocal()
try:
generate_smart_agent(db, username=args.username)
except Exception as e:
print(f"❌ 发生错误: {e}")
import traceback
traceback.print_exc()
finally:
db.close()