refactor(messages): 统一运行期消息插入为 [系统通知|source] 前缀的 user 消息
- 新增 server/chat_flow_task_support.inject_runtime_user_message 作为唯一入口, 统一处理 add_conversation + messages.insert + sender(user_message) 三件事 - 子智能体 / 后台 run_command / runtime_guidance / mode notice 全部走该入口, role 一律 user,inline 仅作 metadata 标记,不再复用 system role 表达语义 - inline poll 移出 tool 执行循环,改为整轮 tool 完成后批量注入, 避免在 assistant.tool_calls 与 tool 序列之间插入 user 导致 API 报错 - 删除 _insert_completion_notice_message / _record_sub_agent_message 以及 build_messages 中 system→user 的回转分支 - 移除已废弃的 wait_sub_agent 工具残留(后端注册、前端图标/动画、 prompts、SKILL 文档;保留 sleep 的 wait_sub_agent_ids 参数) - 前端取消对 is_auto_generated/auto_message_type 的过滤, 让运行期注入的 user 消息在历史和实时推送中正常渲染 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
30e7709df1
commit
67fdb715f5
@ -95,7 +95,6 @@ python3 -m server.app --path <当前目录> --port 8091 --thinking-mode
|
||||
|
||||
工具接口:
|
||||
- `create_sub_agent`: 创建并启动子智能体
|
||||
- `wait_sub_agent`: 等待子智能体完成并获取结果
|
||||
- `close_sub_agent`: 终止运行中的子智能体
|
||||
|
||||
### 🛠️ 丰富的工具集
|
||||
|
||||
@ -38,7 +38,7 @@ description: run_command 工具使用指南。介绍前台与后台模式、后
|
||||
|
||||
- timeout 最长 3600 秒。命令在后台继续执行,工具会等约 5 秒收集当前输出并返回,然后 **立即释放控制权**,让你继续处理其他任务。
|
||||
- 返回值里会包含 `command_id`,`status` 可能是 `running_background`。你会拿到已经产生的输出片段,但最终结果要靠系统通知或手动等待。
|
||||
- 成功或失败完成后,系统会自动发送一条 `system` 消息(内容类似 `后台命令已完成(completed)`,并附带 `command_id`、`command`、`output` 摘要)通知你。不要再依赖 `wait_sub_agent`/手动轮询。
|
||||
- 成功或失败完成后,系统会自动插入一条 user 消息(前缀 `[系统通知|background_command]`,附带 `command_id` 与输出摘要)通知你。不要依赖任何手动轮询。
|
||||
- 适合长时间任务(构建/安装/批处理)或你想“先发起再继续别的工作”。
|
||||
- 返回的 `command_id` 需要保留,用于后续通过 `sleep(wait_runcommand_id=...)` 等待结果或根据通知定位输出。
|
||||
|
||||
|
||||
@ -122,7 +122,7 @@ create_sub_agent(
|
||||
- 你会在对话中看到类似:"这是一句系统自动发送的user消息,用于通知你子智能体已经运行完成..."
|
||||
|
||||
**完成通知机制说明:**
|
||||
- 后台子智能体完成的通知是系统自动插入的 user/system 消息,不依赖手动调用 `wait_sub_agent` 或其它轮询接口。
|
||||
- 后台子智能体完成的通知是系统自动插入的 user 消息(前缀 `[系统通知|sub_agent]`),不依赖手动调用任何轮询接口。
|
||||
- 你只需在收到通知后直接查看交付目录即可,系统会把所有必需信息一并带上。
|
||||
|
||||
**何时使用:**
|
||||
@ -163,8 +163,8 @@ create_sub_agent(agent_id=1, task="大规模日志分析", run_in_background=tru
|
||||
**重要提示:**
|
||||
- 系统会自动处理通知,你只需要在收到通知消息后处理结果即可
|
||||
- 如果你正在执行工具调用,系统会延迟通知,避免打断你的工作流
|
||||
- 通知类型取决于你是否继续工作:继续工作时收到 system 消息,停止输出时收到 user 消息
|
||||
- 后台完成由上述系统通知负责,不要再依赖 `wait_sub_agent` 或类似的手动等待流,系统会在能插入消息时自动通知你。
|
||||
- 不论运行中还是空闲,通知一律是带 `[系统通知|sub_agent]` 前缀的 user 消息;运行中只是通知不触发新一轮回复,空闲时等同于一条新输入会触发回复
|
||||
- 后台完成由上述系统通知负责,不要再依赖任何手动等待流,系统会在能插入消息时自动通知你。
|
||||
|
||||
### 模式选择决策树
|
||||
|
||||
|
||||
@ -332,7 +332,17 @@ class MainTerminalCommandMixin:
|
||||
)
|
||||
system_message = tool_result.get("system_message")
|
||||
if system_message:
|
||||
self._record_sub_agent_message(system_message, tool_result.get("task_id"), inline=False)
|
||||
from server.chat_flow_task_support import inject_runtime_user_message
|
||||
inject_runtime_user_message(
|
||||
web_terminal=self,
|
||||
messages=None,
|
||||
text=system_message,
|
||||
source="sub_agent",
|
||||
sender=None,
|
||||
conversation_id=getattr(self.context_manager, "current_conversation_id", None),
|
||||
inline=False,
|
||||
extra_metadata={"task_id": tool_result.get("task_id")},
|
||||
)
|
||||
|
||||
# 4. 在终端显示执行信息(不保存到历史)
|
||||
if collected_tool_calls:
|
||||
|
||||
@ -431,12 +431,6 @@ class MainTerminalContextMixin:
|
||||
}
|
||||
messages.append(message)
|
||||
|
||||
elif conv["role"] == "system" and metadata.get("sub_agent_notice"):
|
||||
# 转换为用户消息,让模型能及时响应
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": conv["content"]
|
||||
})
|
||||
else:
|
||||
# User 或普通 System 消息
|
||||
images = conv.get("images") or metadata.get("images") or []
|
||||
|
||||
@ -164,7 +164,6 @@ class MainTerminalToolsExecutionMixin:
|
||||
}
|
||||
_SUB_AGENT_SERIES_TOOLS = {
|
||||
"create_sub_agent",
|
||||
"wait_sub_agent",
|
||||
"close_sub_agent",
|
||||
"terminate_sub_agent",
|
||||
"get_sub_agent_status",
|
||||
@ -198,26 +197,6 @@ class MainTerminalToolsExecutionMixin:
|
||||
"terminal_session",
|
||||
}
|
||||
|
||||
def _record_sub_agent_message(self, message: Optional[str], task_id: Optional[str] = None, inline: bool = False):
|
||||
"""以 system 消息记录子智能体状态。"""
|
||||
if not message:
|
||||
return
|
||||
if task_id and task_id in self._announced_sub_agent_tasks:
|
||||
return
|
||||
if task_id:
|
||||
self._announced_sub_agent_tasks.add(task_id)
|
||||
logger.info(
|
||||
"[SubAgent] record message | task=%s | inline=%s | content=%s",
|
||||
task_id,
|
||||
inline,
|
||||
message.replace("\n", "\\n")[:200],
|
||||
)
|
||||
metadata = {"sub_agent_notice": True, "inline": inline, "is_auto_generated": True, "auto_message_type": "sub_agent_notice"}
|
||||
if task_id:
|
||||
metadata["task_id"] = task_id
|
||||
self.context_manager.add_conversation("system", message, metadata=metadata)
|
||||
print(f"{OUTPUT_FORMATS['info']} {message}")
|
||||
|
||||
# 扩展的只读命令白名单(包含常用管道命令)
|
||||
_READONLY_ALLOWED_EXECUTABLES = {
|
||||
"grep", "find", "ls", "pwd", "tree", "cat", "head", "tail",
|
||||
|
||||
@ -75,7 +75,7 @@ TOOL_CATEGORIES: Dict[str, ToolCategory] = {
|
||||
),
|
||||
"sub_agent": ToolCategory(
|
||||
label="子智能体",
|
||||
tools=["create_sub_agent", "wait_sub_agent", "close_sub_agent"],
|
||||
tools=["create_sub_agent", "close_sub_agent"],
|
||||
),
|
||||
"easter_egg": ToolCategory(
|
||||
label="彩蛋实验",
|
||||
|
||||
@ -958,7 +958,7 @@ class SubAgentManager:
|
||||
return None
|
||||
|
||||
def lookup_task(self, *, task_id: Optional[str] = None, agent_id: Optional[int] = None) -> Optional[Dict]:
|
||||
"""只读查询任务信息,供 wait_sub_agent 自动调整超时时间。"""
|
||||
"""只读查询任务信息,供 sleep(wait_sub_agent_ids=...) 自动调整超时时间。"""
|
||||
task = self._select_task(task_id, agent_id)
|
||||
if not task:
|
||||
return None
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
## 5. 操作技巧
|
||||
|
||||
- **待办事项**:任务≥2步时使用 `todo_create`;当用户提出稍微复杂的要求、预计超过3个步骤时,应积极创建待办事项,概述≤50字,任务2-6条,完成立即更新
|
||||
- **子智能体**:独立子任务并行处理,最多5个同时运行,`wait_sub_agent` 超时需≥创建时设置;刚开始修改一个项目、需要先找到某个功能模块的修改位置时,优先考虑创建子智能体快速了解
|
||||
- **子智能体**:独立子任务并行处理,最多5个同时运行;刚开始修改一个项目、需要先找到某个功能模块的修改位置时,优先考虑创建子智能体快速了解
|
||||
- **记忆管理**:`update_memory` 支持追加/替换/删除,main 为长期记忆,task 为当前对话记忆
|
||||
|
||||
---
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
## 5. 操作技巧
|
||||
|
||||
- **待办事项**:任务≥2步时使用 `todo_create`;当用户提出稍微复杂的要求、预计超过3个步骤时,应积极创建待办事项,概述≤50字,任务2-6条,完成立即更新
|
||||
- **子智能体**:独立子任务并行处理,最多5个同时运行,`wait_sub_agent` 超时需≥创建时设置;刚开始修改一个项目、需要先找到某个功能模块的修改位置时,优先考虑创建子智能体快速了解
|
||||
- **子智能体**:独立子任务并行处理,最多5个同时运行;刚开始修改一个项目、需要先找到某个功能模块的修改位置时,优先考虑创建子智能体快速了解
|
||||
- **记忆管理**:`update_memory` 支持追加/替换/删除,main 为长期记忆,task 为当前对话记忆
|
||||
|
||||
---
|
||||
|
||||
@ -53,12 +53,6 @@ def _dispatch_runtime_mode_notice(terminal: WebTerminal, username: str, text: st
|
||||
notice = str(text or "").strip()
|
||||
if not notice:
|
||||
return
|
||||
metadata = {
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": notice,
|
||||
"runtime_mode_notice": True,
|
||||
"message_source": "notify",
|
||||
}
|
||||
|
||||
# 若有运行任务,优先入队到该任务(下一轮工具后注入),确保会进入后续 API 请求上下文。
|
||||
try:
|
||||
@ -84,26 +78,25 @@ def _dispatch_runtime_mode_notice(terminal: WebTerminal, username: str, text: st
|
||||
pass
|
||||
|
||||
# 无运行任务时,直接写入当前会话并广播前端显示(来源固定为 notify)。
|
||||
try:
|
||||
if getattr(terminal, "context_manager", None):
|
||||
terminal.context_manager.add_conversation("user", notice, metadata=metadata)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
socketio.emit(
|
||||
"user_message",
|
||||
{
|
||||
"message": notice,
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": notice,
|
||||
"runtime_mode_notice": True,
|
||||
"message_source": "notify",
|
||||
"conversation_id": getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None),
|
||||
},
|
||||
room=f"user_{username}",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
from .chat_flow_task_support import inject_runtime_user_message
|
||||
|
||||
conversation_id = getattr(getattr(terminal, "context_manager", None), "current_conversation_id", None)
|
||||
|
||||
def _emit(event: str, payload: Dict[str, Any]) -> None:
|
||||
try:
|
||||
socketio.emit(event, payload, room=f"user_{username}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
inject_runtime_user_message(
|
||||
web_terminal=terminal,
|
||||
messages=None,
|
||||
text=notice,
|
||||
source="notify",
|
||||
sender=_emit,
|
||||
conversation_id=conversation_id,
|
||||
inline=True,
|
||||
)
|
||||
|
||||
@chat_bp.route('/api/thinking-mode', methods=['POST'])
|
||||
@api_login_required
|
||||
|
||||
@ -2,43 +2,110 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from modules.sub_agent_manager import TERMINAL_STATUSES
|
||||
|
||||
|
||||
def _insert_completion_notice_message(
|
||||
_VALID_SOURCES = {
|
||||
"guidance",
|
||||
"notify",
|
||||
"user",
|
||||
"presend",
|
||||
"sub_agent",
|
||||
"background_command",
|
||||
}
|
||||
|
||||
|
||||
def inject_runtime_user_message(
|
||||
*,
|
||||
messages: List[Dict],
|
||||
after_tool_call_id: Optional[str],
|
||||
message: str,
|
||||
web_terminal,
|
||||
messages: Optional[List[Dict]],
|
||||
text: str,
|
||||
source: str,
|
||||
sender: Optional[Callable[[str, Dict[str, Any]], None]] = None,
|
||||
conversation_id: Optional[str] = None,
|
||||
after_tool_call_id: Optional[str] = None,
|
||||
inline: bool,
|
||||
sender,
|
||||
metadata: Optional[Dict] = None,
|
||||
sub_agent_notice: bool = False,
|
||||
):
|
||||
"""统一插入完成通知(system/user)并发送事件。"""
|
||||
insert_index = len(messages)
|
||||
if after_tool_call_id:
|
||||
for idx, msg in enumerate(messages):
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id") == after_tool_call_id:
|
||||
insert_index = idx + 1
|
||||
break
|
||||
extra_metadata: Optional[Dict[str, Any]] = None,
|
||||
persist: bool = True,
|
||||
) -> Optional[str]:
|
||||
"""运行期/空闲期统一向对话插入一条 user 消息(带 [系统通知|{source}] 前缀)。
|
||||
|
||||
insert_role = "system" if inline else "user"
|
||||
meta = dict(metadata or {})
|
||||
meta["inline"] = inline
|
||||
messages.insert(insert_index, {
|
||||
"role": insert_role,
|
||||
"content": message,
|
||||
"metadata": meta
|
||||
})
|
||||
- inline=True 表示运行期插入(仅作为通知,不主动触发新一轮回复)。
|
||||
- inline=False 表示空闲期插入(等价于一条用户消息,由调用方决定是否启动新一轮回复)。
|
||||
- messages=None 时仅持久化到对话历史并广播前端(无运行中的 messages 列表可写)。
|
||||
"""
|
||||
raw = "" if text is None else str(text).strip()
|
||||
if not raw:
|
||||
return None
|
||||
|
||||
payload = {'content': message, 'inline': inline}
|
||||
if sub_agent_notice:
|
||||
payload['sub_agent_notice'] = True
|
||||
payload.update({k: v for k, v in meta.items() if k in {"command_id", "task_id", "background_command_notice"}})
|
||||
sender('system_message', payload)
|
||||
src = str(source or "").strip().lower()
|
||||
if src not in _VALID_SOURCES:
|
||||
src = "guidance"
|
||||
|
||||
formatted = f"[系统通知|{src}]\n{raw}"
|
||||
|
||||
metadata: Dict[str, Any] = {
|
||||
"runtime_injected": True,
|
||||
"source": src,
|
||||
"inline": inline,
|
||||
"is_auto_generated": True,
|
||||
"auto_message_type": f"runtime_{src}",
|
||||
"message_source": src,
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": raw,
|
||||
}
|
||||
if extra_metadata:
|
||||
metadata.update({k: v for k, v in extra_metadata.items() if v is not None})
|
||||
|
||||
if persist:
|
||||
try:
|
||||
ctx_manager = getattr(web_terminal, "context_manager", None)
|
||||
if ctx_manager is not None:
|
||||
ctx_manager.add_conversation("user", formatted, metadata=metadata)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if messages is not None:
|
||||
insert_index = len(messages)
|
||||
if after_tool_call_id:
|
||||
for idx, msg in enumerate(messages):
|
||||
if msg.get("role") == "tool" and msg.get("tool_call_id") == after_tool_call_id:
|
||||
# 跳过紧随其后的所有 tool 消息,避免把 user 插进同一 assistant.tool_calls 的 tool 序列中间,
|
||||
# 否则会触发 "insufficient tool messages following tool_calls" 错误。
|
||||
end = idx + 1
|
||||
while end < len(messages) and messages[end].get("role") == "tool":
|
||||
end += 1
|
||||
insert_index = end
|
||||
break
|
||||
messages.insert(insert_index, {
|
||||
"role": "user",
|
||||
"content": formatted,
|
||||
"metadata": dict(metadata),
|
||||
})
|
||||
|
||||
if callable(sender):
|
||||
payload: Dict[str, Any] = {
|
||||
"message": formatted,
|
||||
"content": formatted,
|
||||
"conversation_id": conversation_id,
|
||||
"inline": inline,
|
||||
"source": src,
|
||||
"message_source": src,
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": raw,
|
||||
}
|
||||
for key in ("task_id", "command_id"):
|
||||
value = (extra_metadata or {}).get(key)
|
||||
if value is not None:
|
||||
payload[key] = value
|
||||
try:
|
||||
sender("user_message", payload)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return formatted
|
||||
|
||||
|
||||
async def process_sub_agent_updates(*, messages: List[Dict], inline: bool = False, after_tool_call_id: Optional[str] = None, web_terminal, sender, debug_log, maybe_mark_failure_from_message):
|
||||
@ -126,13 +193,6 @@ async def process_sub_agent_updates(*, messages: List[Dict], inline: bool = Fals
|
||||
|
||||
debug_log(f"[SubAgent] update task={task_id} inline={inline} msg={message}")
|
||||
|
||||
# 记录到对话历史(用于后续 build_messages 转换为 user 消息)
|
||||
if hasattr(web_terminal, "_record_sub_agent_message"):
|
||||
try:
|
||||
web_terminal._record_sub_agent_message(message, task_id, inline=inline)
|
||||
except Exception as exc:
|
||||
debug_log(f"[SubAgent] 记录子智能体消息失败: {exc}")
|
||||
|
||||
# 标记任务已通知
|
||||
if task_id:
|
||||
web_terminal._announced_sub_agent_tasks.add(task_id)
|
||||
@ -144,19 +204,17 @@ async def process_sub_agent_updates(*, messages: List[Dict], inline: bool = Fals
|
||||
except Exception as exc:
|
||||
debug_log(f"[SubAgent] 保存通知状态失败: {exc}")
|
||||
|
||||
# 运行中插入 system 消息,避免触发新的 user 轮次;非运行中保持 user 通知
|
||||
if not inline:
|
||||
prefix = "这是一句系统自动发送的user消息,用于通知你子智能体已经运行完成"
|
||||
if not message.startswith(prefix):
|
||||
message = f"{prefix}\n\n{message}"
|
||||
_insert_completion_notice_message(
|
||||
conversation_id = getattr(getattr(web_terminal, "context_manager", None), "current_conversation_id", None)
|
||||
inject_runtime_user_message(
|
||||
web_terminal=web_terminal,
|
||||
messages=messages,
|
||||
after_tool_call_id=after_tool_call_id,
|
||||
message=message,
|
||||
inline=inline,
|
||||
text=message,
|
||||
source="sub_agent",
|
||||
sender=sender,
|
||||
metadata={"sub_agent_notice": True, "task_id": task_id},
|
||||
sub_agent_notice=True,
|
||||
conversation_id=conversation_id,
|
||||
after_tool_call_id=after_tool_call_id,
|
||||
inline=inline,
|
||||
extra_metadata={"task_id": task_id},
|
||||
)
|
||||
if inline:
|
||||
web_terminal._inline_sub_agent_notified.add(inline_key)
|
||||
@ -194,34 +252,16 @@ async def process_background_command_updates(*, messages: List[Dict], inline: bo
|
||||
f"output_len={len(output)} inline={inline}"
|
||||
)
|
||||
|
||||
# 与子智能体 inline 通知一致:写入对话历史,确保前端/历史都可见
|
||||
try:
|
||||
web_terminal.context_manager.add_conversation(
|
||||
"system",
|
||||
message,
|
||||
metadata={
|
||||
"background_command_notice": True,
|
||||
"inline": inline,
|
||||
"command_id": command_id,
|
||||
"is_auto_generated": True,
|
||||
"auto_message_type": "background_command_notice",
|
||||
},
|
||||
)
|
||||
debug_log(f"[BgCmdDebug] persisted system notice command_id={command_id}")
|
||||
except Exception as exc:
|
||||
debug_log(f"[BgCmdDebug] persist system notice failed command_id={command_id}: {exc}")
|
||||
|
||||
_insert_completion_notice_message(
|
||||
inject_runtime_user_message(
|
||||
web_terminal=web_terminal,
|
||||
messages=messages,
|
||||
after_tool_call_id=after_tool_call_id,
|
||||
message=message,
|
||||
inline=inline,
|
||||
text=message,
|
||||
source="background_command",
|
||||
sender=sender,
|
||||
metadata={
|
||||
"background_command_notice": True,
|
||||
"command_id": command_id,
|
||||
},
|
||||
sub_agent_notice=False,
|
||||
conversation_id=conversation_id,
|
||||
after_tool_call_id=after_tool_call_id,
|
||||
inline=inline,
|
||||
extra_metadata={"command_id": command_id},
|
||||
)
|
||||
try:
|
||||
manager.mark_notified(str(command_id))
|
||||
|
||||
@ -22,6 +22,7 @@ from config import TOOL_CALL_COOLDOWN
|
||||
from modules.personalization_manager import load_personalization_config, resolve_context_compression_settings
|
||||
from modules.auto_approval_service import run_auto_approval
|
||||
from .deep_compression import run_deep_compression
|
||||
from .chat_flow_task_support import inject_runtime_user_message
|
||||
|
||||
|
||||
def _format_numbered_lines(lines: List[str], start_line_no: int) -> List[Dict[str, Any]]:
|
||||
@ -186,44 +187,15 @@ def _inject_runtime_mode_notice(
|
||||
sender=None,
|
||||
conversation_id: Optional[str] = None,
|
||||
) -> None:
|
||||
if not isinstance(content, str) or not content.strip():
|
||||
return
|
||||
text = content.strip()
|
||||
metadata = {
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": text,
|
||||
"runtime_mode_notice": True,
|
||||
"message_source": "notify",
|
||||
}
|
||||
try:
|
||||
web_terminal.context_manager.add_conversation(
|
||||
"user",
|
||||
text,
|
||||
metadata=metadata,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
messages.append(
|
||||
{
|
||||
"role": "user",
|
||||
"content": text,
|
||||
"metadata": metadata,
|
||||
}
|
||||
inject_runtime_user_message(
|
||||
web_terminal=web_terminal,
|
||||
messages=messages,
|
||||
text=content,
|
||||
source="notify",
|
||||
sender=sender,
|
||||
conversation_id=conversation_id,
|
||||
inline=True,
|
||||
)
|
||||
if callable(sender):
|
||||
try:
|
||||
sender(
|
||||
"user_message",
|
||||
{
|
||||
"message": text,
|
||||
"conversation_id": conversation_id,
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": text,
|
||||
"message_source": "notify",
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _format_rejected_tool_text(reason: str) -> str:
|
||||
@ -863,18 +835,6 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
|
||||
action_message = summary
|
||||
debug_log(f"{function_name} 执行完成: {summary or '无摘要'}")
|
||||
|
||||
if function_name == "wait_sub_agent":
|
||||
system_msg = result_data.get("system_message")
|
||||
if system_msg:
|
||||
messages.append({
|
||||
"role": "system",
|
||||
"content": system_msg
|
||||
})
|
||||
sender('system_message', {
|
||||
'content': system_msg,
|
||||
'inline': False
|
||||
})
|
||||
maybe_mark_failure_from_message(web_terminal, system_msg)
|
||||
if function_name == "sleep":
|
||||
try:
|
||||
if isinstance(result_data, dict) and result_data.get("mode") == "wait_sub_agent_ids":
|
||||
@ -1121,11 +1081,20 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
|
||||
except Exception as exc:
|
||||
debug_log(f"[ContextCompression] 同步替换本轮 messages 失败: {exc}")
|
||||
debug_log(f"💾 增量保存:工具结果 {function_name}")
|
||||
if function_name != "wait_sub_agent":
|
||||
system_message = result_data.get("system_message") if isinstance(result_data, dict) else None
|
||||
if system_message:
|
||||
web_terminal._record_sub_agent_message(system_message, result_data.get("task_id"), inline=False)
|
||||
maybe_mark_failure_from_message(web_terminal, system_message)
|
||||
system_message = result_data.get("system_message") if isinstance(result_data, dict) else None
|
||||
if system_message:
|
||||
inject_runtime_user_message(
|
||||
web_terminal=web_terminal,
|
||||
messages=messages,
|
||||
text=system_message,
|
||||
source="sub_agent",
|
||||
sender=sender,
|
||||
conversation_id=conversation_id,
|
||||
after_tool_call_id=tool_call_id,
|
||||
inline=False,
|
||||
extra_metadata={"task_id": result_data.get("task_id")},
|
||||
)
|
||||
maybe_mark_failure_from_message(web_terminal, system_message)
|
||||
|
||||
# 自动深层压缩(工具调用后触发)
|
||||
current_context_tokens = web_terminal.context_manager.get_current_context_tokens(conversation_id)
|
||||
@ -1176,18 +1145,36 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
|
||||
except Exception as exc:
|
||||
debug_log(f"[RuntimeMode] 应用挂起模式变更失败: {exc}")
|
||||
|
||||
debug_log(f"[SubAgent] after tool={function_name} call_id={tool_call_id} -> poll updates")
|
||||
await process_sub_agent_updates(messages=messages, inline=True, after_tool_call_id=tool_call_id, web_terminal=web_terminal, sender=sender, debug_log=debug_log, maybe_mark_failure_from_message=maybe_mark_failure_from_message)
|
||||
debug_log(f"[BgCmdDebug] after tool={function_name} call_id={tool_call_id} -> poll background command updates (inline)")
|
||||
await process_background_command_updates(messages=messages, inline=True, after_tool_call_id=tool_call_id, web_terminal=web_terminal, sender=sender, debug_log=debug_log, maybe_mark_failure_from_message=maybe_mark_failure_from_message)
|
||||
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
if tool_failed:
|
||||
mark_force_thinking(web_terminal, reason=f"{function_name}_failed")
|
||||
|
||||
# 同一轮全部 tool_call 完成后,再统一 poll 子智能体/后台命令并注入通知,
|
||||
# 避免在 assistant.tool_calls 与对应 tool 消息序列之间插入 user 消息导致 API 报错。
|
||||
debug_log("[SubAgent] poll updates after all tool_calls finished")
|
||||
await process_sub_agent_updates(
|
||||
messages=messages,
|
||||
inline=True,
|
||||
after_tool_call_id=None,
|
||||
web_terminal=web_terminal,
|
||||
sender=sender,
|
||||
debug_log=debug_log,
|
||||
maybe_mark_failure_from_message=maybe_mark_failure_from_message,
|
||||
)
|
||||
debug_log("[BgCmdDebug] poll background command updates after all tool_calls finished")
|
||||
await process_background_command_updates(
|
||||
messages=messages,
|
||||
inline=True,
|
||||
after_tool_call_id=None,
|
||||
web_terminal=web_terminal,
|
||||
sender=sender,
|
||||
debug_log=debug_log,
|
||||
maybe_mark_failure_from_message=maybe_mark_failure_from_message,
|
||||
)
|
||||
|
||||
# 运行期模式通知:必须等待同一轮全部 tool_call 都完成后再注入,
|
||||
# 避免在 assistant.tool_calls 与对应 tool 消息之间插入 system 消息导致 API 报错。
|
||||
# 避免在 assistant.tool_calls 与对应 tool 消息之间插入 user 消息导致 API 报错。
|
||||
if pending_runtime_mode_notices:
|
||||
for notice in pending_runtime_mode_notices:
|
||||
_inject_runtime_mode_notice(
|
||||
@ -1222,39 +1209,16 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
|
||||
)
|
||||
else:
|
||||
runtime_guidance_text = str(raw_item or "").strip()
|
||||
if runtime_guidance_source not in {"guidance", "notify", "user", "presend", "sub_agent", "background_command"}:
|
||||
runtime_guidance_source = "guidance"
|
||||
if not runtime_guidance_text:
|
||||
continue
|
||||
runtime_guidance_meta = {
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": runtime_guidance_text,
|
||||
"message_source": runtime_guidance_source,
|
||||
}
|
||||
try:
|
||||
web_terminal.context_manager.add_conversation(
|
||||
"user",
|
||||
runtime_guidance_text,
|
||||
metadata=runtime_guidance_meta,
|
||||
)
|
||||
except Exception as exc:
|
||||
debug_log(f"[RuntimeGuidance] 持久化引导消息失败: {exc}")
|
||||
messages.append(
|
||||
{
|
||||
"role": "user",
|
||||
"content": runtime_guidance_text,
|
||||
"metadata": runtime_guidance_meta,
|
||||
}
|
||||
)
|
||||
sender(
|
||||
"user_message",
|
||||
{
|
||||
"message": runtime_guidance_text,
|
||||
"conversation_id": conversation_id,
|
||||
"runtime_guidance": True,
|
||||
"runtime_guidance_original": runtime_guidance_text,
|
||||
"message_source": runtime_guidance_source,
|
||||
},
|
||||
inject_runtime_user_message(
|
||||
web_terminal=web_terminal,
|
||||
messages=messages,
|
||||
text=runtime_guidance_text,
|
||||
source=runtime_guidance_source,
|
||||
sender=sender,
|
||||
conversation_id=conversation_id,
|
||||
inline=True,
|
||||
)
|
||||
injected_count += 1
|
||||
if injected_count:
|
||||
|
||||
@ -261,14 +261,6 @@ export const historyMethods = {
|
||||
debugLog('跳过系统代发的图片/视频消息(仅用于模型查看,不在前端展示)');
|
||||
return;
|
||||
}
|
||||
if (message.role === 'user' && isSystemAutoUserMessageMeta(meta)) {
|
||||
debugLog('跳过系统自动代发的 user 消息(前端不渲染)', {
|
||||
auto_message_type: meta.auto_message_type || null,
|
||||
sub_agent_notice: !!meta.sub_agent_notice,
|
||||
background_command_notice: !!meta.background_command_notice
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.role === 'user') {
|
||||
// 用户消息 - 先结束之前的assistant消息
|
||||
|
||||
@ -1547,15 +1547,14 @@ export const taskPollingMethods = {
|
||||
background_command_notice: !!data?.background_command_notice
|
||||
});
|
||||
const isAutoUserMessage = isSystemAutoUserMessagePayload(data);
|
||||
const isRuntimeModeNotice = isRuntimeModeNoticePayload(data);
|
||||
const incomingImages = Array.isArray(data?.images) ? data.images : [];
|
||||
const incomingVideos = Array.isArray(data?.videos) ? data.videos : [];
|
||||
const incomingMediaRefs = Array.isArray(data?.media_refs)
|
||||
? data.media_refs
|
||||
: Array.isArray(data?.mediaRefs)
|
||||
? data.mediaRefs
|
||||
: [];
|
||||
if (!isAutoUserMessage) {
|
||||
const incomingImages = Array.isArray(data?.images) ? data.images : [];
|
||||
const incomingVideos = Array.isArray(data?.videos) ? data.videos : [];
|
||||
const incomingMediaRefs = Array.isArray(data?.media_refs)
|
||||
? data.media_refs
|
||||
: Array.isArray(data?.mediaRefs)
|
||||
? data.mediaRefs
|
||||
: [];
|
||||
const last = getOptimisticUserEchoTarget(this.messages || []);
|
||||
const isLikelyOptimisticEcho = !!(
|
||||
last &&
|
||||
@ -1582,13 +1581,16 @@ export const taskPollingMethods = {
|
||||
this.taskInProgress = true;
|
||||
this.streamingMessage = false;
|
||||
this.stopRequested = false;
|
||||
} else if (isRuntimeModeNotice) {
|
||||
this.chatAddUserMessage(message, [], [], [], 'notify');
|
||||
} else {
|
||||
debugLog('[TaskPolling] 跳过自动代发 user_message 渲染', {
|
||||
messagePreview: message.slice(0, 80),
|
||||
data
|
||||
});
|
||||
// 运行期/空闲期由系统注入的 user 消息(runtime_guidance / sub_agent / background_command / notify 等)
|
||||
// 现已统一为前缀 [系统通知|...] 的 user 消息,前端按普通 user 渲染。
|
||||
this.chatAddUserMessage(
|
||||
message,
|
||||
incomingImages,
|
||||
incomingVideos,
|
||||
incomingMediaRefs,
|
||||
resolveUserMessageSource(data)
|
||||
);
|
||||
}
|
||||
if (data?.sub_agent_notice) {
|
||||
if (typeof data?.has_running_sub_agents === 'boolean') {
|
||||
@ -1660,7 +1662,7 @@ export const taskPollingMethods = {
|
||||
|
||||
this.uiPushToast({
|
||||
title: '即将重试',
|
||||
message: `将在 ${retryIn} 秒后重试(第 ${attempt}/${maxAttempts} 次)`,
|
||||
message: `将在 ${retryIn} 秒后重试(第 ${attempt}/${maxAttempts} 次)\n错误:${data?.message || '未知错误'}`,
|
||||
type: 'info',
|
||||
duration: Math.max(retryIn, 1) * 1000
|
||||
});
|
||||
|
||||
@ -682,9 +682,6 @@ const filteredMessages = computed(() => {
|
||||
if (m?.role === 'system') {
|
||||
return false;
|
||||
}
|
||||
if (isSystemAutoUserMessage(m)) {
|
||||
return false;
|
||||
}
|
||||
if (isEmptyAssistantMessage(m)) {
|
||||
userMDebug('ChatArea.filteredMessages:drop-empty-assistant', {
|
||||
actions: Array.isArray(m?.actions) ? m.actions.map((a: any) => a?.type) : [],
|
||||
|
||||
@ -24,8 +24,7 @@ const RUNNING_ANIMATIONS: Record<string, string> = {
|
||||
terminal_snapshot: 'terminal-animation',
|
||||
todo_create: 'file-animation',
|
||||
todo_update_task: 'file-animation',
|
||||
create_sub_agent: 'terminal-animation',
|
||||
wait_sub_agent: 'wait-animation'
|
||||
create_sub_agent: 'terminal-animation'
|
||||
};
|
||||
|
||||
const RUNNING_STATUS_TEXTS: Record<string, string> = {
|
||||
|
||||
@ -70,7 +70,6 @@ export const TOOL_ICON_MAP = Object.freeze({
|
||||
terminal_snapshot: 'clipboard',
|
||||
unfocus_file: 'eye',
|
||||
update_memory: 'brain',
|
||||
wait_sub_agent: 'bot',
|
||||
get_sub_agent_status: 'bot',
|
||||
web_search: 'search',
|
||||
trigger_easter_egg: 'sparkles',
|
||||
|
||||
@ -1062,7 +1062,6 @@ TOOL_FORMATTERS = {
|
||||
"update_memory": _format_update_memory,
|
||||
"manage_personalization": _format_manage_personalization,
|
||||
"create_sub_agent": _format_create_sub_agent,
|
||||
"wait_sub_agent": _format_wait_sub_agent,
|
||||
"close_sub_agent": _format_close_sub_agent,
|
||||
"get_sub_agent_status": _format_get_sub_agent_status,
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user