Files
aiagent/backend/app/services/marketplace_bootstrap.py
renjianbo beff3fac8d fix: delete agent 500 error + dynamic personality + deployment guide
- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions,
  schedules, executions, team_members) and unbind goals/tasks before delete
- Remove hardcoded personality templates in Android, replace with dynamic
  system prompt generation from name + description
- Set promptSectionsEnabled=false to bypass PromptComposer for personality
- Add Tencent Cloud Linux deployment guide (Docker Compose)
- Accumulated backend service updates, frontend UI fixes, Android app changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-29 01:17:21 +08:00

241 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
模板市场预置模板引导
应用启动时自动检测并注入 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