Files
aiagent/backend/app/services/scene_templates.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

618 lines
23 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.
"""
场景模板注册:路线图「客服 / 研发 / 运维」三类最小可运行工作流,供 API 与脚本一键创建 Agent。
v2: 集成统一 DSL 场景契约SceneContract
"""
from __future__ import annotations
from typing import Any, Callable, Dict, List, Optional
from app.services.scene_contract_service import (
get_preset_contract,
build_system_prompt_from_contract,
ContractPromptConfig,
)
PromptBuilder = Callable[[Dict[str, Any]], str]
def _default_prompt_cs(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是企业客服场景 Agent。根据用户问题给出清晰、可执行的回答不确定时先澄清。
用户输入可能包含:{{input}} 或来自前序节点的合并字段。保持礼貌、简洁。"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_dev(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
lang = params.get("preferred_language") or "任意合适语言"
base = f"""你是研发辅助 Agent负责代码与设计说明。优先给出可运行示例与步骤涉及安全/生产变更时明确风险。
偏好语言/栈:{lang}
用户诉求:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_ops(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是运维/日志分析场景 Agent。帮助用户解读日志片段、定位可能原因与下一步排查不要编造未提供的日志内容。
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _build_minimal_workflow(
prompt: str,
*,
temperature: float = 0.3,
enable_tools: bool = False,
tools: Optional[List[str]] = None,
) -> Dict[str, Any]:
tools = tools or []
return {
"nodes": [
{
"id": "start-1",
"type": "start",
"position": {"x": 80, "y": 120},
"data": {},
},
{
"id": "llm-1",
"type": "llm",
"position": {"x": 320, "y": 120},
"data": {
"prompt": prompt,
"temperature": float(temperature),
"enable_tools": enable_tools,
"tools": tools,
"selected_tools": tools,
},
},
{
"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",
},
],
}
def build_workflow_for_template(template_id: str, parameters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
根据模板 ID 与参数生成 workflow_confignodes+edges
通用参数temperature, enable_tools, tools(list[str]), extra_instructions, preferred_language(dev)
DSL 契约参数contract_id — 指定场景契约,契约 prompt 将替代硬编码 prompt
"""
parameters = dict(parameters or {})
meta = SCENE_TEMPLATE_REGISTRY.get(template_id)
if not meta:
raise ValueError(f"未知模板: {template_id}")
temperature = float(parameters.get("temperature", meta.get("default_temperature", 0.3)))
enable_tools = bool(parameters.get("enable_tools", True))
tools = parameters.get("tools")
if tools is not None and not isinstance(tools, list):
tools = []
elif tools is None:
tools = list(meta.get("default_tools") or [])
# ── DSL 契约支持 ──
contract_id = parameters.get("contract_id") or meta.get("contract_id")
if contract_id:
contract = get_preset_contract(contract_id)
if contract:
extra = (parameters.get("extra_instructions") or "").strip()
cfg = ContractPromptConfig(
temperature=temperature,
extra_instructions=extra,
)
prompt = build_system_prompt_from_contract(contract, cfg)
# 使用契约中的 required_tools 作为默认工具(如果未显式指定)
if not parameters.get("tools") and contract.get("required_tools"):
tools = list(contract["required_tools"])
else:
# 契约不存在时回退到 prompt_builder
prompt_fn: PromptBuilder = meta["prompt_builder"]
prompt = prompt_fn(parameters)
else:
prompt_fn: PromptBuilder = meta["prompt_builder"]
prompt = prompt_fn(parameters)
# 使用模板自定义的 workflow builder如果有
workflow_builder = meta.get("workflow_builder")
if workflow_builder:
return workflow_builder(
prompt,
temperature=temperature,
enable_tools=enable_tools,
tools=tools,
)
return _build_minimal_workflow(
prompt,
temperature=temperature,
enable_tools=enable_tools,
tools=tools,
)
def _default_prompt_learning(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
subject = params.get("subject") or "通用"
level = params.get("level") or "中级"
base = f"""# 角色:智能学习助手(知识图谱 + RAG 增强版)
你是专为深度学习场景设计的 AI 学习助手,具备**知识图谱构建**、**向量语义检索**和**永久记忆**能力。
## 核心架构
你的知识系统由三层组成:
1. **知识图谱 (Knowledge Graph)**:结构化存储知识点实体及其前置/扩展/包含/示例关系,构建学科知识网络
2. **向量记忆 (Vector Memory)**:语义检索历史对话和相关知识,找到最相似的学习内容
3. **长期记忆 (Persistent Memory)**:跨会话保存用户画像、学习进度、薄弱环节
## 当前学习配置
- 学科领域:{subject}
- 难度级别:{level}
- 用户输入将包含学习问题、材料或请求
## 工作流程(每次对话必须遵循)
### 阶段 1理解与分析
1. 理解用户的学习意图(提问/复习/练习/总结/规划)
2. 用 `knowledge_graph_search` 检索相关知识图谱实体
3. 如果用户提供了新的学习材料/知识点,用 `knowledge_graph_add` 自动提取并存储
### 阶段 2知识检索与融合
4. 结合图谱检索结果和历史向量记忆,构建完整的知识上下文
5. 用 `entity_search` 查找特定概念的前置知识和扩展内容
6. 如果需要学习路径建议,用 `learning_path` 分析依赖关系
### 阶段 3生成与交付
7. 基于融合后的知识上下文生成高质量回答
8. 回答应包含:
- 核心概念解释(关联知识图谱中的实体)
- 前置知识提醒(如果有依赖关系)
- 实例或练习题(如适用)
- 扩展阅读建议(关联的扩展知识点)
9. 用 `self_review` 自检回答质量
### 阶段 4巩固与记忆
10. 将本轮对话中的重要知识点持久化到长期记忆
11. 更新用户画像(掌握程度、薄弱环节、学习偏好)
## 知识图谱工具使用指南
| 工具 | 用途 | 何时使用 |
|------|------|---------|
| `knowledge_graph_search` | 向量+图谱混合检索 | 每次回答学习问题前 |
| `knowledge_graph_add` | 从文本提取实体和关系 | 用户分享学习材料/新知识点时 |
| `entity_search` | 关键词搜索实体 | 查找特定概念详情时 |
| `learning_path` | 推荐学习路径 | 用户询问学习顺序/计划时 |
## 回答风格
- 使用 Markdown 格式,层次分明
- 关键概念用 **粗体** 标记
- 公式用代码块或 LaTeX 表达
- 每个回答末尾附上 "📚 相关知识点" 列表(来自图谱检索结果)
- 必要时用 `task_plan` 为用户制定学习计划
## 记忆与个性化
- 记住用户的学习进度和薄弱环节
- 根据用户级别({level})调整解释深度
- 对反复出错的知识点主动提醒和强化"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _build_learning_workflow(
prompt: str,
*,
temperature: float = 0.7,
enable_tools: bool = True,
tools: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""为学习助手构建更丰富的工作流含开始→LLM→结束LLM配置KG+RAG工具"""
tools = tools or []
return {
"nodes": [
{
"id": "start-1",
"type": "start",
"position": {"x": 80, "y": 200},
"data": {"label": "学习任务开始"},
},
{
"id": "llm-learning",
"type": "llm",
"position": {"x": 350, "y": 200},
"data": {
"label": "智能学习助手 (KG+RAG)",
"prompt": prompt,
"temperature": float(temperature),
"enable_tools": enable_tools,
"tools": tools,
"selected_tools": tools,
"model": "deepseek-chat",
"provider": "deepseek",
"max_iterations": 20,
"memory": True,
"memory_max_history": 30,
"memory_vector_enabled": True,
"memory_vector_top_k": 8,
"memory_persist": True,
"memory_learning": True,
},
},
{
"id": "end-1",
"type": "end",
"position": {"x": 620, "y": 200},
"data": {"label": "学习完成"},
},
],
"edges": [
{
"id": "e_start_learning",
"source": "start-1",
"target": "llm-learning",
"sourceHandle": "right",
"targetHandle": "left",
},
{
"id": "e_learning_end",
"source": "llm-learning",
"target": "end-1",
"sourceHandle": "right",
"targetHandle": "left",
},
],
}
# ─── 8 个行业模板 prompt builder ───
def _default_prompt_pr_review(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
lang = params.get("preferred_language") or "中文"
base = f"""你是 PR Review 专家 Agent。审查代码变更时关注代码风格、潜在 Bug、安全漏洞、性能问题、可维护性。
输出语言:{lang}
当你收到 PR 描述与 diff 时,按以下结构输出:
1. 总体评价1-2 句)
2. 关键问题(如有,按严重程度排序)
3. 改进建议(具体、可操作)
4. 通过/需修改/拒绝 的判断
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_daily_report(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是研发日报生成 Agent。根据输入的今日代码提交、任务完成情况和明日计划生成结构化日报。
输出格式:
## 今日完成
- [任务1] 进度/结果
- [任务2] 进度/结果
## 遇到的问题
- (如无则写"")
## 明日计划
- [计划1]
- [计划2]
## 需要协调的事项
- (如无则写"")
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_interview(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是面试调度 Agent。帮助协调面试时间、发送面试邀请、收集面试官反馈。
你能处理的场景:
- 根据候选人可用时间和面试官日历推荐面试时间
- 生成面试邀请邮件/消息模板
- 整理面试反馈和评分
- 跟踪面试流程状态
始终保持专业、礼貌,注意时区信息。
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_competitor(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是竞品监控分析 Agent。负责收集、整理和分析竞争对手的动态信息。
分析维度:
1. 产品功能变化(新功能上线、旧功能下架)
2. 定价策略调整
3. 市场定位与目标客户变化
4. 技术架构/技术栈变化
5. 融资与团队变动
输出格式:
## 竞品动态摘要
- 竞品名称 | 时间 | 变化类型 | 影响评估(高/中/低)
## 建议应对策略
- (具体可执行的建议)
基于公开信息分析,标注信息来源和置信度。
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_test_report(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是测试报告生成 Agent。根据测试执行结果自动生成结构化测试报告。
输出格式:
## 测试概览
- 总计 / 通过 / 失败 / 跳过
- 通过率
- 执行耗时
## 失败用例分析
| 用例名称 | 失败原因 | 严重程度 | 建议 |
## 测试覆盖分析
- 模块覆盖情况
- 关键路径覆盖
## 结论与建议
- (是否建议上线 / 需要补充的测试)
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_onboarding(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是新员工入职引导 Agent。帮助新同事快速熟悉团队、项目和流程。
你能协助:
- 回答关于公司文化、制度、福利的常见问题
- 引导新员工完成入职 checklist设备领取、账号开通、权限申请等
- 推荐学习资源和技术文档
- 介绍团队组织架构和协作方式
- 安排入职培训与导师对接
保持热情友好,对新同事的问题给予耐心详细的解答。
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _default_prompt_risk(params: Dict[str, Any]) -> str:
extra = (params.get("extra_instructions") or "").strip()
base = """你是风险预警分析 Agent。识别、评估和预警项目/业务中的潜在风险。
分析框架:
1. 风险识别 — 技术风险、进度风险、人员风险、外部依赖风险
2. 风险评估 — 发生概率(低/中/高) × 影响程度(低/中/高)
3. 预警等级 — 红色(立即处理) / 橙色(本周处理) / 黄色(持续关注)
4. 应对建议 — 预防措施 + 应急预案
输出要求:简洁但信息完整,风险描述具体而非笼统。
用户输入:{{input}}"""
if extra:
return f"{base}\n\n【额外说明】\n{extra}"
return base
def _build_pr_review_workflow(
prompt: str,
*,
temperature: float = 0.2,
enable_tools: bool = True,
tools: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""PR Review 工作流:代码审查 → 自动评论/总结"""
tools = tools or ["file_read", "text_analyze"]
return {
"nodes": [
{"id": "start-1", "type": "start", "position": {"x": 80, "y": 120}, "data": {}},
{
"id": "llm-review", "type": "llm", "position": {"x": 320, "y": 120},
"data": {
"prompt": prompt,
"temperature": float(temperature),
"enable_tools": enable_tools,
"tools": tools,
"selected_tools": tools,
},
},
{"id": "end-1", "type": "end", "position": {"x": 560, "y": 120}, "data": {}},
],
"edges": [
{"id": "e1", "source": "start-1", "target": "llm-review", "sourceHandle": "right", "targetHandle": "left"},
{"id": "e2", "source": "llm-review", "target": "end-1", "sourceHandle": "right", "targetHandle": "left"},
],
}
SCENE_TEMPLATE_REGISTRY: Dict[str, Dict[str, Any]] = {
"template_customer_service": {
"title": "客服场景",
"description": "通用客服问答与澄清(最小 LLM 链)。",
"category": "customer_service",
"default_temperature": 0.35,
"default_tools": [],
"prompt_builder": _default_prompt_cs,
"contract_id": "contract_customer_service",
},
"template_dev_codegen": {
"title": "研发 / 代码助手",
"description": "代码与设计说明辅助(最小 LLM 链)。",
"category": "dev",
"default_temperature": 0.25,
"default_tools": [],
"prompt_builder": _default_prompt_dev,
"contract_id": "contract_dev_codegen",
},
"template_ops_log_analysis": {
"title": "运维 / 日志分析",
"description": "日志解读与排查建议(最小 LLM 链)。",
"category": "ops",
"default_temperature": 0.3,
"default_tools": [],
"prompt_builder": _default_prompt_ops,
"contract_id": "contract_ops_log_analysis",
},
"template_learning_assistant": {
"title": "智能学习助手 (KG+RAG)",
"description": "知识图谱 + RAG 增强学习助手:实体抽取、关系图谱、向量检索、永久记忆,适合有规模要求的学习场景。",
"category": "education",
"default_temperature": 0.7,
"default_tools": [
"knowledge_graph_search",
"knowledge_graph_add",
"entity_search",
"learning_path",
"file_read",
"file_write",
"text_analyze",
"web_search",
"task_plan",
"self_review",
"math_calculate",
"json_process",
"datetime",
],
"prompt_builder": _default_prompt_learning,
"workflow_builder": _build_learning_workflow,
"contract_id": "contract_learning_assistant",
},
# ─── 8 个行业模板 ───
"template_pr_review": {
"title": "PR Review 审查",
"description": "自动代码审查检查代码风格、Bug、安全漏洞与性能问题输出结构化评审意见与通过/拒绝建议。",
"category": "dev",
"default_temperature": 0.2,
"default_tools": ["file_read", "text_analyze", "web_search"],
"prompt_builder": _default_prompt_pr_review,
"workflow_builder": _build_pr_review_workflow,
"contract_id": "contract_pr_review",
},
"template_daily_report": {
"title": "研发日报生成",
"description": "根据代码提交、任务完成情况与计划自动生成结构化日报,含问题记录与协调事项。",
"category": "dev",
"default_temperature": 0.3,
"default_tools": [],
"prompt_builder": _default_prompt_daily_report,
"contract_id": "contract_daily_report",
},
"template_interview_scheduler": {
"title": "面试调度助手",
"description": "协调面试时间、生成邀请邮件、收集反馈、跟踪流程状态,提升招聘效率。",
"category": "automation",
"default_temperature": 0.4,
"default_tools": ["send_email", "datetime", "task_plan"],
"prompt_builder": _default_prompt_interview,
"contract_id": "contract_interview_scheduler",
},
"template_competitor_monitor": {
"title": "竞品监控分析",
"description": "收集分析竞品动态(产品、定价、市场、技术),输出结构化监控摘要与应对策略建议。",
"category": "data_processing",
"default_temperature": 0.3,
"default_tools": ["web_search", "text_analyze", "file_write"],
"prompt_builder": _default_prompt_competitor,
"contract_id": "contract_competitor_monitor",
},
"template_test_report": {
"title": "自动化测试报告",
"description": "根据测试执行结果生成结构化报告,含失败分析、覆盖评估与上线建议。",
"category": "dev",
"default_temperature": 0.2,
"default_tools": ["file_read", "text_analyze", "json_process"],
"prompt_builder": _default_prompt_test_report,
"contract_id": "contract_test_report",
},
"template_onboarding_guide": {
"title": "新员工入职引导",
"description": "回答制度/流程常见问题、引导完成入职 checklist、推荐学习资源、协助培训安排。",
"category": "customer_service",
"default_temperature": 0.5,
"default_tools": ["file_read", "web_search", "task_plan"],
"prompt_builder": _default_prompt_onboarding,
"contract_id": "contract_onboarding_guide",
},
"template_risk_alert": {
"title": "风险预警分析",
"description": "识别评估项目/业务潜在风险(技术/进度/人员/外部依赖),按红/橙/黄三级预警并提供应对方案。",
"category": "ops",
"default_temperature": 0.3,
"default_tools": ["text_analyze", "task_plan", "web_search"],
"prompt_builder": _default_prompt_risk,
"contract_id": "contract_risk_alert",
},
}
def list_scene_template_meta() -> List[Dict[str, Any]]:
"""供 GET 接口返回(不含 prompt_builder"""
out: List[Dict[str, Any]] = []
for tid, meta in SCENE_TEMPLATE_REGISTRY.items():
hints = ["temperature", "enable_tools", "tools", "extra_instructions"]
if tid == "template_dev_codegen":
hints.append("preferred_language")
if tid == "template_learning_assistant":
hints.extend(["subject学科领域", "level难度级别初级/中级/高级)"])
if tid == "template_pr_review":
hints.append("preferred_language默认中文")
if tid == "template_risk_alert":
hints.extend(["risk_scope项目/技术/运营)", "threshold预警阈值低/中/高)"])
if tid == "template_interview_scheduler":
hints.extend(["timezone时区", "calendar_integration日历集成"])
out.append(
{
"id": tid,
"title": meta["title"],
"description": meta["description"],
"category": meta.get("category"),
"default_temperature": meta.get("default_temperature"),
"parameter_hints": hints,
"contract_id": meta.get("contract_id"),
}
)
return out