fix(personalization): 修复子智能体最大执行轮次保存丢失,多智能体豁免该上限
- sanitize_personalization_payload 白名单注册 sub_agent_max_turns / sub_agent_compress_threshold_tokens(此前保存时被静默丢弃,刷新回默认) - PUT /api/multiagent/settings 恢复默认由 pop() 改为写显式 null, 绕开 save 的 fallback=existing 复活旧值问题 - task.py:multi_agent_mode 任务一律不设轮次上限(含历史任务恢复), 该设置仅对传统后台子智能体生效 - 个人中心文案标明适用范围
This commit is contained in:
parent
ff927d6739
commit
34bb5d61cc
@ -2032,16 +2032,13 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
task_message = build_master_dispatch_text(arguments.get("task", ""))
|
task_message = build_master_dispatch_text(arguments.get("task", ""))
|
||||||
summary_text = (arguments.get("summary") or f"{role.name}作业")[:80]
|
summary_text = (arguments.get("summary") or f"{role.name}作业")[:80]
|
||||||
thinking_mode = arguments.get("thinking_mode") or role.thinking_mode or "fast"
|
thinking_mode = arguments.get("thinking_mode") or role.thinking_mode or "fast"
|
||||||
# 读取子智能体压缩阈值与最大轮次配置
|
# 读取子智能体压缩阈值配置(多智能体成员长期存在,不设轮次上限,
|
||||||
|
# sub_agent_max_turns 仅对传统后台子智能体生效,这里不读取)
|
||||||
_compress_threshold = 150_000
|
_compress_threshold = 150_000
|
||||||
_max_turns = None
|
|
||||||
try:
|
try:
|
||||||
from modules.personalization_manager import load_personalization_config
|
from modules.personalization_manager import load_personalization_config
|
||||||
_prefs = load_personalization_config(data_dir) or {}
|
_prefs = load_personalization_config(data_dir) or {}
|
||||||
_compress_threshold = int(_prefs.get("sub_agent_compress_threshold_tokens", 150_000))
|
_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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# 走原行 发事件创建(避免后期重建提供重复工能重费,直接使用 multi_agent_mode=True 调用)
|
# 走原行 发事件创建(避免后期重建提供重复工能重费,直接使用 multi_agent_mode=True 调用)
|
||||||
@ -2059,7 +2056,7 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
system_prompt=system_prompt,
|
system_prompt=system_prompt,
|
||||||
task_message=task_message,
|
task_message=task_message,
|
||||||
compress_threshold_tokens=_compress_threshold,
|
compress_threshold_tokens=_compress_threshold,
|
||||||
max_turns=_max_turns,
|
max_turns=None, # 多智能体成员不设轮次上限(task.py 对 multi_agent_mode 亦强制豁免)
|
||||||
)
|
)
|
||||||
# 在多智能体模式下,子智能体是团队协作成员,不是传统后台任务。
|
# 在多智能体模式下,子智能体是团队协作成员,不是传统后台任务。
|
||||||
# run_in_background=False 避免触发后台完成通知轮询,保持主对话输入区可用。
|
# run_in_background=False 避免触发后台完成通知轮询,保持主对话输入区可用。
|
||||||
|
|||||||
@ -119,6 +119,9 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
|
|||||||
"goal_end_conditions": ["max_turns"], # 结束方式,可多选:max_turns / max_tokens
|
"goal_end_conditions": ["max_turns"], # 结束方式,可多选:max_turns / max_tokens
|
||||||
"goal_max_turns": GOAL_MAX_TURNS_DEFAULT, # 最多自动续命轮数
|
"goal_max_turns": GOAL_MAX_TURNS_DEFAULT, # 最多自动续命轮数
|
||||||
"goal_max_tokens": None, # 累计(输入+输出)token 上限;None 表示不启用
|
"goal_max_tokens": None, # 累计(输入+输出)token 上限;None 表示不启用
|
||||||
|
# 传统模式子智能体设置(个人空间-子智能体管理;与多智能体无关)
|
||||||
|
"sub_agent_compress_threshold_tokens": 150000, # 子智能体上下文压缩阈值,最小 10000
|
||||||
|
"sub_agent_max_turns": None, # 子智能体最大执行轮次:None-默认 50 / 0-无上限 / 正整数-该值
|
||||||
}
|
}
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -630,9 +633,48 @@ def sanitize_personalization_payload(
|
|||||||
base.get("goal_max_tokens"), min_value=GOAL_MAX_TOKENS_MIN, max_value=GOAL_MAX_TOKENS_MAX
|
base.get("goal_max_tokens"), min_value=GOAL_MAX_TOKENS_MIN, max_value=GOAL_MAX_TOKENS_MAX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 传统模式子智能体:上下文压缩阈值(最小 10000,与 PUT /api/multiagent/settings 校验一致)
|
||||||
|
if "sub_agent_compress_threshold_tokens" in data:
|
||||||
|
base["sub_agent_compress_threshold_tokens"] = (
|
||||||
|
_sanitize_optional_int(
|
||||||
|
data.get("sub_agent_compress_threshold_tokens"),
|
||||||
|
min_value=10000,
|
||||||
|
max_value=10_000_000,
|
||||||
|
)
|
||||||
|
or 150000
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
base["sub_agent_compress_threshold_tokens"] = (
|
||||||
|
_sanitize_optional_int(
|
||||||
|
base.get("sub_agent_compress_threshold_tokens"),
|
||||||
|
min_value=10000,
|
||||||
|
max_value=10_000_000,
|
||||||
|
)
|
||||||
|
or 150000
|
||||||
|
)
|
||||||
|
|
||||||
|
# 传统模式子智能体:最大执行轮次(None-默认 50 / 0-无上限 / 正整数-该值)
|
||||||
|
if "sub_agent_max_turns" in data:
|
||||||
|
base["sub_agent_max_turns"] = _sanitize_sub_agent_max_turns(data.get("sub_agent_max_turns"))
|
||||||
|
else:
|
||||||
|
base["sub_agent_max_turns"] = _sanitize_sub_agent_max_turns(base.get("sub_agent_max_turns"))
|
||||||
|
|
||||||
return base
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_sub_agent_max_turns(value: Any) -> Optional[int]:
|
||||||
|
"""清洗子智能体最大轮次:None/''/非法值/负数 → None(未设置,下游默认 50);0 → 无上限;正整数 → 该值。"""
|
||||||
|
if value is None or value == "" or isinstance(value, bool):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
parsed = int(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
if parsed < 0:
|
||||||
|
return None
|
||||||
|
return min(parsed, 100_000) # 防御性上限;需要更大时用 0 表示无上限
|
||||||
|
|
||||||
|
|
||||||
def _sanitize_goal_end_conditions(value: Any) -> list:
|
def _sanitize_goal_end_conditions(value: Any) -> list:
|
||||||
"""清洗目标模式结束方式列表,保证至少包含 max_turns。"""
|
"""清洗目标模式结束方式列表,保证至少包含 max_turns。"""
|
||||||
cleaned: list = []
|
cleaned: list = []
|
||||||
|
|||||||
@ -79,14 +79,18 @@ class SubAgentTask:
|
|||||||
# 上下文压缩配置
|
# 上下文压缩配置
|
||||||
# 默认阈值 150k tokens,可由外部覆盖(如个人空间子智能体管理配置)
|
# 默认阈值 150k tokens,可由外部覆盖(如个人空间子智能体管理配置)
|
||||||
self.compress_threshold_tokens: int = int(task_record.get("compress_threshold_tokens") or 150_000)
|
self.compress_threshold_tokens: int = int(task_record.get("compress_threshold_tokens") or 150_000)
|
||||||
# 最大执行轮次(对应个人空间子智能体设置项 sub_agent_max_turns):
|
# 最大执行轮次(对应个人空间子智能体设置项 sub_agent_max_turns,仅传统模式生效):
|
||||||
# 未设置 → 默认 50;0(或负数)→ None 表示无上限;正整数 → 该值
|
# 未设置 → 默认 50;0(或负数)→ None 表示无上限;正整数 → 该值
|
||||||
_raw_max_turns = task_record.get("max_turns")
|
# 多智能体模式的子智能体是长期协作成员,一律无上限,不受该设置约束
|
||||||
if _raw_max_turns is None:
|
if multi_agent_mode:
|
||||||
self.max_turns: Optional[int] = 50
|
self.max_turns: Optional[int] = None
|
||||||
else:
|
else:
|
||||||
_max_turns_int = int(_raw_max_turns)
|
_raw_max_turns = task_record.get("max_turns")
|
||||||
self.max_turns = _max_turns_int if _max_turns_int > 0 else None
|
if _raw_max_turns is None:
|
||||||
|
self.max_turns = 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.current_context_tokens: int = 0
|
||||||
self._compress_round: int = 0
|
self._compress_round: int = 0
|
||||||
|
|
||||||
|
|||||||
@ -250,8 +250,11 @@ def update_multi_agent_settings_api():
|
|||||||
if "sub_agent_max_turns" in settings:
|
if "sub_agent_max_turns" in settings:
|
||||||
max_turns_raw = settings.get("sub_agent_max_turns")
|
max_turns_raw = settings.get("sub_agent_max_turns")
|
||||||
if max_turns_raw is None:
|
if max_turns_raw is None:
|
||||||
if prefs.pop("sub_agent_max_turns", None) is not None:
|
# 置空恢复默认 50:写入显式 null(sanitize 会保留 None),不能 pop——
|
||||||
|
# save_personalization_config 的 fallback=existing 会把已存在的旧值带回来
|
||||||
|
if prefs.get("sub_agent_max_turns") is not None:
|
||||||
dirty = True
|
dirty = True
|
||||||
|
prefs["sub_agent_max_turns"] = None
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
max_turns = int(max_turns_raw)
|
max_turns = int(max_turns_raw)
|
||||||
|
|||||||
@ -1594,7 +1594,7 @@
|
|||||||
<span class="settings-row-copy">
|
<span class="settings-row-copy">
|
||||||
<span class="settings-row-title">最大执行轮次</span>
|
<span class="settings-row-title">最大执行轮次</span>
|
||||||
<span class="settings-row-desc">
|
<span class="settings-row-desc">
|
||||||
子智能体单次任务的最大执行轮次(一轮 = 一次模型调用)。留空默认 50 轮;填 0 表示无上限(慎用,失控任务会持续消耗 API 额度)
|
传统后台子智能体单次任务的最大执行轮次(一轮 = 一次模型调用)。留空默认 50 轮;填 0 表示无上限(慎用,失控任务会持续消耗 API 额度)。多智能体模式的团队成员是长期协作角色,不受此限制
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<div style="display: flex; gap: 6px; align-items: center">
|
<div style="display: flex; gap: 6px; align-items: center">
|
||||||
@ -2580,7 +2580,7 @@ watch(
|
|||||||
const subAgentRoles = ref<any[]>([]);
|
const subAgentRoles = ref<any[]>([]);
|
||||||
const subAgentRolesLoading = ref(false);
|
const subAgentRolesLoading = ref(false);
|
||||||
const subAgentCompressThreshold = ref(150000);
|
const subAgentCompressThreshold = ref(150000);
|
||||||
/** 子智能体最大执行轮次:null/'' = 默认 50;0 = 无上限;正整数 = 该值 */
|
/** 子智能体最大执行轮次(仅传统后台子智能体;多智能体成员不受限):null/'' = 默认 50;0 = 无上限;正整数 = 该值 */
|
||||||
const subAgentMaxTurns = ref<number | null>(null);
|
const subAgentMaxTurns = ref<number | null>(null);
|
||||||
const subAgentSettingsSaving = ref(false);
|
const subAgentSettingsSaving = ref(false);
|
||||||
const subAgentModels = ref<any[]>([]);
|
const subAgentModels = ref<any[]>([]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user