mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-11-08 03:30:34 +00:00
* feat(subagents): continue native batches and honor item dependencies * docs: link native batch follow-up changelog entries * test(migrations): pin isolated history-seed upgrade to 0050 * fix(subagents): preserve follow-up scope, blockers and fair scheduling * test(memory): freeze capacity evaluation time * fix(subagents): guard follow-up admission and retain run controls * fix(subagents): enforce runs:cancel on tool cancellation of follow-up batches * style(tests): format test_batch_followup_host with ruff * fix(subagents): drop the admission checkpoint check and keep SDK cancel compatible The checkpoint check inside run admission waited on the checkpoint saver's lock while holding the SQLite write lock, which could make another chat's checkpoint write fail. Remove it; dispatch still checks the conversation before admission. A follow-up waiting for an unanswered question now keeps its claim until the lease expires instead of rereading the conversation every poll. Cancel passes the caller's run authority only to submitters that take it, so existing SDK submitters keep the two-argument call. Document that retrying a prerequisite by hand does not requeue its blocked items, and list 0052 as the migration head. --------- Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
110 lines
5.1 KiB
Python
110 lines
5.1 KiB
Python
"""Prompt-contract tests for benefit-based subagent routing."""
|
|
|
|
from deerflow.agents.lead_agent import prompt as prompt_module
|
|
from deerflow.subagents.builtins.bash_agent import BASH_AGENT_CONFIG
|
|
from deerflow.subagents.builtins.general_purpose import GENERAL_PURPOSE_CONFIG
|
|
from deerflow.tools.builtins.task_tool import task_tool
|
|
|
|
|
|
def _build_section(monkeypatch, names: list[str] | None = None, max_concurrent: int = 3) -> str:
|
|
monkeypatch.setattr(prompt_module, "get_available_subagent_names", lambda: names or ["general-purpose"])
|
|
return prompt_module._build_subagent_section(max_concurrent)
|
|
|
|
|
|
def test_routing_requires_clear_net_benefit(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch)
|
|
|
|
assert "Default to direct execution" in section
|
|
assert "Do not delegate merely because a task is complex" in section
|
|
assert "Delegate only when the expected benefit is clearly greater than the expected cost" in section
|
|
assert "parallel wall-clock savings" in section
|
|
assert "specialist capability" in section
|
|
assert "context isolation" in section
|
|
assert "duplicate context and repository discovery" in section
|
|
assert "coordination and synthesis" in section
|
|
assert "state-conflict risk" in section
|
|
assert "side-effect risk" in section
|
|
|
|
|
|
def test_durable_batch_guidance_allows_only_explicit_prerequisites(monkeypatch) -> None:
|
|
monkeypatch.setattr(prompt_module, "get_available_subagent_names", lambda: ["general-purpose"])
|
|
section = prompt_module._build_subagent_section(3, batch_enabled=True)
|
|
assert "depends_on" in section
|
|
assert "must not depend on another item's output" not in section
|
|
assert "simultaneously runnable items independent" in section
|
|
assert "depends_on" not in prompt_module._build_subagent_section(3, batch_enabled=False)
|
|
|
|
|
|
def test_hard_vetoes_apply_to_parallel_dispatch_not_single_agent_chains(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch)
|
|
|
|
assert "Hard vetoes for parallel dispatch" in section
|
|
assert "Inter-agent dependencies" in section
|
|
assert "overlapping files, shared mutable state, or external side effects" in section
|
|
assert "A bounded sequential chain may still be delegated to one subagent" in section
|
|
assert "Delegation costs and negative signals" in section
|
|
assert "Duplicate discovery" in section
|
|
assert "Cheap direct path" in section
|
|
|
|
|
|
def test_later_batches_retain_within_batch_parallel_benefit(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch)
|
|
|
|
assert "Re-evaluate the remaining work after every batch" in section
|
|
assert "Later batches cannot overlap earlier batches" in section
|
|
assert "material within-batch parallel savings" in section
|
|
assert "Use the fewest subagents needed" in section
|
|
|
|
|
|
def test_hard_limit_warning_is_emphatic_and_explains_lost_work(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch)
|
|
|
|
assert "HARD LIMITS - NON-NEGOTIABLE" in section
|
|
assert "MAXIMUM 3 `task` CALLS PER RESPONSE - NEVER emit more" in section
|
|
assert "VIOLATION IS A HARD ERROR" in section
|
|
assert "Excess calls are discarded and their work is lost" in section
|
|
|
|
|
|
def test_multi_batch_example_preserves_reassessment_and_synthesis(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch)
|
|
|
|
assert "Multi-batch example (limit 3)" in section
|
|
assert "Batch 1: launch up to 3 independent scopes" in section
|
|
assert "Wait for the batch, then re-evaluate" in section
|
|
assert "Batch 2" in section
|
|
assert "Synthesize all retained results" in section
|
|
|
|
|
|
def test_single_subagent_limit_omits_parallel_batch_guidance(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch, max_concurrent=1)
|
|
|
|
assert "Expected benefit = specialist capability + context isolation" in section
|
|
assert "delegate only for material specialist or context-isolation benefit" in section
|
|
assert "Parallel dispatch cannot reduce wall-clock latency" in section
|
|
assert "parallel wall-clock savings" not in section
|
|
assert "material within-batch parallel savings" not in section
|
|
assert "Multi-batch example" not in section
|
|
assert "Compare independent providers" not in section
|
|
|
|
|
|
def test_general_purpose_and_task_descriptions_match_routing_policy() -> None:
|
|
tool_description = task_tool.description
|
|
role_description = GENERAL_PURPOSE_CONFIG.description
|
|
|
|
assert "expected benefit" in tool_description
|
|
assert "independent" in tool_description
|
|
assert "Splitting dependent steps across parallel subagents" in tool_description
|
|
assert "clear delegation benefit" in role_description
|
|
assert "merely because it is sequential" in role_description
|
|
assert "bounded dependent chain may still be delegated" in role_description
|
|
|
|
|
|
def test_bash_descriptions_require_benefit_beyond_routine_commands(monkeypatch) -> None:
|
|
section = _build_section(monkeypatch, ["general-purpose", "bash"])
|
|
policy = "Routine git, build, test, or deploy operations are not sufficient reason to delegate"
|
|
|
|
assert policy in section
|
|
assert policy in task_tool.description
|
|
assert policy in BASH_AGENT_CONFIG.description
|
|
assert "Execute commands one at a time when they depend on each other" in BASH_AGENT_CONFIG.system_prompt
|