Files
aiagent/backend/app/services/marketplace_bootstrap.py

241 lines
12 KiB
Python
Raw Normal View History

"""
模板市场预置模板引导
应用启动时自动检测并注入 8 个预置模板(若市场为空)。
"""
from __future__ import annotations
import logging
import uuid
from datetime import datetime, timedelta
from typing import List, Dict, Any
from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
# 8 个预置模板定义
PRESET_TEMPLATES: List[Dict[str, Any]] = [
# ─── 1. 客服场景 ───
{
"name": "智能客服工作流",
"description": "通用客服问答场景,含问题分类与转人工逻辑,适合企业客服自动化。",
"category": "customer_service",
"tags": ["客服", "AI", "自动化", "FAQ"],
"is_featured": True,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 80, "y": 120}, "data": {}},
{"id": "llm-1", "type": "llm", "position": {"x": 320, "y": 120},
"data": {"prompt": "你是企业客服场景 Agent。根据用户问题给出清晰、可执行的回答;不确定时先澄清。\n用户输入:{{input}}", "temperature": 0.35, "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 560, "y": 120}, "data": {}},
],
"edges": [
{"id": "e_start_llm", "source": "start-1", "target": "llm-1", "sourceHandle": "right", "targetHandle": "left"},
{"id": "e_llm_end", "source": "llm-1", "target": "end-1", "sourceHandle": "right", "targetHandle": "left"},
],
},
# ─── 2. 研发/代码助手 ───
{
"name": "研发代码助手",
"description": "代码生成与设计说明辅助,优先给出可运行示例与步骤。",
"category": "dev",
"tags": ["代码", "开发", "LLM", "辅助"],
"is_featured": True,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 80, "y": 120}, "data": {}},
{"id": "llm-1", "type": "llm", "position": {"x": 320, "y": 120},
"data": {"prompt": "你是研发辅助 Agent,负责代码与设计说明。优先给出可运行示例与步骤;涉及安全/生产变更时明确风险。\n用户诉求:{{input}}", "temperature": 0.25, "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 560, "y": 120}, "data": {}},
],
"edges": [
{"id": "e_start_llm", "source": "start-1", "target": "llm-1", "sourceHandle": "right", "targetHandle": "left"},
{"id": "e_llm_end", "source": "llm-1", "target": "end-1", "sourceHandle": "right", "targetHandle": "left"},
],
},
# ─── 3. 运维/日志分析 ───
{
"name": "运维日志分析",
"description": "日志解读与排查建议,帮助快速定位线上问题根因。",
"category": "ops",
"tags": ["运维", "日志", "排查", "监控"],
"is_featured": True,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 80, "y": 120}, "data": {}},
{"id": "llm-1", "type": "llm", "position": {"x": 320, "y": 120},
"data": {"prompt": "你是运维/日志分析场景 Agent。帮助用户解读日志片段、定位可能原因与下一步排查;不要编造未提供的日志内容。\n用户输入:{{input}}", "temperature": 0.3, "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 560, "y": 120}, "data": {}},
],
"edges": [
{"id": "e_start_llm", "source": "start-1", "target": "llm-1", "sourceHandle": "right", "targetHandle": "left"},
{"id": "e_llm_end", "source": "llm-1", "target": "end-1", "sourceHandle": "right", "targetHandle": "left"},
],
},
# ─── 4. 简单LLM工作流 ───
{
"name": "简单LLM工作流",
"description": "最基础的三节点工作流(开始→LLM→结束),适合快速体验 Agent 编排。",
"category": "llm",
"tags": ["入门", "LLM", "基础"],
"is_featured": True,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 100, "y": 100}, "data": {"label": "开始"}},
{"id": "llm-1", "type": "llm", "position": {"x": 100, "y": 250},
"data": {"label": "LLM处理", "prompt": "请处理以下输入:\n{{input}}", "provider": "deepseek", "model": "deepseek-chat", "temperature": 0.7}},
{"id": "end-1", "type": "end", "position": {"x": 100, "y": 400}, "data": {"label": "结束"}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "llm-1", "sourceHandle": "bottom", "targetHandle": "top"},
{"id": "e2", "source": "llm-1", "target": "end-1", "sourceHandle": "bottom", "targetHandle": "top"},
],
},
# ─── 5. 条件判断LLM工作流 ───
{
"name": "条件判断工作流",
"description": "含条件分支的 LLM 工作流,根据输入值走向不同处理路径。",
"category": "llm",
"tags": ["条件", "分支", "LLM", "中级"],
"is_featured": False,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 100, "y": 100}, "data": {"label": "开始"}},
{"id": "condition-1", "type": "condition", "position": {"x": 100, "y": 200},
"data": {"label": "条件判断", "condition": "{{value}} > 10"}},
{"id": "llm-true", "type": "llm", "position": {"x": -100, "y": 350},
"data": {"label": "True分支LLM", "prompt": "值大于10,请分析:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "llm-false", "type": "llm", "position": {"x": 300, "y": 350},
"data": {"label": "False分支LLM", "prompt": "值小于等于10,请分析:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 100, "y": 500}, "data": {"label": "结束"}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "condition-1"},
{"id": "e2", "source": "condition-1", "target": "llm-true", "sourceHandle": "true"},
{"id": "e3", "source": "condition-1", "target": "llm-false", "sourceHandle": "false"},
{"id": "e4", "source": "llm-true", "target": "end-1"},
{"id": "e5", "source": "llm-false", "target": "end-1"},
],
},
# ─── 6. 数据转换+LLM ───
{
"name": "数据转换与LLM",
"description": "先进行数据映射转换,再将结果送入 LLM 处理,适合 ETL+AI 场景。",
"category": "data_processing",
"tags": ["数据", "转换", "ETL", "LLM"],
"is_featured": False,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 100, "y": 100}, "data": {"label": "开始"}},
{"id": "transform-1", "type": "transform", "position": {"x": 100, "y": 200},
"data": {"label": "数据转换", "mode": "mapping", "mapping": {"input_text": "raw_input", "user_id": "id"}}},
{"id": "llm-1", "type": "llm", "position": {"x": 100, "y": 300},
"data": {"label": "LLM处理", "prompt": "处理转换后的数据:{{input_text}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 100, "y": 400}, "data": {"label": "结束"}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "transform-1"},
{"id": "e2", "source": "transform-1", "target": "llm-1"},
{"id": "e3", "source": "llm-1", "target": "end-1"},
],
},
# ─── 7. 多LLM链式工作流 ───
{
"name": "多LLM链式处理",
"description": "三个 LLM 节点链式串联:分析→处理→总结,实现复杂多步推理。",
"category": "llm",
"tags": ["链式", "多步", "LLM", "高级"],
"is_featured": True,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 100, "y": 100}, "data": {"label": "开始"}},
{"id": "llm-1", "type": "llm", "position": {"x": 100, "y": 200},
"data": {"label": "第一步分析", "prompt": "第一步:分析输入数据:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "llm-2", "type": "llm", "position": {"x": 100, "y": 300},
"data": {"label": "第二步处理", "prompt": "第二步:基于第一步的结果进行处理:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "llm-3", "type": "llm", "position": {"x": 100, "y": 400},
"data": {"label": "第三步总结", "prompt": "第三步:总结最终结果:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 100, "y": 500}, "data": {"label": "结束"}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "llm-1"},
{"id": "e2", "source": "llm-1", "target": "llm-2"},
{"id": "e3", "source": "llm-2", "target": "llm-3"},
{"id": "e4", "source": "llm-3", "target": "end-1"},
],
},
# ─── 8. 定时自动化工作流 ───
{
"name": "定时自动化工作流",
"description": "含定时触发器的数据同步工作流,适合周期性批量任务。",
"category": "automation",
"tags": ["定时", "自动化", "同步", "批量"],
"is_featured": False,
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 100, "y": 100}, "data": {"label": "开始"}},
{"id": "schedule-1", "type": "schedule", "position": {"x": 100, "y": 200},
"data": {"label": "定时触发", "delay_type": "fixed", "delay_value": 3600, "delay_unit": "seconds"}},
{"id": "llm-1", "type": "llm", "position": {"x": 100, "y": 300},
"data": {"label": "任务处理", "prompt": "执行定时任务并生成报告:{{input}}", "provider": "deepseek", "model": "deepseek-chat"}},
{"id": "end-1", "type": "end", "position": {"x": 100, "y": 400}, "data": {"label": "结束"}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "schedule-1"},
{"id": "e2", "source": "schedule-1", "target": "llm-1"},
{"id": "e3", "source": "llm-1", "target": "end-1"},
],
},
]
def bootstrap_preset_templates(db: Session) -> int:
"""若模板市场为空,注入 8 个预置模板。返回注入数量。"""
from app.models.workflow_template import WorkflowTemplate
from app.models.user import User
# 检查是否已有模板
existing_count = db.query(WorkflowTemplate).count()
if existing_count >= 8:
logger.info("模板市场已有 %s 个模板,跳过预置注入", existing_count)
return 0
# 获取系统用户(优先 admin,否则第一个用户)
admin = db.query(User).filter(User.username == "admin").first()
if not admin:
admin = db.query(User).order_by(User.created_at.asc()).first()
if not admin:
logger.warning("无可用用户,跳过模板市场预置注入")
return 0
logger.info("模板市场仅 %s 个模板,开始注入 8 个预置模板 (用户=%s)...", existing_count, admin.username)
added = 0
now = datetime.utcnow()
for template_info in PRESET_TEMPLATES:
# 跳过已存在的同名模板
exists = db.query(WorkflowTemplate).filter(
WorkflowTemplate.name == template_info["name"]
).first()
if exists:
continue
template = WorkflowTemplate(
id=str(uuid.uuid4()),
name=template_info["name"],
description=template_info["description"],
category=template_info["category"],
tags=template_info["tags"],
nodes=template_info["nodes"],
edges=template_info["edges"],
is_public=True,
is_featured=template_info.get("is_featured", False),
view_count=0,
use_count=0,
rating_count=0,
rating_avg=0.0,
user_id=admin.id,
created_at=now - timedelta(days=30 - added), # 分散创建时间
updated_at=now,
)
db.add(template)
added += 1
logger.info(" 预置模板: %s", template_info["name"])
db.commit()
logger.info("模板市场预置注入完成,共注入 %s 个模板", added)
return added