Files
aiagent/backend/app/agent_runtime/workflow_integration.py
renjianbo b8b01254ca feat: Phase 1 - output quality verification + node-level auto-retry
- Add enterprise_review tool (35th builtin) for LLM-based quality assessment
- Add evaluator workflow node type for quality gating in DAG
- Add AgentRuntime built-in self-review with auto-correction loop
- Rewrite error_handler node from stub to real retry mechanism
- Add engine-level per-node retry with configurable max_retries/delay/on_exhausted
- Add AgentExtension model for extension tracking
- Enhance validation in agent_create_tool and tool_register_tool
- Update 全能助手 system prompt with self-evolution workflow
- Docs: 缺失能力.md and 解决缺失能力计划.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 22:05:28 +08:00

132 lines
4.2 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.
"""
Agent Runtime ⇄ WorkflowEngine 桥接。
让 workflow_engine.execute_node() 通过寥寥几行调用 Agent Runtime。
"""
from __future__ import annotations
import logging
from typing import Any, Dict, Optional
from app.agent_runtime.core import AgentRuntime
from app.agent_runtime.schemas import (
AgentConfig,
AgentLLMConfig,
AgentToolConfig,
AgentBudgetConfig,
)
logger = logging.getLogger(__name__)
async def run_agent_node(
node_data: Dict[str, Any],
input_data: Dict[str, Any],
execution_logger: Optional[Any] = None,
user_id: Optional[str] = None,
on_tool_executed: Optional[Any] = None,
on_llm_invocation: Optional[Any] = None,
budget_limits: Optional[Dict[str, int]] = None,
) -> Dict[str, Any]:
"""
在工作流中执行 Agent 节点。
node_data 支持的字段:
system_prompt — Agent 人格/指令(支持 {{variable}} 模板)
tools — 可选工具白名单,默认全部
exclude_tools — 可选工具黑名单
model — 模型名称
provider — 提供商openai/deepseek
temperature — 温度
max_iterations — ReAct 最大步数
memory — 是否启用长期记忆
input_data 中的 "query""input" 字段作为用户输入。
"""
# 1. 解析配置
query = (
input_data.get("query")
or input_data.get("input")
or input_data.get("text", "")
)
if not isinstance(query, str):
query = str(query) if query else ""
if not query:
return {"output": "错误Agent 节点未收到用户输入", "status": "error"}
# 2. 解析 system_prompt支持模板变量
raw_prompt = node_data.get("system_prompt", "你是一个有用的AI助手。")
try:
formatted_prompt = raw_prompt.format(**input_data)
except (KeyError, ValueError):
formatted_prompt = raw_prompt
# 3. 构建 Agent 配置
llm_config = AgentLLMConfig(
provider=node_data.get("provider", "openai"),
model=node_data.get("model", "gpt-4o-mini"),
temperature=float(node_data.get("temperature", 0.7)),
max_iterations=int(node_data.get("max_iterations", 10)),
)
# 允许节点内联 api_key/base_url
if node_data.get("api_key"):
llm_config.api_key = node_data["api_key"]
if node_data.get("base_url"):
llm_config.base_url = node_data["base_url"]
# 3a. 构建预算配置(接收工作流级预算限制)
budget = AgentBudgetConfig()
if budget_limits:
if "max_llm_invocations" in budget_limits:
budget.max_llm_invocations = max(1, int(budget_limits["max_llm_invocations"]))
if "max_tool_calls" in budget_limits:
budget.max_tool_calls = max(1, int(budget_limits["max_tool_calls"]))
agent_config = AgentConfig(
name=node_data.get("label", "agent_node"),
system_prompt=formatted_prompt,
llm=llm_config,
tools=AgentToolConfig(
include_tools=node_data.get("tools", []),
exclude_tools=node_data.get("exclude_tools", []),
),
memory={
"enabled": node_data.get("memory", True),
"persist_to_db": node_data.get("memory", True),
},
budget=budget,
user_id=user_id,
self_review_enabled=node_data.get("self_review_enabled", False),
)
# 4. 执行 Agent
runtime = AgentRuntime(
config=agent_config,
execution_logger=execution_logger,
on_tool_executed=on_tool_executed,
)
# 注入 LLM 预算回调(使 Agent 内部 LLM 调用计入工作流预算)
if on_llm_invocation:
runtime.on_llm_invocation = on_llm_invocation
result = await runtime.run(query)
# 5. 返回结果(兼容工作流引擎的输出格式)
if result.success:
return {
"output": result.content,
"status": "success",
"agent_meta": {
"iterations": result.iterations_used,
"tool_calls": result.tool_calls_made,
"truncated": result.truncated,
},
}
else:
return {
"output": result.content,
"status": "error",
"error": result.error,
}