Published February 13, 2026 · 9 min read

Building Framework-Agnostic AI Agents

The AI agent ecosystem is fragmented. LangChain, CrewAI, LangGraph, AutoGen, OpenAI Assistants, Claude Tools—every few months brings a new framework promising to be "the one."

Here's the problem: if you build deep into one framework, you're locked in. When something better comes along (and it will), you can't migrate without losing everything.

This guide shows you how to build agents that are framework-agnostic—portable, future-proof, and free from vendor lock-in.

The Lock-In Problem

What You Lose When Locked In

  • Workflows — Defined in framework-specific DSLs
  • State — Stored in framework-specific formats
  • Agent configurations — Tied to framework abstractions
  • History — What agents learned is trapped
  • Integrations — Framework-specific tools/plugins

Imagine you built your agent system with LangChain in 2023. Now in 2026, you want to use LangGraph's superior state machine capabilities. Your options:

  1. Rewrite everything — Weeks of work, risk of bugs
  2. Run both — Operational nightmare
  3. Stay locked in — Miss out on better tools

None of these are good. The solution is to design for portability from the start.

The Framework-Agnostic Architecture

The key insight: separate what you're building FROM how you're orchestrating it.

┌─────────────────────────────────────────────────────────────┐
│                    Your Agent Application                    │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  LangChain  │  │   CrewAI    │  │  LangGraph  │  ...    │
│  │   Agent     │  │   Crew      │  │    Graph    │         │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘         │
│         │                │                │                 │
│         └────────────────┼────────────────┘                 │
│                          │                                  │
│                          ▼                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │           Framework-Agnostic Control Plane           │   │
│  │  • Portable State    • Versioned Workflows           │   │
│  │  • Standard Handoffs • Universal Audit Trail         │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

Frameworks do orchestration. The control plane handles persistence, state, and coordination in a framework-independent way.

Principles of Framework-Agnostic Design

1. Portable State Format

Store state in a format any framework can read:

// ❌ Framework-specific state
langchain_state = {
    "memory": BufferMemory(...),
    "agent_scratchpad": AgentScratchpad(...),
    "intermediate_steps": [AgentAction(...), ...]
}

// ✅ Portable state
portable_state = {
    "conversation": [
        {"role": "user", "content": "..."},
        {"role": "assistant", "content": "..."}
    ],
    "context": {
        "customer_id": "123",
        "current_task": "billing_inquiry",
        "gathered_info": {...}
    },
    "decisions": [
        {"timestamp": "...", "decision": "...", "reason": "..."}
    ]
}

2. Plain-Text Workflows

Define workflows in markdown, not framework DSLs:

// ❌ Framework-specific workflow
const graph = new StateGraph()
    .addNode("research", researchNode)
    .addNode("write", writeNode)
    .addEdge("research", "write")
    .compile();

// ✅ Portable workflow (markdown)
const workflow = `
# Customer Support Workflow

## Step 1: Classify Request
- Read customer message
- Classify as: billing, technical, general, escalation
- If unclear, ask clarifying question

## Step 2: Gather Context
- Retrieve customer history from CRM
- Check for open tickets
- Note any relevant policies

## Step 3: Respond
- Draft response using gathered context
- If billing: reference invoice numbers
- If technical: provide troubleshooting steps
- If escalation: hand off to human

## Edge Cases
- If customer is angry: acknowledge frustration first
- If request is outside policy: explain and offer alternatives
`;

Any framework can execute a markdown workflow. The intelligence is in following the instructions, not in the format.

3. Standard APIs for State Operations

// Framework-agnostic state API
interface StateStore {
    read(component: string, key: string): Promise;
    write(component: string, key: string, value: any, ttl?: number): Promise;
    delete(component: string, key: string): Promise;
}

// Works identically from LangChain, CrewAI, or custom code
const state = await stateStore.read("customer", "user_123");
await stateStore.write("customer", "user_123", updatedState);

4. Universal Handoff Protocol

// Framework-agnostic handoff
interface Handoff {
    id: string;
    from_agent: string;
    to_agent: string;
    context: Record;  // Plain JSON
    workflow?: string;             // Workflow name or inline markdown
    status: "pending" | "accepted" | "rejected" | "completed";
}

// Any agent can create/accept handoffs regardless of framework
await handoffs.create({
    to_agent: "specialist-agent",  // Could be LangGraph, CrewAI, whatever
    context: portableState,
    workflow: "escalation-process"
});

Implementation Pattern

Here's how to structure a framework-agnostic agent:

// agent-wrapper.ts
// Thin wrapper that makes any framework agent portable

import { AgentMemo } from '@agentmemo/sdk';

class PortableAgent {
    private agentmemo: AgentMemo;
    private frameworkAgent: any;  // LangChain, CrewAI, whatever
    
    constructor(frameworkAgent: any) {
        this.agentmemo = new AgentMemo({ apiKey: process.env.AGENTMEMO_KEY });
        this.frameworkAgent = frameworkAgent;
    }
    
    async run(input: any) {
        // 1. Load portable state
        const state = await this.agentmemo.state.read("context", input.sessionId);
        
        // 2. Load workflow (if applicable)
        const workflow = await this.agentmemo.workflows.get(input.workflowName);
        
        // 3. Run framework-specific agent
        const result = await this.frameworkAgent.run({
            input: input.message,
            context: state,
            workflow: workflow?.definition
        });
        
        // 4. Save updated state (in portable format)
        await this.agentmemo.state.write("context", input.sessionId, {
            ...state,
            lastInteraction: new Date(),
            latestDecision: result.decision
        });
        
        // 5. Log to audit trail
        await this.agentmemo.audit.log({
            agent: this.frameworkAgent.name,
            action: "process",
            input: input.message,
            output: result.response
        });
        
        return result;
    }
    
    async handoff(toAgent: string, reason: string) {
        const state = await this.agentmemo.state.read("context", this.sessionId);
        
        return this.agentmemo.handoffs.create({
            to_agent: toAgent,
            context: state,
            metadata: { reason, from_framework: "langchain" }
        });
    }
}

Migration Example

With portable state and workflows, migration is trivial:

// Before: LangChain agent
const langchainAgent = new PortableAgent(
    new LangChainAgent({ ... })
);

// After: LangGraph agent (workflows and state transfer automatically)
const langgraphAgent = new PortableAgent(
    new LangGraphAgent({ ... })
);

// Same state, same workflows, different orchestration

Checklist: Is Your Agent Portable?

The Test

Ask yourself: "If I had to rewrite this agent in a different framework tomorrow, what would I lose?"

If the answer is "nothing except the orchestration code," you're framework-agnostic.

Build Portable Agents with AgentMemo

Framework-agnostic state, workflows, and handoffs out of the box.

Start Free Trial →