mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add background agent support to harness agent (#6155)
* Add background agent support to harness agent * Address PR comments
This commit is contained in:
committed by
GitHub
Unverified
parent
78d175a1e2
commit
52a8045bb6
@@ -14,12 +14,13 @@ import logging
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from .._agents import Agent
|
||||
from .._agents import Agent, SupportsAgentRun
|
||||
from .._clients import SupportsWebSearchTool
|
||||
from .._compaction import CompactionProvider, ContextWindowCompactionStrategy, ToolResultCompactionStrategy
|
||||
from .._feature_stage import ExperimentalFeature, experimental
|
||||
from .._sessions import ContextProvider, HistoryProvider, InMemoryHistoryProvider
|
||||
from .._skills import SkillsProvider
|
||||
from ._background_agents import BackgroundAgentsProvider
|
||||
from ._memory import MemoryContextProvider, MemoryStore
|
||||
from ._mode import AgentModeProvider
|
||||
from ._todo import TodoProvider
|
||||
@@ -103,6 +104,8 @@ def _assemble_context_providers(
|
||||
memory_store: MemoryStore | None,
|
||||
skills_provider: SkillsProvider | None,
|
||||
skills_paths: Sequence[str] | None,
|
||||
background_agents: Sequence[SupportsAgentRun] | None,
|
||||
background_agents_instructions: str | None,
|
||||
extra_context_providers: Sequence[ContextProvider] | None,
|
||||
) -> list[ContextProvider]:
|
||||
"""Assemble the ordered list of context providers."""
|
||||
@@ -130,6 +133,10 @@ def _assemble_context_providers(
|
||||
if skills_paths:
|
||||
providers.append(SkillsProvider.from_paths(*skills_paths))
|
||||
|
||||
# Background agents are opt-in: only added when agents are provided.
|
||||
if background_agents:
|
||||
providers.append(BackgroundAgentsProvider(background_agents, instructions=background_agents_instructions))
|
||||
|
||||
# Append any user-supplied additional providers.
|
||||
if extra_context_providers:
|
||||
providers.extend(extra_context_providers)
|
||||
@@ -165,6 +172,8 @@ def create_harness_agent(
|
||||
memory_store: MemoryStore | None = None,
|
||||
skills_provider: SkillsProvider | None = None,
|
||||
skills_paths: Sequence[str] | None = None,
|
||||
background_agents: Sequence[SupportsAgentRun] | None = None,
|
||||
background_agents_instructions: str | None = None,
|
||||
disable_web_search: bool = False,
|
||||
otel_provider_name: str | None = None,
|
||||
context_providers: Sequence[ContextProvider] | None = None,
|
||||
@@ -182,6 +191,7 @@ def create_harness_agent(
|
||||
- **AgentModeProvider** — plan/execute mode tracking
|
||||
- **MemoryContextProvider** — file-based durable memory (when ``memory_store`` provided)
|
||||
- **SkillsProvider** — skill discovery and progressive loading
|
||||
- **BackgroundAgentsProvider** — delegate work to background sub-agents
|
||||
- **OpenTelemetry** — observability via ``AgentTelemetryLayer``
|
||||
|
||||
Each feature can be disabled or customized via keyword arguments.
|
||||
@@ -253,6 +263,13 @@ def create_harness_agent(
|
||||
skills_paths: Paths for file-based skill discovery (looks for SKILL.md files).
|
||||
Can be combined with ``skills_provider``. When neither ``skills_provider``
|
||||
nor ``skills_paths`` is provided, no SkillsProvider is added.
|
||||
background_agents: Collection of agents available for background task delegation.
|
||||
When provided, a ``BackgroundAgentsProvider`` is automatically included,
|
||||
enabling the agent to start, monitor, and retrieve results from background tasks.
|
||||
Each agent must have a non-empty, unique name (case-insensitive).
|
||||
background_agents_instructions: Optional instruction override for the
|
||||
``BackgroundAgentsProvider``. May include ``{background_agents}`` placeholder
|
||||
which will be replaced with the agent listing.
|
||||
disable_web_search: When True, skip automatic web search tool inclusion.
|
||||
When False (default), the web search tool is automatically added if the
|
||||
client implements SupportsWebSearchTool. A warning is logged if the client
|
||||
@@ -302,6 +319,8 @@ def create_harness_agent(
|
||||
memory_store=memory_store,
|
||||
skills_provider=skills_provider,
|
||||
skills_paths=skills_paths,
|
||||
background_agents=background_agents,
|
||||
background_agents_instructions=background_agents_instructions,
|
||||
extra_context_providers=context_providers,
|
||||
)
|
||||
|
||||
|
||||
@@ -394,3 +394,94 @@ def test_create_harness_agent_logs_warning_when_no_web_search(caplog: pytest.Log
|
||||
max_output_tokens=16_384,
|
||||
)
|
||||
assert any("SupportsWebSearchTool" in msg for msg in caplog.messages)
|
||||
|
||||
|
||||
# --- Background Agents Tests ---
|
||||
|
||||
|
||||
class _FakeBackgroundAgent:
|
||||
"""Minimal agent stub satisfying SupportsAgentRun for background agents tests."""
|
||||
|
||||
def __init__(self, name: str, description: str | None = None):
|
||||
self.id = f"agent-{name}"
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
def create_session(self, *, session_id: str | None = None) -> AgentSession:
|
||||
return AgentSession(session_id=session_id)
|
||||
|
||||
def get_session(self, service_session_id: str, *, session_id: str | None = None) -> AgentSession:
|
||||
return AgentSession(service_session_id=service_session_id, session_id=session_id)
|
||||
|
||||
async def run(self, messages: Any = None, *, stream: bool = False, session: Any = None, **kwargs: Any) -> Any:
|
||||
from agent_framework import AgentResponse
|
||||
|
||||
return AgentResponse(messages=[], response_id="fake-bg-response")
|
||||
|
||||
|
||||
def test_create_harness_agent_no_background_agents_by_default() -> None:
|
||||
"""No BackgroundAgentsProvider should be included when background_agents is not provided."""
|
||||
from agent_framework._harness._background_agents import BackgroundAgentsProvider
|
||||
|
||||
agent = create_harness_agent(
|
||||
client=_FakeChatClient(), # type: ignore[arg-type]
|
||||
max_context_window_tokens=128_000,
|
||||
max_output_tokens=16_384,
|
||||
disable_web_search=True,
|
||||
)
|
||||
providers = agent.context_providers or []
|
||||
assert not any(isinstance(p, BackgroundAgentsProvider) for p in providers)
|
||||
|
||||
|
||||
def test_create_harness_agent_adds_background_agents_provider() -> None:
|
||||
"""BackgroundAgentsProvider should be included when background_agents are provided."""
|
||||
from agent_framework._harness._background_agents import BackgroundAgentsProvider
|
||||
|
||||
bg_agent = _FakeBackgroundAgent("WebSearcher", "Searches the web")
|
||||
agent = create_harness_agent(
|
||||
client=_FakeChatClient(), # type: ignore[arg-type]
|
||||
max_context_window_tokens=128_000,
|
||||
max_output_tokens=16_384,
|
||||
disable_web_search=True,
|
||||
background_agents=[bg_agent],
|
||||
)
|
||||
providers = agent.context_providers or []
|
||||
bg_providers = [p for p in providers if isinstance(p, BackgroundAgentsProvider)]
|
||||
assert len(bg_providers) == 1
|
||||
|
||||
|
||||
def test_create_harness_agent_background_agents_custom_instructions() -> None:
|
||||
"""Custom instructions should be passed to BackgroundAgentsProvider."""
|
||||
from agent_framework._harness._background_agents import BackgroundAgentsProvider
|
||||
|
||||
custom_instructions = "## Custom\n\nUse agents wisely.\n\n{background_agents}"
|
||||
bg_agent = _FakeBackgroundAgent("Helper", "A helper agent")
|
||||
agent = create_harness_agent(
|
||||
client=_FakeChatClient(), # type: ignore[arg-type]
|
||||
max_context_window_tokens=128_000,
|
||||
max_output_tokens=16_384,
|
||||
disable_web_search=True,
|
||||
background_agents=[bg_agent],
|
||||
background_agents_instructions=custom_instructions,
|
||||
)
|
||||
providers = agent.context_providers or []
|
||||
bg_providers = [p for p in providers if isinstance(p, BackgroundAgentsProvider)]
|
||||
assert len(bg_providers) == 1
|
||||
# Verify the custom instructions were used (placeholder replaced with agent list).
|
||||
assert "Custom" in bg_providers[0]._instructions
|
||||
assert "Helper" in bg_providers[0]._instructions
|
||||
|
||||
|
||||
def test_create_harness_agent_empty_background_agents_list() -> None:
|
||||
"""An empty background_agents list should NOT add a BackgroundAgentsProvider."""
|
||||
from agent_framework._harness._background_agents import BackgroundAgentsProvider
|
||||
|
||||
agent = create_harness_agent(
|
||||
client=_FakeChatClient(), # type: ignore[arg-type]
|
||||
max_context_window_tokens=128_000,
|
||||
max_output_tokens=16_384,
|
||||
disable_web_search=True,
|
||||
background_agents=[],
|
||||
)
|
||||
providers = agent.context_providers or []
|
||||
assert not any(isinstance(p, BackgroundAgentsProvider) for p in providers)
|
||||
|
||||
Reference in New Issue
Block a user