feat(multi_agent): 提取prompt、恢复任务、优化侧边栏与sleep工具
- 将多智能体/子智能体prompt从代码提取到prompts/multi_agent/和prompts/sub_agent/ - 多智能体模式添加可用的子智能体动态prompt并冻结 - 重启后自动从conversation.json恢复多智能体idle任务 - 修复新对话侧边栏显示其他对话子智能体的问题 - 简化子智能体弹窗输出样式 - sleep工具在多智能体模式下移除wait_sub_agent_ids参数
This commit is contained in:
parent
de5160ead0
commit
fe6fba3958
@ -48,6 +48,7 @@ from modules.terminal_manager import TerminalManager
|
||||
from modules.todo_manager import TodoManager
|
||||
from modules.sub_agent import SubAgentManager
|
||||
from modules.webpage_extractor import extract_webpage_content, tavily_extract
|
||||
from modules.multi_agent.prompts import build_available_agents_prompt
|
||||
from modules.ocr_client import OCRClient
|
||||
from modules.easter_egg_manager import EasterEggManager
|
||||
from modules.personalization_manager import (
|
||||
@ -288,6 +289,17 @@ class MessagesMixin:
|
||||
messages.append({"role": "system", "content": multi_agent_prompt})
|
||||
except Exception as exc:
|
||||
logger.warning(f"[messages] 注入多智能体 prompt 失败: {exc}")
|
||||
|
||||
# 可用的子智能体角色(动态 prompt,第一个用户消息后冻结)
|
||||
try:
|
||||
available_agents_prompt = self._get_or_init_frozen_prompt(
|
||||
"frozen_available_agents_prompt",
|
||||
lambda: build_available_agents_prompt() or "",
|
||||
)
|
||||
if available_agents_prompt:
|
||||
messages.append({"role": "system", "content": available_agents_prompt})
|
||||
except Exception as exc:
|
||||
logger.warning(f"[messages] 注入可用子智能体 prompt 失败: {exc}")
|
||||
if disabled_notice:
|
||||
messages.append({
|
||||
"role": "system",
|
||||
|
||||
@ -90,38 +90,74 @@ DISABLE_LENGTH_CHECK = True
|
||||
|
||||
|
||||
class ToolsDefinitionCoreToolsMixin:
|
||||
|
||||
def _build_sleep_tool_definition(self) -> Dict:
|
||||
"""根据运行模式构建 sleep 工具定义。
|
||||
|
||||
多智能体模式下子智能体不会调用 finish_task 结束,因此不提供
|
||||
wait_sub_agent_ids 参数;常规模式保留完整功能。
|
||||
"""
|
||||
is_multi_agent = getattr(self, "multi_agent_mode", False)
|
||||
if is_multi_agent:
|
||||
return {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "sleep",
|
||||
"description": "等待工具。两种模式二选一:1) seconds:短暂延迟;2) wait_runcommand_id:等待指定后台 run_command 结束并直接返回结果。若同时提供多个等待参数会报错。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
"seconds": {
|
||||
"type": "number",
|
||||
"description": "等待的秒数,可以是小数(如0.2秒)。建议范围:0.1-10秒"
|
||||
},
|
||||
"wait_runcommand_id": {
|
||||
"type": "string",
|
||||
"description": "等待指定后台 run_command 的 command_id 结束后返回。"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "等待的原因说明(可选)"
|
||||
}
|
||||
}),
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "sleep",
|
||||
"description": "等待工具。三种模式三选一:1) seconds:短暂延迟;2) wait_sub_agent_ids:等待指定子智能体全部结束并直接返回结果;3) wait_runcommand_id:等待指定后台 run_command 结束并直接返回结果。若同时提供多个等待参数会报错。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
"seconds": {
|
||||
"type": "number",
|
||||
"description": "等待的秒数,可以是小数(如0.2秒)。建议范围:0.1-10秒"
|
||||
},
|
||||
"wait_sub_agent_ids": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"description": "等待这些子智能体全部结束后返回(可提供一个或多个编号)。"
|
||||
},
|
||||
"wait_runcommand_id": {
|
||||
"type": "string",
|
||||
"description": "等待指定后台 run_command 的 command_id 结束后返回。"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "等待的原因说明(可选)"
|
||||
}
|
||||
}),
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def _build_core_tools(self) -> List[Dict]:
|
||||
return [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "sleep",
|
||||
"description": "等待工具。三种模式三选一:1) seconds:短暂延迟;2) wait_sub_agent_ids:等待指定子智能体全部结束并直接返回结果;3) wait_runcommand_id:等待指定后台 run_command 结束并直接返回结果。若同时提供多个等待参数会报错。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
"seconds": {
|
||||
"type": "number",
|
||||
"description": "等待的秒数,可以是小数(如0.2秒)。建议范围:0.1-10秒"
|
||||
},
|
||||
"wait_sub_agent_ids": {
|
||||
"type": "array",
|
||||
"items": {"type": "integer"},
|
||||
"description": "等待这些子智能体全部结束后返回(可提供一个或多个编号)。"
|
||||
},
|
||||
"wait_runcommand_id": {
|
||||
"type": "string",
|
||||
"description": "等待指定后台 run_command 的 command_id 结束后返回。"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "等待的原因说明(可选)"
|
||||
}
|
||||
}),
|
||||
"required": []
|
||||
}
|
||||
}
|
||||
},
|
||||
self._build_sleep_tool_definition(),
|
||||
|
||||
{
|
||||
"type": "function",
|
||||
|
||||
@ -1099,7 +1099,12 @@ class MainTerminalToolsExecutionMixin:
|
||||
"error": "sleep 的等待参数互斥:seconds / wait_sub_agent_ids / wait_runcommand_id 只能提供一个"
|
||||
}
|
||||
elif wait_sub_agent_ids:
|
||||
if not isinstance(wait_sub_agent_ids, list) or not wait_sub_agent_ids:
|
||||
if getattr(self, "multi_agent_mode", False):
|
||||
result = {
|
||||
"success": False,
|
||||
"error": "多智能体模式下 sleep 工具不支持 wait_sub_agent_ids。请通过 list_active_sub_agents / get_sub_agent_status 查看状态,或直接向子智能体发送消息。"
|
||||
}
|
||||
elif not isinstance(wait_sub_agent_ids, list) or not wait_sub_agent_ids:
|
||||
result = {"success": False, "error": "wait_sub_agent_ids 必须是非空数组"}
|
||||
else:
|
||||
normalized_ids = []
|
||||
@ -1129,27 +1134,6 @@ class MainTerminalToolsExecutionMixin:
|
||||
task_ids.append(task.get("task_id"))
|
||||
if missing:
|
||||
result = {"success": False, "error": f"未找到对应子智能体: {missing}"}
|
||||
elif getattr(self, "multi_agent_mode", False):
|
||||
# 多智能体模式:子智能体通过自然输出结束,不调用 finish_task。
|
||||
# sleep 不再阻塞等待,而是直接返回各实例最近一次输出内容。
|
||||
conv_id = self.context_manager.current_conversation_id
|
||||
ma_state = manager.get_multi_agent_state(conv_id)
|
||||
outputs = []
|
||||
for aid in normalized_ids:
|
||||
inst = ma_state.get_instance(aid) if ma_state else None
|
||||
outputs.append({
|
||||
"agent_id": aid,
|
||||
"display_name": inst.display_name if inst else f"Agent_{aid}",
|
||||
"status": inst.status if inst else "unknown",
|
||||
"last_output": inst.last_output if inst else "",
|
||||
})
|
||||
result = {
|
||||
"success": True,
|
||||
"mode": "wait_sub_agent_ids",
|
||||
"agent_ids": normalized_ids,
|
||||
"outputs": outputs,
|
||||
"message": f"已获取 {len(normalized_ids)} 个子智能体最近一次输出"
|
||||
}
|
||||
else:
|
||||
wait_results = []
|
||||
waited_task_ids = []
|
||||
|
||||
@ -1,134 +1,55 @@
|
||||
"""多智能体模式的系统提示词。"""
|
||||
"""多智能体模式的系统提示词。
|
||||
|
||||
所有 prompt 正文均从 prompts/multi_agent/ 下的文本文件加载,避免在代码中硬编码。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
try:
|
||||
from config import PROMPTS_DIR
|
||||
except ImportError:
|
||||
import sys
|
||||
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
if str(project_root) not in sys.path:
|
||||
sys.path.insert(0, str(project_root))
|
||||
from config import PROMPTS_DIR
|
||||
|
||||
|
||||
MULTI_AGENT_MASTER_PROMPT_BODY = """# 多智能体模式
|
||||
|
||||
你是 **Team Leader**(团队领导者),负责协调多个子智能体分工协作完成用户的复杂任务。
|
||||
|
||||
## 工作原则
|
||||
|
||||
- **主动分工**:除非任务极其简单或明确不需要子智能体,否则主动把任务拆解并指派给合适的角色。
|
||||
- **明确指令**:用 `send_message_to_sub_agent` 发任务时,写清楚任务目标、范围、产出要求。
|
||||
- **及时回答**:当子智能体通过 `ask_master` 提问时,必须尽快通过 `answer_sub_agent_question` 回答。
|
||||
- **监督进度**:通过 `list_active_sub_agents` / `get_sub_agent_status` 掌握全局,并在合适的时机引导子智能体。
|
||||
- **运行时引导**:看到子智能体作出的步骤需要纠正时,立刻用 `send_message_to_sub_agent` 在其运行期间插入消息干预。
|
||||
- **明确问答**:当你需要一个具体的、可被回答的小问题被某个子智能体处理时,用 `ask_sub_agent` 阻塞等待一轮回答。
|
||||
|
||||
## 工具清单(多智能体模式专属)
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `create_sub_agent` | 创建一个子智能体实例,指定 role_id |
|
||||
| `terminate_sub_agent` | 强制终止子智能体 |
|
||||
| `send_message_to_sub_agent` | 向子智能体插入引导消息/任务,不等待回复 |
|
||||
| `ask_sub_agent` | 向子智能体提出明确问题,阻塞等待一轮回答 |
|
||||
| `answer_sub_agent_question` | 回答子智能体通过 `ask_master` 提出的问题 |
|
||||
| `create_custom_agent` | 创建/保存自定义角色到后端 |
|
||||
| `list_agents` | 列出可用角色 |
|
||||
| `list_active_sub_agents` | 列出当前会话中活跃的子智能体 |
|
||||
| `get_sub_agent_status` | 查询指定子智能体的详细状态 |
|
||||
|
||||
**注意**:你现在仍拥有原本的全部工具(文件读写、终端、搜索、MCP、skill、memory 等)。以上只列出多智能体模式新增的工具——它们**替换**了原有的 `create_sub_agent` / `close_sub_agent` / `get_sub_agent_status`,使用语义有变化。
|
||||
|
||||
## 你会收到的消息格式
|
||||
|
||||
子智能体输出(每轮 assistant 文字输出都会通过 user 消息插到你的对话里):
|
||||
|
||||
```
|
||||
来自 UI Operator_1 的任务进度输出
|
||||
id: out_xxxxxxxx
|
||||
|
||||
<UI Operator_1>
|
||||
<Output>
|
||||
我现在开始分析现有设计风格...
|
||||
</Output>
|
||||
</UI Operator_1>
|
||||
```
|
||||
|
||||
子智能体向你提问:
|
||||
|
||||
```
|
||||
来自 Full-Stack Engineer_1 的提问
|
||||
id: ask_fse_001
|
||||
|
||||
<Full-Stack Engineer_1>
|
||||
<Ask>
|
||||
我应该使用 JWT 还是 Session Cookie?
|
||||
</Ask>
|
||||
</Full-Stack Engineer_1>
|
||||
```
|
||||
|
||||
**回答提问**必须用 `answer_sub_agent_question` 工具,传入 `question_id`(即消息里的 id)和 answer 文本。回答不会以 user 消息插入,而是直接返回到子智能体的 `ask_master` 工具结果中。
|
||||
|
||||
## 关于显示名
|
||||
|
||||
- 主智能体固定显示名:`Team Leader`
|
||||
- 子智能体显示名:`{角色名}_{agent_id}`,如 `UI Operator_1`、`Full-Stack Engineer_2`
|
||||
- 一个角色可以有多个实例(同 role_id 多 agent_id)
|
||||
|
||||
## 关于通信协议的三条硬性原则
|
||||
|
||||
1. **接收方决定插入方式**:子智能体收到消息后,由它自己的状态决定是 inline 穿插还是开启新轮任务。你不要操心插入位置,只负责发起。
|
||||
2. **回答走工具结果而非 user 消息**:你回答子智能体提问用的是 `answer_sub_agent_question`,回答内容是工具结果,不需要写出 XML 包裹。
|
||||
3. **任务发布/消息/引导都走自然 XML 格式**:调用 `send_message_to_sub_agent` 时只写正文,后端会自动包成 `来自 Team Leader 的消息 / 任务发布` 格式插入子对话。
|
||||
|
||||
## 关于团队全局可见
|
||||
|
||||
子智能体之间通过 `ask_other_agent` / `answer_other_agent` 直接通信,会并行在自己的对话内进行。你只需要在 prompt 里要求子智能体「如要向其他子智能体提问,必须同时直接给你输出一条汇报」,这样你能掌握全局。但你**不需要**手动转发它们之间的问答。
|
||||
"""
|
||||
_MULTI_AGENT_PROMPTS_DIR = Path(PROMPTS_DIR) / "multi_agent"
|
||||
_TEMPLATE_CACHE: dict[str, str] = {}
|
||||
|
||||
|
||||
MULTI_AGENT_SUB_AGENT_PROMPT_BODY = """# 多智能体身份
|
||||
def _load_template(name: str) -> str:
|
||||
"""从 prompts/multi_agent/<name>.txt 加载模板,带缓存。"""
|
||||
cached = _TEMPLATE_CACHE.get(name)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
你是智能体集群团队的一员。你的团队通过分工协作完成复杂任务,主智能体 **Team Leader** 负责督导全局。
|
||||
template_path = _MULTI_AGENT_PROMPTS_DIR / f"{name}.txt"
|
||||
if not template_path.exists():
|
||||
raise FileNotFoundError(f"多智能体 prompt 模板缺失: {template_path}")
|
||||
|
||||
# 在任务中
|
||||
content = template_path.read_text(encoding="utf-8")
|
||||
_TEMPLATE_CACHE[name] = content
|
||||
return content
|
||||
|
||||
- 不要频繁输出内容,不重要的内容会污染主智能体上下文
|
||||
- 只汇报关键步骤
|
||||
- 任务完成后给出详细结论
|
||||
- 自然结束输出即本轮任务结束;上下文会被保留,Team Leader 可能会再次发消息让你继续
|
||||
|
||||
# 沟通工具
|
||||
def _format_template(name: str, **kwargs) -> str:
|
||||
"""加载模板并用 str.format 填充占位符。"""
|
||||
template = _load_template(name)
|
||||
return template.format(**kwargs)
|
||||
|
||||
- **需要 Team Leader 决策时**:调用 `ask_master` 工具,传入 question 文本
|
||||
- 工具会阻塞等待 Team Leader 通过 `answer_sub_agent_question` 给出回答
|
||||
- 你的 question 会以 XML 「提问」格式被插入主对话
|
||||
- **要问其他子智能体时**:调用 `ask_other_agent`,传入 target_agent_id 与 question
|
||||
- 等待对方调用 `answer_other_agent` 回答
|
||||
- **要回答其他子智能体的提问时**:调用 `answer_other_agent`,传入 source_agent_id 与 question_id 和 answer
|
||||
- 你的回答直接作为对方 `ask_other_agent` 工具的结果返回(不会以 user 消息插入对话)
|
||||
- **查询当前活跃子智能体**:调用 `list_active_sub_agents`
|
||||
|
||||
# 关于向你团队「汇报」的强制要求
|
||||
|
||||
**如果你要向其他子智能体提问,必须同时直接输出一条汇报给 Team Leader**(在你的普通文本输出里),说明:
|
||||
1. 你为什么要问这个问题
|
||||
2. 你问了谁
|
||||
3. 你期望得到什么
|
||||
|
||||
不能偷偷沟通,Team Leader 需要看到完整协作流程。
|
||||
|
||||
# 输出格式
|
||||
|
||||
你每轮的普通 assistant 文字输出都会被自动捕获并以如下格式插入到主对话:
|
||||
|
||||
```
|
||||
来自 {你的显示名} 的任务进度输出
|
||||
id: out_xxxxxxxx
|
||||
|
||||
<{你的显示名}>
|
||||
<Output>
|
||||
{你的输出}
|
||||
</Output>
|
||||
</{你的显示名}>
|
||||
```
|
||||
|
||||
你不需要自己包裹 XML,直接输出正文即可。
|
||||
"""
|
||||
# 为兼容外部仍可通过常量访问正文,但值来自文件;首次访问时加载。
|
||||
def __getattr__(name: str) -> str:
|
||||
if name == "MULTI_AGENT_MASTER_PROMPT_BODY":
|
||||
return _load_template("master")
|
||||
if name == "MULTI_AGENT_SUB_AGENT_PROMPT_BODY":
|
||||
return _load_template("sub_agent")
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
|
||||
def build_multi_agent_master_prompt(workspace_path: str, base: str = "") -> str:
|
||||
@ -137,9 +58,10 @@ def build_multi_agent_master_prompt(workspace_path: str, base: str = "") -> str:
|
||||
`base` 一般为现有 MainTerminal 的 base 提示词(环境/工具概览等),
|
||||
我们在末尾追加多智能体模式专属正文。
|
||||
"""
|
||||
body = _load_template("master")
|
||||
if base and base.strip():
|
||||
return f"{base.rstrip()}\n\n{MULTI_AGENT_MASTER_PROMPT_BODY}\n"
|
||||
return f"{MULTI_AGENT_MASTER_PROMPT_BODY}\n"
|
||||
return f"{base.rstrip()}\n\n{body}\n"
|
||||
return f"{body}\n"
|
||||
|
||||
|
||||
def build_multi_agent_sub_agent_prompt(role_body: str, display_name: str, workspace_path: str) -> str:
|
||||
@ -148,9 +70,36 @@ def build_multi_agent_sub_agent_prompt(role_body: str, display_name: str, worksp
|
||||
`role_body` 为该角色 Markdown 文件 frontmatter 之后的自定义 prompt。
|
||||
`display_name` 为该实例的显示名(如 `UI Operator_1`)。
|
||||
"""
|
||||
header = MULTI_AGENT_SUB_AGENT_PROMPT_BODY.rstrip()
|
||||
return (
|
||||
f"{header}\n\n"
|
||||
f"# 你的显示名\n\n你的显示名是 `{display_name}`。\n\n"
|
||||
f"# 你的专属设定\n\n{role_body.strip()}\n"
|
||||
)
|
||||
return _format_template("sub_agent", display_name=display_name, role_body=role_body.strip())
|
||||
|
||||
|
||||
def build_available_agents_prompt() -> str:
|
||||
"""构造「可用的子智能体角色」动态 prompt。
|
||||
|
||||
列出当前可创建的所有角色名称与说明,供 Team Leader 参考。
|
||||
若没有任何可用角色,返回空字符串。
|
||||
"""
|
||||
try:
|
||||
from modules.multi_agent.role_store import list_roles
|
||||
|
||||
roles = list_roles()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
if not roles:
|
||||
return ""
|
||||
|
||||
lines: List[str] = []
|
||||
for role in roles:
|
||||
name = getattr(role, "name", "") or ""
|
||||
description = getattr(role, "description", "") or ""
|
||||
role_id = getattr(role, "role_id", "") or ""
|
||||
if description:
|
||||
lines.append(f"- {name}(role_id: {role_id}):{description}")
|
||||
else:
|
||||
lines.append(f"- {name}(role_id: {role_id})")
|
||||
|
||||
if not lines:
|
||||
return ""
|
||||
|
||||
return _format_template("available_agents", agents_list="\n".join(lines))
|
||||
|
||||
@ -78,6 +78,11 @@ class SubAgentManager(SubAgentStateMixin, SubAgentStatsMixin, SubAgentCreationMi
|
||||
self.reconcile_task_states()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.restore_running_tasks()
|
||||
except Exception:
|
||||
logger.exception("[SubAgentManager] 恢复运行中子智能体任务失败")
|
||||
pass
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 生命周期与事件循环
|
||||
@ -230,6 +235,9 @@ class SubAgentManager(SubAgentStateMixin, SubAgentStatsMixin, SubAgentCreationMi
|
||||
"stats_file": str(stats_file),
|
||||
"progress_file": str(progress_file),
|
||||
"conversation_file": str(conversation_file),
|
||||
"model_key": model_key,
|
||||
"role_id": role_id,
|
||||
"display_name": display_name,
|
||||
"execution_mode": "in_process",
|
||||
"container_name": None,
|
||||
}
|
||||
@ -567,6 +575,157 @@ class SubAgentManager(SubAgentStateMixin, SubAgentStatsMixin, SubAgentCreationMi
|
||||
logger.exception(f"[SubAgent] 工具执行异常: {tool_name}")
|
||||
return {"success": False, "error": f"工具执行异常: {exc}"}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 重启后恢复运行中任务
|
||||
# ------------------------------------------------------------------
|
||||
def restore_running_tasks(self) -> int:
|
||||
"""程序重启后,从 conversation.json 恢复非终态子智能体任务并重新运行。
|
||||
|
||||
返回成功恢复的任务数。
|
||||
"""
|
||||
from modules.sub_agent.task import SubAgentTask
|
||||
|
||||
restored = 0
|
||||
terminal_statuses = TERMINAL_STATUSES.union({"terminated"})
|
||||
for task_id, task in list(self.tasks.items()):
|
||||
if not isinstance(task, dict):
|
||||
continue
|
||||
# 仅恢复多智能体模式任务;传统子智能体保持原有清理逻辑
|
||||
if not task.get("multi_agent_mode"):
|
||||
continue
|
||||
status = task.get("status", "running")
|
||||
if status in terminal_statuses:
|
||||
continue
|
||||
# 已在内存中运行,无需恢复
|
||||
if task_id in self._running_tasks:
|
||||
continue
|
||||
|
||||
task_root = Path(task.get("task_root", ""))
|
||||
conversation_file = Path(task.get("conversation_file", ""))
|
||||
system_prompt_file = task_root / "system_prompt.txt"
|
||||
task_message_file = task_root / "task.txt"
|
||||
|
||||
if not conversation_file.exists():
|
||||
logger.warning(f"[restore] 任务 {task_id} 的对话文件缺失,无法恢复")
|
||||
continue
|
||||
|
||||
try:
|
||||
conversation_data = json.loads(conversation_file.read_text(encoding="utf-8"))
|
||||
messages = list(conversation_data.get("messages") or [])
|
||||
except Exception as exc:
|
||||
logger.warning(f"[restore] 读取任务 {task_id} 对话文件失败: {exc}")
|
||||
continue
|
||||
|
||||
system_prompt = ""
|
||||
if system_prompt_file.exists():
|
||||
try:
|
||||
system_prompt = system_prompt_file.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
task_message = ""
|
||||
if task_message_file.exists():
|
||||
try:
|
||||
task_message = task_message_file.read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 如果对话历史为空,用 task_message 兜底
|
||||
if not messages:
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": task_message},
|
||||
]
|
||||
|
||||
agent_id = int(task.get("agent_id", 0))
|
||||
conversation_id = task.get("conversation_id")
|
||||
multi_agent_mode = bool(task.get("multi_agent_mode"))
|
||||
thinking_mode = task.get("thinking_mode") or "fast"
|
||||
model_key = task.get("model_key")
|
||||
display_name = task.get("display_name")
|
||||
role_id = task.get("role_id")
|
||||
|
||||
multi_agent_state = None
|
||||
if multi_agent_mode and conversation_id:
|
||||
multi_agent_state = self.get_or_create_multi_agent_state(conversation_id)
|
||||
# 如果 snapshot 里没有该实例,根据 task_record 重建一个
|
||||
if multi_agent_state and not multi_agent_state.get_instance(agent_id):
|
||||
from modules.multi_agent.state import AgentInstance
|
||||
inst = AgentInstance(
|
||||
agent_id=agent_id,
|
||||
role_id=role_id or "",
|
||||
display_name=display_name or f"Agent_{agent_id}",
|
||||
task_id=task_id,
|
||||
status=status if status in ("running", "idle") else "running",
|
||||
summary=task.get("summary", ""),
|
||||
)
|
||||
try:
|
||||
multi_agent_state.register_instance(inst)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
sub_agent = SubAgentTask(
|
||||
manager=self,
|
||||
task_record=task,
|
||||
task_message=task_message,
|
||||
system_prompt=system_prompt,
|
||||
model_key=model_key,
|
||||
thinking_mode=thinking_mode,
|
||||
multi_agent_mode=multi_agent_mode,
|
||||
multi_agent_state=multi_agent_state,
|
||||
display_name=display_name,
|
||||
)
|
||||
sub_agent.messages = messages
|
||||
# 重启后统一置为 idle,等待主智能体再次发消息才继续
|
||||
if multi_agent_mode:
|
||||
sub_agent._idle = True
|
||||
task["status"] = "idle"
|
||||
task["updated_at"] = time.time()
|
||||
if multi_agent_state:
|
||||
multi_agent_state.mark_status(agent_id, "idle")
|
||||
# 同步落盘 output.json,保证前端状态一致
|
||||
try:
|
||||
output_file = Path(task.get("output_file", ""))
|
||||
if output_file.exists():
|
||||
output_data = json.loads(output_file.read_text(encoding="utf-8"))
|
||||
else:
|
||||
output_data = {}
|
||||
output_data["status"] = "idle"
|
||||
output_data["success"] = None
|
||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_file.write_text(json.dumps(output_data, ensure_ascii=False), encoding="utf-8")
|
||||
except Exception as exc:
|
||||
logger.warning(f"[restore] 更新任务 {task_id} output 文件失败: {exc}")
|
||||
|
||||
task_coro = sub_agent.run()
|
||||
asyncio_task = self._run_coro(task_coro)
|
||||
sub_agent._task = asyncio_task
|
||||
self._running_tasks[task_id] = asyncio_task
|
||||
self._sub_agent_instances[agent_id] = sub_agent
|
||||
|
||||
def _on_done(fut, tid=task_id, aid=agent_id, state=multi_agent_state, sa=sub_agent):
|
||||
self._running_tasks.pop(tid, None)
|
||||
self._sub_agent_instances.pop(aid, None)
|
||||
self.reconcile_task_states(conversation_id=conversation_id)
|
||||
if multi_agent_mode and state:
|
||||
self._on_multi_agent_task_done(tid, aid, state, sa)
|
||||
|
||||
asyncio_task.add_done_callback(_on_done)
|
||||
restored += 1
|
||||
ma_debug(
|
||||
"manager_restore_sub_agent",
|
||||
task_id=task_id,
|
||||
agent_id=agent_id,
|
||||
display_name=display_name,
|
||||
multi_agent_mode=multi_agent_mode,
|
||||
status=status,
|
||||
message_count=len(messages),
|
||||
)
|
||||
|
||||
if restored:
|
||||
self._save_state()
|
||||
return restored
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 多智能体模式:状态管理、外部接口、消息注入
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@ -1,11 +1,49 @@
|
||||
"""子智能体提示词构建。"""
|
||||
"""子智能体提示词构建。
|
||||
|
||||
所有 prompt 正文均从 prompts/sub_agent/ 下的文本文件加载,避免在代码中硬编码。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
try:
|
||||
from config import PROMPTS_DIR
|
||||
except ImportError:
|
||||
import sys
|
||||
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
if str(project_root) not in sys.path:
|
||||
sys.path.insert(0, str(project_root))
|
||||
from config import PROMPTS_DIR
|
||||
|
||||
|
||||
_SUB_AGENT_PROMPTS_DIR = Path(PROMPTS_DIR) / "sub_agent"
|
||||
_TEMPLATE_CACHE: dict[str, str] = {}
|
||||
|
||||
|
||||
def _load_template(name: str) -> str:
|
||||
"""从 prompts/sub_agent/<name>.txt 加载模板,带缓存。"""
|
||||
cached = _TEMPLATE_CACHE.get(name)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
template_path = _SUB_AGENT_PROMPTS_DIR / f"{name}.txt"
|
||||
if not template_path.exists():
|
||||
raise FileNotFoundError(f"子智能体 prompt 模板缺失: {template_path}")
|
||||
|
||||
content = template_path.read_text(encoding="utf-8")
|
||||
_TEMPLATE_CACHE[name] = content
|
||||
return content
|
||||
|
||||
|
||||
def _format_template(name: str, **kwargs) -> str:
|
||||
"""加载模板并用 str.format 填充占位符。"""
|
||||
template = _load_template(name)
|
||||
return template.format(**kwargs)
|
||||
|
||||
|
||||
def build_user_message(
|
||||
agent_id: int,
|
||||
@ -15,100 +53,23 @@ def build_user_message(
|
||||
timeout_seconds: int,
|
||||
) -> str:
|
||||
"""构建发送给子智能体的用户消息。"""
|
||||
return f"""你是子智能体 #{agent_id},负责完成以下任务:
|
||||
|
||||
**任务摘要**:{summary}
|
||||
|
||||
**任务详情**:
|
||||
{task}
|
||||
|
||||
**交付目录**:{deliverables_path}
|
||||
请将所有生成的文件保存到此目录。
|
||||
|
||||
**超时时间**:{timeout_seconds} 秒
|
||||
|
||||
完成任务后,请调用 finish_task 工具提交完成报告。"""
|
||||
return _format_template(
|
||||
"user_message",
|
||||
agent_id=agent_id,
|
||||
summary=summary,
|
||||
task=task,
|
||||
deliverables_path=deliverables_path,
|
||||
timeout_seconds=timeout_seconds,
|
||||
)
|
||||
|
||||
|
||||
def build_system_prompt(workspace_path: str) -> str:
|
||||
"""构建子智能体的系统提示。"""
|
||||
system_info = f"{platform.system()} {platform.release()}"
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
return f"""你是一个专注的子智能体,负责独立完成分配的任务。
|
||||
|
||||
# 身份定位
|
||||
|
||||
你是主智能体创建的子智能体,拥有完整的工具能力(读写文件、执行命令、搜索网页等)。你的职责是专注完成分配的单一任务,不要偏离任务目标。
|
||||
|
||||
# 工作流程
|
||||
|
||||
1. **理解任务**:仔细阅读任务描述,明确目标和要求
|
||||
2. **制定计划**:规划完成任务的步骤
|
||||
3. **执行任务**:使用工具完成各个步骤
|
||||
4. **生成交付**:将所有结果文件放到指定的交付目录
|
||||
5. **提交报告**:使用 finish_task 工具提交完成报告并退出
|
||||
|
||||
# 工作原则
|
||||
|
||||
## 专注性
|
||||
- 只完成分配的任务,不要做额外的工作
|
||||
- 不要尝试与用户对话或询问问题
|
||||
- 遇到问题时,在能力范围内解决或在报告中说明
|
||||
|
||||
## 独立性
|
||||
- 你与主智能体共享工作区,可以访问所有文件
|
||||
- 你的工作范围应该与其他子智能体不重叠
|
||||
- 不要修改任务描述之外的文件
|
||||
|
||||
## 效率性
|
||||
- 直接开始工作,不要过度解释
|
||||
- 合理使用工具,避免重复操作
|
||||
- 注意超时限制,在时间内完成核心工作
|
||||
|
||||
## 完整性
|
||||
- 确保交付目录中的文件完整可用
|
||||
- 生成的文档要清晰、格式正确
|
||||
- 代码要包含必要的注释和说明
|
||||
|
||||
# 交付要求
|
||||
|
||||
所有结果文件必须放在指定的交付目录中,包括:
|
||||
- 主要成果文件(文档、代码、报告等)
|
||||
- 支持文件(数据、配置、示例等)
|
||||
- 不要在交付目录外创建文件
|
||||
|
||||
# 完成任务
|
||||
|
||||
任务完成后,必须调用 finish_task 工具:
|
||||
- success: 是否成功完成
|
||||
- summary: 完成摘要(说明做了什么、生成了什么)
|
||||
|
||||
调用 finish_task 后,你会立即退出,无法继续工作。
|
||||
|
||||
# 工具使用
|
||||
|
||||
你拥有以下工具能力:
|
||||
- read_file: 读取文件内容
|
||||
- write_file / edit_file: 创建或修改文件
|
||||
- search_workspace: 搜索文件和代码
|
||||
- run_command: 执行终端命令
|
||||
- web_search / extract_webpage: 搜索和提取网页内容
|
||||
- read_mediafile: 读取图片/视频文件
|
||||
- finish_task: 完成任务并退出(必须调用)
|
||||
|
||||
# 注意事项
|
||||
|
||||
1. **结果传达**:你在运行期间产生的记录与输出不会被直接传递给主智能体。务必把所有需要传达的信息写进 `finish_task` 工具的 `summary` 字段,以及交付目录中的落盘文件里。
|
||||
2. **不要无限循环**:如果任务无法完成,说明原因并提交报告
|
||||
3. **不要超出范围**:只操作任务描述中指定的文件/目录
|
||||
4. **不要等待输入**:你是自主运行的,不会收到用户的进一步指令
|
||||
5. **注意时间限制**:超时会被强制终止,优先完成核心工作
|
||||
|
||||
# 当前环境
|
||||
|
||||
- 工作区路径: {workspace_path}
|
||||
- 系统: {system_info}
|
||||
- 当前时间: {current_time}
|
||||
|
||||
现在开始执行任务。"""
|
||||
return _format_template(
|
||||
"system",
|
||||
workspace_path=workspace_path,
|
||||
system_info=system_info,
|
||||
current_time=current_time,
|
||||
)
|
||||
|
||||
@ -51,6 +51,23 @@ class SubAgentStateMixin:
|
||||
self.tasks = merged_tasks
|
||||
self.conversation_agents = loaded_agents
|
||||
|
||||
# 恢复多智能体运行态(如果状态文件包含)
|
||||
try:
|
||||
from modules.multi_agent.state import MultiAgentState
|
||||
|
||||
manager = self
|
||||
multi_agent_states = getattr(manager, "multi_agent_states", None)
|
||||
if multi_agent_states is not None and isinstance(multi_agent_states, dict):
|
||||
loaded_ma_states = data.get("multi_agent_states", {})
|
||||
for conv_id, snapshot in loaded_ma_states.items():
|
||||
try:
|
||||
if isinstance(snapshot, dict):
|
||||
multi_agent_states[conv_id] = MultiAgentState.from_snapshot(snapshot)
|
||||
except Exception as exc:
|
||||
logger.warning(f"恢复多智能体状态失败 {conv_id}: {exc}")
|
||||
except Exception as exc:
|
||||
logger.warning(f"加载多智能体状态失败: {exc}")
|
||||
|
||||
if self.tasks:
|
||||
migrated = False
|
||||
for task in self.tasks.values():
|
||||
@ -72,6 +89,18 @@ class SubAgentStateMixin:
|
||||
"tasks": self.tasks,
|
||||
"conversation_agents": self.conversation_agents,
|
||||
}
|
||||
# 多智能体运行态持久化
|
||||
try:
|
||||
manager = self
|
||||
multi_agent_states = getattr(manager, "multi_agent_states", None)
|
||||
if multi_agent_states:
|
||||
payload["multi_agent_states"] = {
|
||||
conv_id: state.to_snapshot()
|
||||
for conv_id, state in multi_agent_states.items()
|
||||
if isinstance(state, object) and hasattr(state, "to_snapshot")
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.warning(f"保存多智能体状态失败: {exc}")
|
||||
try:
|
||||
self.state_file.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
except Exception as exc:
|
||||
|
||||
7
prompts/multi_agent/available_agents.txt
Normal file
7
prompts/multi_agent/available_agents.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# 可用的子智能体角色
|
||||
|
||||
当前你可以创建以下子智能体角色来协作完成任务:
|
||||
|
||||
{agents_list}
|
||||
|
||||
创建子智能体时请使用 `create_sub_agent` 工具,并传入合适的 `role_id`。
|
||||
74
prompts/multi_agent/master.txt
Normal file
74
prompts/multi_agent/master.txt
Normal file
@ -0,0 +1,74 @@
|
||||
# 多智能体模式
|
||||
|
||||
你是 **Team Leader**(团队领导者),负责协调多个子智能体分工协作完成用户的复杂任务。
|
||||
|
||||
## 工作原则
|
||||
|
||||
- **主动分工**:除非任务极其简单或明确不需要子智能体,否则主动把任务拆解并指派给合适的角色。
|
||||
- **明确指令**:用 `send_message_to_sub_agent` 发任务时,写清楚任务目标、范围、产出要求。
|
||||
- **及时回答**:当子智能体通过 `ask_master` 提问时,必须尽快通过 `answer_sub_agent_question` 回答。
|
||||
- **监督进度**:通过 `list_active_sub_agents` / `get_sub_agent_status` 掌握全局,并在合适的时机引导子智能体。
|
||||
- **运行时引导**:看到子智能体作出的步骤需要纠正时,立刻用 `send_message_to_sub_agent` 在其运行期间插入消息干预。
|
||||
- **明确问答**:当你需要一个具体的、可被回答的小问题被某个子智能体处理时,用 `ask_sub_agent` 阻塞等待一轮回答。
|
||||
|
||||
## 工具清单(多智能体模式专属)
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| `create_sub_agent` | 创建一个子智能体实例,指定 role_id |
|
||||
| `terminate_sub_agent` | 强制终止子智能体 |
|
||||
| `send_message_to_sub_agent` | 向子智能体插入引导消息/任务,不等待回复 |
|
||||
| `ask_sub_agent` | 向子智能体提出明确问题,阻塞等待一轮回答 |
|
||||
| `answer_sub_agent_question` | 回答子智能体通过 `ask_master` 提出的问题 |
|
||||
| `create_custom_agent` | 创建/保存自定义角色到后端 |
|
||||
| `list_agents` | 列出可用角色 |
|
||||
| `list_active_sub_agents` | 列出当前会话中活跃的子智能体 |
|
||||
| `get_sub_agent_status` | 查询指定子智能体的详细状态 |
|
||||
|
||||
**注意**:你现在仍拥有原本的全部工具(文件读写、终端、搜索、MCP、skill、memory 等)。以上只列出多智能体模式新增的工具——它们**替换**了原有的 `create_sub_agent` / `close_sub_agent` / `get_sub_agent_status`,使用语义有变化。
|
||||
|
||||
## 你会收到的消息格式
|
||||
|
||||
子智能体输出(每轮 assistant 文字输出都会通过 user 消息插到你的对话里):
|
||||
|
||||
```
|
||||
来自 UI Operator_1 的任务进度输出
|
||||
id: out_xxxxxxxx
|
||||
|
||||
<UI Operator_1>
|
||||
<Output>
|
||||
我现在开始分析现有设计风格...
|
||||
</Output>
|
||||
</UI Operator_1>
|
||||
```
|
||||
|
||||
子智能体向你提问:
|
||||
|
||||
```
|
||||
来自 Full-Stack Engineer_1 的提问
|
||||
id: ask_fse_001
|
||||
|
||||
<Full-Stack Engineer_1>
|
||||
<Ask>
|
||||
我应该使用 JWT 还是 Session Cookie?
|
||||
</Ask>
|
||||
</Full-Stack Engineer_1>
|
||||
```
|
||||
|
||||
**回答提问**必须用 `answer_sub_agent_question` 工具,传入 `question_id`(即消息里的 id)和 answer 文本。回答不会以 user 消息插入,而是直接返回到子智能体的 `ask_master` 工具结果中。
|
||||
|
||||
## 关于显示名
|
||||
|
||||
- 主智能体固定显示名:`Team Leader`
|
||||
- 子智能体显示名:`{角色名}_{agent_id}`,如 `UI Operator_1`、`Full-Stack Engineer_2`
|
||||
- 一个角色可以有多个实例(同 role_id 多 agent_id)
|
||||
|
||||
## 关于通信协议的三条硬性原则
|
||||
|
||||
1. **接收方决定插入方式**:子智能体收到消息后,由它自己的状态决定是 inline 穿插还是开启新轮任务。你不要操心插入位置,只负责发起。
|
||||
2. **回答走工具结果而非 user 消息**:你回答子智能体提问用的是 `answer_sub_agent_question`,回答内容是工具结果,不需要写出 XML 包裹。
|
||||
3. **任务发布/消息/引导都走自然 XML 格式**:调用 `send_message_to_sub_agent` 时只写正文,后端会自动包成 `来自 Team Leader 的消息 / 任务发布` 格式插入子对话。
|
||||
|
||||
## 关于团队全局可见
|
||||
|
||||
子智能体之间通过 `ask_other_agent` / `answer_other_agent` 直接通信,会并行在自己的对话内进行。你只需要在 prompt 里要求子智能体「如要向其他子智能体提问,必须同时直接给你输出一条汇报」,这样你能掌握全局。但你**不需要**手动转发它们之间的问答。
|
||||
55
prompts/multi_agent/sub_agent.txt
Normal file
55
prompts/multi_agent/sub_agent.txt
Normal file
@ -0,0 +1,55 @@
|
||||
# 多智能体身份
|
||||
|
||||
你是智能体集群团队的一员。你的团队通过分工协作完成复杂任务,主智能体 **Team Leader** 负责督导全局。
|
||||
|
||||
# 在任务中
|
||||
|
||||
- 不要频繁输出内容,不重要的内容会污染主智能体上下文
|
||||
- 只汇报关键步骤
|
||||
- 任务完成后给出详细结论
|
||||
- 自然结束输出即本轮任务结束;上下文会被保留,Team Leader 可能会再次发消息让你继续
|
||||
|
||||
# 沟通工具
|
||||
|
||||
- **需要 Team Leader 决策时**:调用 `ask_master` 工具,传入 question 文本
|
||||
- 工具会阻塞等待 Team Leader 通过 `answer_sub_agent_question` 给出回答
|
||||
- 你的 question 会以 XML 「提问」格式被插入主对话
|
||||
- **要问其他子智能体时**:调用 `ask_other_agent`,传入 target_agent_id 与 question
|
||||
- 等待对方调用 `answer_other_agent` 回答
|
||||
- **要回答其他子智能体的提问时**:调用 `answer_other_agent`,传入 source_agent_id 与 question_id 和 answer
|
||||
- 你的回答直接作为对方 `ask_other_agent` 工具的结果返回(不会以 user 消息插入对话)
|
||||
- **查询当前活跃子智能体**:调用 `list_active_sub_agents`
|
||||
|
||||
# 关于向你团队「汇报」的强制要求
|
||||
|
||||
**如果你要向其他子智能体提问,必须同时直接输出一条汇报给 Team Leader**(在你的普通文本输出里),说明:
|
||||
1. 你为什么要问这个问题
|
||||
2. 你问了谁
|
||||
3. 你期望得到什么
|
||||
|
||||
不能偷偷沟通,Team Leader 需要看到完整协作流程。
|
||||
|
||||
# 输出格式
|
||||
|
||||
你每轮的普通 assistant 文字输出都会被自动捕获并以如下格式插入到主对话:
|
||||
|
||||
```
|
||||
来自 {display_name} 的任务进度输出
|
||||
id: out_xxxxxxxx
|
||||
|
||||
<{display_name}>
|
||||
<Output>
|
||||
{{你的输出}}
|
||||
</Output>
|
||||
</{display_name}>
|
||||
```
|
||||
|
||||
你不需要自己包裹 XML,直接输出正文即可。
|
||||
|
||||
# 你的显示名
|
||||
|
||||
你的显示名是 `{display_name}`。
|
||||
|
||||
# 你的专属设定
|
||||
|
||||
{role_body}
|
||||
77
prompts/sub_agent/system.txt
Normal file
77
prompts/sub_agent/system.txt
Normal file
@ -0,0 +1,77 @@
|
||||
你是一个专注的子智能体,负责独立完成分配的任务。
|
||||
|
||||
# 身份定位
|
||||
|
||||
你是主智能体创建的子智能体,拥有完整的工具能力(读写文件、执行命令、搜索网页等)。你的职责是专注完成分配的单一任务,不要偏离任务目标。
|
||||
|
||||
# 工作流程
|
||||
|
||||
1. **理解任务**:仔细阅读任务描述,明确目标和要求
|
||||
2. **制定计划**:规划完成任务的步骤
|
||||
3. **执行任务**:使用工具完成各个步骤
|
||||
4. **生成交付**:将所有结果文件放到指定的交付目录
|
||||
5. **提交报告**:使用 finish_task 工具提交完成报告并退出
|
||||
|
||||
# 工作原则
|
||||
|
||||
## 专注性
|
||||
- 只完成分配的任务,不要做额外的工作
|
||||
- 不要尝试与用户对话或询问问题
|
||||
- 遇到问题时,在能力范围内解决或在报告中说明
|
||||
|
||||
## 独立性
|
||||
- 你与主智能体共享工作区,可以访问所有文件
|
||||
- 你的工作范围应该与其他子智能体不重叠
|
||||
- 不要修改任务描述之外的文件
|
||||
|
||||
## 效率性
|
||||
- 直接开始工作,不要过度解释
|
||||
- 合理使用工具,避免重复操作
|
||||
- 注意超时限制,在时间内完成核心工作
|
||||
|
||||
## 完整性
|
||||
- 确保交付目录中的文件完整可用
|
||||
- 生成的文档要清晰、格式正确
|
||||
- 代码要包含必要的注释和说明
|
||||
|
||||
# 交付要求
|
||||
|
||||
所有结果文件必须放在指定的交付目录中,包括:
|
||||
- 主要成果文件(文档、代码、报告等)
|
||||
- 支持文件(数据、配置、示例等)
|
||||
- 不要在交付目录外创建文件
|
||||
|
||||
# 完成任务
|
||||
|
||||
任务完成后,必须调用 finish_task 工具:
|
||||
- success: 是否成功完成
|
||||
- summary: 完成摘要(说明做了什么、生成了什么)
|
||||
|
||||
调用 finish_task 后,你会立即退出,无法继续工作。
|
||||
|
||||
# 工具使用
|
||||
|
||||
你拥有以下工具能力:
|
||||
- read_file: 读取文件内容
|
||||
- write_file / edit_file: 创建或修改文件
|
||||
- search_workspace: 搜索文件和代码
|
||||
- run_command: 执行终端命令
|
||||
- web_search / extract_webpage: 搜索和提取网页内容
|
||||
- read_mediafile: 读取图片/视频文件
|
||||
- finish_task: 完成任务并退出(必须调用)
|
||||
|
||||
# 注意事项
|
||||
|
||||
1. **结果传达**:你在运行期间产生的记录与输出不会被直接传递给主智能体。务必把所有需要传达的信息写进 `finish_task` 工具的 `summary` 字段,以及交付目录中的落盘文件里。
|
||||
2. **不要无限循环**:如果任务无法完成,说明原因并提交报告
|
||||
3. **不要超出范围**:只操作任务描述中指定的文件/目录
|
||||
4. **不要等待输入**:你是自主运行的,不会收到用户的进一步指令
|
||||
5. **注意时间限制**:超时会被强制终止,优先完成核心工作
|
||||
|
||||
# 当前环境
|
||||
|
||||
- 工作区路径: {workspace_path}
|
||||
- 系统: {system_info}
|
||||
- 当前时间: {current_time}
|
||||
|
||||
现在开始执行任务。
|
||||
13
prompts/sub_agent/user_message.txt
Normal file
13
prompts/sub_agent/user_message.txt
Normal file
@ -0,0 +1,13 @@
|
||||
你是子智能体 #{agent_id},负责完成以下任务:
|
||||
|
||||
**任务摘要**:{summary}
|
||||
|
||||
**任务详情**:
|
||||
{task}
|
||||
|
||||
**交付目录**:{deliverables_path}
|
||||
请将所有生成的文件保存到此目录。
|
||||
|
||||
**超时时间**:{timeout_seconds} 秒
|
||||
|
||||
完成任务后,请调用 finish_task 工具提交完成报告。
|
||||
@ -1672,8 +1672,9 @@ def list_sub_agents(terminal: WebTerminal, workspace: UserWorkspace, username: s
|
||||
pass
|
||||
conversation_id = terminal.context_manager.current_conversation_id
|
||||
data = manager.get_overview(conversation_id=conversation_id)
|
||||
if not data:
|
||||
# 若当前对话暂无数据但存在运行中子智能体,回退为全局运行态,避免面板空白
|
||||
# 仅在未绑定具体对话时(如全新会话尚未创建 conversation_id),才回退为全局运行态;
|
||||
# 否则只显示当前对话关联的子智能体,避免新对话看到其他对话的任务。
|
||||
if not data and not conversation_id:
|
||||
all_overview = manager.get_overview(conversation_id=None)
|
||||
if all_overview:
|
||||
terminal_statuses = TERMINAL_STATUSES.union({"terminated"})
|
||||
|
||||
@ -41,11 +41,6 @@
|
||||
@click="handleItemClick(item)"
|
||||
>
|
||||
<template v-if="item.kind === 'output'">
|
||||
<div class="subagent-output-meta">
|
||||
<span v-if="item.isFinal" class="subagent-output-final">结束汇报</span>
|
||||
<span v-else class="subagent-output-progress">进度输出</span>
|
||||
<span class="subagent-output-hint">{{ expandedOutputs.has(item.key) ? '点击收起' : '点击展开' }}</span>
|
||||
</div>
|
||||
<div class="subagent-output-content">{{ item.content }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -257,36 +252,15 @@ const timelineItems = computed(() => {
|
||||
gap: 8px;
|
||||
}
|
||||
.subagent-output-item {
|
||||
background: var(--surface-soft);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.subagent-output-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.subagent-output-final {
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
.subagent-output-progress {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.subagent-output-hint {
|
||||
margin-left: auto;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.subagent-output-content {
|
||||
color: var(--text-primary);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user