Files
aiagent/backend/app/services/scene_templates.py
renjianbo eabf90c496 feat: add AI学习助手 agent (KG+RAG ideal) and renshenguo feishu bot
- Add AI学习助手 agent creation script with all 39 tools, 3-layer KG+RAG memory
- Add renshenguo (人参果) feishu bot integration (app_service + ws_handler)
- Register renshenguo WS client in main.py startup
- Add RENSHENGUO_APP_ID / RENSHENGUO_APP_SECRET / RENSHENGUO_AGENT_ID config
- Reorganize docs from root into docs/ subdirectories
- Move startup scripts to scripts/startup/
- Various backend optimizations and tool improvements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-06 01:37:13 +08:00

343 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.
"""
场景模板注册:路线图「客服 / 研发 / 运维」三类最小可运行工作流,供 API 与脚本一键创建 Agent。
"""
from __future__ import annotations
from typing import Any, Callable, Dict, List, Optional
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)
"""
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 [])
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",
},
],
}
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,
},
"template_dev_codegen": {
"title": "研发 / 代码助手",
"description": "代码与设计说明辅助(最小 LLM 链)。",
"category": "dev",
"default_temperature": 0.25,
"default_tools": [],
"prompt_builder": _default_prompt_dev,
},
"template_ops_log_analysis": {
"title": "运维 / 日志分析",
"description": "日志解读与排查建议(最小 LLM 链)。",
"category": "ops",
"default_temperature": 0.3,
"default_tools": [],
"prompt_builder": _default_prompt_ops,
},
"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,
},
}
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难度级别初级/中级/高级)"])
out.append(
{
"id": tid,
"title": meta["title"],
"description": meta["description"],
"category": meta.get("category"),
"default_temperature": meta.get("default_temperature"),
"parameter_hints": hints,
}
)
return out