Master Graph Engineering With Opus 5 (Exact Config)

@ajay4ai
Ajay@ajay4ai
17 views Aug 11, 2026 ~12 min read
Advertisement

Graph memory has one killer cost:

Media image

Every episode you ingest can trigger an extraction call.

If you feed thousands of conversations, documents, events, or code changes into a knowledge graph, you're effectively asking a frontier model to repeatedly perform the same mechanical job:

Read text → extract entities → extract relationships → attach timestamps → write to graph.

Do that naively at full model rates and your memory layer can become more expensive than the application itself.

The trick isn't to stop using a frontier model.

It's to stop wasting frontier-model intelligence on work that doesn't need it.

With Claude Opus 4.8, prompt caching can reduce repeated input costs dramatically, while Batch API can cut non-time-sensitive workloads by 50%. Opus 4.8 is currently priced at $5/M input tokens and $25/M output tokens, while cached reads are $0.50/M and Batch input is $2.50/M.

That changes how you should architect graph ingestion.

Cheap, repeatable extraction.
Expensive, careful reasoning.

Media image

Here's the full setup 👇

Why graphs and frontier models fit together

A vector database gives an agent semantic similarity.

A knowledge graph gives it structure.

Instead of storing:

"Ajay worked on project X."

you can represent:

Ajay
└── worked_on
└── Project X
├── started_at → 2026-04
├── involved → Alice
└── depends_on → Service Y

Now the agent can traverse relationships.

It can answer questions like:

  • What projects was Ajay working on last year?
  • Who was involved in Project X?
  • What decisions led to the current architecture?
  • Which systems depend on Service Y?
  • What changed between two points in time?
  • And importantly, the memory can survive context compaction, session boundaries, and individual conversations.

    But there's a problem.

    Graphs are expensive to build.

    Every new piece of information needs to be converted into structured knowledge.

    That means extraction.

    And extraction happens constantly.

    If you ingest:

  • conversations
  • documents
  • emails
  • GitHub activity
  • meeting notes
  • tickets
  • logs
  • user events
  • you're repeatedly sending similar instructions to the model.

    That's where the architecture matters.

    The key idea: separate extraction from reasoning

    Graph engineering has two fundamentally different model-facing workloads.

    And treating them the same is where people burn money.

  • Extraction
  • High volume.

    Low judgment.

    You want the model to:

  • identify entities
  • normalize names
  • extract relationships
  • identify timestamps
  • return structured JSON
  • This happens thousands of times.

    It should be:

    fast + cheap + deterministic + heavily cached.

  • Traversal and reasoning
  • Low volume.

    High judgment.

    The model receives a relevant subgraph and has to:

  • understand multiple relationships
  • connect information across time
  • resolve conflicts
  • reason over several hops
  • produce an answer grounded in graph evidence
  • This is where you want your expensive reasoning budget.

    The model shouldn't spend frontier-level reasoning tokens deciding whether "Apple Inc." is an organization.

    Save that intelligence for questions like:

    "Why did the architecture change after the migration in March?"

    That's the difference between a cheap graph and an expensive graph.

    The extraction config

    The most important optimization is simple:

    Keep the stable instructions stable.

    Your schema, extraction rules, formatting requirements, and normalization instructions should remain identical across requests.

    The changing data should come afterward.

    Conceptually:

    [STABLE SYSTEM / SCHEMA]

    [CACHED PREFIX]

    [VARIABLE EPISODE]

    [EXTRACTION]

    For example:

    import anthropic

    client = anthropic.Anthropic()

    EXTRACTION_SYSTEM = """
    Extract a knowledge graph from the text.

    Return JSON only:

    {
    "entities": [
    {
    "name": "...",
    "type": "...",
    "description": "..."
    }
    ],
    "edges": [
    {
    "source": "...",
    "target": "...",
    "relation": "...",
    "valid_from": "..."
    }
    ]
    }

    Rules:

    - Use canonical entity names.
    - Resolve aliases when the identity is unambiguous.
    - Extract only relationships supported by the text.
    - Add valid_from when the text provides temporal information.
    - Never invent relationships.
    - Keep descriptions concise.
    """

    def extract(episode_text, occurred_at):
    return client.messages.create(
    model="claude-opus-4-8",
    max_tokens=2000,
    system=[
    {
    "type": "text",
    "text": EXTRACTION_SYSTEM,
    "cache_control": {
    "type": "ephemeral"
    }
    }
    ],
    messages=[
    {
    "role": "user",
    "content": (
    f"reference_time: {occurred_at}\n\n"
    f"{episode_text}"
    )
    }
    ],
    )

    The important architectural pattern isn't the exact Python syntax.

    It's this:

    Schema first. Variable data last.

    The schema is reused.

    The episode changes.

    That is exactly the kind of repeated context prompt caching is designed to optimize. Anthropic currently lists Opus 4.8 cache hits at $0.50/M tokens versus $5/M for standard input.

    The three things that actually move the bill

    1. Cache the stable prefix

    If every extraction request contains the same:

  • schema
  • instructions
  • entity rules
  • relationship rules
  • output format
  • don't treat that text like fresh input every time.

    Cache it.

    A 600-token extraction schema repeated across 5,000 episodes means you're repeatedly sending 3 million tokens of essentially identical context.

    Caching turns that repeated context into a much cheaper input category.

    2. Don't use maximum reasoning for mechanical extraction

    Extraction is mostly structured parsing.

    You don't need the model spending its maximum reasoning budget deciding:

    "Google" → organization
    "John" → person
    "worked_at" → relationship

    The expensive reasoning budget belongs on the query side.

    Your architecture should therefore look more like:

    ┌─────────────────────┐
    │ Raw Episode │
    └──────────┬──────────┘

    ┌───────────────────┐
    │ Cheap Extract │
    │ + Cached Schema │
    └─────────┬─────────┘

    ┌───────────────────┐
    │ Knowledge │
    │ Graph │
    └─────────┬─────────┘

    ┌───────────────────┐
    │ Retrieve Subgraph │
    └─────────┬─────────┘

    ┌───────────────────┐
    │ Deep Reasoning │
    │ + Synthesis │
    └───────────────────┘

    Extraction is a pipeline problem.

    Traversal is a reasoning problem.

    Don't optimize them the same way.

    3. Batch historical ingestion

    This is another easy win.

    Suppose you're importing:

  • 100,000 old conversations
  • 50,000 documents
  • years of GitHub activity
  • historical support tickets
  • Nobody needs those results synchronously.

    So don't process them like interactive requests.

    Use the Batch API.

    Anthropic currently gives Batch processing a 50% discount on input and output tokens, and those savings can be combined with prompt caching.

    The architecture becomes:

    Historical Data

    Batch Jobs

    Cached Extraction Schema

    Structured Episodes

    Knowledge Graph

    Instead of:

    Historical Data

    Request

    Wait

    Request

    Wait

    Request

    ...

    Backfills are exactly the kind of workload that should be asynchronous.

    The traversal side

    Extraction is where you optimize for volume.

    Traversal is where you optimize for answer quality.

    Don't throw the entire graph into the model.

    Retrieve the smallest relevant subgraph first.

    For example:

    User Question

    Entity Resolution

    Graph Search

    Relevant Nodes

    Relevant Edges

    Temporal Filtering

    Context Assembly

    Opus Reasoning

    Grounded Answer

    The model shouldn't have to search through 50,000 nodes just to answer a question about three entities.

    Give it the evidence first.

    Then make it reason.

    Your routing policy should look like this

    Graph Routing

    ### Ingestion

    - Extract entities and relationships from every episode.
    - Keep extraction instructions stable.
    - Cache the reusable extraction prefix.
    - Keep variable episode content outside the stable prefix.
    - Use asynchronous batch processing for historical backfills.
    - Store timestamps whenever the source provides temporal information.
    - Validate extracted entities and relationships before writing.

    ### Traversal

    - Resolve the user's entities first.
    - Retrieve only the relevant subgraph.
    - Apply temporal filters before reasoning.
    - Prefer direct graph evidence over model assumptions.
    - Use a high reasoning setting for difficult multi-hop questions.
    - Cite the graph edges used to construct the answer.

    ### Never

    - Send the entire graph to the model.
    - Rebuild the extraction prompt unnecessarily.
    - Run massive historical backfills synchronously.
    - Ask the model to invent missing relationships.
    - Treat a vector similarity result as a verified relationship.

    That last point is especially important.

    A graph edge is evidence.

    A model-generated assumption isn't.

    Add temporal reasoning or you're leaving half the graph unused

    A normal knowledge graph says:

    Alice → works_at → Company X

    A temporal graph says:

    Alice
    ├── works_at → Company X
    │ └── valid_from → 2024
    │ └── valid_until → 2026

    └── works_at → Company Y
    └── valid_from → 2026

    Now your agent can answer:

    "Where did Alice work when Project X started?"

    instead of:

    "Where does Alice work?"

    That difference is huge.

    For agent memory, time is not metadata.

    Time is part of the knowledge.

    Don't trust extraction blindly

    Another improvement I'd add to the basic architecture:

    Put validation between the model and the graph.

    The pipeline should be:

    LLM Extraction

    JSON Schema Validation

    Entity Normalization

    Duplicate Detection

    Relationship Validation

    Temporal Validation

    Graph Write

    Why?

    Because a knowledge graph has a nasty property:

    Bad data compounds.

    If one incorrect relationship gets written to a graph, future retrieval can surface it as if it were a fact.

    Then the model reasons over the incorrect fact.

    Then the incorrect conclusion becomes another piece of memory.

    You can end up with:

    Bad extraction

    Bad graph edge

    Bad retrieval

    Bad reasoning

    Bad memory

    More bad retrieval

    So ingestion should be treated like a data pipeline, not just an LLM call.

    Graph + vector isn't either/or

    Another mistake is treating knowledge graphs and vector databases as competitors.

    They solve different problems.

    Use vectors for:

    "Find things that mean something similar."

    Use graphs for:

    "Show me how these things are connected."

    A strong memory architecture can use both:

    User Question

    ┌────────┴────────┐
    ↓ ↓
    Vector Search Graph Search
    ↓ ↓
    Semantic Context Relationships
    └────────┬────────┘

    Context Fusion

    Opus Reasoning

    Final Answer

    Vector retrieval finds the neighborhood.

    Graph traversal explains the structure.

    The frontier model synthesizes the answer.

    The economics

    Let's use a simple example.

    Assume:

  • 5,000 episodes
  • 800 tokens of variable episode text
  • 600 tokens of reusable extraction instructions
  • That's:

    4 million variable input tokens

    and

    3 million reusable prefix tokens.

    At current Opus 4.8 standard pricing:

    Standard input:
    7M × $5/M
    = $35

    If the reusable prefix is successfully cached:

    Episode text:
    4M × $5/M
    = $20

    Cached prefix:
    3M × $0.50/M
    = $1.50

    Total input:
    ≈ $21.50

    For a non-time-sensitive batch backfill, the variable input can also receive the 50% Batch discount:

    Episode text:
    4M × $2.50/M
    = $10

    Cached prefix in batch:
    potentially lower still, depending on cache usage

    Total:
    ≈ $11.50 + cache-write/other costs

    The exact bill depends on cache duration, cache writes, output tokens, request shape, and whether the workload qualifies for the relevant pricing tier, so treat these as architecture examples rather than a guaranteed invoice. Anthropic explicitly notes that Batch and prompt-caching discounts can stack.

    The important point isn't the exact dollar amount.

    It's the shape of the optimization:

    Don't pay frontier prices repeatedly for identical context.

    Claude Code + Graphiti

    If you're using Claude Code, you can put the graph behind an MCP server and let the coding agent access persistent memory as a tool.

    A conceptual MCP configuration might look like:

    {
    "mcpServers": {
    "graphiti": {
    "command": "uvx",
    "args": ["graphiti-mcp"],
    "env": {
    "NEO4J_URI": "bolt://localhost:7687",
    "NEO4J_PASSWORD": "${NEO4J_PASSWORD}"
    }
    }
    }
    }

    The important part isn't the specific MCP configuration.

    It's the separation of responsibilities:

    Claude Code

    MCP

    Graph Memory

    Neo4j / Graph Store

    The agent can retrieve relevant memory when needed instead of stuffing the entire history into its context window.

    That is a much more scalable pattern.

    The production architecture

    If I were building this seriously, I'd make the system look like this:

    ┌─────────────────┐
    │ Conversations │
    │ Documents │
    │ GitHub Events │
    │ Tickets │
    └────────┬────────┘

    ┌─────────────────┐
    │ Ingestion Queue │
    └────────┬────────┘

    ┌───────────────────────────┐
    │ Cached Extraction Prompt │
    │ Low-cost / batch capable │
    └─────────────┬─────────────┘

    ┌───────────────────┐
    │ Structured Facts │
    └─────────┬─────────┘

    ┌─────────────────────────────┐
    │ Validation + Normalization │
    └──────────────┬──────────────┘

    ┌───────────────────┐
    │ Knowledge Graph │
    └─────────┬─────────┘

    ┌────────────────────┐
    │ Hybrid Retrieval │
    │ Vector + Graph │
    └─────────┬──────────┘

    ┌────────────────────┐
    │ Opus 4.8 Reasoning │
    └─────────┬──────────┘

    Grounded Answer

    This architecture gives each component one job.

    LLM: extract and reason.

    Graph: preserve relationships.

    Vector store: retrieve semantic context.

    Cache: eliminate repeated prompt cost.

    Batch API: make backfills cheaper.

    Validation: stop bad facts from entering memory.

    Common mistakes

  • Using the most expensive model for everything
  • You don't need maximum intelligence to extract:

    "Sam joined OpenAI in 2025."

    That's structured extraction.

    Save expensive reasoning for:

    "How did Sam's move to OpenAI affect the projects he was previously working on?"
  • Forgetting prompt caching
  • If your extraction schema is identical across thousands of requests, sending it as fresh input every time is throwing money away.

  • Sending the entire graph to the model
  • Retrieval exists for a reason.

    Find the relevant subgraph first.

    Then reason over it.

  • Treating every relationship as permanent
  • Relationships change.

    People change jobs.

    Projects get renamed.

    Dependencies disappear.

    Use temporal metadata whenever the source supports it.

  • Letting unvalidated extraction write directly to production
  • One hallucinated relationship can contaminate every future answer.

    Validate before writing.

  • Using synchronous processing for historical data
  • If you're backfilling months or years of history, users aren't waiting for those requests.

    Batch them.

  • Building only a vector memory
  • Vectors are great at similarity.

    They aren't a replacement for explicit relationships and temporal state.

    For serious agent memory, hybrid retrieval is often the stronger architecture.

    The 20-minute prototype

    You don't need a massive infrastructure project to test this.

    Step 1 — Start Neo4j

    Run a local graph database.

    Step 2 — Add Graphiti or your graph layer

    Expose the graph through MCP if you're using Claude Code.

    Step 3 — Define one extraction schema

    Keep it stable.

    Don't dynamically rewrite the schema for every episode.

    Step 4 — Add prompt caching

    Cache the reusable extraction instructions.

    Step 5 — Ingest a small dataset

    Start with 100–500 episodes.

    Measure:

  • extraction accuracy
  • duplicate entities
  • relationship accuracy
  • temporal accuracy
  • cache hit rate
  • input tokens
  • output tokens
  • cost per episode
  • Step 6 — Build one multi-hop query

    Ask something the graph actually needs to solve.

    For example:

    "What decisions led to the current architecture, and which earlier projects influenced them?"

    Then inspect the retrieved edges.

    If the answer can't be traced back to graph evidence, your retrieval layer isn't ready.

    The real graph-engineering lesson

    The interesting part isn't that a newer model is cheaper.

    It's that model economics are becoming an architecture problem.

    A naive agent treats every token the same.

    A good agent doesn't.

    It knows:

    Repeated context → cache it

    Historical workload → batch it

    Mechanical extraction → cheap configuration

    Deep reasoning → expensive configuration

    Large graph → retrieve first

    Time-sensitive facts → preserve timestamps

    Model output → validate before storage

    That's graph engineering.

    Not:

    "Put everything in Neo4j and call Claude."

    The winning architecture is the one that spends intelligence exactly where intelligence is needed.

    Cheap ingestion.
    Structured memory.
    Selective retrieval.
    Deep reasoning only at the edge.

    That's how you make graph memory economically viable at scale.

    Thanks for reading.

    Actions
    What You Can Do
    • Export as PDF or Markdown
    • Batch Export to Notion
    • Bookmark & Highlight
    • LinkedIn & Instagram Carousel Maker
    Create Free Account

    Includes 7-day Premium trial

    Advertisement