Files
aiagent/backend/scripts/create_complex_enterprise_agent.py

321 lines
11 KiB
Python
Raw Normal View History

#!/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())