Files
deerflow-code/offline-backend-20260512/backend/tests/test_roundtable_seat_modes.py
董宏凯andClaude Opus 4.8 3ab674e438 feat(roundtable): Step2 提速四件套 + 方案总结报告意图对齐
Step2 提速:
- 每席位推理深度覆盖(seat_mode): 业务链条逐席位可配 flash/thinking/pro/ultra,
  覆盖链条级 seatMode;前台三处 dispatch + 后台 job 透传, run_seat 用 _SEAT_MODE_PARAMS 解析
- recommend 后端批内并行: engine._run_recommend 由串行 for 改 asyncio.gather(对齐前端)
- 研讨前并行取数两段式(gather_first, 业务链条 opt-in): 研讨前全席位并行只取数,
  产出作为共享资料注入研讨轮(_seat_message gathered + 前台 runGatherPhase);
  新增 gather_first 列(migration 20260620_02 + idempotent SQL)
- 削减重复广播: suppressPreBroadcast 扩到 recommend(round>=2)/chain(i>0)/chain收口,
  保留首轮背景打底,省 N 次读写大 thread

方案总结报告效果:
- summary 加"意图对齐+缺口核对+防幻觉护栏": SOUL + 后端 build_summary_prompt +
  前端 buildSummaryPrompt 三处对齐; 推断标注(综合推断)、缺真实信息标注待补充交给用户、绝不凭空编

Tests: test_roundtable_seat_modes / test_roundtable_gather_phase / test_roundtable_summary_prompt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:28:02 +08:00

119 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""每席位「推理深度覆盖」(seat_modes)在进程内网关 run_seat 的解析测试。
语义(与前端 useStep2Orchestration「覆盖优先、否则跟随链条级 selectedMode」逐席位对齐):
- 席位命中 seat_modes → 用该档(_SEAT_MODE_PARAMS)派生的 thinking/reasoning/subagent **覆盖**
作业级 seat_*(不论作业级是什么);
- 席位未命中 → 跟随作业级 seat_thinking_enabled / seat_reasoning_effort / seat_subagent_enabled;
- 作业级仍为 None → 落 policy("seat") 默认。
只验证传给 _run_agent_turn 的三个运行参数,故把 _run_agent_turn / policy / 文本解析全部打桩,
不触发真实 LLM / 沙箱。
"""
from __future__ import annotations
from types import SimpleNamespace
import pytest
@pytest.fixture
def _gw(monkeypatch):
"""构造一个被打桩的 InProcessRoundtableGateway,返回 (build, captured)。
build(seat_modes=..., seat_thinking_enabled=..., seat_reasoning_effort=..., seat_subagent_enabled=...)
返回 gateway 实例;captured 记录最近一次 _run_agent_turn 收到的 thinking/reasoning/subagent。
"""
import app.gateway.roundtable_inprocess_gateway as gw
captured: dict[str, object] = {}
async def _fake_run_agent_turn(*args, **kwargs):
captured["thinking_enabled"] = kwargs.get("thinking_enabled")
captured["reasoning_effort"] = kwargs.get("reasoning_effort")
captured["subagent_enabled"] = kwargs.get("subagent_enabled")
return []
# policy("seat") 默认:thinking=True / reasoning="medium"(= 现有席位默认 pro 行为)。
monkeypatch.setattr(
gw,
"roundtable_run_policy",
lambda role: SimpleNamespace(excluded_tools=[], skill_stop_names=[], thinking_enabled=True, reasoning_effort="medium"),
)
monkeypatch.setattr(gw, "_run_agent_turn", _fake_run_agent_turn)
# 技能强化与本测试无关 → 原样返回任务,避免触发 load_agent_config / skill storage。
monkeypatch.setattr(gw, "append_seat_skill_directive", lambda agent_id, task, **k: task)
monkeypatch.setattr(gw, "_latest_ai_text", lambda messages: "")
monkeypatch.setattr(gw, "_tool_calls_since_last_human", lambda messages: [])
def _build(**kwargs):
return gw.InProcessRoundtableGateway(SimpleNamespace(state=SimpleNamespace()), user_id="u1", agents=[], **kwargs)
return _build, captured
async def _run(gateway):
await gateway.run_seat(thread_ids={"seat-1": "tid-1"}, agent_id="seat-1", task="任务", model="m")
@pytest.mark.asyncio
async def test_seat_mode_override_wins_over_job_level(_gw):
"""席位配了 flash → 覆盖作业级 pro(thinking off / minimal / 无子代理)。"""
build, captured = _gw
gateway = build(
seat_modes={"seat-1": "flash"},
seat_thinking_enabled=True,
seat_reasoning_effort="high",
seat_subagent_enabled=True,
)
await _run(gateway)
assert captured["thinking_enabled"] is False
assert captured["reasoning_effort"] == "minimal"
assert captured["subagent_enabled"] is False
@pytest.mark.asyncio
async def test_ultra_override_enables_subagent(_gw):
build, captured = _gw
gateway = build(seat_modes={"seat-1": "ultra"})
await _run(gateway)
assert captured["thinking_enabled"] is True
assert captured["reasoning_effort"] == "high"
assert captured["subagent_enabled"] is True
@pytest.mark.asyncio
async def test_no_override_follows_job_level(_gw):
"""席位未命中 seat_modes → 跟随作业级 seat_*。"""
build, captured = _gw
gateway = build(
seat_modes={"other": "flash"},
seat_thinking_enabled=True,
seat_reasoning_effort="low",
seat_subagent_enabled=False,
)
await _run(gateway)
assert captured["thinking_enabled"] is True
assert captured["reasoning_effort"] == "low"
assert captured["subagent_enabled"] is False
@pytest.mark.asyncio
async def test_no_override_no_job_level_falls_back_to_policy(_gw):
"""既无席位覆盖、作业级也为 None → 落 policy 默认(thinking=True / medium)。"""
build, captured = _gw
gateway = build(seat_modes={})
await _run(gateway)
assert captured["thinking_enabled"] is True
assert captured["reasoning_effort"] == "medium"
assert captured["subagent_enabled"] is False
@pytest.mark.asyncio
async def test_unknown_mode_value_ignored_falls_through(_gw):
"""非法档位值(前端不会下发,防御)→ 安全忽略,回退作业级/policy。"""
build, captured = _gw
gateway = build(seat_modes={"seat-1": "bogus"}, seat_reasoning_effort="low", seat_thinking_enabled=True)
await _run(gateway)
assert captured["thinking_enabled"] is True
assert captured["reasoning_effort"] == "low"