Files
aiagent/backend/scripts/create_complex_enterprise_agent.py
renjianbo 0608161c82 feat: 完善企业场景多线路由与执行稳定性
补齐平台模板与场景 DSL、预算控制、执行看板和企业场景脚本,增强 Windows 启动/迁移与前端代理和聊天会话记忆,修复执行创建阶段 500 与异步链路排障体验。

Made-with: Cursor
2026-04-09 21:58:53 +08:00

321 lines
11 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.
#!/usr/bin/env python3
"""
创建「企业复杂编排」示例 Agent单 DAG、无环
Start → Condition(用户 query 是否含触发标记)
├─ True: Code(预检/标注) → LLM(多内置工具)
└─ False: LLM(轻量直连)
→ Merge → End
触发深度链路:在用户问题中任意位置包含 **[[深度]]**(不含引号),例如:
「[[深度]]请用 http_request 拉取 https://httpbin.org/get 的 JSON 并摘要」
未包含标记则走轻量分支(仍可调 LLM但默认关闭工具
用法:
cd backend && .\\venv\\Scripts\\python.exe scripts/create_complex_enterprise_agent.py
USE_TESTCLIENT=1 # 不经 TCP直接内存请求 FastAPI推荐本机多实例抢端口时
环境变量:
PLATFORM_BASE_URL, PLATFORM_USERNAME, PLATFORM_PASSWORD
COMPLEX_AGENT_NAME默认 企业复杂编排_深度分流
"""
from __future__ import annotations
import json
import os
import sys
from typing import Any, Dict, List
DEEP_MARKER = "[[深度]]"
DEFAULT_NAME = os.getenv("COMPLEX_AGENT_NAME", "企业复杂编排_深度分流")
TOOLS_DEEP = [
"http_request",
"text_analyze",
"json_process",
"datetime",
"math_calculate",
"file_read",
"system_info",
]
PROMPT_DEEP = f"""你是企业级「深度分析」助手。用户消息中可能含有触发标记「{DEEP_MARKER}」,回答时请忽略该标记本身,专注实质需求。
可调用工具完成HTTP 请求、文本分析、JSON 处理、时间、数学、读文件、系统信息。按需调用,勿编造工具结果。
最后用简洁中文总结结论。"""
PROMPT_FAST = """你是企业助手「快速通道」。用户未走深度编排;请直接、简洁地回答,避免冗长。若信息不足先追问一句。"""
CODE_PREFLIGHT = """
d = input_data if isinstance(input_data, dict) else {}
q = str(d.get("query", "") or "")
result = dict(d)
result["_deep_preflight"] = True
result["_query_chars"] = len(q)
result["_marker_detected"] = "[[深度]]" in q
""".strip()
def _sanitize_edges(edges: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
seen: set = set()
out: List[Dict[str, Any]] = []
for e in edges or []:
s, t = e.get("source"), e.get("target")
if not s or not t or s == t:
continue
key = (s, t, e.get("sourceHandle") or "")
if key in seen:
continue
seen.add(key)
ne = dict(e)
if not ne.get("targetHandle"):
ne["targetHandle"] = "left"
if not ne.get("id"):
sh = ne.get("sourceHandle") or "r"
ne["id"] = f"e_{s}_{t}_{sh}"
out.append(ne)
return out
def build_complex_workflow() -> Dict[str, Any]:
"""Condition 表达式与引擎一致:{{query}} contains \"...\" """
cond_expr = f'{{query}} contains "{DEEP_MARKER}"'
nodes: List[Dict[str, Any]] = [
{"id": "start-1", "type": "start", "position": {"x": 40, "y": 260}, "data": {"label": "开始"}},
{
"id": "cond-1",
"type": "condition",
"position": {"x": 280, "y": 260},
"data": {"label": "深度分流", "condition": cond_expr},
},
{
"id": "code-pf",
"type": "code",
"position": {"x": 520, "y": 120},
"data": {
"label": "预检/标注",
"language": "python",
"code": CODE_PREFLIGHT,
"timeout": 15,
},
},
{
"id": "llm-deep",
"type": "llm",
"position": {"x": 760, "y": 120},
"data": {
"label": "深度 LLM+工具",
"prompt": PROMPT_DEEP,
"temperature": 0.25,
"enable_tools": True,
"tools": TOOLS_DEEP,
"selected_tools": TOOLS_DEEP,
},
},
{
"id": "llm-fast",
"type": "llm",
"position": {"x": 520, "y": 400},
"data": {
"label": "快速 LLM",
"prompt": PROMPT_FAST,
"temperature": 0.35,
"enable_tools": False,
"tools": [],
"selected_tools": [],
},
},
{
"id": "merge-1",
"type": "merge",
"position": {"x": 1000, "y": 260},
"data": {"label": "合并输出", "mode": "merge_all", "strategy": "object"},
},
{"id": "end-1", "type": "end", "position": {"x": 1240, "y": 260}, "data": {"label": "结束"}},
]
edges = _sanitize_edges(
[
{"source": "start-1", "target": "cond-1", "sourceHandle": "right", "targetHandle": "left"},
{"source": "cond-1", "target": "code-pf", "sourceHandle": "true", "targetHandle": "left"},
{"source": "cond-1", "target": "llm-fast", "sourceHandle": "false", "targetHandle": "left"},
{"source": "code-pf", "target": "llm-deep", "sourceHandle": "right", "targetHandle": "left"},
{"source": "llm-deep", "target": "merge-1", "sourceHandle": "right", "targetHandle": "left"},
{"source": "llm-fast", "target": "merge-1", "sourceHandle": "right", "targetHandle": "left"},
{"source": "merge-1", "target": "end-1", "sourceHandle": "right", "targetHandle": "left"},
]
)
return {"nodes": nodes, "edges": edges}
def _validate_local(wf: Dict[str, Any]) -> None:
from app.services.workflow_validator import validate_workflow
r = validate_workflow(wf.get("nodes") or [], wf.get("edges") or [])
if not r.get("valid"):
errs = r.get("errors") or []
raise ValueError("工作流校验失败: " + "; ".join(errs))
def _via_requests() -> int:
import requests
base = os.getenv("PLATFORM_BASE_URL", "http://127.0.0.1:8037").rstrip("/")
user = os.getenv("PLATFORM_USERNAME", "admin")
pwd = os.getenv("PLATFORM_PASSWORD", "123456")
wf = build_complex_workflow()
_validate_local(wf)
lr = requests.post(
f"{base}/api/v1/auth/login",
data={"username": user, "password": pwd},
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=20,
)
if lr.status_code != 200:
print("登录失败:", lr.status_code, lr.text[:500], file=sys.stderr)
return 1
token = lr.json().get("access_token")
if not token:
print("无 access_token", file=sys.stderr)
return 1
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def find_id() -> str | None:
rr = requests.get(f"{base}/api/v1/agents", params={"search": DEFAULT_NAME, "limit": 50}, headers=h, timeout=30)
if rr.status_code != 200:
return None
for a in rr.json() or []:
if a.get("name") == DEFAULT_NAME:
return a.get("id")
return None
desc = (
f"复杂编排示例条件分流query 含「{DEEP_MARKER}」→ 代码预检 + 多工具 LLM否则快速 LLMMerge 汇总后结束。"
" 适合演示画布、引擎分支与预算。"
)
budget = {"max_steps": 120, "max_llm_invocations": 6, "max_tool_calls": 48}
existing = find_id()
if existing:
ur = requests.put(
f"{base}/api/v1/agents/{existing}",
headers=h,
json={"description": desc, "workflow_config": wf, "budget_config": budget},
timeout=120,
)
if ur.status_code != 200:
print("更新失败:", ur.status_code, ur.text[:800], file=sys.stderr)
return 1
aid = existing
print("已更新:", DEFAULT_NAME, aid)
else:
cr = requests.post(
f"{base}/api/v1/agents",
headers=h,
json={
"name": DEFAULT_NAME,
"description": desc,
"workflow_config": wf,
"budget_config": budget,
},
timeout=120,
)
if cr.status_code != 201:
print("创建失败:", cr.status_code, cr.text[:800], file=sys.stderr)
return 1
aid = cr.json()["id"]
print("已创建:", DEFAULT_NAME, aid)
print(
json.dumps(
{
"id": aid,
"name": DEFAULT_NAME,
"deep_marker": DEEP_MARKER,
"hint": f"执行时在 query 中加入 {DEEP_MARKER} 可走深度分支",
},
ensure_ascii=False,
)
)
return 0
def _via_testclient() -> int:
from fastapi.testclient import TestClient
from app.main import app
wf = build_complex_workflow()
_validate_local(wf)
c = TestClient(app)
lr = c.post(
"/api/v1/auth/login",
data={
"username": os.getenv("PLATFORM_USERNAME", "admin"),
"password": os.getenv("PLATFORM_PASSWORD", "123456"),
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
if lr.status_code != 200:
print("login:", lr.status_code, lr.text[:400], file=sys.stderr)
return 1
token = lr.json().get("access_token")
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
gr = c.get("/api/v1/agents", params={"search": DEFAULT_NAME, "limit": 50}, headers=h)
existing = None
if gr.status_code == 200:
for a in gr.json() or []:
if a.get("name") == DEFAULT_NAME:
existing = a.get("id")
break
desc = (
f"复杂编排示例条件分流query 含「{DEEP_MARKER}」→ 代码预检 + 多工具 LLM否则快速 LLMMerge 汇总后结束。"
)
budget = {"max_steps": 120, "max_llm_invocations": 6, "max_tool_calls": 48}
if existing:
ur = c.put(
f"/api/v1/agents/{existing}",
headers=h,
json={"description": desc, "workflow_config": wf, "budget_config": budget},
)
if ur.status_code != 200:
print("put:", ur.status_code, ur.text[:800], file=sys.stderr)
return 1
aid = existing
print("已更新(TestClient):", DEFAULT_NAME, aid)
else:
cr = c.post(
"/api/v1/agents",
headers=h,
json={
"name": DEFAULT_NAME,
"description": desc,
"workflow_config": wf,
"budget_config": budget,
},
)
if cr.status_code != 201:
print("post:", cr.status_code, cr.text[:800], file=sys.stderr)
return 1
aid = cr.json()["id"]
print("已创建(TestClient):", DEFAULT_NAME, aid)
print(
json.dumps(
{"id": aid, "name": DEFAULT_NAME, "deep_marker": DEEP_MARKER},
ensure_ascii=False,
)
)
return 0
def main() -> int:
if os.getenv("USE_TESTCLIENT", "").strip() in ("1", "true", "yes"):
return _via_testclient()
return _via_requests()
if __name__ == "__main__":
raise SystemExit(main())