|
|
@@ -1,15 +1,16 @@
|
|
|
"""
|
|
|
-Sub-Agent 工具 - 统一 explore/delegate/evaluate
|
|
|
+Sub-Agent 工具 - agent / evaluate
|
|
|
|
|
|
-作为普通工具运行:创建(或继承)子 Trace,执行并返回结构化结果。
|
|
|
+agent: 创建 Agent 执行任务(单任务 delegate 或多任务并行 explore)
|
|
|
+evaluate: 评估目标执行结果是否满足要求
|
|
|
"""
|
|
|
|
|
|
import asyncio
|
|
|
from datetime import datetime
|
|
|
-from typing import Any, Dict, List, Optional
|
|
|
+from typing import Any, Dict, List, Optional, Union
|
|
|
|
|
|
from agent.tools import tool
|
|
|
-from agent.trace.models import Trace
|
|
|
+from agent.trace.models import Trace, Messages
|
|
|
from agent.trace.trace_id import generate_sub_trace_id
|
|
|
from agent.trace.goal_models import GoalTree
|
|
|
from agent.trace.websocket import broadcast_sub_trace_started, broadcast_sub_trace_completed
|
|
|
@@ -21,69 +22,6 @@ def _make_run_config(**kwargs):
|
|
|
return RunConfig(**kwargs)
|
|
|
|
|
|
|
|
|
-def _build_explore_prompt(branches: List[str], background: Optional[str]) -> str:
|
|
|
- lines = ["# 探索任务", ""]
|
|
|
- if background:
|
|
|
- lines.extend([background, ""])
|
|
|
- lines.append("请探索以下方案:")
|
|
|
- for i, branch in enumerate(branches, 1):
|
|
|
- lines.append(f"{i}. {branch}")
|
|
|
- return "\n".join(lines)
|
|
|
-
|
|
|
-
|
|
|
-async def _build_evaluate_prompt(
|
|
|
- store,
|
|
|
- trace_id: str,
|
|
|
- target_goal_id: str,
|
|
|
- evaluation_input: Dict[str, Any],
|
|
|
- requirements: Optional[str],
|
|
|
-) -> str:
|
|
|
- goal_tree = await store.get_goal_tree(trace_id)
|
|
|
- target_desc = ""
|
|
|
- if goal_tree:
|
|
|
- target_goal = goal_tree.find(target_goal_id)
|
|
|
- if target_goal:
|
|
|
- target_desc = target_goal.description
|
|
|
-
|
|
|
- goal_description = evaluation_input.get("goal_description") or target_desc or f"Goal {target_goal_id}"
|
|
|
- actual_result = evaluation_input.get("actual_result", "(无执行结果)")
|
|
|
-
|
|
|
- lines = [
|
|
|
- "# 评估任务",
|
|
|
- "",
|
|
|
- "请评估以下任务的执行结果是否满足要求。",
|
|
|
- "",
|
|
|
- "## 目标描述",
|
|
|
- "",
|
|
|
- str(goal_description),
|
|
|
- "",
|
|
|
- "## 执行结果",
|
|
|
- "",
|
|
|
- str(actual_result),
|
|
|
- "",
|
|
|
- ]
|
|
|
-
|
|
|
- if requirements:
|
|
|
- lines.extend(["## 评估要求", "", requirements, ""])
|
|
|
-
|
|
|
- lines.extend(
|
|
|
- [
|
|
|
- "## 输出格式",
|
|
|
- "",
|
|
|
- "## 评估结论",
|
|
|
- "[通过/不通过]",
|
|
|
- "",
|
|
|
- "## 评估理由",
|
|
|
- "[详细说明通过或不通过原因]",
|
|
|
- "",
|
|
|
- "## 修改建议(如果不通过)",
|
|
|
- "1. [建议1]",
|
|
|
- "2. [建议2]",
|
|
|
- ]
|
|
|
- )
|
|
|
- return "\n".join(lines)
|
|
|
-
|
|
|
-
|
|
|
# ===== 辅助函数 =====
|
|
|
|
|
|
async def _update_collaborator(
|
|
|
@@ -125,6 +63,7 @@ async def _update_collaborator(
|
|
|
trace.context["collaborators"] = collaborators
|
|
|
await store.update_trace(trace_id, context=trace.context)
|
|
|
|
|
|
+
|
|
|
async def _update_goal_start(
|
|
|
store, trace_id: str, goal_id: str, mode: str, sub_trace_ids: List[str]
|
|
|
) -> None:
|
|
|
@@ -155,20 +94,76 @@ async def _update_goal_complete(
|
|
|
)
|
|
|
|
|
|
|
|
|
-def _format_explore_results(
|
|
|
- branches: List[str], results: List[Dict[str, Any]]
|
|
|
-) -> str:
|
|
|
- """格式化 explore 模式的汇总结果(Markdown)"""
|
|
|
- lines = ["## 探索结果\n"]
|
|
|
+def _aggregate_stats(results: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
+ """聚合多个结果的统计信息"""
|
|
|
+ total_messages = 0
|
|
|
+ total_tokens = 0
|
|
|
+ total_cost = 0.0
|
|
|
+
|
|
|
+ for result in results:
|
|
|
+ if isinstance(result, dict) and "stats" in result:
|
|
|
+ stats = result["stats"]
|
|
|
+ total_messages += stats.get("total_messages", 0)
|
|
|
+ total_tokens += stats.get("total_tokens", 0)
|
|
|
+ total_cost += stats.get("total_cost", 0.0)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "total_messages": total_messages,
|
|
|
+ "total_tokens": total_tokens,
|
|
|
+ "total_cost": total_cost
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _get_allowed_tools(single: bool, context: dict) -> Optional[List[str]]:
|
|
|
+ """获取允许工具列表。single=True: 全部(去掉 agent/evaluate); single=False: 只读"""
|
|
|
+ if not single:
|
|
|
+ return ["read_file", "grep_content", "glob_files", "goal"]
|
|
|
+ # single (delegate): 获取所有工具,排除 agent 和 evaluate
|
|
|
+ runner = context.get("runner")
|
|
|
+ if runner and hasattr(runner, "tools") and hasattr(runner.tools, "registry"):
|
|
|
+ all_tools = list(runner.tools.registry.keys())
|
|
|
+ return [t for t in all_tools if t not in ("agent", "evaluate")]
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def _format_single_result(result: Dict[str, Any], sub_trace_id: str, continued: bool) -> Dict[str, Any]:
|
|
|
+ """格式化单任务(delegate)结果"""
|
|
|
+ lines = ["## 委托任务完成\n"]
|
|
|
+ summary = result.get("summary", "")
|
|
|
+ if summary:
|
|
|
+ lines.append(summary)
|
|
|
+ lines.append("")
|
|
|
+ lines.append("---\n")
|
|
|
+ lines.append("**执行统计**:")
|
|
|
+ stats = result.get("stats", {})
|
|
|
+ if stats:
|
|
|
+ lines.append(f"- 消息数: {stats.get('total_messages', 0)}")
|
|
|
+ lines.append(f"- Tokens: {stats.get('total_tokens', 0)}")
|
|
|
+ lines.append(f"- 成本: ${stats.get('total_cost', 0.0):.4f}")
|
|
|
+ formatted_summary = "\n".join(lines)
|
|
|
|
|
|
+ return {
|
|
|
+ "mode": "delegate",
|
|
|
+ "sub_trace_id": sub_trace_id,
|
|
|
+ "continue_from": continued,
|
|
|
+ **result,
|
|
|
+ "summary": formatted_summary,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+def _format_multi_result(
|
|
|
+ tasks: List[str], results: List[Dict[str, Any]], sub_trace_ids: List[Dict]
|
|
|
+) -> Dict[str, Any]:
|
|
|
+ """格式化多任务(explore)聚合结果"""
|
|
|
+ lines = ["## 探索结果\n"]
|
|
|
successful = 0
|
|
|
failed = 0
|
|
|
total_tokens = 0
|
|
|
total_cost = 0.0
|
|
|
|
|
|
- for i, (branch, result) in enumerate(zip(branches, results)):
|
|
|
- branch_name = chr(ord('A') + i) # A, B, C...
|
|
|
- lines.append(f"### 方案 {branch_name}: {branch}")
|
|
|
+ for i, (task_item, result) in enumerate(zip(tasks, results)):
|
|
|
+ branch_name = chr(ord('A') + i)
|
|
|
+ lines.append(f"### 方案 {branch_name}: {task_item}")
|
|
|
|
|
|
if isinstance(result, dict):
|
|
|
status = result.get("status", "unknown")
|
|
|
@@ -181,7 +176,7 @@ def _format_explore_results(
|
|
|
|
|
|
summary = result.get("summary", "")
|
|
|
if summary:
|
|
|
- lines.append(f"**摘要**: {summary[:200]}...") # 限制长度
|
|
|
+ lines.append(f"**摘要**: {summary[:200]}...")
|
|
|
|
|
|
stats = result.get("stats", {})
|
|
|
if stats:
|
|
|
@@ -199,545 +194,517 @@ def _format_explore_results(
|
|
|
|
|
|
lines.append("---\n")
|
|
|
lines.append("## 总结")
|
|
|
- lines.append(f"- 总分支数: {len(branches)}")
|
|
|
+ lines.append(f"- 总分支数: {len(tasks)}")
|
|
|
lines.append(f"- 成功: {successful}")
|
|
|
lines.append(f"- 失败: {failed}")
|
|
|
lines.append(f"- 总 tokens: {total_tokens}")
|
|
|
lines.append(f"- 总成本: ${total_cost:.4f}")
|
|
|
|
|
|
- return "\n".join(lines)
|
|
|
-
|
|
|
-
|
|
|
-def _format_delegate_result(result: Dict[str, Any]) -> str:
|
|
|
- """格式化 delegate 模式的详细结果"""
|
|
|
- lines = ["## 委托任务完成\n"]
|
|
|
-
|
|
|
- summary = result.get("summary", "")
|
|
|
- if summary:
|
|
|
- lines.append(summary)
|
|
|
- lines.append("")
|
|
|
-
|
|
|
- lines.append("---\n")
|
|
|
- lines.append("**执行统计**:")
|
|
|
-
|
|
|
- stats = result.get("stats", {})
|
|
|
- if stats:
|
|
|
- lines.append(f"- 消息数: {stats.get('total_messages', 0)}")
|
|
|
- lines.append(f"- Tokens: {stats.get('total_tokens', 0)}")
|
|
|
- lines.append(f"- 成本: ${stats.get('total_cost', 0.0):.4f}")
|
|
|
-
|
|
|
- return "\n".join(lines)
|
|
|
-
|
|
|
+ aggregated_summary = "\n".join(lines)
|
|
|
+ overall_status = "completed" if successful > 0 else "failed"
|
|
|
|
|
|
-def _format_evaluate_result(result: Dict[str, Any]) -> str:
|
|
|
- """格式化 evaluate 模式的评估结果"""
|
|
|
- summary = result.get("summary", "")
|
|
|
- return summary # evaluate 的 summary 已经是格式化的评估结果
|
|
|
+ return {
|
|
|
+ "mode": "explore",
|
|
|
+ "status": overall_status,
|
|
|
+ "summary": aggregated_summary,
|
|
|
+ "sub_trace_ids": sub_trace_ids,
|
|
|
+ "tasks": tasks,
|
|
|
+ "stats": _aggregate_stats(results),
|
|
|
+ }
|
|
|
|
|
|
|
|
|
-def _get_allowed_tools_for_mode(mode: str, context: dict) -> Optional[List[str]]:
|
|
|
- """获取模式对应的允许工具列表"""
|
|
|
- if mode == "explore":
|
|
|
- return ["read_file", "grep_content", "glob_files", "goal"]
|
|
|
- elif mode in ["delegate", "evaluate"]:
|
|
|
- # 获取所有工具,排除 subagent
|
|
|
- runner = context.get("runner")
|
|
|
- if runner and hasattr(runner, "tools") and hasattr(runner.tools, "registry"):
|
|
|
- all_tools = list(runner.tools.registry.keys())
|
|
|
- return [t for t in all_tools if t != "subagent"]
|
|
|
- return None # 使用默认(所有工具)
|
|
|
+async def _get_goal_description(store, trace_id: str, goal_id: str) -> str:
|
|
|
+ """从 GoalTree 获取目标描述"""
|
|
|
+ if not goal_id:
|
|
|
+ return ""
|
|
|
+ goal_tree = await store.get_goal_tree(trace_id)
|
|
|
+ if goal_tree:
|
|
|
+ target_goal = goal_tree.find(goal_id)
|
|
|
+ if target_goal:
|
|
|
+ return target_goal.description
|
|
|
+ return f"Goal {goal_id}"
|
|
|
|
|
|
|
|
|
-def _aggregate_stats(results: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
- """聚合多个结果的统计信息"""
|
|
|
- total_messages = 0
|
|
|
- total_tokens = 0
|
|
|
- total_cost = 0.0
|
|
|
+def _build_evaluate_prompt(goal_description: str, messages: Optional[Messages]) -> str:
|
|
|
+ """
|
|
|
+ 构建评估 prompt。
|
|
|
|
|
|
- for result in results:
|
|
|
- if isinstance(result, dict) and "stats" in result:
|
|
|
- stats = result["stats"]
|
|
|
- total_messages += stats.get("total_messages", 0)
|
|
|
- total_tokens += stats.get("total_tokens", 0)
|
|
|
- total_cost += stats.get("total_cost", 0.0)
|
|
|
+ Args:
|
|
|
+ goal_description: 代码从 GoalTree 注入的目标描述
|
|
|
+ messages: 模型提供的消息(执行结果+上下文)
|
|
|
+ """
|
|
|
+ # 从 messages 提取文本内容
|
|
|
+ result_text = ""
|
|
|
+ if messages:
|
|
|
+ parts = []
|
|
|
+ for msg in messages:
|
|
|
+ content = msg.get("content", "")
|
|
|
+ if isinstance(content, str):
|
|
|
+ parts.append(content)
|
|
|
+ elif isinstance(content, list):
|
|
|
+ # 多模态内容,提取文本部分
|
|
|
+ for item in content:
|
|
|
+ if isinstance(item, dict) and item.get("type") == "text":
|
|
|
+ parts.append(item.get("text", ""))
|
|
|
+ result_text = "\n".join(parts)
|
|
|
|
|
|
- return {
|
|
|
- "total_messages": total_messages,
|
|
|
- "total_tokens": total_tokens,
|
|
|
- "total_cost": total_cost
|
|
|
- }
|
|
|
+ lines = [
|
|
|
+ "# 评估任务",
|
|
|
+ "",
|
|
|
+ "请评估以下任务的执行结果是否满足要求。",
|
|
|
+ "",
|
|
|
+ "## 目标描述",
|
|
|
+ "",
|
|
|
+ goal_description,
|
|
|
+ "",
|
|
|
+ "## 执行结果",
|
|
|
+ "",
|
|
|
+ result_text or "(无执行结果)",
|
|
|
+ "",
|
|
|
+ "## 输出格式",
|
|
|
+ "",
|
|
|
+ "## 评估结论",
|
|
|
+ "[通过/不通过]",
|
|
|
+ "",
|
|
|
+ "## 评估理由",
|
|
|
+ "[详细说明通过或不通过原因]",
|
|
|
+ "",
|
|
|
+ "## 修改建议(如果不通过)",
|
|
|
+ "1. [建议1]",
|
|
|
+ "2. [建议2]",
|
|
|
+ ]
|
|
|
+ return "\n".join(lines)
|
|
|
|
|
|
|
|
|
-# ===== 模式处理函数 =====
|
|
|
+# ===== 统一内部执行函数 =====
|
|
|
|
|
|
-async def _handle_explore_mode(
|
|
|
- branches: List[str],
|
|
|
- background: Optional[str],
|
|
|
+async def _run_agents(
|
|
|
+ tasks: List[str],
|
|
|
+ per_agent_msgs: List[Messages],
|
|
|
continue_from: Optional[str],
|
|
|
- store, current_trace_id: str, current_goal_id: str, runner
|
|
|
+ store, trace_id: str, goal_id: str, runner, context: dict,
|
|
|
) -> Dict[str, Any]:
|
|
|
- """Explore 模式:并行探索多个方案"""
|
|
|
+ """
|
|
|
+ 统一 agent 执行逻辑。
|
|
|
|
|
|
- # 1. 检查 continue_from(不支持)
|
|
|
- if continue_from:
|
|
|
- return {
|
|
|
- "status": "failed",
|
|
|
- "error": "explore mode does not support continue_from parameter"
|
|
|
- }
|
|
|
+ single (len(tasks)==1): delegate 模式,全量工具(排除 agent/evaluate)
|
|
|
+ multi (len(tasks)>1): explore 模式,只读工具,并行执行
|
|
|
+ """
|
|
|
+ single = len(tasks) == 1
|
|
|
+ parent_trace = await store.get_trace(trace_id)
|
|
|
+
|
|
|
+ # continue_from: 复用已有 trace(仅 single)
|
|
|
+ sub_trace_id = None
|
|
|
+ continued = False
|
|
|
+ if single and continue_from:
|
|
|
+ existing = await store.get_trace(continue_from)
|
|
|
+ if not existing:
|
|
|
+ return {"status": "failed", "error": f"Continue-from trace not found: {continue_from}"}
|
|
|
+ sub_trace_id = continue_from
|
|
|
+ continued = True
|
|
|
+ goal_tree = await store.get_goal_tree(continue_from)
|
|
|
+ mission = goal_tree.mission if goal_tree else tasks[0]
|
|
|
+ sub_trace_ids = [{"trace_id": sub_trace_id, "mission": mission}]
|
|
|
+ else:
|
|
|
+ sub_trace_ids = []
|
|
|
|
|
|
- # 2. 获取父 Trace 信息(用于继承 uid、model)
|
|
|
- parent_trace = await store.get_trace(current_trace_id)
|
|
|
+ # 创建 sub-traces 和执行协程
|
|
|
+ coros = []
|
|
|
+ all_sub_trace_ids = list(sub_trace_ids) # copy for continue_from case
|
|
|
|
|
|
- # 3. 创建所有 Sub-Traces
|
|
|
- sub_trace_ids = []
|
|
|
- tasks = []
|
|
|
+ for i, (task_item, msgs) in enumerate(zip(tasks, per_agent_msgs)):
|
|
|
+ if single and continued:
|
|
|
+ # continue_from 已经设置了 sub_trace_id
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ agent_type = "delegate" if single else "explore"
|
|
|
+ suffix = "delegate" if single else f"explore-{i+1:03d}"
|
|
|
+ stid = generate_sub_trace_id(trace_id, suffix)
|
|
|
+
|
|
|
+ sub_trace = Trace(
|
|
|
+ trace_id=stid,
|
|
|
+ mode="agent",
|
|
|
+ task=task_item,
|
|
|
+ parent_trace_id=trace_id,
|
|
|
+ parent_goal_id=goal_id,
|
|
|
+ agent_type=agent_type,
|
|
|
+ uid=parent_trace.uid if parent_trace else None,
|
|
|
+ model=parent_trace.model if parent_trace else None,
|
|
|
+ status="running",
|
|
|
+ context={"created_by_tool": "agent"},
|
|
|
+ created_at=datetime.now(),
|
|
|
+ )
|
|
|
+ await store.create_trace(sub_trace)
|
|
|
+ await store.update_goal_tree(stid, GoalTree(mission=task_item))
|
|
|
|
|
|
- for i, branch in enumerate(branches):
|
|
|
- # 生成唯一的 sub_trace_id
|
|
|
- sub_trace_id = generate_sub_trace_id(current_trace_id, f"explore-{i+1:03d}")
|
|
|
- sub_trace_ids.append({
|
|
|
- "trace_id": sub_trace_id,
|
|
|
- "mission": branch
|
|
|
- })
|
|
|
+ all_sub_trace_ids.append({"trace_id": stid, "mission": task_item})
|
|
|
|
|
|
- # 创建 Sub-Trace
|
|
|
- sub_trace = Trace(
|
|
|
- trace_id=sub_trace_id,
|
|
|
- mode="agent",
|
|
|
- task=branch,
|
|
|
- parent_trace_id=current_trace_id,
|
|
|
- parent_goal_id=current_goal_id,
|
|
|
- agent_type="explore",
|
|
|
- uid=parent_trace.uid if parent_trace else None,
|
|
|
- model=parent_trace.model if parent_trace else None,
|
|
|
- status="running",
|
|
|
- context={"subagent_mode": "explore", "created_by_tool": "subagent"},
|
|
|
- created_at=datetime.now(),
|
|
|
- )
|
|
|
- await store.create_trace(sub_trace)
|
|
|
- await store.update_goal_tree(sub_trace_id, GoalTree(mission=branch))
|
|
|
+ # 广播 sub_trace_started
|
|
|
+ await broadcast_sub_trace_started(
|
|
|
+ trace_id, stid, goal_id or "",
|
|
|
+ agent_type, task_item,
|
|
|
+ )
|
|
|
|
|
|
- # 广播 sub_trace_started
|
|
|
- await broadcast_sub_trace_started(
|
|
|
- current_trace_id, sub_trace_id, current_goal_id or "",
|
|
|
- "explore", branch
|
|
|
- )
|
|
|
+ if single:
|
|
|
+ sub_trace_id = stid
|
|
|
|
|
|
# 注册为活跃协作者
|
|
|
+ cur_stid = sub_trace_id if single else all_sub_trace_ids[-1]["trace_id"]
|
|
|
+ collab_name = task_item[:30] if single and not continued else (
|
|
|
+ f"delegate-{cur_stid[:8]}" if single else f"explore-{i+1}"
|
|
|
+ )
|
|
|
await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=f"explore-{i+1}", sub_trace_id=sub_trace_id,
|
|
|
- status="running", summary=branch[:80],
|
|
|
+ store, trace_id,
|
|
|
+ name=collab_name, sub_trace_id=cur_stid,
|
|
|
+ status="running", summary=task_item[:80],
|
|
|
)
|
|
|
|
|
|
- # 创建执行任务
|
|
|
- task_coro = runner.run_result(
|
|
|
- messages=[{"role": "user", "content": branch}],
|
|
|
+ # 构建消息
|
|
|
+ agent_msgs = list(msgs) + [{"role": "user", "content": task_item}]
|
|
|
+ allowed_tools = _get_allowed_tools(single, context)
|
|
|
+
|
|
|
+ coro = runner.run_result(
|
|
|
+ messages=agent_msgs,
|
|
|
config=_make_run_config(
|
|
|
- trace_id=sub_trace_id,
|
|
|
- agent_type="explore",
|
|
|
+ trace_id=cur_stid,
|
|
|
+ agent_type="delegate" if single else "explore",
|
|
|
model=parent_trace.model if parent_trace else "gpt-4o",
|
|
|
uid=parent_trace.uid if parent_trace else None,
|
|
|
- tools=["read_file", "grep_content", "glob_files", "goal"],
|
|
|
- name=branch,
|
|
|
+ tools=allowed_tools,
|
|
|
+ name=task_item[:50],
|
|
|
),
|
|
|
)
|
|
|
- tasks.append(task_coro)
|
|
|
-
|
|
|
- # 4. 更新主 Goal 为 in_progress
|
|
|
- await _update_goal_start(store, current_trace_id, current_goal_id, "explore", sub_trace_ids)
|
|
|
+ coros.append((i, cur_stid, collab_name, coro))
|
|
|
|
|
|
- # 5. 并行执行所有分支
|
|
|
- results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
+ # 更新主 Goal 为 in_progress
|
|
|
+ await _update_goal_start(
|
|
|
+ store, trace_id, goal_id,
|
|
|
+ "delegate" if single else "explore",
|
|
|
+ all_sub_trace_ids,
|
|
|
+ )
|
|
|
|
|
|
- # 6. 处理结果并广播完成事件
|
|
|
- processed_results = []
|
|
|
+ # 执行
|
|
|
+ if single:
|
|
|
+ # 单任务直接执行(带异常处理)
|
|
|
+ _, stid, collab_name, coro = coros[0]
|
|
|
+ try:
|
|
|
+ result = await coro
|
|
|
|
|
|
- for i, result in enumerate(results):
|
|
|
- sub_tid = sub_trace_ids[i]["trace_id"]
|
|
|
- if isinstance(result, Exception):
|
|
|
- # 异常处理
|
|
|
- error_result = {
|
|
|
- "status": "failed",
|
|
|
- "summary": f"执行出错: {str(result)}",
|
|
|
- "stats": {"total_messages": 0, "total_tokens": 0, "total_cost": 0.0}
|
|
|
- }
|
|
|
- processed_results.append(error_result)
|
|
|
- await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_tid, "failed", str(result), {}
|
|
|
- )
|
|
|
- await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=f"explore-{i+1}", sub_trace_id=sub_tid,
|
|
|
- status="failed", summary=str(result)[:80],
|
|
|
- )
|
|
|
- else:
|
|
|
- processed_results.append(result)
|
|
|
await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_tid,
|
|
|
+ trace_id, stid,
|
|
|
result.get("status", "completed"),
|
|
|
result.get("summary", ""),
|
|
|
- result.get("stats", {})
|
|
|
+ result.get("stats", {}),
|
|
|
)
|
|
|
await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=f"explore-{i+1}", sub_trace_id=sub_tid,
|
|
|
+ store, trace_id,
|
|
|
+ name=collab_name, sub_trace_id=stid,
|
|
|
status=result.get("status", "completed"),
|
|
|
summary=result.get("summary", "")[:80],
|
|
|
)
|
|
|
|
|
|
- # 7. 格式化汇总结果
|
|
|
- aggregated_summary = _format_explore_results(branches, processed_results)
|
|
|
+ formatted = _format_single_result(result, stid, continued)
|
|
|
|
|
|
- # 8. 更新主 Goal 为 completed
|
|
|
- overall_status = "completed" if any(
|
|
|
- r.get("status") == "completed" for r in processed_results if isinstance(r, dict)
|
|
|
- ) else "failed"
|
|
|
+ await _update_goal_complete(
|
|
|
+ store, trace_id, goal_id,
|
|
|
+ result.get("status", "completed"),
|
|
|
+ formatted["summary"],
|
|
|
+ all_sub_trace_ids,
|
|
|
+ )
|
|
|
+ return formatted
|
|
|
|
|
|
- await _update_goal_complete(
|
|
|
- store, current_trace_id, current_goal_id,
|
|
|
- overall_status, aggregated_summary, sub_trace_ids
|
|
|
- )
|
|
|
+ except Exception as e:
|
|
|
+ error_msg = str(e)
|
|
|
+ await broadcast_sub_trace_completed(
|
|
|
+ trace_id, stid, "failed", error_msg, {},
|
|
|
+ )
|
|
|
+ await _update_collaborator(
|
|
|
+ store, trace_id,
|
|
|
+ name=collab_name, sub_trace_id=stid,
|
|
|
+ status="failed", summary=error_msg[:80],
|
|
|
+ )
|
|
|
+ await _update_goal_complete(
|
|
|
+ store, trace_id, goal_id,
|
|
|
+ "failed", f"委托任务失败: {error_msg}",
|
|
|
+ all_sub_trace_ids,
|
|
|
+ )
|
|
|
+ return {
|
|
|
+ "mode": "delegate",
|
|
|
+ "status": "failed",
|
|
|
+ "error": error_msg,
|
|
|
+ "sub_trace_id": stid,
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ # 多任务并行执行
|
|
|
+ raw_results = await asyncio.gather(
|
|
|
+ *(coro for _, _, _, coro in coros),
|
|
|
+ return_exceptions=True,
|
|
|
+ )
|
|
|
|
|
|
- # 9. 返回结果
|
|
|
- return {
|
|
|
- "mode": "explore",
|
|
|
- "status": overall_status,
|
|
|
- "summary": aggregated_summary,
|
|
|
- "sub_trace_ids": sub_trace_ids,
|
|
|
- "branches": branches,
|
|
|
- "stats": _aggregate_stats(processed_results)
|
|
|
- }
|
|
|
+ processed_results = []
|
|
|
+ for idx, raw in enumerate(raw_results):
|
|
|
+ _, stid, collab_name, _ = coros[idx]
|
|
|
+ if isinstance(raw, Exception):
|
|
|
+ error_result = {
|
|
|
+ "status": "failed",
|
|
|
+ "summary": f"执行出错: {str(raw)}",
|
|
|
+ "stats": {"total_messages": 0, "total_tokens": 0, "total_cost": 0.0},
|
|
|
+ }
|
|
|
+ processed_results.append(error_result)
|
|
|
+ await broadcast_sub_trace_completed(
|
|
|
+ trace_id, stid, "failed", str(raw), {},
|
|
|
+ )
|
|
|
+ await _update_collaborator(
|
|
|
+ store, trace_id,
|
|
|
+ name=collab_name, sub_trace_id=stid,
|
|
|
+ status="failed", summary=str(raw)[:80],
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ processed_results.append(raw)
|
|
|
+ await broadcast_sub_trace_completed(
|
|
|
+ trace_id, stid,
|
|
|
+ raw.get("status", "completed"),
|
|
|
+ raw.get("summary", ""),
|
|
|
+ raw.get("stats", {}),
|
|
|
+ )
|
|
|
+ await _update_collaborator(
|
|
|
+ store, trace_id,
|
|
|
+ name=collab_name, sub_trace_id=stid,
|
|
|
+ status=raw.get("status", "completed"),
|
|
|
+ summary=raw.get("summary", "")[:80],
|
|
|
+ )
|
|
|
+
|
|
|
+ formatted = _format_multi_result(tasks, processed_results, all_sub_trace_ids)
|
|
|
|
|
|
+ await _update_goal_complete(
|
|
|
+ store, trace_id, goal_id,
|
|
|
+ formatted["status"],
|
|
|
+ formatted["summary"],
|
|
|
+ all_sub_trace_ids,
|
|
|
+ )
|
|
|
+ return formatted
|
|
|
|
|
|
-async def _handle_delegate_mode(
|
|
|
- task: str,
|
|
|
- continue_from: Optional[str],
|
|
|
- store, current_trace_id: str, current_goal_id: str, runner, context: dict
|
|
|
+
|
|
|
+# ===== 工具定义 =====
|
|
|
+
|
|
|
+@tool(description="创建 Agent 执行任务")
|
|
|
+async def agent(
|
|
|
+ task: Union[str, List[str]],
|
|
|
+ messages: Optional[Union[Messages, List[Messages]]] = None,
|
|
|
+ continue_from: Optional[str] = None,
|
|
|
+ context: Optional[dict] = None,
|
|
|
) -> Dict[str, Any]:
|
|
|
- """Delegate 模式:委托单个任务"""
|
|
|
+ """
|
|
|
+ 创建 Agent 执行任务。
|
|
|
|
|
|
- # 1. 获取父 Trace 信息
|
|
|
- parent_trace = await store.get_trace(current_trace_id)
|
|
|
+ 单任务 (task: str): delegate 模式,全量工具
|
|
|
+ 多任务 (task: List[str]): explore 模式,只读工具,并行执行
|
|
|
|
|
|
- # 2. 处理 continue_from 或创建新 Sub-Trace
|
|
|
- if continue_from:
|
|
|
- existing_trace = await store.get_trace(continue_from)
|
|
|
- if not existing_trace:
|
|
|
- return {"status": "failed", "error": f"Continue-from trace not found: {continue_from}"}
|
|
|
- sub_trace_id = continue_from
|
|
|
- # 获取 mission
|
|
|
- goal_tree = await store.get_goal_tree(continue_from)
|
|
|
- mission = goal_tree.mission if goal_tree else task
|
|
|
- sub_trace_ids = [{"trace_id": sub_trace_id, "mission": mission}]
|
|
|
- else:
|
|
|
- sub_trace_id = generate_sub_trace_id(current_trace_id, "delegate")
|
|
|
- sub_trace = Trace(
|
|
|
- trace_id=sub_trace_id,
|
|
|
- mode="agent",
|
|
|
- task=task,
|
|
|
- parent_trace_id=current_trace_id,
|
|
|
- parent_goal_id=current_goal_id,
|
|
|
- agent_type="delegate",
|
|
|
- uid=parent_trace.uid if parent_trace else None,
|
|
|
- model=parent_trace.model if parent_trace else None,
|
|
|
- status="running",
|
|
|
- context={"subagent_mode": "delegate", "created_by_tool": "subagent"},
|
|
|
- created_at=datetime.now(),
|
|
|
- )
|
|
|
- await store.create_trace(sub_trace)
|
|
|
- await store.update_goal_tree(sub_trace_id, GoalTree(mission=task))
|
|
|
- sub_trace_ids = [{"trace_id": sub_trace_id, "mission": task}]
|
|
|
+ Args:
|
|
|
+ task: 任务描述。字符串=单任务,列表=多任务并行
|
|
|
+ messages: 预置消息。1D 列表=所有 agent 共享;2D 列表=per-agent
|
|
|
+ continue_from: 继续已有 trace(仅单任务)
|
|
|
+ context: 框架自动注入的上下文
|
|
|
+ """
|
|
|
+ if not context:
|
|
|
+ return {"status": "failed", "error": "context is required"}
|
|
|
|
|
|
- # 广播 sub_trace_started
|
|
|
- await broadcast_sub_trace_started(
|
|
|
- current_trace_id, sub_trace_id, current_goal_id or "",
|
|
|
- "delegate", task
|
|
|
- )
|
|
|
+ store = context.get("store")
|
|
|
+ trace_id = context.get("trace_id")
|
|
|
+ goal_id = context.get("goal_id")
|
|
|
+ runner = context.get("runner")
|
|
|
|
|
|
- # 注册为活跃协作者
|
|
|
- delegate_name = task[:30] if not continue_from else f"delegate-{sub_trace_id[:8]}"
|
|
|
- await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=delegate_name, sub_trace_id=sub_trace_id,
|
|
|
- status="running", summary=task[:80],
|
|
|
- )
|
|
|
+ missing = []
|
|
|
+ if not store:
|
|
|
+ missing.append("store")
|
|
|
+ if not trace_id:
|
|
|
+ missing.append("trace_id")
|
|
|
+ if not runner:
|
|
|
+ missing.append("runner")
|
|
|
+ if missing:
|
|
|
+ return {"status": "failed", "error": f"Missing required context: {', '.join(missing)}"}
|
|
|
|
|
|
- # 3. 更新主 Goal 为 in_progress
|
|
|
- await _update_goal_start(store, current_trace_id, current_goal_id, "delegate", sub_trace_ids)
|
|
|
+ # 归一化 task → list
|
|
|
+ single = isinstance(task, str)
|
|
|
+ tasks = [task] if single else task
|
|
|
|
|
|
- # 4. 执行任务
|
|
|
- try:
|
|
|
- allowed_tools = _get_allowed_tools_for_mode("delegate", context)
|
|
|
- result = await runner.run_result(
|
|
|
- messages=[{"role": "user", "content": task}],
|
|
|
- config=_make_run_config(
|
|
|
- trace_id=sub_trace_id,
|
|
|
- agent_type="delegate",
|
|
|
- model=parent_trace.model if parent_trace else "gpt-4o",
|
|
|
- uid=parent_trace.uid if parent_trace else None,
|
|
|
- tools=allowed_tools,
|
|
|
- name=task[:50],
|
|
|
- ),
|
|
|
- )
|
|
|
+ if not tasks:
|
|
|
+ return {"status": "failed", "error": "task is required"}
|
|
|
|
|
|
- # 4. 广播 sub_trace_completed
|
|
|
- await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_trace_id,
|
|
|
- result.get("status", "completed"),
|
|
|
- result.get("summary", ""),
|
|
|
- result.get("stats", {})
|
|
|
- )
|
|
|
+ # 归一化 messages → List[Messages](per-agent)
|
|
|
+ if messages is None:
|
|
|
+ per_agent_msgs: List[Messages] = [[] for _ in tasks]
|
|
|
+ elif messages and isinstance(messages[0], list):
|
|
|
+ per_agent_msgs = messages # 2D: per-agent
|
|
|
+ else:
|
|
|
+ per_agent_msgs = [messages] * len(tasks) # 1D: 共享
|
|
|
|
|
|
- # 更新协作者状态
|
|
|
- await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=delegate_name, sub_trace_id=sub_trace_id,
|
|
|
- status=result.get("status", "completed"),
|
|
|
- summary=result.get("summary", "")[:80],
|
|
|
- )
|
|
|
+ if continue_from and not single:
|
|
|
+ return {"status": "failed", "error": "continue_from requires single task"}
|
|
|
|
|
|
- # 5. 格式化结果
|
|
|
- formatted_summary = _format_delegate_result(result)
|
|
|
+ return await _run_agents(
|
|
|
+ tasks, per_agent_msgs, continue_from,
|
|
|
+ store, trace_id, goal_id, runner, context,
|
|
|
+ )
|
|
|
|
|
|
- # 6. 更新主 Goal 为 completed
|
|
|
- await _update_goal_complete(
|
|
|
- store, current_trace_id, current_goal_id,
|
|
|
- result.get("status", "completed"), formatted_summary, sub_trace_ids
|
|
|
- )
|
|
|
|
|
|
- # 7. 返回结果
|
|
|
- return {
|
|
|
- "mode": "delegate",
|
|
|
- "sub_trace_id": sub_trace_id,
|
|
|
- "continue_from": bool(continue_from),
|
|
|
- **result,
|
|
|
- "summary": formatted_summary
|
|
|
- }
|
|
|
+@tool(description="评估目标执行结果是否满足要求")
|
|
|
+async def evaluate(
|
|
|
+ messages: Optional[Messages] = None,
|
|
|
+ target_goal_id: Optional[str] = None,
|
|
|
+ continue_from: Optional[str] = None,
|
|
|
+ context: Optional[dict] = None,
|
|
|
+) -> Dict[str, Any]:
|
|
|
+ """
|
|
|
+ 评估目标执行结果是否满足要求。
|
|
|
|
|
|
- except Exception as e:
|
|
|
- # 错误处理
|
|
|
- error_msg = str(e)
|
|
|
- await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_trace_id,
|
|
|
- "failed", error_msg, {}
|
|
|
- )
|
|
|
+ 代码自动从 GoalTree 注入目标描述。模型把执行结果和上下文放在 messages 中。
|
|
|
|
|
|
- await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
- name=delegate_name, sub_trace_id=sub_trace_id,
|
|
|
- status="failed", summary=error_msg[:80],
|
|
|
- )
|
|
|
+ Args:
|
|
|
+ messages: 执行结果和上下文消息(OpenAI 格式)
|
|
|
+ target_goal_id: 要评估的目标 ID(默认当前 goal_id)
|
|
|
+ continue_from: 继续已有评估 trace
|
|
|
+ context: 框架自动注入的上下文
|
|
|
+ """
|
|
|
+ if not context:
|
|
|
+ return {"status": "failed", "error": "context is required"}
|
|
|
|
|
|
- await _update_goal_complete(
|
|
|
- store, current_trace_id, current_goal_id,
|
|
|
- "failed", f"委托任务失败: {error_msg}", sub_trace_ids
|
|
|
- )
|
|
|
+ store = context.get("store")
|
|
|
+ trace_id = context.get("trace_id")
|
|
|
+ current_goal_id = context.get("goal_id")
|
|
|
+ runner = context.get("runner")
|
|
|
|
|
|
- return {
|
|
|
- "mode": "delegate",
|
|
|
- "status": "failed",
|
|
|
- "error": error_msg,
|
|
|
- "sub_trace_id": sub_trace_id
|
|
|
- }
|
|
|
+ missing = []
|
|
|
+ if not store:
|
|
|
+ missing.append("store")
|
|
|
+ if not trace_id:
|
|
|
+ missing.append("trace_id")
|
|
|
+ if not runner:
|
|
|
+ missing.append("runner")
|
|
|
+ if missing:
|
|
|
+ return {"status": "failed", "error": f"Missing required context: {', '.join(missing)}"}
|
|
|
|
|
|
+ # target_goal_id 默认 context["goal_id"]
|
|
|
+ goal_id = target_goal_id or current_goal_id
|
|
|
|
|
|
-async def _handle_evaluate_mode(
|
|
|
- target_goal_id: str,
|
|
|
- evaluation_input: Dict[str, Any],
|
|
|
- requirements: Optional[str],
|
|
|
- continue_from: Optional[str],
|
|
|
- store, current_trace_id: str, current_goal_id: str, runner, context: dict
|
|
|
-) -> Dict[str, Any]:
|
|
|
- """Evaluate 模式:评估任务结果"""
|
|
|
+ # 从 GoalTree 获取目标描述
|
|
|
+ goal_desc = await _get_goal_description(store, trace_id, goal_id)
|
|
|
|
|
|
- # 1. 构建评估 prompt
|
|
|
- task_prompt = await _build_evaluate_prompt(
|
|
|
- store, current_trace_id, target_goal_id,
|
|
|
- evaluation_input, requirements
|
|
|
- )
|
|
|
+ # 构建 evaluator prompt
|
|
|
+ eval_prompt = _build_evaluate_prompt(goal_desc, messages)
|
|
|
|
|
|
- # 2. 获取父 Trace 信息
|
|
|
- parent_trace = await store.get_trace(current_trace_id)
|
|
|
+ # 获取父 Trace 信息
|
|
|
+ parent_trace = await store.get_trace(trace_id)
|
|
|
|
|
|
- # 3. 处理 continue_from 或创建新 Sub-Trace
|
|
|
+ # 处理 continue_from 或创建新 Sub-Trace
|
|
|
if continue_from:
|
|
|
existing_trace = await store.get_trace(continue_from)
|
|
|
if not existing_trace:
|
|
|
return {"status": "failed", "error": f"Continue-from trace not found: {continue_from}"}
|
|
|
sub_trace_id = continue_from
|
|
|
- # 获取 mission
|
|
|
goal_tree = await store.get_goal_tree(continue_from)
|
|
|
- mission = goal_tree.mission if goal_tree else task_prompt
|
|
|
+ mission = goal_tree.mission if goal_tree else eval_prompt
|
|
|
sub_trace_ids = [{"trace_id": sub_trace_id, "mission": mission}]
|
|
|
else:
|
|
|
- sub_trace_id = generate_sub_trace_id(current_trace_id, "evaluate")
|
|
|
+ sub_trace_id = generate_sub_trace_id(trace_id, "evaluate")
|
|
|
sub_trace = Trace(
|
|
|
trace_id=sub_trace_id,
|
|
|
mode="agent",
|
|
|
- task=task_prompt,
|
|
|
- parent_trace_id=current_trace_id,
|
|
|
+ task=eval_prompt,
|
|
|
+ parent_trace_id=trace_id,
|
|
|
parent_goal_id=current_goal_id,
|
|
|
agent_type="evaluate",
|
|
|
uid=parent_trace.uid if parent_trace else None,
|
|
|
model=parent_trace.model if parent_trace else None,
|
|
|
status="running",
|
|
|
- context={"subagent_mode": "evaluate", "created_by_tool": "subagent"},
|
|
|
+ context={"created_by_tool": "evaluate"},
|
|
|
created_at=datetime.now(),
|
|
|
)
|
|
|
await store.create_trace(sub_trace)
|
|
|
- await store.update_goal_tree(sub_trace_id, GoalTree(mission=task_prompt))
|
|
|
- sub_trace_ids = [{"trace_id": sub_trace_id, "mission": task_prompt}]
|
|
|
+ await store.update_goal_tree(sub_trace_id, GoalTree(mission=eval_prompt))
|
|
|
+ sub_trace_ids = [{"trace_id": sub_trace_id, "mission": eval_prompt}]
|
|
|
|
|
|
# 广播 sub_trace_started
|
|
|
await broadcast_sub_trace_started(
|
|
|
- current_trace_id, sub_trace_id, current_goal_id or "",
|
|
|
- "evaluate", task_prompt
|
|
|
+ trace_id, sub_trace_id, current_goal_id or "",
|
|
|
+ "evaluate", eval_prompt,
|
|
|
)
|
|
|
|
|
|
- # 4. 更新主 Goal 为 in_progress
|
|
|
- await _update_goal_start(store, current_trace_id, current_goal_id, "evaluate", sub_trace_ids)
|
|
|
+ # 更新主 Goal 为 in_progress
|
|
|
+ await _update_goal_start(store, trace_id, current_goal_id, "evaluate", sub_trace_ids)
|
|
|
|
|
|
# 注册为活跃协作者
|
|
|
- eval_name = f"评估: {target_goal_id[:20]}"
|
|
|
+ eval_name = f"评估: {(goal_id or 'unknown')[:20]}"
|
|
|
await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
+ store, trace_id,
|
|
|
name=eval_name, sub_trace_id=sub_trace_id,
|
|
|
- status="running", summary=f"评估 Goal {target_goal_id}",
|
|
|
+ status="running", summary=f"评估 Goal {goal_id}",
|
|
|
)
|
|
|
|
|
|
- # 5. 执行评估
|
|
|
+ # 执行评估
|
|
|
try:
|
|
|
- allowed_tools = _get_allowed_tools_for_mode("evaluate", context)
|
|
|
+ # evaluate 使用只读工具 + goal
|
|
|
+ allowed_tools = ["read_file", "grep_content", "glob_files", "goal"]
|
|
|
result = await runner.run_result(
|
|
|
- messages=[{"role": "user", "content": task_prompt}],
|
|
|
+ messages=[{"role": "user", "content": eval_prompt}],
|
|
|
config=_make_run_config(
|
|
|
trace_id=sub_trace_id,
|
|
|
agent_type="evaluate",
|
|
|
model=parent_trace.model if parent_trace else "gpt-4o",
|
|
|
uid=parent_trace.uid if parent_trace else None,
|
|
|
tools=allowed_tools,
|
|
|
- name=f"评估: {target_goal_id}",
|
|
|
+ name=f"评估: {goal_id}",
|
|
|
),
|
|
|
)
|
|
|
|
|
|
- # 5. 广播 sub_trace_completed
|
|
|
await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_trace_id,
|
|
|
+ trace_id, sub_trace_id,
|
|
|
result.get("status", "completed"),
|
|
|
result.get("summary", ""),
|
|
|
- result.get("stats", {})
|
|
|
+ result.get("stats", {}),
|
|
|
)
|
|
|
-
|
|
|
- # 更新协作者状态
|
|
|
await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
+ store, trace_id,
|
|
|
name=eval_name, sub_trace_id=sub_trace_id,
|
|
|
status=result.get("status", "completed"),
|
|
|
summary=result.get("summary", "")[:80],
|
|
|
)
|
|
|
|
|
|
- # 6. 格式化结果
|
|
|
- formatted_summary = _format_evaluate_result(result)
|
|
|
+ formatted_summary = result.get("summary", "")
|
|
|
|
|
|
- # 7. 更新主 Goal 为 completed
|
|
|
await _update_goal_complete(
|
|
|
- store, current_trace_id, current_goal_id,
|
|
|
- result.get("status", "completed"), formatted_summary, sub_trace_ids
|
|
|
+ store, trace_id, current_goal_id,
|
|
|
+ result.get("status", "completed"),
|
|
|
+ formatted_summary,
|
|
|
+ sub_trace_ids,
|
|
|
)
|
|
|
|
|
|
- # 8. 返回结果
|
|
|
return {
|
|
|
"mode": "evaluate",
|
|
|
"sub_trace_id": sub_trace_id,
|
|
|
"continue_from": bool(continue_from),
|
|
|
**result,
|
|
|
- "summary": formatted_summary
|
|
|
+ "summary": formatted_summary,
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
- # 错误处理
|
|
|
error_msg = str(e)
|
|
|
await broadcast_sub_trace_completed(
|
|
|
- current_trace_id, sub_trace_id,
|
|
|
- "failed", error_msg, {}
|
|
|
+ trace_id, sub_trace_id, "failed", error_msg, {},
|
|
|
)
|
|
|
-
|
|
|
await _update_collaborator(
|
|
|
- store, current_trace_id,
|
|
|
+ store, trace_id,
|
|
|
name=eval_name, sub_trace_id=sub_trace_id,
|
|
|
status="failed", summary=error_msg[:80],
|
|
|
)
|
|
|
-
|
|
|
await _update_goal_complete(
|
|
|
- store, current_trace_id, current_goal_id,
|
|
|
- "failed", f"评估任务失败: {error_msg}", sub_trace_ids
|
|
|
+ store, trace_id, current_goal_id,
|
|
|
+ "failed", f"评估任务失败: {error_msg}",
|
|
|
+ sub_trace_ids,
|
|
|
)
|
|
|
-
|
|
|
return {
|
|
|
"mode": "evaluate",
|
|
|
"status": "failed",
|
|
|
"error": error_msg,
|
|
|
- "sub_trace_id": sub_trace_id
|
|
|
+ "sub_trace_id": sub_trace_id,
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-@tool(description="创建 Sub-Agent 执行任务(evaluate/delegate/explore)")
|
|
|
-async def subagent(
|
|
|
- mode: str,
|
|
|
- task: Optional[str] = None,
|
|
|
- target_goal_id: Optional[str] = None,
|
|
|
- evaluation_input: Optional[Dict[str, Any]] = None,
|
|
|
- requirements: Optional[str] = None,
|
|
|
- branches: Optional[List[str]] = None,
|
|
|
- background: Optional[str] = None,
|
|
|
- continue_from: Optional[str] = None,
|
|
|
- context: Optional[dict] = None,
|
|
|
-) -> Dict[str, Any]:
|
|
|
- # 1. 验证 context
|
|
|
- if not context:
|
|
|
- return {"status": "failed", "error": "context is required"}
|
|
|
-
|
|
|
- store = context.get("store")
|
|
|
- current_trace_id = context.get("trace_id")
|
|
|
- current_goal_id = context.get("goal_id")
|
|
|
- runner = context.get("runner")
|
|
|
-
|
|
|
- missing = []
|
|
|
- if not store:
|
|
|
- missing.append("store")
|
|
|
- if not current_trace_id:
|
|
|
- missing.append("trace_id")
|
|
|
- if not runner:
|
|
|
- missing.append("runner")
|
|
|
- if missing:
|
|
|
- return {"status": "failed", "error": f"Missing required context: {', '.join(missing)}"}
|
|
|
-
|
|
|
- # 2. 验证 mode
|
|
|
- if mode not in {"evaluate", "delegate", "explore"}:
|
|
|
- return {"status": "failed", "error": "Invalid mode: must be evaluate/delegate/explore"}
|
|
|
-
|
|
|
- # 3. 验证模式特定参数
|
|
|
- if mode == "delegate" and not task:
|
|
|
- return {"status": "failed", "error": "delegate mode requires task"}
|
|
|
- if mode == "explore" and not branches:
|
|
|
- return {"status": "failed", "error": "explore mode requires branches"}
|
|
|
- if mode == "evaluate" and (not target_goal_id or evaluation_input is None):
|
|
|
- return {"status": "failed", "error": "evaluate mode requires target_goal_id and evaluation_input"}
|
|
|
-
|
|
|
- # 4. 路由到模式处理函数
|
|
|
- if mode == "explore":
|
|
|
- return await _handle_explore_mode(
|
|
|
- branches, background, continue_from,
|
|
|
- store, current_trace_id, current_goal_id, runner
|
|
|
- )
|
|
|
- elif mode == "delegate":
|
|
|
- return await _handle_delegate_mode(
|
|
|
- task, continue_from,
|
|
|
- store, current_trace_id, current_goal_id, runner, context
|
|
|
- )
|
|
|
- else: # evaluate
|
|
|
- return await _handle_evaluate_mode(
|
|
|
- target_goal_id, evaluation_input, requirements, continue_from,
|
|
|
- store, current_trace_id, current_goal_id, runner, context
|
|
|
- )
|