# Harness

A bare `Agent` is just a model loop. The **harness** is the set of opt-in primitives you compose onto it to give it richer capabilities — context assembly, persistent knowledge, sub-task spawning, and the supporting middleware they wire in.

This page is the configuration reference for those primitives. For the conversational entry point (`agent.ask()`, tools, HITL, observing events), see [Agent Communication](https://docs.ag2.ai/0.14.0/docs/beta/agents/).

## Constructor [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#constructor "Permanent link")

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>``` | ```<br>Agent(<br>    name: str,<br>    prompt: str | Callable | Iterable = (),<br>    *,<br>    config: ModelConfig | None = None,<br>    tools: Iterable = (),<br>    middleware: Iterable = (),<br>    observers: Iterable = (),<br>    dependencies: dict | None = None,<br>    variables: dict | None = None,<br>    response_schema: ResponseProto | type | None = None,<br>    hitl_hook: HumanHook | None = None,<br>    plugins: Iterable[Plugin] = (),<br>    assembly: Iterable[AssemblyPolicy] = (),<br>    knowledge: KnowledgeConfig | None = None,<br>    tasks: TaskConfig | Literal[False] | None = None,<br>)<br>``` |

The loop-related parameters (`config`, `tools`, `middleware`, `observers`, `prompt`, …) are covered in [Agent Communication](https://docs.ag2.ai/0.14.0/docs/beta/agents/) and the parameter-specific guides. The harness hooks are `assembly=`, `knowledge=`, and `tasks=`, each documented below.

## `assembly=` — context policies [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#assembly-context-policies "Permanent link")

A list of [`AssemblyPolicy`](https://docs.ag2.ai/0.14.0/docs/beta/advanced/assembly/) instances. When non-empty, the Agent wires an internal `AssemblerMiddleware` at the outermost position of the middleware chain so your policies transform `(prompts, events)` before every LLM call.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>``` | ```<br>from autogen.beta import Agent<br>from autogen.beta.policies import (<br>    AlertPolicy,<br>    SlidingWindowPolicy,<br>    WorkingMemoryPolicy,<br>)<br>agent = Agent(<br>    "assistant",<br>    config=config,<br>    assembly=[<br>        WorkingMemoryPolicy(),                # inject /memory/working.md<br>        AlertPolicy(),                         # deliver ObserverAlerts, halt on FATAL<br>        SlidingWindowPolicy(max_events=50),   # cap history footprint<br>    ],<br>)<br>``` |

Order matters — see the [ordering rule in the assembly doc](https://docs.ag2.ai/0.14.0/docs/beta/advanced/assembly/#ordering-matters). `AssemblerMiddleware.validate_order()` will flag known problematic compositions.

## `knowledge=` — KnowledgeConfig [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#knowledge-knowledgeconfig "Permanent link")

Groups everything that involves the [`KnowledgeStore`](https://docs.ag2.ai/0.14.0/docs/beta/advanced/knowledge_store/): the store itself, optional bootstrap, and optional compaction + aggregation strategies.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>``` | ```<br>from dataclasses import dataclass<br>@dataclass<br>class KnowledgeConfig:<br>    store: KnowledgeStore<br>    expose_tool: bool = True<br>    write_event_log: bool = True<br>    compact: CompactStrategy | None = None<br>    compact_trigger: CompactTrigger | None = None<br>    aggregate: AggregateStrategy | None = None<br>    aggregate_trigger: AggregateTrigger | None = None<br>    bootstrap: StoreBootstrap | None = None<br>``` |

| Field | What it does |
| --- | --- |
| `store` | Registered in `context.dependencies[KnowledgeStore]` so policies like `WorkingMemoryPolicy` / `EpisodicMemoryPolicy` can read it. |
| `expose_tool` | When `True` (default), the agent gets an auto-injected `knowledge` action-group tool that lets the LLM call `read` / `write` / `list` / `delete` on the store. Set to `False` when the store should be policy-only — the model never sees the tool, and the bootstrap SKILL.md text drops its "use the `knowledge` tool" sentence. |
| `write_event_log` | When `True` (default), the agent persists its stream history to `/log/{stream_id}.jsonl` at the end of each `ask()`. Set to `False` to keep the store free of stream logs (e.g. when the store is purely user-facing memory). |
| `compact` / `compact_trigger` | Wires a compaction middleware that fires [`compact()`](https://docs.ag2.ai/0.14.0/docs/beta/advanced/compaction/) between turns when the trigger thresholds are exceeded. |
| `aggregate` / `aggregate_trigger` | Wires an aggregation middleware that fires [`aggregate()`](https://docs.ag2.ai/0.14.0/docs/beta/advanced/aggregation/) on the configured cadence. Failures emit `AggregationFailed` on the stream — see [Aggregation › Wiring onto an Agent](https://docs.ag2.ai/0.14.0/docs/beta/advanced/aggregation/#wiring-onto-an-agent) for the full lifecycle event triple. |
| `bootstrap` | Runs once on first use to seed the store. `None` falls back to `DefaultBootstrap(mention_tool=expose_tool)`, so the generated SKILL.md text matches whether the LLM can actually call the `knowledge` tool. |

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>``` | ```<br>from autogen.beta import Agent, KnowledgeConfig<br>from autogen.beta.aggregate import AggregateTrigger, ConversationSummaryAggregate<br>from autogen.beta.compact import CompactTrigger, TailWindowCompact<br>from autogen.beta.knowledge import DiskKnowledgeStore<br>from autogen.beta.policies import WorkingMemoryPolicy<br>from pathlib import Path<br>store = DiskKnowledgeStore(Path("./knowledge"))<br>agent = Agent(<br>    "assistant",<br>    config=main_config,<br>    knowledge=KnowledgeConfig(<br>        store=store,<br>        compact=TailWindowCompact(target=100),<br>        compact_trigger=CompactTrigger(max_events=200),<br>        aggregate=ConversationSummaryAggregate(config=summarizer_config),<br>        aggregate_trigger=AggregateTrigger(every_n_turns=10, on_end=True),<br>    ),<br>    assembly=[WorkingMemoryPolicy()],<br>)<br>``` |

The compaction and aggregation middleware are opt-in per field: passing `compact=` without `compact_trigger=` still works (a default `CompactTrigger()` with all thresholds disabled is used). Omit a strategy entirely and the corresponding middleware is not wired.

## `tasks=` — TaskConfig [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#tasks-taskconfig "Permanent link")

Sub-task delegation is **off by default** — a bare Agent has no `run_subtask` / `run_subtasks` tools. Pass `tasks=TaskConfig(...)` to opt in, and the Agent will auto-inject the pair of sub-task tools that let the LLM spawn isolated child Agents to handle self-contained work. `TaskConfig` configures how those children are built.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>``` | ```<br>from dataclasses import dataclass<br>@dataclass<br>class TaskConfig:<br>    config: ModelConfig | None = None<br>    prompt: str = (<br>        "You are a task agent. Complete the assigned task thoroughly and "<br>        "concisely. Return only the result."<br>    )<br>    include_tools: Iterable[str] | None = None<br>    exclude_tools: Iterable[str] = ()<br>    extra_tools: Iterable[Callable | Tool] = ()<br>``` |

| Field | What it does |
| --- | --- |
| `config` | The `ModelConfig` used for sub-task Agents. Falls back to the parent Agent's `config`. |
| `prompt` | Default system prompt for sub-task Agents. |
| `include_tools` | Allowlist of parent-tool names to inherit. `None` means "inherit all". |
| `exclude_tools` | Blocklist of parent-tool names to drop. Applied after `include_tools`. |
| `extra_tools` | Additional tools given to sub-tasks that the parent does not have. |

By default a sub-task Agent inherits **all** of the parent's user-supplied tools. Sub-tasks are themselves constructed with `tasks=False` (the Agent default), so they have no `run_subtask` / `run_subtasks` tools — recursive delegation is structurally impossible and no depth limit is needed.

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>``` | ```<br>from autogen.beta import Agent, TaskConfig<br>agent = Agent(<br>    "orchestrator",<br>    config=main_config,<br>    tools=[search, fetch_url, summarize],<br>    tasks=TaskConfig(<br>        config=worker_config,                 # cheaper model for sub-tasks<br>        prompt="You are a focused worker; one step only.",<br>        include_tools=["search", "fetch_url"],  # don't expose summarize to children<br>    ),<br>)<br>``` |

### `tasks=False` — the default [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#tasksfalse-the-default "Permanent link")

`tasks=False` is the Agent default, so a bare Agent never spawns children. You only need to pass it explicitly to be self-documenting; otherwise just omit `tasks=` entirely.

|     |     |
| --- | --- |
| ```<br>1<br>2<br>3<br>4<br>5<br>6<br>``` | ```<br>focused = Agent(<br>    "summarizer",<br>    prompt="Summarise the input. Do not delegate.",<br>    config=main_config,<br>    # tasks=False is the default — no run_subtask / run_subtasks tools.<br>)<br>``` |

## `run_subtask` / `run_subtasks` — auto-injected tools [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#run_subtask-run_subtasks-auto-injected-tools "Permanent link")

When you opt in via `tasks=TaskConfig(...)`, the Agent exposes two tools to the LLM:

- `run_subtask(task: str)` — spawn one sub-task Agent. Useful when the LLM has a single self-contained piece of work to delegate.
- `run_subtasks(tasks: list[str], parallel: bool = True)` — spawn multiple sub-tasks in one tool call. Defaults to running them concurrently with `asyncio.gather`; pass `parallel=False` only when later tasks depend on earlier results.

The LLM is told (via the tool descriptions) that it can call `run_subtask` multiple times in parallel within a single response, and that `run_subtasks` is the deliberate fan-out form. Each child gets a fresh `MemoryStream` and the parent's tools (filtered by `TaskConfig`).

For a more explicit, named delegate where the parent LLM sees a tool like `task_researcher` instead of generic `run_subtask`, use [`Agent.as_tool()`](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#agent-as-tool). The two patterns can coexist: a coordinator can have both auto-injected sub-tasks and a named `task_researcher` tool.

See [Subagents](https://docs.ag2.ai/0.14.0/docs/beta/subagents/) for the full sub-task delegation guide — context flow and custom streams for self-delegation via `as_tool()`.

## Agent.as_tool() [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#agentas_tool "Permanent link")

Expose any Agent as a `FunctionTool` so another Agent can invoke it like any other tool:

|     |     |
| --- | --- |
| ```<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>``` | ```<br>child = Agent(<br>    "researcher",<br>    prompt="Answer the objective concisely.",<br>    config=main_config,<br>)<br>parent = Agent(<br>    "lead",<br>    config=main_config,<br>    tools=[child.as_tool(description="Delegate fact-finding to a researcher.")],<br>)<br>reply = await parent.ask("Find out where Melbourne is.")<br>``` |

`as_tool()` returns a `FunctionTool` named `task_{child.name}` that accepts an `objective` parameter and forwards it into the child's stream. See [Subagents](https://docs.ag2.ai/0.14.0/docs/beta/subagents/) for sub-task streams, depth limiting, and stream factories.

## Turn lifecycle [\#](https://docs.ag2.ai/0.14.0/docs/beta/agent_harness/#turn-lifecycle "Permanent link")

Each `await agent.ask(...)` runs through the middleware chain in this order (outermost → innermost):

```
1. AssemblerMiddleware              (if assembly=[...])
2. _HaltCheckMiddleware             (if assembly=[...] — watches for HaltEvent)
3. _CompactionMiddleware            (if knowledge.compact configured)
4. _AggregationMiddleware           (if knowledge.aggregate configured)
5. User-provided middleware         (retry, rate-limit, logging, …)
6. LLM client                       (innermost)
```

The internal harness middleware (`_AssemblerMiddleware`, `_HaltCheckMiddleware`, `_CompactionMiddleware`, `_AggregationMiddleware`) are assembled conditionally — you only pay for what you turn on.

Lifecycle events emitted during a turn include `ObserverStarted` / `ObserverCompleted`, `CompactionCompleted`, `AggregationCompleted`, and `HaltEvent`. Subscribe to any of them via an [Observer](https://docs.ag2.ai/0.14.0/docs/beta/advanced/observers/) or a stream subscriber.

Back to top
