feat(sub-agent): 子智能体最大轮次支持个人空间自定义
- 原 task.py 硬编码 max_turns=50,改为从任务记录读取(None=默认50、0=无上限、N=N轮) - 个人空间-子智能体新增「最大执行轮次」设置项,PUT /api/multiagent/settings 支持 sub_agent_max_turns(null 删键恢复默认,负数 400) - 多智能体与传统后台两条创建链路均接入该配置
This commit is contained in:
parent
2c2c5aba4d
commit
2da6c87245
@ -1955,12 +1955,16 @@ class MainTerminalToolsExecutionMixin:
|
||||
task_message = build_master_dispatch_text(arguments.get("task", ""))
|
||||
summary_text = (arguments.get("summary") or f"{role.name}作业")[:80]
|
||||
thinking_mode = arguments.get("thinking_mode") or role.thinking_mode or "fast"
|
||||
# 读取子智能体压缩阈值配置
|
||||
# 读取子智能体压缩阈值与最大轮次配置
|
||||
_compress_threshold = 150_000
|
||||
_max_turns = None
|
||||
try:
|
||||
from modules.personalization_manager import load_personalization_config
|
||||
_prefs = load_personalization_config(data_dir) or {}
|
||||
_compress_threshold = int(_prefs.get("sub_agent_compress_threshold_tokens", 150_000))
|
||||
_raw_max_turns = _prefs.get("sub_agent_max_turns")
|
||||
if _raw_max_turns is not None:
|
||||
_max_turns = int(_raw_max_turns)
|
||||
except Exception:
|
||||
pass
|
||||
# 走原行 发事件创建(避免后期重建提供重复工能重费,直接使用 multi_agent_mode=True 调用)
|
||||
@ -1978,6 +1982,7 @@ class MainTerminalToolsExecutionMixin:
|
||||
system_prompt=system_prompt,
|
||||
task_message=task_message,
|
||||
compress_threshold_tokens=_compress_threshold,
|
||||
max_turns=_max_turns,
|
||||
)
|
||||
# 在多智能体模式下,子智能体是团队协作成员,不是传统后台任务。
|
||||
# run_in_background=False 避免触发后台完成通知轮询,保持主对话输入区可用。
|
||||
@ -1985,6 +1990,16 @@ class MainTerminalToolsExecutionMixin:
|
||||
logger.exception("[multi_agent] create_sub_agent failed")
|
||||
result = {"success": False, "error": str(exc)}
|
||||
else:
|
||||
# 读取子智能体最大轮次配置(None=默认50、0=无上限、N=N轮)
|
||||
_max_turns = None
|
||||
try:
|
||||
from modules.personalization_manager import load_personalization_config
|
||||
_prefs = load_personalization_config(str(getattr(self, "data_dir", ""))) or {}
|
||||
_raw_max_turns = _prefs.get("sub_agent_max_turns")
|
||||
if _raw_max_turns is not None:
|
||||
_max_turns = int(_raw_max_turns)
|
||||
except Exception:
|
||||
pass
|
||||
result = self.sub_agent_manager.create_sub_agent(
|
||||
agent_id=arguments.get("agent_id"),
|
||||
summary=arguments.get("summary", ""),
|
||||
@ -1993,7 +2008,8 @@ class MainTerminalToolsExecutionMixin:
|
||||
run_in_background=arguments.get("run_in_background", False),
|
||||
timeout_seconds=arguments.get("timeout_seconds"),
|
||||
thinking_mode=arguments.get("thinking_mode"),
|
||||
conversation_id=self.context_manager.current_conversation_id
|
||||
conversation_id=self.context_manager.current_conversation_id,
|
||||
max_turns=_max_turns,
|
||||
)
|
||||
|
||||
# 如果不是后台运行,阻塞等待完成
|
||||
|
||||
@ -239,6 +239,7 @@ class SubAgentManager(SubAgentStateMixin, SubAgentStatsMixin, SubAgentCreationMi
|
||||
system_prompt: Optional[str] = None,
|
||||
task_message: Optional[str] = None,
|
||||
compress_threshold_tokens: Optional[int] = None,
|
||||
max_turns: Optional[int] = None,
|
||||
) -> Dict:
|
||||
"""创建子智能体任务并启动协程。
|
||||
|
||||
@ -329,6 +330,7 @@ class SubAgentManager(SubAgentStateMixin, SubAgentStatsMixin, SubAgentCreationMi
|
||||
"display_name": display_name,
|
||||
"execution_mode": "in_process",
|
||||
"compress_threshold_tokens": compress_threshold_tokens,
|
||||
"max_turns": max_turns,
|
||||
"container_name": None,
|
||||
}
|
||||
# 多智能体模式:为该会话创建或复用 MultiAgentState
|
||||
|
||||
@ -79,6 +79,14 @@ class SubAgentTask:
|
||||
# 上下文压缩配置
|
||||
# 默认阈值 150k tokens,可由外部覆盖(如个人空间子智能体管理配置)
|
||||
self.compress_threshold_tokens: int = int(task_record.get("compress_threshold_tokens") or 150_000)
|
||||
# 最大执行轮次(对应个人空间子智能体设置项 sub_agent_max_turns):
|
||||
# 未设置 → 默认 50;0(或负数)→ None 表示无上限;正整数 → 该值
|
||||
_raw_max_turns = task_record.get("max_turns")
|
||||
if _raw_max_turns is None:
|
||||
self.max_turns: Optional[int] = 50
|
||||
else:
|
||||
_max_turns_int = int(_raw_max_turns)
|
||||
self.max_turns = _max_turns_int if _max_turns_int > 0 else None
|
||||
self.current_context_tokens: int = 0
|
||||
self._compress_round: int = 0
|
||||
|
||||
@ -191,7 +199,7 @@ class SubAgentTask:
|
||||
tools.append(FINISH_TOOL)
|
||||
|
||||
start_time = time.time()
|
||||
max_turns = 50
|
||||
max_turns = self.max_turns # None 表示无上限(个人空间可配置,默认 50)
|
||||
turn = 0
|
||||
|
||||
while not self._cancelled:
|
||||
@ -262,7 +270,7 @@ class SubAgentTask:
|
||||
continue
|
||||
|
||||
turn += 1
|
||||
if turn > max_turns:
|
||||
if max_turns is not None and turn > max_turns:
|
||||
await self._write_failure("任务执行超过最大轮次限制", max_turns_exceeded=True)
|
||||
return
|
||||
|
||||
|
||||
@ -205,14 +205,17 @@ def get_multi_agent_settings_api():
|
||||
_, workspace = get_user_resources(username)
|
||||
if not workspace:
|
||||
return jsonify({"success": False, "error": "工作区未就绪"}), 503
|
||||
# 从个人化配置中读取子智能体压缩阈值
|
||||
# 从个人化配置中读取子智能体设置
|
||||
from modules.personalization_manager import load_personalization_config
|
||||
prefs = load_personalization_config(workspace.data_dir) or {}
|
||||
compress_threshold = prefs.get("sub_agent_compress_threshold_tokens", 150000)
|
||||
# 最大执行轮次:None(未设置)表示默认 50;0 表示无上限;正整数为该值
|
||||
max_turns = prefs.get("sub_agent_max_turns")
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"settings": {
|
||||
"sub_agent_compress_threshold_tokens": compress_threshold,
|
||||
"sub_agent_max_turns": max_turns,
|
||||
},
|
||||
})
|
||||
except Exception as exc:
|
||||
@ -232,15 +235,33 @@ def update_multi_agent_settings_api():
|
||||
return jsonify({"success": False, "error": "工作区未就绪"}), 503
|
||||
data = request.get_json() or {}
|
||||
settings = data.get("settings") or {}
|
||||
from modules.personalization_manager import load_personalization_config, save_personalization_config
|
||||
prefs = load_personalization_config(workspace.data_dir) or {}
|
||||
dirty = False
|
||||
# 更新子智能体压缩阈值
|
||||
threshold = settings.get("sub_agent_compress_threshold_tokens")
|
||||
if threshold is not None:
|
||||
threshold = int(threshold)
|
||||
if threshold < 10000:
|
||||
return jsonify({"success": False, "error": "压缩阈值不能小于 10000"}), 400
|
||||
from modules.personalization_manager import load_personalization_config, save_personalization_config
|
||||
prefs = load_personalization_config(workspace.data_dir) or {}
|
||||
prefs["sub_agent_compress_threshold_tokens"] = threshold
|
||||
dirty = True
|
||||
# 更新子智能体最大轮次(键存在才处理:None → 删除键恢复默认 50;0 → 无上限;正整数 → 该值)
|
||||
if "sub_agent_max_turns" in settings:
|
||||
max_turns_raw = settings.get("sub_agent_max_turns")
|
||||
if max_turns_raw is None:
|
||||
if prefs.pop("sub_agent_max_turns", None) is not None:
|
||||
dirty = True
|
||||
else:
|
||||
try:
|
||||
max_turns = int(max_turns_raw)
|
||||
except (TypeError, ValueError):
|
||||
return jsonify({"success": False, "error": "最大轮次必须是整数"}), 400
|
||||
if max_turns < 0:
|
||||
return jsonify({"success": False, "error": "最大轮次不能为负数(0 表示无上限)"}), 400
|
||||
prefs["sub_agent_max_turns"] = max_turns
|
||||
dirty = True
|
||||
if dirty:
|
||||
save_personalization_config(workspace.data_dir, prefs)
|
||||
return jsonify({"success": True})
|
||||
except Exception as exc:
|
||||
|
||||
@ -1772,6 +1772,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最大执行轮次配置 -->
|
||||
<div class="settings-action-row" style="margin-bottom: 16px">
|
||||
<span class="settings-row-copy">
|
||||
<span class="settings-row-title">最大执行轮次</span>
|
||||
<span class="settings-row-desc">
|
||||
子智能体单次任务的最大执行轮次(一轮 = 一次模型调用)。留空默认 50 轮;填 0 表示无上限(慎用,失控任务会持续消耗 API 额度)
|
||||
</span>
|
||||
</span>
|
||||
<div style="display: flex; gap: 6px; align-items: center">
|
||||
<input
|
||||
type="number"
|
||||
class="settings-number-input"
|
||||
v-model.number="subAgentMaxTurns"
|
||||
:min="0"
|
||||
:step="10"
|
||||
placeholder="50"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="settings-secondary-button"
|
||||
:disabled="subAgentSettingsSaving"
|
||||
@click="saveSubAgentSettings"
|
||||
>
|
||||
{{ subAgentSettingsSaving ? '保存中...' : '保存' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 角色列表 -->
|
||||
<div class="settings-section-header" style="margin-bottom: 8px">
|
||||
<span class="settings-section-title">角色列表</span>
|
||||
@ -2732,6 +2761,8 @@ watch(
|
||||
const subAgentRoles = ref<any[]>([]);
|
||||
const subAgentRolesLoading = ref(false);
|
||||
const subAgentCompressThreshold = ref(150000);
|
||||
/** 子智能体最大执行轮次:null/'' = 默认 50;0 = 无上限;正整数 = 该值 */
|
||||
const subAgentMaxTurns = ref<number | null>(null);
|
||||
const subAgentSettingsSaving = ref(false);
|
||||
const subAgentModels = ref<any[]>([]);
|
||||
const roleEditorOpen = ref(false);
|
||||
@ -2779,6 +2810,8 @@ const loadSubAgentSettings = async () => {
|
||||
const data = await resp.json();
|
||||
if (data.success && data.settings) {
|
||||
subAgentCompressThreshold.value = data.settings.sub_agent_compress_threshold_tokens || 150000;
|
||||
// 未设置时为 null,输入框留空(placeholder 提示默认 50)
|
||||
subAgentMaxTurns.value = data.settings.sub_agent_max_turns ?? null;
|
||||
}
|
||||
} catch (e) {
|
||||
// 静默处理
|
||||
@ -2792,7 +2825,16 @@ const saveSubAgentSettings = async () => {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'same-origin',
|
||||
body: JSON.stringify({ settings: { sub_agent_compress_threshold_tokens: subAgentCompressThreshold.value } })
|
||||
body: JSON.stringify({
|
||||
settings: {
|
||||
sub_agent_compress_threshold_tokens: subAgentCompressThreshold.value,
|
||||
// 留空(null/'')提交 null → 服务端删除该键恢复默认 50;0 → 无上限
|
||||
sub_agent_max_turns:
|
||||
subAgentMaxTurns.value === null || (subAgentMaxTurns.value as unknown) === ''
|
||||
? null
|
||||
: Number(subAgentMaxTurns.value)
|
||||
}
|
||||
})
|
||||
});
|
||||
} finally {
|
||||
subAgentSettingsSaving.value = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user