Agentic KnowledgeGraph Engineering 101: From Schema to Reasoning

Architecture Advanced

Your agent asked a question and then couldn’t find the answer it needed

You built an agent that uses a knowledge graph. The agent thinks: “I need to find the customer’s purchase history.” It should query the graph for [Customer] –purchase–> [Product]. But instead it searches for “customer purchases” in the vector database and gets back 47 documents.

The agent has a knowledge graph available but doesn’t know how to use it. It’s like having a structured database and treating it like Google.

This is the problem agentic knowledge graph engineering solves.

Three layers: Data, Agent, Verification

Input: "What's the revenue impact of Feature X on customers in region Y?"
  ↓
[Layer 1] Agent understands the question
  - Parses: Need Feature, Customer Region, Revenue metrics
  ↓
[Layer 2] Agent queries the graph
  - Finds: All customers in Region Y who use Feature X
  - Finds: Their revenue before/after adopting Feature X
  ↓
[Layer 3] Agent verifies the answer
  - Computes: Revenue impact = (after - before) / before
  - Formats: "Revenue increased by 23% for region Y customers"
  ↓
Output: Answer with proof path

Layer 1: Agent understands questions (semantic parsing)

Your agent receives a question. It needs to break it into graph queries.

Problem: How does the agent know what to search for?

Solution: Schema-aware reasoning

class SchemaAwareAgent:
    def __init__(self, graph_schema):
        self.schema = graph_schema
        """
        Schema knows:
        - Node types: Customer, Product, Feature, Region, etc.
        - Relationships: uses, has_feature, in_region, etc.
        - Properties: Customer.id, Product.revenue, etc.
        """
    
    def parse_question(self, question):
        """Break question into graph queries"""
        prompt = f"""
        The knowledge graph has these entities and relationships:
        {self.schema}
        
        Question: {question}
        
        What entities does this question ask about?
        What relationships do you need to follow?
        
        Return a structured plan, not a query yet.
        """
        
        plan = call_claude(prompt)
        return plan
        # Returns something like:
        # "Need to find [Feature X] --has--> [Product]
        #  then find [Customer] --in--> [Region Y] --uses--> [Product]
        #  then find [Revenue] data for those customers"

Then: Translate plan to queries

    def plan_to_queries(self, plan):
        """Convert the plan to actual Cypher queries"""
        prompt = f"""
        Given this plan: {plan}
        And this schema: {self.schema}
        
        Write Cypher queries to answer it.
        Return one query per step.
        """
        
        queries = call_claude(prompt)
        return queries
        # Returns:
        # Query 1: MATCH (f:Feature {name: "Feature X"}) RETURN f
        # Query 2: MATCH (c:Customer)-[:in]->(r:Region {name: "Region Y"})
        #          (c)-[:uses]->(p:Product)-[:has]->(f:Feature {name: "Feature X"})
        #          RETURN c
        # Query 3: MATCH (c:Customer) WHERE c.id IN [...] 
        #          RETURN c.id, c.revenue_before, c.revenue_after

Result: Agent asked a natural language question, broke it into graph queries, got structured data back.

Layer 2: Agent queries the graph (navigation)

Your agent has a plan and queries. Now it needs to execute them and reason about results.

class GraphNavigator:
    def __init__(self, graph_db):
        self.db = graph_db  # Neo4j connection
    
    def navigate(self, queries, question):
        """Execute queries and reason about results"""
        results = []
        for i, query in enumerate(queries):
            result = self.db.execute(query)
            results.append(result)
            
            # After each query, decide what to do next
            if i < len(queries) - 1:
                # Intermediate step, continue
                pass
            else:
                # Final result, analyze it
                answer = self.analyze_results(results, question)
                return answer
    
    def analyze_results(self, results, question):
        """Turn raw query results into an answer"""
        prompt = f"""
        The user asked: {question}
        
        Here are the query results:
        {results}
        
        What's the answer to the question?
        Show your reasoning.
        """
        
        answer = call_claude(prompt)
        return answer

Example execution:

Question: "What's the revenue impact of Feature X on region Y?"

Query 1 result:
  Feature X exists (found it)

Query 2 result:
  5 customers in region Y use Feature X
  [customer_ids: 101, 102, 103, 104, 105]

Query 3 result:
  Customer 101: revenue before = $50k, after = $65k (+30%)
  Customer 102: revenue before = $80k, after = $95k (+19%)
  ... (5 customers total)

Analysis:
  Average revenue impact: +23%
  Total revenue impact: +$120k/year
  Customers benefiting: 5

Answer: "Feature X increased revenue by 23% for region Y customers
        (5 customers, +$120k total impact)"

Layer 3: Agent verifies answers (reasoning)

The agent found an answer. Before returning it, verify it’s correct.

Verification strategies:

Strategy 1: Re-derive the answer

def verify_by_rederiving(answer):
    """Ask the LLM to verify by deriving again independently"""
    prompt = f"""
    I got this answer: {answer}
    
    Verify it by deriving the same result from first principles.
    Show your work.
    If you get a different answer, flag it.
    """
    
    verification = call_claude(prompt)
    if verification.matches(answer):
        return True  # Verified
    else:
        return False  # Mismatch, investigate

Strategy 2: Check constraints

def verify_constraints(answer):
    """Verify the answer satisfies all constraints"""
    constraints = [
        "Revenue impact must be > -100% (can't lose more than you have)",
        "Customer count must match query results",
        "Time periods must be consistent",
    ]
    
    for constraint in constraints:
        check = call_claude(f"Does {answer} satisfy: {constraint}?")
        if not check:
            return False  # Failed a constraint
    return True  # All constraints satisfied

Strategy 3: Query alternative paths

def verify_by_alternative_path(question):
    """Derive the answer via a different graph path"""
    # Original path: Customer -> Feature usage -> Revenue
    # Alternative path: Product -> Revenue changes -> Customers using it
    
    alt_query = """
    MATCH (p:Product)-[:has]->(f:Feature {name: "Feature X"})
          (r:RevenueChange)-[:caused_by]->(f)
          (c:Customer)-[:affected_by]->(r)
    WHERE c.region = "Region Y"
    RETURN SUM(r.impact)
    """
    
    alt_result = execute_query(alt_query)
    
    # Compare original result with alternative
    if alt_result  original_result:
        return True  # Verified via independent path
    else:
        return False  # Results don't match, investigate

Real example: Procurement agent

Setup: Knowledge graph with vendors, products, costs, quality ratings.

Nodes:
- Vendor, Product, Contract, CustomerAccount, Region
Relationships:
- Vendor --supplies--> Product
- Product --costs--> Price
- Product --quality--> Rating
- Vendor --has-contract--> Contract
- Contract --with--> CustomerAccount
- CustomerAccount --in--> Region

Agent request: “Find the best vendor for Product X for our California customers within $50k budget”

Layer 1: Parse

Entities needed: Product (X), Region (California), Budget ($50k)
Relationships: Product --supplied-by--> Vendor
              Vendor --costs--> Price (must be < $50k)
              Vendor --quality--> Rating
              CustomerAccount --in--> Region

Layer 2: Query

Query 1: Find all vendors supplying Product X
MATCH (v:Vendor)-[:supplies]->(p:Product {name: "Product X"}) 
RETURN v

Query 2: Filter by budget and get quality ratings
MATCH (v:Vendor)-[:supplies]->(p:Product {name: "Product X"})
       (v)-[:costs {amount: cost}]->(price)
WHERE cost < 50000
RETURN v.name, v.quality_rating, cost
ORDER BY v.quality_rating DESC

Query 3: Check contracts with our California customers
MATCH (v:Vendor)-[:supplies]->(p:Product {name: "Product X"})
       (v)-[:has_contract]->(c:Contract)-[:with]->(ca:CustomerAccount)
       (ca)-[:in]->(r:Region {name: "California"})
RETURN DISTINCT v.name

Layer 3: Verify

Verify: "Is Vendor A really the best choice?"

Constraint checks:
- Cost < $50k? ✓
- Supplies Product X? ✓
- Has existing contracts in California? ✓
- Quality rating > 0.85? ✓

Re-derive: "Which vendor has highest quality + existing CA presence?"
Result: Vendor A (matching original answer)

Answer: "Vendor A is the best choice: $40k, quality 0.92, existing CA contracts"

Common pitfalls

Pitfall 1: Over-specifying the schema

Don’t create 100 relationship types. Start with 10-15 that matter.

Bad: vendor_supplies_product_at_cost_in_q1_2026
Good: vendor --supplies--> product (with properties: cost, quarter)

Pitfall 2: Not versioning the graph

Products change, prices change, regions change. Your graph gets stale.

# Wrong: Update nodes in place, lose history
MATCH (p:Product {id: 123})
SET p.price = 50  # Lost the old price

# Right: Version the data
CREATE (price:Price {amount: 50, effective_date: "2026-04", valid: true})
MATCH (p:Product {id: 123})
SET p.valid = false
CREATE (p)-[:superseded_by]->(new_p:Product {id: 123, version: 2})

Pitfall 3: Forgetting the agent can’t always query the graph

Sometimes the question is ambiguous. Sometimes the graph is incomplete.

# Handle uncertainty
if graph_query_succeeded:
    answer = use_graph_result()
elif graph_incomplete:
    answer = "I need more information about [X]"
elif question_ambiguous:
    answer = "Did you mean option A or option B?"
else:
    answer = "I couldn't find that in my knowledge base"

The bridge to reasoning engines

This post showed how agents reason over a knowledge graph. The next post shows how to add logic on top:

  • This post: Agent navigates the graph (structured reasoning)
  • Next post: Logic engines verify answers (formal reasoning)
  • Post 12: Combine both in production

The takeaway

Agentic knowledge graph engineering is about three things:

  1. Schema-aware parsing: Agent understands what entities and relationships matter
  2. Efficient navigation: Agent executes smart queries, not brute-force searches
  3. Verification: Agent checks its own answers before returning them

Start simple:

  1. Define your schema (10-15 key entities and relationships)
  2. Extract into a graph (using LLMs)
  3. Build an agent that queries it (few-shot learning)
  4. Verify answers (re-derivation or constraints)

Then iterate. Add relationships as you discover they matter. Update your schema as your domain evolves.

Next in this series: Reasoning Engines: Logic Programming + LLM Inference