Agent Architecture

Model-Agnostic Advisor/Executor Strategy

Pick any advisor model. Pick any executor model. Plug into any CLI or IDE via MCP. No vendor lock-in.

Most AI coding agents today are built as monoliths: a single model decides what to do, calls tools, and interprets the results. That design is simple to start with, but it quietly couples your product roadmap to one provider's API quirks, pricing changes, and capability gaps. When that provider releases a better model, raises prices, or changes a tool-calling schema, you are forced to rewrite agent logic rather than just swap a model name.

The advisor/executor pattern is a different bet. It separates the brain that reasons from the hands that act. The advisor produces a structured plan; the executor runs each step against tools. Because each role is served by an independent model configuration, you can mix, match, and migrate models without touching the orchestration logic. This article walks through the architecture, the trade-offs, and a working open-source kernel you can run today.

Abhigyan June 2026 AI Architecture

The Architecture

At the center of the design is a small kernel with three layers. Every layer communicates through a stable contract instead of a vendor SDK, which is what makes the whole system portable. The first layer is the advisor, a reasoning model that reads the user request, relevant context, and the list of available tools, then emits a plan as an ordered list of steps. The advisor does not call tools; it only decides what should happen and why. This constraint makes the plan auditable and gives you a single artifact to log, replay, or approve before any side effects occur.

1

Advisor

A reasoning model that ingests the user request, context, and available tools, then emits a Plan: an ordered list of steps. It does not touch tools.

plan: list[Step] step: {id, action, reasoning, expected_outcome}
2

Model Router

Normalizes every provider to a single OpenAI-style chat-completion interface. Adapters translate Anthropic, OpenAI, Ollama, Gemini, Bedrock, and others into one shape.

chat(messages, tools, model_config) -> response
3

Executor + Tool Registry

Runs each plan step against tools. Built-in tools and MCP servers register the same way, so the agent is IDE and CLI agnostic.

call(tool_name, arguments) -> result

The second layer is the model router. Its job is to hide provider differences behind one OpenAI-style chat-completion interface. Adapters for Anthropic, OpenAI, Ollama, Gemini, Bedrock, and others all translate into the same message, tool-schema, and response shapes. The kernel never learns provider details; it only sees models with names, providers, and API keys. The third layer is the executor paired with a tool registry. The executor takes each step from the plan, resolves the right tool, and invokes it. Built-in tools and MCP servers register through the same path, so adding a new capability is a config change, not a code change.

The runtime loop is straightforward: a user request goes to the advisor, the advisor returns a plan, the executor iterates through the steps, and if a step fails the advisor is called again to replan. Because advisor and executor are independent model configs, you can pair a cheap local model for deterministic execution with a frontier cloud model for high-value planning, or swap either one as soon as a better option appears.

Advantages

Splitting planning from execution pays off in several ways. The most immediate is model portability. Changing the advisor from Claude to GPT-4o to a local Qwen is a one-line config edit, because the kernel only consumes the normalized chat interface. The same applies to the executor. This makes it easy to run experiments, respond to price changes, and avoid betting your entire agent on a single provider's roadmap.

Tool portability is equally important. By using the Model Context Protocol as the tool layer, any MCP server, whether it exposes a filesystem, a GitHub client, a database, or an internal company service, registers automatically under a predictable namespace. You do not need per-tool SDK glue or custom plugins for every editor. That same mechanism also makes the agent host-agnostic: expose the kernel itself as an MCP server over stdio, and VS Code, Cursor, Claude Desktop, Zed, Claude Code, and Hermes can all consume it without any editor-specific code.

The separation also improves economics and observability. You can assign a small, fast model to deterministic execution calls and reserve the most capable model for planning. Every plan is a structured object, so you can log, replay, audit, and insert human approval before execution. A mock adapter lets CI and new contributors run the full loop without cloud credentials, which keeps the feedback loop fast and cheap during development.

Swap models without rewriting logic

Change one YAML key to move the advisor or executor between Claude, GPT-4o, Ollama, or any future provider.

Tool-agnostic via MCP

Any MCP server registers automatically. No per-tool SDK glue and no custom IDE plugins.

Cost control by role

Use a small, cheap model for deterministic execution and a frontier model only for high-value planning.

Test without API keys

A mock adapter lets CI and new contributors run the full loop with zero cloud credentials.

Works in any editor

Expose the kernel as an MCP server over stdio and every supporting host can discover it.

Observable by design

Plans are structured objects. Log, replay, audit, and require human approval before execution.

Costs & Trade-offs

No architecture is free. The advisor/executor split adds complexity because you now have two model calls and a small orchestrator instead of a single chat completion. The key is to keep the loop simple and reuse the model router everywhere so the extra surface area does not explode. Latency is another real cost: the executor must wait for the advisor to finish planning before it can start work. You can mitigate this by streaming the plan, caching tool schemas, and allowing partial execution of early steps while later steps are still being generated.

Maintenance also shifts. Provider APIs change, and adapters need occasional updates. You can reduce this burden by keeping the adapter surface small and using LiteLLM or OpenRouter as a fallback when a native adapter is missing. Debugging becomes two-dimensional: a failure can come from a bad plan or from a correctly planned step that failed during execution. The fix is to log the full plan plus every tool call, result, retry, and error, and to design the executor so you can replay from any step. Finally, there is a standardization tax. Not every model supports tool calling in the same way, so the kernel normalizes everything to JSON schema and structured output adapters. That is a one-time investment that pays back every time you add a new provider.

Category What you pay Mitigation
Complexity Two model calls plus orchestration instead of one. Keep the loop simple and reuse the model router everywhere.
Latency Advisor planning adds a serial round-trip. Stream the plan, cache tool schemas, and allow partial execution.
Maintenance Provider adapters drift as APIs change. Use LiteLLM or OpenRouter as a fallback and keep adapters small.
Debugging Failures can sit in the plan or in execution. Log plan, per-step I/O, and replays from any step.
Standardization tax Models vary in tool-calling support. Normalize to JSON schema with structured-output adapters.

How-To

The accompanying repository is a minimal, runnable implementation of the pattern. It is designed so you can verify the loop without API keys, then gradually connect real models and tools. Start by cloning the repo and installing the package with uv. The base dependencies are enough for mock mode, which uses deterministic canned plans to exercise the orchestration and built-in tools.

1. Run the PoC in mock mode

# clone git clone https://github.com/abster12/advisor-executor-strategy.git cd advisor-executor-strategy # install with uv uv venv uv pip install -e . # run with no API keys python -m advisor_executor_poc "Read a file and run a shell command" --config config.yaml

In mock mode the advisor emits a fixed plan and the executor calls real built-in tools, so you can confirm the loop works without any cloud credentials.

2. Use a local model

Once the loop is working, point it at a local Ollama instance running any OpenAI-compatible model. This requires no API key and lets you test real model reasoning on your own hardware.

python -m advisor_executor_poc "List files in the current directory" --config config-ollama.yaml

3. Add MCP tools

The included config-mcp.yaml registers the time MCP server. Any stdio MCP server works the same way; the kernel discovers its tools and registers them under a predictable mcp_{server}_{tool} namespace.

python -m advisor_executor_poc "What time is it?" --config config-mcp.yaml

4. Plug into an IDE

The kernel can expose itself as an MCP server, which means any editor that speaks MCP can use it without a custom plugin. Start the stdio server and point your editor at it.

python -m advisor_executor_poc --stdio --config config.yaml

For VS Code, add this to .vscode/mcp.json:

{ "servers": { "advisor-executor": { "type": "stdio", "command": "python", "args": ["-m", "advisor_executor_poc", "--stdio", "--config", "config.yaml"] } } }

5. Configure your own models

Edit config.yaml to mix providers. The advisor and executor entries are completely independent, so you can pair an OpenAI planner with an Anthropic executor, a local Ollama executor with a cloud advisor, or any other combination that fits your cost and quality targets.

models: advisor: provider: openai model: gpt-4o api_key: ${OPENAI_API_KEY} executor: provider: anthropic model: claude-sonnet-4 api_key: ${ANTHROPIC_API_KEY}

Do's & Don'ts

After building and testing this pattern, a few guidelines stand out. First, treat the internal interface as a contract. Pick one chat schema and force every adapter to it, even when a native SDK offers something different. Second, keep the advisor honest: it should produce structured plans, not free-text instructions, because structured plans are easier to validate, log, and replay. Third, mock the loop early. Running the agent without API keys during development saves money and surfaces orchestration bugs before they reach real models.

On the flip side, do not let the advisor execute tools directly. That collapses the separation and reintroduces the coupling this pattern is meant to avoid. Do not hard-code provider SDKs in the kernel; they belong only in adapters. And do not build a custom IDE plugin as a first step. MCP gives you every major editor and CLI host for free, so start there and only add custom plugins when you need deep UI integration.

Do

  • Standardize internally. Pick one chat schema and force every adapter to it.
  • Make plans structured. JSON or Pydantic models are easier to audit than free text.
  • Start with mock mode. Validate the loop before burning API credits.
  • Expose via MCP. Let editors and CLIs discover you instead of building N plugins.
  • Log everything. Plans, tool calls, errors, retries, and final results.
  • Retry at the step level. One failed tool should not kill the whole plan.

Don't

  • Let the advisor execute. Keep reasoning and action separate.
  • Hard-code provider SDKs in the kernel. They belong in adapters only.
  • Skip validation. Always parse and validate the plan before running it.
  • Build a custom IDE plugin first. MCP gives you every editor for free.
  • Ignore cost attribution. Tag advisor vs executor usage so you can optimize.
  • Assume tool names are unique. Namespace MCP tools by server.

Build your own

The repo includes the full Python kernel, adapters for OpenAI, Anthropic, Ollama, and mock providers, MCP client and server wrappers, and sample configs. Fork it, swap in your own models, and see how far the separation of planning and execution can take you.

Explore the Code