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>
This commit is contained in:
renjianbo
2026-05-04 22:05:28 +08:00
parent 244ed31274
commit b8b01254ca
13 changed files with 1821 additions and 60 deletions

View File

@@ -134,6 +134,7 @@ class AgentRuntime:
tool_schemas = self.tool_manager.get_tool_schemas()
has_tools = self.tool_manager.has_tools()
steps: List[AgentStep] = []
_self_review_attempted = False # 防止无限修正循环
# 构建 LLM 调用回调(包装 on_llm_call补充上下文
llm_callback_ctx = {"step_type": "think", "tool_name": None}
@@ -221,6 +222,33 @@ class AgentRuntime:
# LLM 直接返回文本 → 结束
self.context.add_assistant_message(content)
final_text = content or "(模型未返回有效内容)"
# 输出质量自检默认关闭Agent 节点可开启)
if self.config.self_review_enabled and not _self_review_attempted:
review = await self._self_review(final_text, task_context=user_input)
steps.append(AgentStep(
iteration=self.context.iteration,
type="tool_result",
content=f"self_review: score={review['score']:.2f} passed={review['passed']}",
tool_name="self_review",
tool_input={"content": final_text[:200]},
tool_result=json.dumps(review, ensure_ascii=False),
))
if review["passed"]:
logger.info("self_review 通过 (%.2f >= %.2f)", review["score"], review["threshold"])
else:
logger.info("self_review 未通过 (%.2f < %.2f),追加修正", review["score"], review["threshold"])
_self_review_attempted = True
# 追加修正提示
fix_prompt = (
f"你的上一个回答未通过质量检查(评分 {review['score']:.1f}/{review['threshold']})。\n"
f"问题:{''.join(review['issues'][:3])}\n"
f"改进建议:{''.join(review['suggestions'][:3])}\n"
"请修正你的回答,确保满足上述建议。"
)
self.context.add_user_message(fix_prompt)
continue # 回到 ReAct 循环,让 LLM 修正
steps.append(AgentStep(
iteration=self.context.iteration,
type="final",
@@ -383,6 +411,7 @@ class AgentRuntime:
tool_schemas = self.tool_manager.get_tool_schemas()
has_tools = self.tool_manager.has_tools()
steps: List[AgentStep] = []
_self_review_attempted = False
llm_callback_ctx = {"step_type": "think", "tool_name": None}
@@ -458,6 +487,37 @@ class AgentRuntime:
# LLM 直接返回文本 → 结束
self.context.add_assistant_message(content)
final_text = content or "(模型未返回有效内容)"
# 输出质量自检(默认关闭)
if self.config.self_review_enabled and not _self_review_attempted:
review = await self._self_review(final_text, task_context=user_input)
yield {
"type": "tool_result",
"content": f"self_review: score={review['score']:.2f} passed={review['passed']}",
"tool_name": "self_review",
"iteration": self.context.iteration,
"session_id": self.context.session_id,
}
if review["passed"]:
logger.info("self_review 通过 (%.2f >= %.2f)", review["score"], review["threshold"])
else:
logger.info("self_review 未通过 (%.2f < %.2f),追加修正", review["score"], review["threshold"])
_self_review_attempted = True
yield {
"type": "think",
"content": f"自检未通过({review['score']:.1f}),正在修正:{''.join(review['suggestions'][:2])}",
"iteration": self.context.iteration,
"session_id": self.context.session_id,
}
fix_prompt = (
f"你的上一个回答未通过质量检查(评分 {review['score']:.1f}/{review['threshold']})。\n"
f"问题:{''.join(review['issues'][:3])}\n"
f"改进建议:{''.join(review['suggestions'][:3])}\n"
"请修正你的回答,确保满足上述建议。"
)
self.context.add_user_message(fix_prompt)
continue # 回到 ReAct 循环,让 LLM 修正
yield {
"type": "final",
"content": final_text,
@@ -663,6 +723,88 @@ class AgentRuntime:
if db:
db.close()
async def _self_review(self, content: str, task_context: str = "") -> dict:
"""输出质量自检:用轻量 LLM 评判输出,返回 {score, passed, issues, suggestions}。"""
criteria = (
"回答必须准确、完整、切题。"
"包含具体可执行的步骤或代码示例。"
"无明显事实错误或遗漏。"
"格式清晰,便于阅读。"
)
try:
from app.agent_runtime.core import _LLMClient
from app.agent_runtime.schemas import AgentLLMConfig
review_config = AgentLLMConfig(
provider=getattr(self.config.llm, 'provider', 'deepseek'),
model="deepseek-v4-flash",
temperature=0.1,
max_tokens=800,
request_timeout=30.0,
)
if self.config.llm.api_key:
review_config.api_key = self.config.llm.api_key
if self.config.llm.base_url:
review_config.base_url = self.config.llm.base_url
client = _LLMClient(review_config)
judge_prompt = (
"你是严格的内容质量评审专家。请根据以下标准对内容进行评分。\n\n"
f"【评判标准】\n{criteria}\n\n"
f"【待评审内容】\n{content[:8000]}\n"
)
if task_context:
judge_prompt += f"\n【任务背景】\n{task_context[:2000]}\n"
judge_prompt += (
"\n请以 JSON 格式返回评审结果(严格只返回 JSON不要任何其他文字\n"
'{"score": 0.75, "passed": true, "issues": ["问题1"], '
'"suggestions": ["建议1"], "summary": "一句话总结"}\n\n'
"评分规则1.0完美 0.8良好 0.6基本满足 0.4大部分未满足 0.2完全不满足\n"
"score >= 0.6 时 passed=true否则 passed=false\n"
)
messages = [{"role": "user", "content": judge_prompt}]
resp = await client.chat(messages=messages, tools=None, iteration=0)
judge_text = getattr(resp, 'content', '') or (
resp.get('content', '') if isinstance(resp, dict) else str(resp)
)
# 解析 JSON
try:
judge_clean = judge_text.strip()
if judge_clean.startswith("```"):
lines = judge_clean.split("\n")
judge_clean = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
result = json.loads(judge_clean)
except json.JSONDecodeError:
import re as _sr_re
m = _sr_re.search(r'\{[^{}]*"score"\s*:\s*[\d.]+[^{}]*\}', judge_text, _sr_re.DOTALL)
if m:
try:
result = json.loads(m.group())
except json.JSONDecodeError:
result = {"score": 0.5, "passed": False, "issues": ["无法解析评审结果"], "suggestions": [], "summary": ""}
else:
result = {"score": 0.5, "passed": False, "issues": ["无法解析评审结果"], "suggestions": [], "summary": ""}
score = float(result.get("score", 0.5))
threshold = self.config.llm.self_review_threshold
passed = score >= threshold
return {
"score": score,
"passed": passed,
"threshold": threshold,
"issues": result.get("issues", []),
"suggestions": result.get("suggestions", []),
"summary": result.get("summary", ""),
}
except Exception as e:
logger.warning("self_review 执行失败: %s", e)
return {"score": 0.5, "passed": True, "issues": [], "suggestions": [], "error": str(e)}
@staticmethod
def _extract_tool_calls(response: Any) -> List[Dict[str, Any]]:
"""从 LLM 响应中提取工具调用列表。"""

View File

@@ -36,6 +36,7 @@ class AgentLLMConfig(BaseModel):
max_iterations: int = 10 # ReAct 循环最大步数
request_timeout: float = 120.0
extra_body: Optional[Dict[str, Any]] = None
self_review_threshold: float = 0.6 # self-review 通过阈值0-1
class AgentBudgetConfig(BaseModel):
@@ -55,6 +56,8 @@ class AgentConfig(BaseModel):
user_id: Optional[str] = None
# 持久记忆 / 向量记忆的 scope_id不设时沿用 user_id 或 name易与其他 Agent 串记忆)
memory_scope_id: Optional[str] = None
# 是否开启输出质量自检(结束前用轻量 LLM 评审,不达标则追加修正)
self_review_enabled: bool = False
class AgentMessage(BaseModel):

View File

@@ -97,6 +97,7 @@ async def run_agent_node(
},
budget=budget,
user_id=user_id,
self_review_enabled=node_data.get("self_review_enabled", False),
)
# 4. 执行 Agent

View File

@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
_registered = False
_EXPECTED_BUILTIN = 31
_EXPECTED_BUILTIN = 35
def ensure_builtin_tools_registered() -> None:
@@ -49,6 +49,10 @@ def ensure_builtin_tools_registered() -> None:
deploy_push_tool,
agent_create_tool,
tool_register_tool,
capability_check_tool,
code_tool_create_tool,
extension_log_tool,
self_review_tool,
HTTP_REQUEST_SCHEMA,
FILE_READ_SCHEMA,
FILE_WRITE_SCHEMA,
@@ -80,6 +84,10 @@ def ensure_builtin_tools_registered() -> None:
DEPLOY_PUSH_SCHEMA,
AGENT_CREATE_SCHEMA,
TOOL_REGISTER_SCHEMA,
CAPABILITY_CHECK_SCHEMA,
CODE_TOOL_CREATE_SCHEMA,
EXTENSION_LOG_SCHEMA,
SELF_REVIEW_SCHEMA,
)
tool_registry.register_builtin_tool("http_request", http_request_tool, HTTP_REQUEST_SCHEMA)
@@ -113,6 +121,10 @@ def ensure_builtin_tools_registered() -> None:
tool_registry.register_builtin_tool("deploy_push", deploy_push_tool, DEPLOY_PUSH_SCHEMA)
tool_registry.register_builtin_tool("agent_create", agent_create_tool, AGENT_CREATE_SCHEMA)
tool_registry.register_builtin_tool("tool_register", tool_register_tool, TOOL_REGISTER_SCHEMA)
tool_registry.register_builtin_tool("capability_check", capability_check_tool, CAPABILITY_CHECK_SCHEMA)
tool_registry.register_builtin_tool("code_tool_create", code_tool_create_tool, CODE_TOOL_CREATE_SCHEMA)
tool_registry.register_builtin_tool("extension_log", extension_log_tool, EXTENSION_LOG_SCHEMA)
tool_registry.register_builtin_tool("self_review", self_review_tool, SELF_REVIEW_SCHEMA)
_registered = True
n = tool_registry.builtin_tool_count()

View File

@@ -32,3 +32,21 @@ class Agent(Base):
def __repr__(self):
return f"<Agent(id={self.id}, name={self.name})>"
class AgentExtension(Base):
"""Agent 自主扩展记录表 — 用于 extension_log 工具"""
__tablename__ = "agent_extensions"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="扩展记录ID")
extension_type = Column(String(50), nullable=False, comment="扩展类型: agent_created/tool_registered/code_tool_created")
name = Column(String(100), nullable=False, comment="创建的Agent或工具名称")
reason = Column(Text, comment="创建原因(为什么需要扩展)")
detail = Column(JSON, comment="扩展详情(参数、结果等)")
success_rating = Column(String(20), comment="效果评价: success/partial/failed")
note = Column(Text, comment="反馈备注")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="创建者ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
def __repr__(self):
return f"<AgentExtension(id={self.id}, type={self.extension_type}, name={self.name})>"

View File

@@ -3382,6 +3382,16 @@ async def agent_create_tool(
"""
import uuid
# -- 参数校验 --
if not name or not name.strip():
return json.dumps({"error": "invalid_name", "message": "Agent 名称不能为空"}, ensure_ascii=False)
name = name.strip()
if not system_prompt or len(system_prompt.strip()) < 20:
return json.dumps({
"error": "invalid_prompt",
"message": f"system_prompt 太短({len(system_prompt) if system_prompt else 0}字符至少需要20字符才能定义有效的 Agent 角色",
}, ensure_ascii=False)
try:
from app.core.database import SessionLocal
from app.models.agent import Agent
@@ -3450,7 +3460,7 @@ async def agent_create_tool(
"model": model,
"provider": provider,
"temperature": temperature,
"tools": "ALL (31 builtin tools)",
"tools": "ALL (34 builtin tools)",
"memory": "RAG enabled",
},
"hint": f"现在可以用 agent_call(agent_name=\"{name}\", query=\"...\") 来调用这个新 Agent",
@@ -3560,6 +3570,20 @@ async def tool_register_tool(
},
}
# URL 可达性验证(发送 HEAD 请求检测,不阻塞注册)
url_reachable = False
try:
# 用占位符替换模板参数为 test 值,构造测试 URL
test_url = url
for p in url_params:
test_url = test_url.replace(f"{{{p}}}", "test")
if not url_params:
test_url = url
resp = await httpx.AsyncClient(timeout=5.0).head(test_url, follow_redirects=True)
url_reachable = resp.status_code < 500
except Exception:
url_reachable = False # 不阻断注册,仅记录
db = SessionLocal()
try:
# 检查重名
@@ -3607,6 +3631,7 @@ async def tool_register_tool(
"url_template": url,
"params": required,
},
"url_reachable": url_reachable,
"hint": f"工具已就绪,可直接调用 {name}({', '.join(p + '=...' for p in required) if required else ''})",
}, ensure_ascii=False)
finally:
@@ -3639,3 +3664,667 @@ TOOL_REGISTER_SCHEMA = {
},
},
}
# ── capability_check ──────────────────────────────────────────
async def capability_check_tool(
task_description: str,
required_domains: str = "",
) -> str:
"""
能力自检工具:分析当前 Agent 的工具和子 Agent 是否足以完成给定任务。
Agent 在接到复杂/陌生任务时,应先调用此工具评估自身能力,
根据返回的差距分析决定是否需要创建子 Agent 或注册新工具。
Args:
task_description: 任务描述
required_domains: 任务涉及的领域,逗号分隔(如「数据库,性能优化,SQL」
"""
try:
from app.core.database import SessionLocal
from app.models.agent import Agent as AgentModel
from app.services.tool_registry import tool_registry
# 获取所有可用工具
all_tool_names = []
tool_categories = {}
for schema in tool_registry.get_all_tool_schemas():
fn = schema.get("function", schema)
tname = fn.get("name", "")
if tname:
all_tool_names.append(tname)
desc = fn.get("description", "")
# 简单分类
cat = desc.split("")[0] if "" in desc else desc[:30]
tool_categories[tname] = cat
# 查询数据库中的可用子 Agent
available_agents = []
try:
db = SessionLocal()
try:
agents_in_db = db.query(AgentModel).filter(AgentModel.status == "active").all()
available_agents = [
{"name": a.name, "id": a.id, "description": a.description or ""}
for a in agents_in_db
]
finally:
db.close()
except Exception:
pass
# 领域关键词映射(规则引擎,不调 LLM
domain_tool_map = {
"数据库": ["database_query"],
"SQL": ["database_query"],
"代码": ["code_execute", "git_operation"],
"编程": ["code_execute", "git_operation", "project_scaffold"],
"文件": ["file_read", "file_write", "excel_process"],
"网络": ["http_request", "url_parse", "web_search"],
"HTTP": ["http_request"],
"API": ["http_request", "tool_register"],
"数据": ["json_process", "excel_process", "text_analyze"],
"分析": ["text_analyze", "code_execute", "database_query"],
"报告": ["pdf_generate", "file_write"],
"文档": ["file_read", "pdf_generate", "file_write"],
"部署": ["deploy_push", "docker_manage"],
"容器": ["docker_manage"],
"Docker": ["docker_manage"],
"前端": ["project_scaffold"],
"后端": ["project_scaffold", "http_request"],
"搜索": ["web_search"],
"Git": ["git_operation"],
"测试": ["code_execute"],
"监控": ["http_request", "schedule_create"],
"调度": ["schedule_create", "schedule_list"],
"邮件": ["send_email"],
"Excel": ["excel_process"],
"PDF": ["pdf_generate", "file_read"],
"浏览器": ["browser_use"],
"Android": ["adb_log"],
"加密": ["crypto_util"],
"性能": ["database_query", "code_execute"],
"优化": ["database_query", "code_execute"],
}
# 工具能力覆盖关键词
tool_capability_keywords = {
"http_request": ["HTTP", "API", "网络", "请求"],
"file_read": ["文件", "读取"],
"file_write": ["文件", "写入", "保存"],
"text_analyze": ["文本", "分析"],
"datetime": ["时间", "日期"],
"math_calculate": ["数学", "计算"],
"system_info": ["系统", "信息"],
"json_process": ["JSON", "数据"],
"database_query": ["数据库", "SQL", "查询"],
"adb_log": ["Android", "ADB"],
"schedule_create": ["调度", "定时"],
"crypto_util": ["加密", "解密"],
"random_generate": ["随机", "数据"],
"send_email": ["邮件", "发送"],
"url_parse": ["URL", "解析"],
"regex_test": ["正则", "表达式"],
"agent_call": ["Agent", "委派"],
"code_execute": ["代码", "执行", "Python"],
"git_operation": ["Git", "版本"],
"web_search": ["搜索", "网页"],
"pdf_generate": ["PDF", "报告"],
"project_scaffold": ["项目", "模板"],
"task_plan": ["任务", "规划"],
"excel_process": ["Excel"],
"browser_use": ["浏览器"],
"docker_manage": ["Docker", "容器"],
"deploy_push": ["部署"],
"agent_create": ["创建Agent"],
"tool_register": ["注册工具"],
}
domains = [d.strip() for d in required_domains.split(",") if d.strip()]
if not domains:
# 从 task_description 中自动提取关键词
desc_lower = task_description
domains = [kw for kw in domain_tool_map if kw in desc_lower]
if not domains:
domains = ["通用"]
# 找出推荐的已有工具
recommended_existing = []
for domain in domains:
tools = domain_tool_map.get(domain, [])
for t in tools:
if t in all_tool_names and t not in recommended_existing:
recommended_existing.append(t)
# 差距分析:哪些领域没有匹配的工具
missing_domains = []
missing_tools = []
for domain in domains:
expected = domain_tool_map.get(domain)
if expected:
found = any(t in all_tool_names for t in expected)
if not found:
missing_domains.append(domain)
missing_tools.extend(expected)
missing_tools = sorted(set(missing_tools))
# 严重程度评估
if not missing_domains:
severity = "low"
elif len(missing_domains) <= 2:
severity = "medium"
else:
severity = "high"
# 生成建议
recommendations = []
if missing_domains:
rec_name = f"{domains[0]}专家" if domains else "领域专家"
recommendations.append(f"使用 agent_create 创建「{rec_name}」Agent赋予其专业知识")
if missing_tools:
actionable = [t for t in missing_tools if t in tool_capability_keywords]
if actionable:
recommendations.append(
f"缺少以下能力工具:{', '.join(actionable[:5])}"
f"可通过 tool_register 注册外部 API或通过 code_tool_create 创建代码工具"
)
recommendations.append("使用 web_search 搜索相关的外部 API 或开源工具")
if available_agents:
agent_names = [a["name"] for a in available_agents[:5]]
recommendations.insert(0, f"可尝试调用现有 Agent{', '.join(agent_names)}")
result = {
"task": task_description,
"domains_analyzed": domains,
"available_tools_count": len(all_tool_names),
"available_tools_sample": all_tool_names[:15],
"available_agents": [a["name"] for a in available_agents],
"recommended_existing_tools": recommended_existing,
"gap_analysis": {
"missing_domain_knowledge": missing_domains,
"missing_tools": missing_tools,
"severity": severity,
},
"recommendations": recommendations,
}
return json.dumps(result, ensure_ascii=False)
except Exception as e:
logger.error(f"capability_check 工具执行失败: {e}", exc_info=True)
return json.dumps({"error": "check_failed", "message": str(e)}, ensure_ascii=False)
CAPABILITY_CHECK_SCHEMA = {
"type": "function",
"function": {
"name": "capability_check",
"description": (
"能力自检工具:分析当前可用工具和子 Agent 是否足以完成给定任务。"
"在接到复杂或陌生任务时,先用此工具评估差距,再决定是否需要创建子 Agent 或注册新工具。"
"返回结构化差距分析和行动建议。"
),
"parameters": {
"type": "object",
"properties": {
"task_description": {"type": "string", "description": "任务描述如「分析MySQL慢查询日志并优化」"},
"required_domains": {"type": "string", "description": "任务涉及的领域,逗号分隔(如「数据库,性能优化,SQL」留空则自动提取"},
},
"required": ["task_description"],
},
},
}
# ── code_tool_create ──────────────────────────────────────────
async def code_tool_create_tool(
name: str,
description: str,
code: str,
language: str = "python",
parameter_schema: str = "{}",
test_input: str = "",
) -> str:
"""
将经过验证的代码持久化为可复用工具。
先用 code_execute 验证代码正确性,再写入 tools 表并加载到 tool_registry。
创建后所有 Agent 均可调用该工具。
Args:
name: 工具名称(英文,如 parse_slow_query
description: 工具功能描述
code: 完整的 Python/JS 函数代码
language: 代码语言python/javascript
parameter_schema: 参数 schema JSON{"param1": {"type": "string", "description": "..."}}
test_input: 测试输入 JSON用于沙箱验证可选
"""
import uuid
try:
from app.core.database import SessionLocal
from app.models.tool import Tool
from app.models.user import User
from app.services.tool_registry import tool_registry
# 解析参数 schema
try:
param_schema = json.loads(parameter_schema) if parameter_schema else {}
except json.JSONDecodeError:
return json.dumps({"error": "invalid_schema", "message": "parameter_schema 不是有效 JSON"}, ensure_ascii=False)
# 构建 OpenAI function schema
properties = {}
required_params = []
for pname, pinfo in param_schema.items():
prop = {"description": pinfo.get("description", f"参数 {pname}")}
ptype = pinfo.get("type", "string")
if ptype == "integer":
prop["type"] = "integer"
elif ptype == "number":
prop["type"] = "number"
elif ptype == "boolean":
prop["type"] = "boolean"
else:
prop["type"] = "string"
properties[pname] = prop
if pinfo.get("required", True):
required_params.append(pname)
function_schema = {
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": {
"type": "object",
"properties": properties,
"required": required_params,
},
},
}
# 沙箱测试(如果提供了 test_input
test_result = None
if test_input and language == "python":
try:
test_params = json.loads(test_input) if isinstance(test_input, str) else test_input
# 构建测试调用代码
args_str = ", ".join(
f"{k}={repr(v)}" if not isinstance(v, (int, float, bool)) else f"{k}={v}"
for k, v in test_params.items()
)
test_code = f"{code}\n\n# test\nresult = {name}({args_str})\nprint(json.dumps(result, ensure_ascii=False, default=str))"
proc = await asyncio.create_subprocess_exec(
sys.executable, "-c", test_code,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=15.0)
if proc.returncode == 0:
test_result = {"passed": True, "output": stdout.decode("utf-8", errors="replace").strip()[:2000]}
else:
return json.dumps({
"error": "test_failed",
"message": f"沙箱测试失败,请修正代码后重试",
"stderr": stderr.decode("utf-8", errors="replace")[:1000],
}, ensure_ascii=False)
except asyncio.TimeoutError:
return json.dumps({"error": "test_timeout", "message": "沙箱测试超时15秒请优化代码"}, ensure_ascii=False)
except Exception as e:
return json.dumps({"error": "test_error", "message": f"沙箱测试异常: {e}"}, ensure_ascii=False)
# 写入 tools 表
db = SessionLocal()
try:
existing = db.query(Tool).filter(Tool.name == name).first()
if existing:
db.close()
return json.dumps({
"error": "name_conflict",
"message": f"工具「{name}」已存在id={existing.id}",
"existing_id": existing.id,
}, ensure_ascii=False)
owner = db.query(User).first()
tool_id = str(uuid.uuid4())
tool = Tool(
id=tool_id,
name=name,
description=description,
category="agent_created",
function_schema=function_schema,
implementation_type="code",
implementation_config={
"code": code,
"language": language,
"parameter_schema": param_schema,
},
is_public=False,
user_id=owner.id if owner else None,
)
db.add(tool)
db.commit()
# 加载到 tool_registry
tool_registry.load_tools_from_db(db, tool_names=[name])
result = {
"status": "created",
"tool": {
"id": tool_id,
"name": name,
"description": description,
"language": language,
"params": required_params,
},
"sandbox_test": test_result,
"hint": f"工具已就绪,可直接调用 {name}({', '.join(p + '=...' for p in required_params) if required_params else ''})",
}
return json.dumps(result, ensure_ascii=False)
finally:
db.close()
except Exception as e:
logger.error(f"code_tool_create 工具执行失败: {e}", exc_info=True)
return json.dumps({"error": "creation_failed", "message": f"创建代码工具失败: {e}"}, ensure_ascii=False)
CODE_TOOL_CREATE_SCHEMA = {
"type": "function",
"function": {
"name": "code_tool_create",
"description": (
"将经过验证的代码持久化为可复用的内置工具。"
"先用 code_execute 验证代码,确认正确后调用此工具将其注册为永久工具。"
"创建后所有 Agent 均可直接调用该工具。"
),
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "工具名称(英文,如 parse_slow_query、csv_cleaner"},
"description": {"type": "string", "description": "工具功能描述中文如「解析MySQL慢查询日志提取最慢的N条SQL」"},
"code": {"type": "string", "description": "完整的 Python 函数代码(包含 def 定义和依赖 import"},
"language": {"type": "string", "description": "代码语言", "default": "python"},
"parameter_schema": {"type": "string", "description": "参数 JSON schema{\"log_path\": {\"type\": \"string\", \"description\": \"日志路径\"}}", "default": "{}"},
"test_input": {"type": "string", "description": "测试输入 JSON用于沙箱验证可选但推荐提供"},
},
"required": ["name", "description", "code"],
},
},
}
# ── extension_log ────────────────────────────────────────────
async def extension_log_tool(
action: str = "list",
name: str = "",
extension_type: str = "",
reason: str = "",
detail_json: str = "{}",
success_rating: str = "",
note: str = "",
limit: int = 10,
) -> str:
"""
记录和查询 Agent 自主扩展历史。
Agent 创建子 Agent / 注册工具 / 创建代码工具后,用此工具记录。
后续可查询历史扩展记录进行自我反思。
Args:
action: "list" 查询历史 / "log" 记录新扩展 / "evaluate" 评价已有扩展
name: 扩展名称log/evaluate 时必填)
extension_type: 扩展类型agent_created/tool_registered/code_tool_created
reason: 创建原因
detail_json: 扩展详情 JSON
success_rating: 效果评价success/partial/failed
note: 备注
limit: list 时返回的最大条数
"""
import uuid
from datetime import datetime
try:
from app.core.database import SessionLocal
from app.models.agent import AgentExtension
from app.models.user import User
db = SessionLocal()
try:
if action == "list":
query = db.query(AgentExtension).order_by(AgentExtension.created_at.desc())
if extension_type:
query = query.filter(AgentExtension.extension_type == extension_type)
if name:
query = query.filter(AgentExtension.name.contains(name))
extensions = query.limit(min(limit, 50)).all()
items = []
for ext in extensions:
items.append({
"id": ext.id,
"type": ext.extension_type,
"name": ext.name,
"reason": ext.reason,
"success_rating": ext.success_rating,
"note": ext.note,
"created_at": ext.created_at.isoformat() if ext.created_at else None,
})
return json.dumps({
"extensions": items,
"count": len(items),
"hint": "使用 extension_log(action='evaluate', name='...', success_rating='success') 评价扩展效果",
}, ensure_ascii=False)
elif action == "log":
if not name or not extension_type:
return json.dumps({"error": "missing_fields", "message": "log 操作需要 name 和 extension_type 参数"}, ensure_ascii=False)
owner = db.query(User).first()
ext_id = str(uuid.uuid4())
try:
detail = json.loads(detail_json) if detail_json else {}
except json.JSONDecodeError:
detail = {"raw": detail_json}
ext_record = AgentExtension(
id=ext_id,
extension_type=extension_type,
name=name,
reason=reason,
detail=detail,
user_id=owner.id if owner else None,
)
db.add(ext_record)
db.commit()
return json.dumps({
"status": "logged",
"extension": {"id": ext_id, "type": extension_type, "name": name},
"hint": "扩展已记录。后续可用 extension_log(action='evaluate', ...) 评价效果",
}, ensure_ascii=False)
elif action == "evaluate":
if not name:
return json.dumps({"error": "missing_fields", "message": "evaluate 操作需要 name 参数"}, ensure_ascii=False)
# 查找最近的匹配记录
ext_record = (
db.query(AgentExtension)
.filter(AgentExtension.name == name)
.order_by(AgentExtension.created_at.desc())
.first()
)
if not ext_record:
return json.dumps({
"error": "not_found",
"message": f"未找到名为「{name}」的扩展记录,请先用 action='log' 记录",
}, ensure_ascii=False)
if success_rating:
ext_record.success_rating = success_rating
if note:
ext_record.note = note
db.commit()
return json.dumps({
"status": "evaluated",
"extension": {"id": ext_record.id, "name": name, "success_rating": success_rating, "note": note},
}, ensure_ascii=False)
else:
return json.dumps({"error": "unknown_action", "message": f"未知操作: {action},支持 list/log/evaluate"}, ensure_ascii=False)
finally:
db.close()
except Exception as e:
logger.error(f"extension_log 工具执行失败: {e}", exc_info=True)
return json.dumps({"error": "extension_log_failed", "message": str(e)}, ensure_ascii=False)
EXTENSION_LOG_SCHEMA = {
"type": "function",
"function": {
"name": "extension_log",
"description": (
"记录和查询自主扩展历史。创建子 Agent / 注册工具 / 创建代码工具后,"
"用此工具记录扩展和原因,以便后续反思和优化。"
"支持 list查询、log记录、evaluate评价三种操作。"
),
"parameters": {
"type": "object",
"properties": {
"action": {"type": "string", "enum": ["list", "log", "evaluate"], "description": "操作list 查询 / log 记录 / evaluate 评价", "default": "list"},
"name": {"type": "string", "description": "扩展名称Agent名或工具名log/evaluate 时必填)"},
"extension_type": {"type": "string", "enum": ["agent_created", "tool_registered", "code_tool_created"], "description": "扩展类型log 时必填)"},
"reason": {"type": "string", "description": "创建原因简短说明为什么要扩展log 时推荐填写)"},
"detail_json": {"type": "string", "description": "扩展详情 JSONlog 时可选)"},
"success_rating": {"type": "string", "enum": ["success", "partial", "failed"], "description": "效果评价evaluate 时使用)"},
"note": {"type": "string", "description": "反馈备注evaluate 时使用如「子Agent成功定位3条慢SQL」"},
"limit": {"type": "integer", "description": "list 时返回的最大条数", "default": 10},
},
"required": ["action"],
},
},
}
# ── self_review ──────────────────────────────────────────────
async def self_review_tool(
content: str,
criteria: str,
task_context: str = "",
) -> str:
"""
输出质量自检工具:用 LLM 评判一段内容是否满足质量标准。
Agent 或工作流节点在执行完毕后,可调用此工具对输出进行质量评估。
评分 >= 0.6 为通过,不通过时返回具体问题和改进建议,供修正参考。
Args:
content: 待评估的内容Agent 回答、报告、代码等)
criteria: 评判标准如「回答必须包含索引优化建议和具体SQL示例」
task_context: 原始任务上下文(可选,帮助评判器理解背景)
"""
try:
# 构建评判 prompt
judge_prompt = (
"你是严格的内容质量评审专家。请根据以下标准对内容进行评分。\n\n"
f"【评判标准】\n{criteria}\n\n"
f"【待评审内容】\n{content[:8000]}\n"
)
if task_context:
judge_prompt += f"\n【任务背景】\n{task_context[:2000]}\n"
judge_prompt += (
"\n请以 JSON 格式返回评审结果(严格只返回 JSON不要任何其他文字\n"
'{"score": 0.75, "passed": true, "issues": ["问题1", "问题2"], '
'"suggestions": ["建议1", "建议2"], "summary": "一句话总结"}\n\n'
"评分规则:\n"
"- 1.0 完美满足所有标准\n"
"- 0.8 良好,有小的改进空间\n"
"- 0.6 基本满足,但存在明显不足\n"
"- 0.4 大部分标准未满足\n"
"- 0.2 完全不满足\n"
"- score >= 0.6 时 passed=true否则 passed=false\n"
)
# 调用轻量 LLM 评审
from app.services.llm_service import LLMService
llm = LLMService()
messages = [{"role": "user", "content": judge_prompt}]
resp = await llm.chat(
provider="deepseek",
model="deepseek-v4-flash",
messages=messages,
temperature=0.1,
max_tokens=800,
)
judge_text = resp.get("content", "") if isinstance(resp, dict) else str(resp)
# 解析 JSON 响应
try:
# 清理可能的 markdown 包裹
judge_text_clean = judge_text.strip()
if judge_text_clean.startswith("```"):
lines = judge_text_clean.split("\n")
judge_text_clean = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
result = json.loads(judge_text_clean)
except json.JSONDecodeError:
# 尝试从文本中提取 JSON
import re as _re2
m = _re2.search(r'\{[^{}]*"score"\s*:\s*[\d.]+[^{}]*\}', judge_text, _re2.DOTALL)
if m:
try:
result = json.loads(m.group())
except json.JSONDecodeError:
result = {"score": 0.5, "passed": False, "issues": ["无法解析评审结果"], "suggestions": [], "summary": judge_text[:200]}
else:
result = {"score": 0.5, "passed": False, "issues": ["无法解析评审结果"], "suggestions": [], "summary": judge_text[:200]}
score = float(result.get("score", 0.5))
passed = result.get("passed", score >= 0.6)
if not passed and score >= 0.6:
passed = True
if passed and score < 0.6:
passed = False
return json.dumps({
"score": score,
"passed": passed,
"threshold": 0.6,
"issues": result.get("issues", []),
"suggestions": result.get("suggestions", []),
"summary": result.get("summary", ""),
}, ensure_ascii=False)
except Exception as e:
logger.error(f"self_review 工具执行失败: {e}", exc_info=True)
return json.dumps({"error": "review_failed", "message": str(e), "score": 0, "passed": False}, ensure_ascii=False)
SELF_REVIEW_SCHEMA = {
"type": "function",
"function": {
"name": "self_review",
"description": (
"输出质量自检工具:用 LLM 评判一段内容的输出质量。"
"在完成任务后调用此工具检查输出是否满足标准,"
"不通过时会给出具体问题和改进建议。"
),
"parameters": {
"type": "object",
"properties": {
"content": {"type": "string", "description": "待评估的内容Agent 回答、报告、代码等)"},
"criteria": {"type": "string", "description": "评判标准如「回答必须包含索引优化建议和具体SQL示例」"},
"task_context": {"type": "string", "description": "原始任务上下文(可选,帮助评判器理解背景)"},
},
"required": ["content", "criteria"],
},
},
}

View File

@@ -4066,57 +4066,62 @@ class WorkflowEngine:
}
elif node_type == 'error_handler':
# 错误处理节点:捕获错误、错误重试、错误通知
# 注意:这个节点需要特殊处理,因为它应该包装其他节点的执行
# 这里我们实现一个简化版本,主要用于错误重试和通知
# 错误处理节点:捕获上游节点失败,执行重试/通知/停止策略
node_data = node.get('data', {})
retry_count = node_data.get('retry_count', 3)
retry_delay = node_data.get('retry_delay', 1000) # 毫秒
on_error = node_data.get('on_error', 'notify') # notify, retry, stop
error_handler_workflow = node_data.get('error_handler_workflow', '')
# 这个节点通常用于包装其他节点,但在这里我们只处理输入数据中的错误
# 找到上游节点(从 edges 反查 source
predecessor_id = None
for e in self.active_edges if hasattr(self, 'active_edges') else []:
if e.get("target") == node_id:
predecessor_id = e.get("source")
break
if not predecessor_id:
for e in self.edges:
if e.get("target") == node_id:
predecessor_id = e.get("source")
break
try:
# 检查输入数据中是否有错误
if isinstance(input_data, dict) and input_data.get('status') == 'failed':
error = input_data.get('error', '未知错误')
if on_error == 'retry' and retry_count > 0:
# 重试逻辑(这里简化处理,实际应该重新执行前一个节点)
logger.warning(f"错误处理节点检测到错误,将重试: {error}")
# 注意:实际重试需要重新执行前一个节点,这里只记录
if on_error == 'retry':
logger.warning(f"error_handler 检测到错误,请求重试前驱节点 {predecessor_id}: {error}")
exec_result = {
'output': input_data,
'status': 'retry',
'status': 'retry_predecessor',
'predecessor_id': predecessor_id,
'retry_count': retry_count,
'error': error
'retry_delay_ms': retry_delay,
'error': error,
}
elif on_error == 'notify':
# 通知错误(记录日志)
logger.error(f"错误处理节点捕获错误: {error}")
logger.error(f"error_handler 捕获错误并通知: {error}")
exec_result = {
'output': input_data,
'status': 'error_handled',
'error': error,
'notified': True
'notified': True,
}
else:
# 停止执行
else: # stop
exec_result = {
'output': input_data,
'status': 'failed',
'error': error,
'stopped': True
'stopped': True,
}
else:
# 没有错误,正常通过
# 上游正常,透传
exec_result = {'output': input_data, 'status': 'success'}
if self.logger:
duration = int((time.time() - start_time) * 1000)
self.logger.log_node_complete(node_id, node_type, exec_result.get('output'), duration)
return exec_result
except Exception as e:
if self.logger:
duration = int((time.time() - start_time) * 1000)
@@ -4124,7 +4129,7 @@ class WorkflowEngine:
return {
'output': input_data,
'status': 'failed',
'error': f'错误处理失败: {str(e)}'
'error': f'错误处理失败: {str(e)}',
}
elif node_type == 'csv':
@@ -5427,6 +5432,89 @@ class WorkflowEngine:
self.logger.log_node_complete(node_id, node_type, final_output, duration)
return result
elif node_type == 'evaluator':
# 输出质量评估节点:用 LLM 评判上游节点的输出质量
nd = node.get('data', {}) or {}
criteria = nd.get('criteria', '回答必须准确、完整、切题')
pass_threshold = float(nd.get('pass_threshold', 0.6))
# 从输入数据提取待评估内容
if isinstance(input_data, dict):
content = input_data.get('output', input_data.get('content', str(input_data)))
else:
content = str(input_data) if input_data else ""
# 调用 LLM 评判
try:
from app.services.llm_service import llm_service
judge_prompt = (
"你是严格的内容质量评审专家。请根据以下标准对内容进行评分。\n\n"
f"【评判标准】\n{criteria}\n\n"
f"【待评审内容】\n{str(content)[:8000]}\n\n"
"请以 JSON 格式返回评审结果(严格只返回 JSON不要任何其他文字\n"
'{"score": 0.75, "passed": true, "issues": ["问题1"], '
'"suggestions": ["建议1"], "summary": "一句话总结"}\n\n'
"评分规则1.0完美 0.8良好 0.6基本满足 0.4大部分未满足 0.2完全不满足\n"
"score >= 0.6 时 passed=true否则 passed=false\n"
)
messages = [{"role": "user", "content": judge_prompt}]
resp = llm_service.call_openai_with_tools(
provider="deepseek",
model="deepseek-v4-flash",
messages=messages,
temperature=0.1,
max_tokens=600,
)
judge_text = ""
if isinstance(resp, dict):
choices = resp.get("choices", [])
if choices:
judge_text = choices[0].get("message", {}).get("content", "")
elif hasattr(resp, 'choices'):
judge_text = resp.choices[0].message.content or ""
# 解析 JSON
import re as _eval_re
judge_clean = judge_text.strip()
if judge_clean.startswith("```"):
lines = judge_clean.split("\n")
judge_clean = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
try:
review = json.loads(judge_clean)
except json.JSONDecodeError:
m = _eval_re.search(r'\{[^{}]*"score"\s*:\s*[\d.]+[^{}]*\}', judge_text, _eval_re.DOTALL)
if m:
review = json.loads(m.group())
else:
review = {"score": 0.5, "passed": False, "issues": ["评审解析失败"], "suggestions": []}
score = float(review.get("score", 0.5))
passed = score >= pass_threshold
exec_result = {
'output': {
'score': score,
'passed': passed,
'threshold': pass_threshold,
'issues': review.get('issues', []),
'suggestions': review.get('suggestions', []),
'summary': review.get('summary', ''),
'content': content if isinstance(content, str) else str(content),
},
'status': 'success' if passed else 'quality_failed',
}
except Exception as e:
logger.error(f"evaluator 节点评审失败: {e}")
exec_result = {
'output': {'score': 0, 'passed': False, 'error': str(e), 'content': str(content)},
'status': 'failed',
}
if self.logger:
duration = int((time.time() - start_time) * 1000)
self.logger.log_node_complete(node_id, node_type, exec_result.get('output'), duration)
return exec_result
else:
# 未知节点类型
logger.warning(f"[rjb] 未知节点类型: node_id={node_id}, node_type={node_type}, node keys={list(node.keys())}")
@@ -5751,9 +5839,84 @@ class WorkflowEngine:
# 继续查找循环体内的节点
self._mark_loop_body_executed(target_id, executed_nodes, active_edges)
else:
# 执行失败,停止工作流
# 执行失败或质量不达标 — 支持重试
failed_status = result.get('status', 'failed')
error_msg = result.get('error', '未知错误')
node_type = node.get('type', 'unknown')
# 处理 error_handler 返回的 retry_predecessor
if failed_status == 'retry_predecessor':
pred_id = result.get('predecessor_id')
eh_retry_count = result.get('retry_count', 1)
eh_retry_delay_ms = result.get('retry_delay_ms', 1000)
# 检查是否已有重试计数(多次经过 error_handler
prev_counter = self.node_outputs.get(f"_eh_retry_{pred_id}", {})
remaining = prev_counter.get("remaining", eh_retry_count)
if pred_id and pred_id in self.nodes and remaining > 0:
remaining -= 1
logger.warning(
f"error_handler 请求重试前驱节点 {pred_id},剩余 {remaining}"
)
self.executed_nodes.discard(pred_id)
self.node_outputs.pop(pred_id, None)
self.node_outputs[f"_eh_retry_{pred_id}"] = {
"remaining": remaining,
"delay_ms": eh_retry_delay_ms,
"error_handler_id": next_node_id,
}
executed_nodes.add(next_node_id)
execution_sequence.append(next_node_id)
results[next_node_id] = result
await asyncio.sleep(eh_retry_delay_ms / 1000.0)
continue
elif remaining <= 0:
logger.error(f"error_handler 重试次数耗尽,前驱节点 {pred_id} 停止")
raise WorkflowExecutionError(
detail=f"error_handler 重试次数耗尽: {error_msg}",
node_id=pred_id,
)
# 检查节点级 retry_config
node_data = node.get('data', {}) or {}
retry_cfg = node_data.get('retry_config', {})
max_retries = retry_cfg.get('max_retries', 0) if isinstance(retry_cfg, dict) else 0
retry_delay_ms = retry_cfg.get('retry_delay_ms', 1000) if isinstance(retry_cfg, dict) else 1000
on_exhausted = retry_cfg.get('on_exhausted', 'stop') if isinstance(retry_cfg, dict) else 'stop'
retry_key = f"_retry_{next_node_id}"
retries_done = self.node_outputs.get(retry_key, 0)
if max_retries > 0 and retries_done < max_retries:
self.node_outputs[retry_key] = retries_done + 1
logger.warning(
f"节点 {next_node_id} ({node_type}) 执行失败,重试 {retries_done + 1}/{max_retries}: {error_msg}"
)
await asyncio.sleep(retry_delay_ms / 1000.0)
continue # 不标记已执行,下次循环重新执行
# 重试耗尽或未配置重试
if retries_done >= max_retries and max_retries > 0:
if on_exhausted == 'skip':
logger.warning(f"节点 {next_node_id} 重试耗尽,跳过: {error_msg}")
self.node_outputs[next_node_id] = {
'status': 'skipped', 'error': error_msg
}
executed_nodes.add(next_node_id)
execution_sequence.append(next_node_id)
results[next_node_id] = result
continue
elif on_exhausted == 'notify':
logger.error(f"节点 {next_node_id} 重试耗尽,已通知: {error_msg}")
self.node_outputs[next_node_id] = {
'status': 'error_notified', 'error': error_msg
}
executed_nodes.add(next_node_id)
execution_sequence.append(next_node_id)
results[next_node_id] = result
continue
# 默认:停止工作流
logger.error(f"工作流执行失败 - 节点: {next_node_id} ({node_type}), 错误: {error_msg}")
raise WorkflowExecutionError(
detail=error_msg,

View File

@@ -28,6 +28,10 @@ export const BUILTIN_SKILL_OPTIONS: { name: string; label: string }[] = [
{ name: 'deploy_push', label: '部署推送' },
{ name: 'agent_create', label: '创建 Agent' },
{ name: 'tool_register', label: '注册工具' },
{ name: 'capability_check', label: '能力自检' },
{ name: 'code_tool_create', label: '创建代码工具' },
{ name: 'extension_log', label: '扩展日志' },
{ name: 'self_review', label: '输出自检' },
]
export const BUILTIN_SKILL_LABELS: Record<string, string> = Object.fromEntries(

View File

@@ -208,32 +208,47 @@ agents = [
{
"name": "全能助手",
"description": "综合 AI 助手,可使用所有工具处理各种任务",
"system_prompt": """你是全能助手 OmniBot一个功能全面的 AI 助手。
"system_prompt": """你是全能助手 OmniBot一个功能全面的 AI 助手,具备自主能力扩展能力
## 核心能力
你可以使用平台提供的所有工具,根据用户需求灵活选择最合适的工具完成各类任务
你可以使用平台提供的所有 34 个工具涵盖文件操作、网络请求、数据处理、代码执行、系统信息、数据库、Git、Docker、部署、浏览器操控、Agent 协作等类别
## 可用工具
你拥有丰富的工具库,涵盖以下类别:
- **文件操作**: file_read, file_write
- **网络请求**: http_request, check_website, ip_info, shorten_url, weather_query
- **数据处理**: csv_processor, json_tool, json_process, text_analyze, text_summarize, extract_info, html_to_markdown, base64_codec
- **代码执行**: execute_code, math_calculate
- **系统信息**: system_info, datetime, timestamp, uuid_generator
- **搜索**: grep_search, list_files
- **数据库**: database_query
- **Git**: git_log
- **ADB**: adb_log
## 自主能力扩展
当遇到超出当前能力的复杂或专业任务时,你应当主动扩展自己的能力边界,而非放弃或给出粗糙答案。
### 扩展流程
1. **能力评估** — 使用 capability_check 工具分析任务需求与现有能力的匹配度,识别差距
2. **方案搜索** — 如存在差距,使用 web_search 搜索相关的外部 API、开源工具或专业知识
3. **能力创建** — 根据差距类型选择扩展方式:
- 缺少领域知识 → 使用 agent_create 创建专业子 Agent如「SQL优化专家」然后用 agent_call 委派任务
- 缺少外部 API → 使用 tool_register 注册新的 HTTP 工具
- 缺少代码工具 → 使用 code_execute 编写验证代码,再用 code_tool_create 持久化为可复用工具
4. **验证集成** — 创建后立即测试,确认新能力可用
5. **委派执行** — 将专业任务委派给新创建的子 Agent 或工具
6. **记录反馈** — 使用 extension_log 记录扩展结果和效果评价
### 扩展决策原则
- 任务涉及专业领域知识(医学、法律、特定技术栈)→ 优先创建专业子 Agent
- 任务需要频繁调用某个外部 API → 优先注册 HTTP 工具
- 任务需要自定义数据处理逻辑 → 优先创建代码工具
- 简单任务、已有工具可直接完成 → 不需要扩展
## 安全原则
- 创建的工具和 Agent 默认为私有(仅自己可见)
- 代码工具创建前必须先通过沙箱测试
- 外部 API 注册时会自动检测可达性
- 不在系统提示词中泄露敏感信息
## 工作流程
1. 理解用户需求的本质
2. 选择最合适的工具组合
3. 执行工具并分析结果
4. 给出清晰、完整的答案
1. 理解用户需求的本质和复杂度
2. 对复杂/陌生任务先用 capability_check 评估
3. 选择合适的工具组合(必要时自主扩展)
4. 执行工具并分析结果
5. 给出清晰、完整的答案
## 回答风格
- 先理解再行动,不确定时先确认
- 复杂任务分解步骤
- 复杂任务分解步骤,使用 task_plan 跟踪进度
- 多种方案时对比说明
- 代码和配置示例完整可用""",
"tools": [],

View File

@@ -2,11 +2,11 @@
## 概述
所有 Agent 默认拥有 **31 个内置工具**`tools: []` = 全部可用。工具涵盖文件操作、网络访问、代码执行、项目管理、部署等能力Agent 可在 ReAct 循环中自主选择和调用。
所有 Agent 默认拥有 **34 个内置工具**`tools: []` = 全部可用)。工具涵盖文件操作、网络访问、代码执行、项目管理、部署、自主能力扩展等能力Agent 可在 ReAct 循环中自主选择和调用。
---
## 工具清单31个)
## 工具清单34个)
### 文件与数据
@@ -32,6 +32,7 @@
| 8 | `code_execute` | `code`, `language`, `timeout` | Python/JS 沙箱执行 |
| 9 | `text_analyze` | `text`, `operation` | 文本分析 |
| 10 | `regex_test` | `pattern`, `text` | 正则表达式测试 |
| 11 | `code_tool_create` | `name`, `description`, `code`, `language`, `parameter_schema`, `test_input` | 持久化验证过的代码为可复用工具 |
### 项目管理
@@ -70,19 +71,21 @@
| # | 工具 | 参数 | 说明 |
|---|------|------|------|
| 24 | `send_email` | `to`, `subject`, `body` | 发送邮件 |
| 25 | `agent_call` | `agent_name`, `query`, `max_iterations` | 调用其他 Agent 委派任务 |
| 26 | `agent_create` | `name`, `system_prompt`, `description` | 动态创建专业子 Agent |
| 27 | `tool_register` | `name`, `description`, `method`, `url` | 动态注册 HTTP 工具 |
| 25 | `send_email` | `to`, `subject`, `body` | 发送邮件 |
| 26 | `agent_call` | `agent_name`, `query`, `max_iterations` | 调用其他 Agent 委派任务 |
| 27 | `agent_create` | `name`, `system_prompt`, `description` | 动态创建专业子 Agent |
| 28 | `tool_register` | `name`, `description`, `method`, `url` | 动态注册 HTTP 工具 |
| 29 | `capability_check` | `task_description`, `required_domains` | 能力自检,分析任务与现有能力的匹配度 |
| 30 | `extension_log` | `action`, `name`, `extension_type`, `reason` | 记录和查询自主扩展历史 |
### DevOps
| # | 工具 | 参数 | 说明 |
|---|------|------|------|
| 26 | `database_query` | `query`, `params` | 数据库查询 |
| 27 | `docker_manage` | `operation`, `resource`, `options` | Docker 容器管理(只读) |
| 28 | `browser_use` | `url`, `action`, `selector`, `script` | 无头浏览器操控(截图/提取/填表) |
| 31 | `adb_log` | `device`, `lines`, `tag` | Android ADB 日志 |
| 31 | `database_query` | `query`, `params` | 数据库查询 |
| 32 | `docker_manage` | `operation`, `resource`, `options` | Docker 容器管理(只读) |
| 33 | `browser_use` | `url`, `action`, `selector`, `script` | 无头浏览器操控(截图/提取/填表) |
| 34 | `adb_log` | `device`, `lines`, `tag` | Android ADB 日志 |
---
@@ -350,25 +353,105 @@ tool_register(
---
### 14. capability_check — 能力自检
```
capability_check(task_description="分析MySQL慢查询日志并优化", required_domains="数据库,性能优化,SQL")
→ {
"available_tools_count": 34,
"available_agents": ["数据分析师", "日志分析师", "代码助手"],
"gap_analysis": {
"missing_domain_knowledge": ["MySQL性能优化"],
"missing_tools": ["database_query"],
"severity": "medium"
},
"recommendations": [
"使用 agent_create 创建「数据库专家」Agent",
"使用 web_search 搜索相关的外部 API 或开源工具"
]
}
```
**场景:**
- Agent 接到陌生/复杂任务时,先自检能力是否足够
- 基于关键词和领域规则做差距分析(不消耗 LLM token
- 返回结构化建议创建子Agent / 注册工具 / 搜索方案
---
### 15. code_tool_create — 代码工具持久化
```
code_tool_create(
name="parse_slow_query",
description="解析MySQL慢查询日志提取最慢的N条SQL",
code="def parse_slow_query(log_path, top_n=10):\n import re\n ...",
language="python",
parameter_schema='{"log_path": {"type": "string", "description": "日志路径"}, "top_n": {"type": "integer", "description": "返回前N条"}}',
test_input='{"log_path": "/tmp/slow.log", "top_n": 5}'
)
→ {"status": "created", "tool": {"id": "uuid", "name": "parse_slow_query"}, "sandbox_test": {"passed": true}}
```
**场景:**
- 先用 `code_execute` 编写并验证代码逻辑
- 确认无误后调用此工具持久化 → 写入 tools 表 → 加载到 tool_registry
- 后续所有 Agent 均可直接调用该工具
**安全限制:** 创建前自动在沙箱中测试15s 超时),测试失败则拒绝注册
---
### 16. extension_log — 扩展记录
```
# 记录扩展
extension_log(action="log", extension_type="agent_created", name="SQL优化专家", reason="缺少MySQL优化知识")
→ {"status": "logged", "extension": {"id": "uuid", "type": "agent_created", "name": "SQL优化专家"}}
# 查询历史
extension_log(action="list", limit=5)
→ {"extensions": [{"type": "agent_created", "name": "SQL优化专家", ...}, ...], "count": 5}
# 评价效果
extension_log(action="evaluate", name="SQL优化专家", success_rating="success", note="成功定位3条慢SQL")
→ {"status": "evaluated", "extension": {"name": "SQL优化专家", "success_rating": "success"}}
```
**场景:**
- Agent 每次扩展能力后记录原因和结果
- 后续任务前查询历史 → 评估哪些扩展有效 → 优化决策
- 形成自主进化反馈闭环
---
## Agent 自主能力扩展流程
```
全能助手收到: "监控线上MySQL慢查询并优化"
├─ 检查自身 → 缺少SQL专业深度
├─ web_search("MySQL慢查询分析方案")
├─ capability_check(task_description="分析MySQL慢查询日志并优化")
│ └─ 差距分析:缺少 MySQL优化知识、慢查询解析工具
├─ web_search("MySQL慢查询分析方案 percona-toolkit")
│ └─ 发现 pt-query-digest 工具
├─ agent_create(name="SQL优化专家", system_prompt="你是MySQL性能优化专家...")
│ └─ 新 Agent 写入 DB拥有全部 31 工具
│ └─ 新 Agent 写入 DB拥有全部 34 工具
├─ agent_call("SQL优化专家", "分析这份慢查询日志并给出优化建议")
│ └─ 子 Agent 独立 ReAct 循环执行
├─ extension_log(action="log", extension_type="agent_created", name="SQL优化专家", reason="缺少MySQL优化知识")
├─ 发现 percona-toolkit 有 REST API
├─ tool_register(name="pt_query_digest", url="http://internal-api/analyze?log={path}")
│ └─ 工具立即可用
├─ extension_log(action="log", extension_type="tool_registered", name="pt_query_digest")
└─ 整合结果: "慢查询优化方案如下..."
└─ extension_log(action="evaluate", success_rating="success")
```
---

View File

@@ -2,9 +2,9 @@
## 概述
本系统支持多种方式创建 Agent。**所有创建方式均默认赋予 Agent 全部 31 个内置工具能力**,除非明确限制。
本系统支持多种方式创建 Agent。**所有创建方式均默认赋予 Agent 全部 34 个内置工具能力**,除非明确限制。
## 内置工具清单31个)
## 内置工具清单34个)
| 类别 | 工具 | 用途 |
|------|------|------|

202
缺失能力.md Normal file
View File

@@ -0,0 +1,202 @@
# 缺失能力分析
## 概述
当前项目已有 34 个内置工具、自主进化系统agent_create / tool_register / code_tool_create / capability_check / extension_log、工作流引擎DAG + HITL 审批节点、Agent 记忆系统RAG + 向量 + 持久化、异步执行Celery + Redis等能力。但在生产级复杂任务执行方面仍存在以下 10 项关键缺失。
---
## 第一梯队复杂任务的硬伤3 项)
### 1. 完全没有并行执行
**现象:**
- 工作流 DAG 一次只执行一个节点,即使多个分支之间没有依赖关系
- Orchestrator 的 debate 模式 3 个 Agent 逐个串行执行,而非并发
- `orchestrator.py` 中 debate 用的是 `for agent in agents` 循环,而非 `asyncio.gather`
**影响:**
- 复杂任务耗时 = 所有步骤耗时之和,而非最长路径耗时
- 多 Agent 协作(并行分析、投票)形同虚设
- 吞吐量瓶颈
**涉及文件:**
- `backend/app/services/workflow_engine.py` — DAG 执行循环(`execute()` 方法)
- `backend/app/agent_runtime/orchestrator.py``_debate()` 方法
---
### 2. AgentOrchestrator 不进入工作流
**现象:**
- Orchestrator 的 4 种模式router / sequential / debate / pipeline仅通过 API 端点 `/orchestrate` 暴露
- 工作流引擎的节点类型只有 `start / llm / agent / approval / condition / end` 等,没有 `orchestrator`
- 编排能力与工作流系统完全割裂
**影响:**
- 无法在工作流中组合多 Agent 协作模式
- 复杂场景需要外部脚本调用 API无法在工作流中可视化编排
**涉及文件:**
- `backend/app/api/agent_chat.py` — Orchestrator API 入口
- `backend/app/services/workflow_engine.py` — 缺少 orchestrator 节点类型
- `backend/app/agent_runtime/workflow_integration.py` — 只有 `run_agent_node()`,无 `run_orchestrator_node()`
---
### 3. 输出质量验证完全缺失
**现象:**
- 没有评判/评估节点检查 Agent 输出质量
- 搜索 `evaluate_output``verify_result``quality_check``validate_node` — 零结果
- Agent 执行完直接返回结果,不对质量做任何检查
- `AgentLearningPattern` 只在后台统计成功率,不做实时检查
**影响:**
- Agent 不知道自己做得好不好
- 错误结果直接交付用户
- 自我修正闭环缺失最关键的一环
**涉及文件:**
- 无现有实现,需从零构建
---
## 第二梯队生产可靠性3 项)
### 4. 节点级自动重试是空壳
**现象:**
- `error_handler` 节点类型存在,但只记录意图不实际重试(代码注释:"实际重试需要重新执行前一个节点,这里只记录"
- 一个节点失败 → `WorkflowExecutionError` → 整个工作流停止
- Celery 层面有任务级重试(指数退避),但节点级没有
**影响:**
- LLM 调用偶发超时 = 整个工作流失败
- 无法配置"这个节点失败后重试 3 次"
**涉及文件:**
- `backend/app/services/workflow_engine.py` — error_handler 节点(约 4068-4128 行)
- `backend/app/tasks/workflow_tasks.py` — Celery 重试逻辑
---
### 5. 工具级人工审批缺失
**现象:**
- 人工审批HITL只存在于工作流节点层面`approval` 节点类型)
- AgentRuntime 对所有工具自动执行,不暂停确认
- `deploy_push``send_email``database_query`(写操作)、`schedule_create` 等风险工具无二次确认
**影响:**
- Agent 可能执行危险操作而用户不知情
- 缺少安全围栏
**涉及文件:**
- `backend/app/agent_runtime/core.py` — ReAct 循环中工具自动执行
- `backend/app/agent_runtime/schemas.py``AgentToolConfig` 缺少审批配置
- `backend/app/services/workflow_engine.py` — approval 节点(约 1374-1422 行,可复用逻辑)
---
### 6. 没有降级/回退链
**现象:**
- 模型不可用 → Agent 直接报错
- Agent 执行失败 → 无备选方案
- 搜索 `fallback``degradation``backup` — 零结果
**影响:**
- 单点故障无容错
- 无法配置"主模型挂了自动切备用模型"
**涉及文件:**
- `backend/app/agent_runtime/schemas.py``AgentLLMConfig` 无 fallback 字段
- `backend/app/agent_runtime/core.py` — ReAct 循环无模型切换逻辑
---
## 第三梯队体验与效率4 项)
### 7. 进度上报太粗糙
**现象:**
- WebSocket 只推送 `status: "running"``status: "completed"`
- 进度值固定0 → 50 → 100没有实际进度百分比
- WebSocket 每 2 秒轮询 DB而非主动推送
**影响:**
- 用户看不到任务执行到哪一步
- 复杂任务7 步和简单任务2 步)进度条一样
**涉及文件:**
- `backend/app/websocket/manager.py` — WebSocket 管理器(轮询 DB
- `backend/app/services/workflow_engine.py` — 无进度回调钩子
- `backend/app/tasks/workflow_tasks.py` — Celery 进度更新只调一次
---
### 8. Agent 间知识不共享
**现象:**
- 每个 Agent 的 RAG 记忆按 `(scope_kind, scope_id)` 隔离
- 学习模式也按 Scope 隔离
- "SQL优化专家"学到的知识,"数据分析师"用不了
**影响:**
- 知识孤岛,重复学习
- 自主进化创建的 Agent 从零开始,无法继承父 Agent 经验
**涉及文件:**
- `backend/app/agent_runtime/memory.py` — 记忆隔离
- `backend/app/services/agent_learning_service.py` — 学习模式隔离
---
### 9. 没有结果缓存
**现象:**
- 相同查询发给同一 Agent每次都完整执行 LLM + 工具
- 工具结果、LLM 响应均不缓存
- 搜索 `cache``redis_cache``ttl` — 仅在 Celery 配置中有 `result_backend`
**影响:**
- 重复工作浪费 token 和时间
- 同一用户反复问相似问题,每次都重新跑
**涉及文件:**
- 无现有实现,需从零构建
---
### 10. Agent 独立异步执行是空壳
**现象:**
- `execute_agent_task`Celery 任务)返回 `{"status": "pending"}` 占位符
- 代码注释:`# TODO: 实现Agent执行逻辑`
- 非工作流的 Agent 执行没有真正的异步支持(只能同步调用 API
**影响:**
- Agent 聊天无法异步后台执行
- 调度触发的 Agent 执行不会真正运行
**涉及文件:**
- `backend/app/tasks/agent_tasks.py` — execute_agent_task 是空壳
- `backend/app/tasks/scheduler_tasks.py` — 定时调度无法真正执行 Agent
---
## 总结
| # | 缺失能力 | 梯队 | 复杂度 | 影响面 |
|---|---------|------|--------|--------|
| 1 | 并行执行 | 第一梯队 | 高 | 性能、吞吐量 |
| 2 | Orchestrator 入工作流 | 第一梯队 | 中 | 编排能力整合 |
| 3 | 输出质量验证 | 第一梯队 | 中 | 结果可靠性 |
| 4 | 节点级自动重试 | 第二梯队 | 低 | 稳定性 |
| 5 | 工具级人工审批 | 第二梯队 | 中 | 安全性 |
| 6 | 降级/回退链 | 第二梯队 | 低 | 容错性 |
| 7 | 粒度进度上报 | 第三梯队 | 低 | 用户体验 |
| 8 | Agent 知识共享 | 第三梯队 | 高 | 知识利用率 |
| 9 | 结果缓存 | 第三梯队 | 中 | 效率、成本 |
| 10 | Agent 异步执行 | 第三梯队 | 低 | 调度可用性 |

429
解决缺失能力计划.md Normal file
View File

@@ -0,0 +1,429 @@
# 解决缺失能力计划
## 总体策略
按梯队分阶段实施。每个阶段聚焦 2-3 个能力,完成即可独立提升系统质量。
每阶段包含:涉及文件、核心改动、验证方式。
---
## 第一阶段闭环质量1-2 周)
优先解决"输出质量验证",这是连接自主进化系统的最后一环。
### 1.1 输出质量验证 → 新增 `evaluator` 节点 + `self_review` 工具
**目标:** Agent 执行完后自动检查输出质量,不达标则自我修正。
**方案:**
#### A. 新增 `self_review` 工具(第 35 个内置工具)
```
self_review(content="...", criteria="回答必须包含SQL优化建议和执行计划")
→ {
"score": 0.75,
"passed": true,
"issues": ["缺少具体执行计划"],
"suggestions": ["补充EXPLAIN输出分析"]
}
```
**实现:** 用 LLM 作为评判器deepseek-v4-flash轻量调用根据 criteria 打分 0-1。
#### B. 新增 `evaluator` 工作流节点类型
在工作流引擎中添加 evaluator 节点:
- 接收上游节点输出 + 评判标准criteria
- 调用 LLM 评判输出质量
- 输出 `{score, passed, issues, suggestions}`
- 配合 condition 节点:`passed=false` → 走修正分支 → 重试
#### C. AgentRuntime 内建 self-review
在 AgentRuntime ReAct 循环末尾添加可选的 self-review 步骤:
- `AgentConfig` 添加 `self_review: bool = False`
- 启用后Agent 在返回最终答案前自动调用 `self_review`
- 评分 < 0.6 则追加一轮修正迭代
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/services/builtin_tools.py` | 新增 `self_review_tool` + `SELF_REVIEW_SCHEMA` |
| `backend/app/core/tools_bootstrap.py` | 34→35import + register |
| `backend/app/services/workflow_engine.py` | 新增 `evaluator` 节点类型 |
| `backend/app/agent_runtime/core.py` | 末尾添加 self-review 步骤 |
| `backend/app/agent_runtime/schemas.py` | AgentConfig 添加 `self_review` 字段 |
| `frontend/src/utils/agentSkills.ts` | 新增条目 |
**验证:**
1. `self_review("SELECT * FROM t", "需要包含索引建议")` → score < 0.5
2. 工作流agent → evaluator(criteria="...") → condition(passed?) → 正常结束 / 修正重试
3. 全能助手对话测试:问一个复杂问题,开启 self_review确认输出质量提升
---
### 1.2 节点级自动重试
**目标:** error_handler 从空壳变为真正的重试机制。
**方案:**
修改 `error_handler` 节点执行逻辑:
```
当上游节点 status == "failed" 时:
1. 检查 retry_count剩余重试次数
2. 等待 retry_delay 秒
3. 重新执行上游节点(复用 get_node_input + execute_node
4. 成功 → 继续流程
5. 失败且 retry_count > 0 → 循环
6. 失败且 retry_count == 0 → 触发 on_error 动作
```
`on_error` 动作:
- `stop`:抛出错误停止工作流
- `notify`:发送告警继续
- `skip`:跳过该节点继续
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/services/workflow_engine.py` | 重写 error_handler 节点逻辑 |
| `backend/app/core/exceptions.py` | 添加 `NodeRetryExhausted` 异常 |
**验证:**
1. 模拟 LLM 超时 → error_handler 重试 3 次 → 第 3 次成功 → 继续
2. 模拟连续失败 → error_handler 耗尽重试 → 触发 on_error=notify → 告警记录
---
## 第二阶段编排整合2-3 周)
### 2.1 Orchestrator 进入工作流
**目标:** 4 种编排模式变为工作流节点类型。
**方案:**
新增 `orchestrator` 工作流节点类型,数据配置:
```json
{
"mode": "route | sequential | debate | pipeline",
"agents": ["agent_id_1", "agent_id_2", ...],
"routing_prompt": "用于 route 模式的路由指令",
"aggregation_prompt": "用于 debate 模式的汇总指令"
}
```
`workflow_integration.py` 添加 `run_orchestrator_node()`
- 解析 node_data 中的 agent 列表
- 从 DB 加载 Agent 配置
- 创建 AgentOrchestrator 实例
- 执行对应模式,返回结构化结果
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/agent_runtime/workflow_integration.py` | 新增 `run_orchestrator_node()` |
| `backend/app/services/workflow_engine.py` | 新增 orchestrator 节点类型 |
| `frontend/src/utils/agentSkills.ts` | 无需改动(后端节点类型) |
**验证:**
1. 创建工作流start → orchestrator(debate, [AgentA, AgentB, AgentC]) → end
2. 执行:确认 3 个 Agent 均被调用,结果被汇总
3. 创建工作流start → orchestrator(route, [代码助手, 数据分析师]) → end确认按路由分发
---
### 2.2 工具级人工审批
**目标:** 危险工具执行前暂停等待用户确认。
**方案:**
#### A. AgentToolConfig 扩展
```python
class AgentToolConfig(BaseModel):
include_tools: List[str] = []
exclude_tools: List[str] = []
require_approval: List[str] = [] # 新增:需要审批的工具名列表
```
#### B. AgentRuntime 审批拦截
在 ReAct 循环的工具执行环节(`core.py` 约 287 行):
```
if tool_name in config.tools.require_approval:
yield ApprovalRequest(tool_name, tool_args) # 暂停,等待外部确认
decision = await wait_for_approval() # 阻塞等待
if decision != "approved":
continue # 跳过该工具调用
```
#### C. WebSocket 审批通道
- AgentRuntime 暂停时通过 WebSocket 推送审批请求
- 用户通过 WebSocket 回复 approve/deny
- 超时默认策略可配置approve/deny/skip
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/agent_runtime/schemas.py` | AgentToolConfig 添加 require_approval |
| `backend/app/agent_runtime/core.py` | 工具执行前审批拦截 |
| `backend/app/websocket/manager.py` | 添加审批消息类型 |
**验证:**
1. 配置 Agent: `require_approval: ["deploy_push"]`
2. Agent 调用 deploy_push → WebSocket 收到审批请求
3. 用户 approve → Agent 继续执行
4. 用户 deny → Agent 跳过该工具
---
## 第三阶段性能与效率2-3 周)
### 3.1 并行执行
**目标:** 无依赖节点并行执行Orchestrator debate 并行。
**方案:**
#### A. 工作流 DAG 并行
修改 `execute()` 主循环:
```
当前:每轮取 1 个节点执行
改为:每轮取所有"前置节点全部完成"的节点asyncio.gather 并行执行
```
注意事项:
- 并行节点不能有共享状态依赖
- 需要分别捕获每个并行节点的异常
- WebSocket 需支持多节点同时推送进度
#### B. Orchestrator debate 并行
```python
# 改前
for agent in agents:
result = await agent.run(query)
# 改后
results = await asyncio.gather(*[agent.run(query) for agent in agents])
```
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/services/workflow_engine.py` | execute() 并行执行 |
| `backend/app/agent_runtime/orchestrator.py` | debate/sequential 并行 |
| `backend/app/websocket/manager.py` | 支持多节点并发推送 |
**验证:**
1. 3 个独立分支的 DAG → 总耗时 ≈ 最长分支耗时(而非三者之和)
2. debate 模式 3 Agent → 总耗时 ≈ 最慢 Agent 耗时
---
### 3.2 粒度进度上报
**目标:** 实时推送"第 3/7 步完成"到前端。
**方案:**
#### A. WorkflowEngine 进度回调
`execute()` 循环中添加钩子:
```python
async def on_progress(self, current_step, total_steps, node_name, status):
# 推送到 WebSocket
await ws_manager.broadcast(execution_id, {
"type": "progress",
"current": current_step,
"total": total_steps,
"node": node_name,
"status": status,
"percent": int(current_step / total_steps * 100),
})
```
#### B. 统计 DAG 总步数
执行前遍历拓扑排序结果,计算可达节点总数作为 total_steps。
#### C. WebSocket 改为推送模式
当前是轮询 DB改为 WorkflowEngine 主动调用 WebSocket manager 推送。
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/services/workflow_engine.py` | 添加 on_progress 回调 + 步数统计 |
| `backend/app/websocket/manager.py` | 添加 execution 进度推送方法 |
| `backend/app/api/websocket.py` | 适配新的推送模式 |
**验证:**
1. 执行 7 节点的 DAG → WebSocket 收到 7 条进度消息
2. 前端进度条从 0% 逐步递增到 100%
---
### 3.3 结果缓存
**目标:** 相同输入不重复计算。
**方案:**
添加缓存层,使用 Redis
```python
# 工具结果缓存
cache_key = f"tool:{tool_name}:{hash(json.dumps(args))}"
cached = await redis.get(cache_key)
if cached:
return cached.decode()
# LLM 响应缓存(相同 prompt + 相同 messages
cache_key = f"llm:{hash(messages_json)}"
```
配置:
- TTL 默认 1 小时
- 工具维度可选开启/关闭
- 确定性工具file_read、math_calculate默认开启
- 非确定性工具web_search、http_request默认关闭
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/agent_runtime/tool_manager.py` | 工具执行前后加缓存层 |
| `backend/app/agent_runtime/core.py` | LLM 调用前后加缓存层 |
| `backend/app/core/config.py` | 添加缓存配置项 |
**验证:**
1. 连续两次相同 file_read → 第二次命中缓存(耗时 < 1ms
2. 连续两次相同 math_calculate → 第二次命中缓存
---
## 第四阶段容错与共享2-3 周)
### 4.1 降级/回退链
**目标:** 模型/Agent 失败自动切换备用方案。
**方案:**
AgentLLMConfig 扩展:
```python
class AgentLLMConfig(BaseModel):
provider: str = "openai"
model: str = "gpt-4o-mini"
fallback_llm: Optional['AgentLLMConfig'] = None # 降级模型
```
Agent 节点 data 扩展:
```json
{
"fallback_agent": "备选Agent的ID" // 可选
}
```
执行逻辑:
1. 主模型调用失败 → 切换 fallback_llm 重试
2. Agent 执行失败 → 查找 fallback_agent 重试
3. 所有备用方案都失败 → 抛出错误
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/agent_runtime/schemas.py` | AgentLLMConfig 添加 fallback_llm |
| `backend/app/agent_runtime/core.py` | LLM 调用失败后切换 fallback |
| `backend/app/services/workflow_engine.py` | Agent 节点失败后切换 fallback_agent |
---
### 4.2 Agent 间知识共享
**目标:** 打破记忆隔离Agent 间共享知识。
**方案:**
添加全局知识索引层:
```
每个 Agent 执行完成后 → 提取关键知识 → 写入 global_knowledge 表
Agent 初始化时 → 从 global_knowledge 加载相关条目
```
实现:
- 新增 `GlobalKnowledge` 模型content, embedding, source_agent_id, tags, created_at
- `AgentMemory.initialize()` 添加全局知识检索步骤
- 自主进化创建的 Agent 自动继承创建者的知识
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/models/agent.py` | 新增 GlobalKnowledge 模型 |
| `backend/app/agent_runtime/memory.py` | initialize() 添加全局知识加载 |
| `backend/app/agent_runtime/core.py` | 执行完毕提取知识写入全局 |
---
### 4.3 Agent 异步执行实现
**目标:** 填空 `execute_agent_task`,支持真正异步 Agent 执行。
**方案:**
`execute_agent_task` 当前是空壳,需要实现:
```python
@celery_app.task
def execute_agent_task(agent_id, query, user_id):
db = SessionLocal()
agent = db.query(Agent).filter(Agent.id == agent_id).first()
config = build_agent_config_from_db(agent)
runtime = AgentRuntime(config)
result = asyncio.run(runtime.run(query))
# 更新 execution 记录
return {"status": "completed", "output": result.content}
```
**涉及文件:**
| 文件 | 改动 |
|------|------|
| `backend/app/tasks/agent_tasks.py` | 实现 execute_agent_task |
| `backend/app/tasks/scheduler_tasks.py` | 调度触发真正的异步执行 |
---
## 实施路线图总览
```
第一阶段1-2周
├── 1.1 输出质量验证self_review工具 + evaluator节点 + AgentRuntime自检
└── 1.2 节点级自动重试error_handler从空壳到真正重试
第二阶段2-3周
├── 2.1 Orchestrator进入工作流新增orchestrator节点类型
└── 2.2 工具级人工审批AgentToolConfig审批标记 + WebSocket审批通道
第三阶段2-3周
├── 3.1 并行执行DAG并行 + Orchestrator debate并行
├── 3.2 粒度进度上报on_progress回调 + WebSocket推送
└── 3.3 结果缓存Redis缓存层
第四阶段2-3周
├── 4.1 降级/回退链fallback_llm + fallback_agent
├── 4.2 Agent知识共享GlobalKnowledge + 跨Agent检索
└── 4.3 Agent异步执行填空execute_agent_task
```
## 优先级逻辑
- **第一阶段优先**:质量验证是自主进化的最后一环,没有它进化无方向;重试是稳定性基础
- **第二阶段其次**:编排整合让复杂任务可视化、安全化
- **第三阶段提速**:并行 + 缓存大幅提升性能(生产部署前必备)
- **第四阶段收尾**:容错和共享让系统长期运行可持续