fix(i18n): 子智能体统计与工具结果摘要补齐 tr() 双语,根治漏网硬编码中文

sub_agent/stats.py 的统计摘要(调用/读写/搜索/网页/指令次数、运行时长、交付目录)与 tool_result_formatter/agent_context.py 的子智能体相关格式化函数此前仍是硬编码中文,英文界面显示中文。新增 sub_agent_stats.* 9 个 key、扩展 fmt_agent2.* 30 个 key;补齐后引用完整性 0 缺失、0 占位符漂移,冒烟 6/6,中英双语自检通过。
This commit is contained in:
JOJO 2026-08-29 10:09:19 +08:00
parent 4aac18f7bb
commit e4fdc82319
4 changed files with 213 additions and 40 deletions

View File

@ -177,6 +177,128 @@ MESSAGES = {
"en-US": "Sub-agent paused.",
},
# ── tool_result_formatter/agent_context.py子智能体统计/创建/完成/交付等) ──
"fmt_agent2.api_calls": {
"zh-CN": "调用了{n}",
"en-US": "API calls: {n}",
},
"fmt_agent2.files_read": {
"zh-CN": "阅读了{n}次文件",
"en-US": "Files read: {n}",
},
"fmt_agent2.edit_files": {
"zh-CN": "编辑了{n}次文件",
"en-US": "Files edited: {n}",
},
"fmt_agent2.searches": {
"zh-CN": "搜索了{n}次内容",
"en-US": "Searches: {n}",
},
"fmt_agent2.web_pages": {
"zh-CN": "查看了{n}个网页",
"en-US": "Pages viewed: {n}",
},
"fmt_agent2.commands": {
"zh-CN": "运行了{n}个指令",
"en-US": "Commands run: {n}",
},
"fmt_agent2.running_seconds": {
"zh-CN": "运行了{n}",
"en-US": "Ran for {n} seconds",
},
"fmt_agent2.created_with_status": {
"zh-CN": "{name} 已创建(状态 {status})。",
"en-US": "{name} created (status {status}).",
},
"fmt_agent2.created_header": {
"zh-CN": "子智能体 #{agent_id} 已创建task_id={task_id},状态 {status}{ref_note}{deliver_note})。",
"en-US": "Sub-agent #{agent_id} created (task_id={task_id}, status {status}{ref_note}{deliver_note}).",
},
"fmt_agent2.ref_count_note": {
"zh-CN": ",附带 {n} 份参考文件",
"en-US": ", with {n} reference files",
},
"fmt_agent2.deliver_dir_note": {
"zh-CN": ",交付目录: {dir}",
"en-US": ", deliverables directory: {dir}",
},
"fmt_agent2.completed_header": {
"zh-CN": "子智能体 #{agent_id}/{task_id} 完成",
"en-US": "Sub-agent #{agent_id}/{task_id} completed",
},
"fmt_agent2.copied_to": {
"zh-CN": "交付已复制到 {path}",
"en-US": "Deliverables copied to {path}",
},
"fmt_agent2.deliver_generated": {
"zh-CN": "交付目录已生成",
"en-US": "Deliverables directory generated",
},
"fmt_agent2.abnormal_header": {
"zh-CN": "⚠️ 子智能体 #{agent_id}/{task_id} 状态 {status}",
"en-US": "⚠️ Sub-agent #{agent_id}/{task_id} status {status}",
},
"fmt_agent2.no_status_results": {
"zh-CN": "未找到子智能体状态。",
"en-US": "No sub-agent status found.",
},
"fmt_agent2.label_not_found": {
"zh-CN": "子智能体 {label} 不存在。",
"en-US": "Sub-agent {label} does not exist.",
},
"fmt_agent2.label_completed": {
"zh-CN": "子智能体 {label} 已完成",
"en-US": "Sub-agent {label} completed",
},
"fmt_agent2.label_terminated": {
"zh-CN": "子智能体 {label} 已终止",
"en-US": "Sub-agent {label} terminated",
},
"fmt_agent2.label_abnormal": {
"zh-CN": "⚠️ 子智能体 {label} 状态 {status}",
"en-US": "⚠️ Sub-agent {label} status {status}",
},
"fmt_agent2.label_status": {
"zh-CN": "子智能体 {label} 状态: {status}",
"en-US": "Sub-agent {label} status: {status}",
},
"fmt_agent2.status_note": {
"zh-CN": "(状态 {status}",
"en-US": " (status {status})",
},
"fmt_agent2.force_closed_display": {
"zh-CN": "已强制关闭子智能体 {name}",
"en-US": "Sub-agent {name} was forcibly closed.",
},
"fmt_agent2.force_closed_id": {
"zh-CN": "已强制关闭子智能体 #{agent_id}task_id={task_id})。",
"en-US": "Sub-agent #{agent_id} was forcibly closed (task_id={task_id}).",
},
"fmt_agent2.sent_to_display": {
"zh-CN": "已向 {name} 发送消息。",
"en-US": "Message sent to {name}.",
},
"fmt_agent2.sent_to_id": {
"zh-CN": "已向子智能体 #{agent_id} 发送消息。",
"en-US": "Message sent to sub-agent #{agent_id}.",
},
"fmt_agent2.sent_plain": {
"zh-CN": "消息已发送。",
"en-US": "Message sent.",
},
"fmt_agent2.paused_id": {
"zh-CN": "已暂停子智能体 #{agent_id}{message}",
"en-US": "Sub-agent #{agent_id} paused. {message}",
},
"fmt_agent2.answered_question": {
"zh-CN": "已向子智能体回复问题 {question_id}",
"en-US": "Answered sub-agent question {question_id}.",
},
"fmt_agent2.answered_plain": {
"zh-CN": "回复已发送。",
"en-US": "Reply sent.",
},
# ── utils/context_manager/project_mixin.py ──
"project_mixin.host_mode_tree_unavailable": {
"zh-CN": "宿主机模式下文件树不可用",

View File

@ -207,4 +207,42 @@ MESSAGES = {
"zh-CN": "⚠️ 子智能体任务异常退出,系统已自动清理运行状态。",
"en-US": "⚠️ The sub-agent task exited abnormally; the system auto-cleaned its running state.",
},
# ── modules/sub_agent/stats.py统计摘要与消息组装 ──
"sub_agent_stats.api_calls": {
"zh-CN": "调用了{n}",
"en-US": "API calls: {n}",
},
"sub_agent_stats.files_read": {
"zh-CN": "阅读了{n}次文件",
"en-US": "Files read: {n}",
},
"sub_agent_stats.write_files": {
"zh-CN": "写入了{n}次文件",
"en-US": "Files written: {n}",
},
"sub_agent_stats.edit_files": {
"zh-CN": "编辑了{n}次文件",
"en-US": "Files edited: {n}",
},
"sub_agent_stats.searches": {
"zh-CN": "搜索了{n}次内容",
"en-US": "Searches: {n}",
},
"sub_agent_stats.web_pages": {
"zh-CN": "查看了{n}个网页",
"en-US": "Pages viewed: {n}",
},
"sub_agent_stats.commands": {
"zh-CN": "运行了{n}个指令",
"en-US": "Commands run: {n}",
},
"sub_agent_stats.duration": {
"zh-CN": "运行了{n}",
"en-US": "Ran for {n} seconds",
},
"sub_agent_stats.deliverables": {
"zh-CN": "交付目录:{dir}",
"en-US": "Deliverables directory: {dir}",
},
}

View File

@ -5,6 +5,8 @@ from __future__ import annotations
import time
from typing import Any, Dict, Optional
from modules.i18n import tr
class SubAgentStatsMixin:
"""提供子智能体任务耗时、统计摘要与结果消息组装能力。"""
@ -38,13 +40,13 @@ class SubAgentStatsMixin:
web_pages = self._coerce_stat_int(stats.get("web_pages"))
commands = self._coerce_stat_int(stats.get("commands"))
lines = [
f"调用了{api_calls}",
f"阅读了{files_read}次文件",
f"写入了{write_files}次文件",
f"编辑了{edit_files}次文件",
f"搜索了{searches}次内容",
f"查看了{web_pages}个网页",
f"运行了{commands}个指令",
tr("sub_agent_stats.api_calls", n=api_calls),
tr("sub_agent_stats.files_read", n=files_read),
tr("sub_agent_stats.write_files", n=write_files),
tr("sub_agent_stats.edit_files", n=edit_files),
tr("sub_agent_stats.searches", n=searches),
tr("sub_agent_stats.web_pages", n=web_pages),
tr("sub_agent_stats.commands", n=commands),
]
return "\n".join(lines)
@ -61,9 +63,9 @@ class SubAgentStatsMixin:
if stats_summary:
parts.append(stats_summary)
if duration_seconds is not None:
parts.append(f"运行了{duration_seconds}")
parts.append(tr("sub_agent_stats.duration", n=duration_seconds))
if summary:
parts.append(summary)
if deliverables_dir:
parts.append(f"交付目录:{deliverables_dir}")
parts.append(tr("sub_agent_stats.deliverables", dir=deliverables_dir))
return "\n\n".join(parts)

View File

@ -131,12 +131,12 @@ def _format_sub_agent_stats(stats: Optional[Dict[str, Any]]) -> str:
web_pages = _to_int(stats.get("web_pages"))
commands = _to_int(stats.get("commands"))
lines = [
f"调用了{api_calls}",
f"阅读了{files_read}次文件",
f"编辑了{edit_files}次文件",
f"搜索了{searches}次内容",
f"查看了{web_pages}个网页",
f"运行了{commands}个指令",
tr("fmt_agent2.api_calls", n=api_calls),
tr("fmt_agent2.files_read", n=files_read),
tr("fmt_agent2.edit_files", n=edit_files),
tr("fmt_agent2.searches", n=searches),
tr("fmt_agent2.web_pages", n=web_pages),
tr("fmt_agent2.commands", n=commands),
]
return "\n".join(lines)
@ -147,14 +147,21 @@ def _format_create_sub_agent(result_data: Dict[str, Any]) -> str:
display_name = result_data.get("display_name")
status = result_data.get("status")
if display_name:
return f"{display_name} 已创建(状态 {status or 'running'})。"
return tr("fmt_agent2.created_with_status", name=display_name, status=status or "running")
agent_id = result_data.get("agent_id")
task_id = result_data.get("task_id")
refs = result_data.get("copied_references") or []
ref_note = f",附带 {len(refs)} 份参考文件" if refs else ""
ref_note = tr("fmt_agent2.ref_count_note", n=len(refs)) if refs else ""
deliver_dir = result_data.get("deliverables_dir")
deliver_note = f",交付目录: {deliver_dir}" if deliver_dir else ""
header = f"子智能体 #{agent_id} 已创建task_id={task_id},状态 {status}{ref_note}{deliver_note})。"
deliver_note = tr("fmt_agent2.deliver_dir_note", dir=deliver_dir) if deliver_dir else ""
header = tr(
"fmt_agent2.created_header",
agent_id=agent_id,
task_id=task_id,
status=status,
ref_note=ref_note,
deliver_note=deliver_note,
)
stats_text = _format_sub_agent_stats(
result_data.get("stats") or (result_data.get("final_result") or {}).get("stats")
)
@ -166,7 +173,7 @@ def _format_create_sub_agent(result_data: Dict[str, Any]) -> str:
if stats_text:
lines.append(stats_text)
if status == "completed" and isinstance(elapsed_seconds, (int, float)):
lines.append(f"运行了{int(round(elapsed_seconds))}")
lines.append(tr("fmt_agent2.running_seconds", n=int(round(elapsed_seconds))))
if summary and status in {"completed", "failed", "timeout", "terminated"}:
lines.append(str(summary))
return "\n".join(lines)
@ -185,17 +192,21 @@ def _format_wait_sub_agent(result_data: Dict[str, Any]) -> str:
if result_data.get("success"):
copied_path = result_data.get("copied_path") or result_data.get("deliverables_path")
message = result_data.get("message") or tr("fmt_agent2.task_completed")
deliver_note = f"交付已复制到 {copied_path}" if copied_path else "交付目录已生成"
lines = [f"子智能体 #{agent_id}/{task_id} 完成"]
deliver_note = (
tr("fmt_agent2.copied_to", path=copied_path)
if copied_path
else tr("fmt_agent2.deliver_generated")
)
lines = [tr("fmt_agent2.completed_header", agent_id=agent_id, task_id=task_id)]
if stats_text:
lines.append(stats_text)
if isinstance(elapsed_seconds, (int, float)):
lines.append(f"运行了{int(round(elapsed_seconds))}")
lines.append(tr("fmt_agent2.running_seconds", n=int(round(elapsed_seconds))))
lines.append(message)
lines.append(deliver_note)
return "\n".join(lines)
message = result_data.get("message") or result_data.get("error") or tr("fmt_agent2.task_failed")
lines = [f"⚠️ 子智能体 #{agent_id}/{task_id} 状态 {status}"]
lines = [tr("fmt_agent2.abnormal_header", agent_id=agent_id, task_id=task_id, status=status)]
if stats_text:
lines.append(stats_text)
lines.append(message)
@ -206,14 +217,14 @@ def _format_get_sub_agent_status(result_data: Dict[str, Any]) -> str:
return _format_failure("get_sub_agent_status", result_data)
results = result_data.get("results") or []
if not results:
return "未找到子智能体状态。"
return tr("fmt_agent2.no_status_results")
blocks = []
for item in results:
# 多智能体模式:优先使用角色内编号显示名,不暴露全局 agent_id
agent_id = item.get("agent_id")
label = item.get("display_name") or f"#{agent_id}"
if not item.get("found"):
blocks.append(f"子智能体 {label} 不存在。")
blocks.append(tr("fmt_agent2.label_not_found", label=label))
continue
status = item.get("status")
summary = None
@ -229,17 +240,17 @@ def _format_get_sub_agent_status(result_data: Dict[str, Any]) -> str:
stats_text = _format_sub_agent_stats(item.get("stats"))
if status == "completed":
lines = [f"子智能体 {label} 已完成"]
lines = [tr("fmt_agent2.label_completed", label=label)]
elif status == "terminated":
lines = [f"子智能体 {label} 已终止"]
lines = [tr("fmt_agent2.label_terminated", label=label)]
elif status in {"failed", "timeout"}:
lines = [f"⚠️ 子智能体 {label} 状态 {status}"]
lines = [tr("fmt_agent2.label_abnormal", label=label, status=status)]
else:
lines = [f"子智能体 {label} 状态: {status}"]
lines = [tr("fmt_agent2.label_status", label=label, status=status)]
if stats_text:
lines.append(stats_text)
if status == "completed" and isinstance(elapsed_seconds, (int, float)):
lines.append(f"运行了{int(round(elapsed_seconds))}")
lines.append(tr("fmt_agent2.running_seconds", n=int(round(elapsed_seconds))))
if summary:
lines.append(str(summary))
blocks.append("\n".join(lines))
@ -251,7 +262,7 @@ def _format_close_sub_agent(result_data: Dict[str, Any]) -> str:
message = result_data.get("message") or tr("fmt_agent2.closed")
task_id = result_data.get("task_id")
status = result_data.get("status")
status_note = f"(状态 {status}" if status else ""
status_note = tr("fmt_agent2.status_note", status=status) if status else ""
return f"{message}{status_note}task_id={task_id}"
@ -260,12 +271,12 @@ def _format_terminate_sub_agent(result_data: Dict[str, Any]) -> str:
return _format_failure("terminate_sub_agent", result_data)
display_name = result_data.get("display_name")
if display_name:
return f"已强制关闭子智能体 {display_name}"
return tr("fmt_agent2.force_closed_display", name=display_name)
agent_id = result_data.get("agent_id")
task_id = result_data.get("task_id")
message = result_data.get("message") or tr("fmt_agent2.force_closed")
if agent_id is not None:
return f"已强制关闭子智能体 #{agent_id}task_id={task_id})。"
return tr("fmt_agent2.force_closed_id", agent_id=agent_id, task_id=task_id)
return f"{message}task_id={task_id}"
@ -274,11 +285,11 @@ def _format_send_message_to_sub_agent(result_data: Dict[str, Any]) -> str:
return _format_failure("send_message_to_sub_agent", result_data)
display_name = result_data.get("display_name")
if display_name:
return f"已向 {display_name} 发送消息。"
return tr("fmt_agent2.sent_to_display", name=display_name)
agent_id = result_data.get("agent_id")
if agent_id is not None:
return f"已向子智能体 #{agent_id} 发送消息。"
return "消息已发送。"
return tr("fmt_agent2.sent_to_id", agent_id=agent_id)
return tr("fmt_agent2.sent_plain")
def _format_stop_sub_agent(result_data: Dict[str, Any]) -> str:
@ -291,7 +302,7 @@ def _format_stop_sub_agent(result_data: Dict[str, Any]) -> str:
return message
agent_id = result_data.get("agent_id")
if agent_id is not None:
return f"已暂停子智能体 #{agent_id}{message}"
return tr("fmt_agent2.paused_id", agent_id=agent_id, message=message)
return message
@ -300,8 +311,8 @@ def _format_answer_sub_agent_question(result_data: Dict[str, Any]) -> str:
return _format_failure("answer_sub_agent_question", result_data)
question_id = result_data.get("question_id")
if question_id:
return f"已向子智能体回复问题 {question_id}"
return "回复已发送。"
return tr("fmt_agent2.answered_question", question_id=question_id)
return tr("fmt_agent2.answered_plain")
def _format_create_custom_agent(result_data: Dict[str, Any]) -> str: