Most teams approach multi-agent systems the wrong way.

They start by choosing a framework.

CrewAI, LangGraph, AutoGen, custom orchestrators, whatever is fashionable that week.

That feels natural, but it creates the wrong center of gravity.

The framework becomes the architecture.

And once the framework becomes the architecture, changing anything later gets expensive.

The better question is not:

Which framework should our agents use?

It is:

Where does state live, how do agents coordinate, and what stays stable if we change runtimes later?

That is the question that matters.


Where framework lock-in actually comes from

Framework lock-in does not happen because a team picked the wrong library.

It happens because the team embedded coordination rules inside framework-specific abstractions:

  • graph definitions
  • crew/task objects
  • framework-managed state
  • internal message buses
  • framework-shaped tool contracts

At first this feels productive. Everything is in one place. The demo works. The orchestration looks elegant.

Then reality shows up:

  • a new agent needs different execution semantics
  • a second team wants to build in a different stack
  • one workflow needs durable execution
  • another needs stricter auditability
  • one agent is easier to express in code than in a graph

Now the framework is no longer helping you model the system. It is forcing the system to look like the framework.

That is the trap.


The architectural shift

The core shift is simple:

Treat the framework as a runtime detail.

Keep the durable parts of the system outside it:

  • shared state
  • workflow history
  • permissions
  • entity identity
  • interaction logs
  • coordination contracts

That is how we approach multi-agent systems at CoEdify.

We built Cortex as a memory and workflow layer that sits at the center of the system. Agents read from it and write to it through stable interfaces. The frameworks inside the agents can change. The coordination layer does not.

That design buys you something important:

you can evolve agents without re-architecting the whole system every time.


What should live outside the agent runtime

If you want multi-agent coordination to survive framework churn, several things need to be system-level concerns rather than framework-level concerns.

1. Shared state

No serious multi-agent system should depend on each agent privately carrying the only correct state.

If Agent A updates a contact, Agent B should not need a framework-specific conversation to discover that. The system should have a shared source of truth.

That is why our coordination model centers on shared state first.

2. Workflow history

You need a record of what happened:

  • what the agent saw
  • what it decided
  • what it changed
  • what happened next

Without that, debugging becomes "read the framework traces and hope."

With it, you have a real interaction log.

3. Permissions and identity

Different agents should not automatically inherit the same access. A hiring agent, an SDR agent, and a support agent should not all touch the same entities in the same way.

That boundary should be enforced in the system of record, not only in prompt wording.

4. Coordination contracts

Agents need stable ways to:

  • fetch context
  • write state
  • log outcomes
  • advance workflows

Once those contracts are stable, the agent runtime becomes swappable.


How Cortex fits into this

Cortex is our shared memory and workflow layer for agent systems.

It is not where the reasoning happens.

It is where the system stores and exposes the things agents need to coordinate safely:

  • contacts and companies
  • workflows and stages
  • interaction logs
  • scoped keys and permissions
  • tenant boundaries
  • compressed context for current decisions

The architecture looks roughly like this:

Agent Execution Layer
    |- SDR agent
    |- Hiring agent
    |- Survey agent
    |- Durable execution
    |
    |  all read/write via stable interfaces
    |
Channel Services          Cortex
    |- Email              |- Identity and tenancy
    |- WhatsApp           |- Entity layer
    |- SMS                |- Workflow layer
    |- Voice              |- Interaction and memory layer

The important design rule is this:

Cortex stores state and context. Agents consume that state and produce decisions.

That separation is what keeps the coordination layer stable.


Why shared state beats agent-to-agent chat for most coordination

Many teams imagine multi-agent coordination as agents constantly talking to each other.

That is sometimes useful, but it is not the default pattern we trust most.

Most production coordination is simpler than that:

  • do not duplicate work
  • respect the latest state
  • react to what already happened
  • write your own outcome cleanly

That does not require elaborate inter-agent conversation. It requires shared state and clear workflow semantics.

For example:

  • the SDR agent logs an outreach attempt
  • the hiring agent reads the same contact record and sees that interaction
  • a survey workflow updates the current stage
  • another agent changes behavior because the stage already moved

That is coordination through shared context, not through framework-managed chatter.

It is simpler, easier to debug, and easier to keep stable as the system grows.


What the stable interface looks like

The framework inside the agent can vary.

The contracts it uses to coordinate should not.

A simplified pattern looks like this:

@mcp_tool("cortex_get_context")
def get_context(contact_id: str, workflow_id: str) -> dict:
    ...

@mcp_tool("cortex_update_stage")
def update_stage(contact_id: str, workflow_id: str, target_stage_code: str, reason: str) -> dict:
    ...

@mcp_tool("cortex_log_interaction")
def log_interaction(contact_id: str, workflow_id: str, interaction_type: str, content: str) -> dict:
    ...

That is the key:

  • the framework is an implementation detail
  • the coordination contract is the stable layer

Once that is true, an agent built in LangGraph, CrewAI, or plain code can still participate in the same system.


When direct agent delegation actually matters

Not every multi-agent system needs heavy agent-to-agent delegation.

Most coordination is still better handled through shared state.

Direct delegation matters when one agent truly needs another agent to perform a distinct task with its own lifecycle.

Examples:

  • a planning agent requests specialized research
  • a compliance agent reviews another agent's output
  • a domain-specific agent runs on a different environment or trust boundary

In those cases, explicit agent-to-agent protocols can help. But they should sit on top of a stable coordination model, not replace it.

If the system cannot survive without constant framework-specific inter-agent communication, the architecture is usually too coupled.


The migration test

Here is the simplest diagnostic for framework lock-in:

Can you replace one agent runtime without changing the rest of the system?

If the answer is no, your coordination is too framework-dependent.

That is the test that matters.

When we rewired parts of our own agent layer — moving one workflow from a framework-managed graph to plain async Python — the coordination contracts stayed stable because state, workflow history, and interaction logging already lived outside the framework runtime.

That is what made iteration cheaper.


The practical starting point

If you are building a multi-agent system today, the safest sequence is:

1. Define the shared state model

What entities matter? Contacts, workflows, stages, tasks, approvals, summaries, permissions.

2. Define the coordination contracts

How does an agent:

  • fetch current context
  • write state updates
  • log decisions
  • move a workflow forward

3. Keep framework logic inside the agent boundary

Use whatever runtime is productive for that specific agent, but do not let it become the system of record.

4. Add direct delegation only where it is truly needed

Do not make every coordination problem into agent-to-agent messaging.

Start with shared state. Add delegation where the workflow actually demands it.


The real takeaway

Frameworks are useful. They are not the problem.

The problem starts when the framework becomes the place where state, coordination, and workflow truth all live together.

That makes every future change more expensive.

If you want a multi-agent system that can survive iteration, new runtimes, and changing requirements, keep the durable parts of the system outside the framework:

  • state
  • workflow history
  • permissions
  • interaction logs
  • coordination contracts

Then let each agent use whatever runtime makes sense inside that boundary.

That is how you reduce framework lock-in without slowing down development.


At CoEdify, we build multi-agent systems by keeping the coordination layer stable and the agent runtime flexible. The framework can change. The system of record should not. [coedify.com]