Technology Aug 28, 2026 · 6 min read

How to Build an AI Employee With a Knowledge Graph (Not Just Another Agent)

An AI agent can take an action. An AI employee needs to know what happens next. Most AI agents look something like this: Think → Act → Observe → Repeat That's fine for short-lived tasks. But an AI employee needs to work across hours, days, and weeks. It needs to remember: What happened...

DE
DEV Community
by Bobby Hall Jr
How to Build an AI Employee With a Knowledge Graph (Not Just Another Agent)

An AI agent can take an action. An AI employee needs to know what happens next.

Most AI agents look something like this:

Think → Act → Observe → Repeat

That's fine for short-lived tasks.

But an AI employee needs to work across hours, days, and weeks.

It needs to remember:

  • What happened
  • Who owns the work
  • What is waiting
  • What changed
  • What should happen next
  • When it should wake up
  • When a human needs to approve something

That's where graph engineering becomes interesting.

This is the architecture behind

Roster:
software that can own work the way an employee does, not just fire off a single tool call.

Events wake someone up. A graph holds state, ownership, and history.

The agent reasons, acts, writes the result back, then sleeps until the next event.

For Roster, the loop looks like this:

Event
  ↓
Graph
  ↓
Agent
  ↓
Action
  ↓
Graph Update
  ↓
Sleep
  ↓
Wake Again

Let's build a tiny version.

Table of Contents

  • 1. Model the Work
  • 2. Build the Graph
  • 3. Add Events
  • 4. Build the Agent Loop
  • 5. Add Scheduling
  • 6. Build a Tiny AI Employee
  • 7. Put It Together
  • 8. The Bigger Idea

1. Model the Work

Imagine an AI employee called Maya.

Her job is simple:

Follow up with sales leads.

Her world contains:

Maya
  ↓ owns
Lead
  ↓ belongs_to
Company
  ↓ contacted
Email
  ↓ replied_to
Customer

We don't need a massive graph database.

We just need nodes and relationships.

2. Build the Graph

Here's a minimal TypeScript graph:

type Node = {
  id: string;
  type: string;
  data: Record<string, unknown>;
};

type Edge = {
  from: string;
  to: string;
  type: string;
};

class Graph {
  nodes = new Map<string, Node>();
  edges: Edge[] = [];

  addNode(node: Node) {
    this.nodes.set(node.id, node);
  }

  connect(from: string, type: string, to: string) {
    this.edges.push({ from, type, to });
  }

  neighbors(id: string) {
    return this.edges
      .filter((edge) => edge.from === id)
      .map((edge) => ({
        relationship: edge.type,
        node: this.nodes.get(edge.to),
      }));
  }
}

Now create Maya and a lead:

const graph = new Graph();

graph.addNode({
  id: "maya",
  type: "employee",
  data: {
    name: "Maya",
    role: "sales",
  },
});

graph.addNode({
  id: "lead-123",
  type: "lead",
  data: {
    company: "Acme",
    status: "qualified",
  },
});

graph.connect("maya", "owns", "lead-123");

Our graph now knows:

Maya ──owns──→ Lead #123

That's already more useful than two disconnected database records.

3. Add Events

Employees shouldn't constantly run.

Something should wake them up.

For example, Sarah replies to an email:

const event = {
  id: "event-1",
  type: "email.replied",
  data: {
    leadId: "lead-123",
    message: "Sounds interesting. Follow up next Tuesday.",
  },
};

Now we can find the employee responsible for that lead:

function findOwner(event: typeof event) {
  const leadId = event.data.leadId;
  return graph.edges.find(
    (edge) => edge.to === leadId && edge.type === "owns"
  )?.from;
}

const employeeId = findOwner(event);
console.log(employeeId);
// maya

We just answered:

Who should wake up?

The flow becomes:

Email Reply
    ↓
Event
    ↓
Find Lead
    ↓
Find Owner
    ↓
Wake Maya

4. Build the Agent Loop

Now we give Maya an actual agent loop.

async function runEmployee(employeeId: string, event: any) {
  const employee = graph.nodes.get(employeeId);
  const context = {
    employee,
    event,
    relationships: graph.neighbors(employeeId),
  };

  const decision = await agent(context);
  const result = await execute(decision);

  recordResult(employeeId, decision, result);
}

The important part is the sequence:

Wake
 ↓
Read Graph
 ↓
Reason
 ↓
Act
 ↓
Record

The graph gives the agent persistent context.

5. Add Scheduling

Now imagine Sarah says:

Follow up with me next Tuesday.

Maya shouldn't stay running until Tuesday.

She schedules a future event.

type Job = {
  employeeId: string;
  runAt: Date;
  event: any;
};

const jobs: Job[] = [];

function schedule(job: Job) {
  jobs.push(job);
}

Maya can schedule her next action:

schedule({
  employeeId: "maya",
  runAt: new Date("2026-09-01T09:00:00Z"),
  event: {
    id: "followup-1",
    type: "followup.due",
    data: {
      leadId: "lead-123",
    },
  },
});

Then Maya sleeps.

When the time arrives:

async function processJobs() {
  const now = new Date();
  for (const job of jobs) {
    if (job.runAt <= now) {
      await runEmployee(job.employeeId, job.event);
    }
  }
}

Now we have two ways to wake an employee:

Customer Reply ──────┐
                     │
Approval Granted ────┼──→ Wake Employee
                     │
Schedule Due ────────┘

6. Build a Tiny AI Employee

Now let's connect an LLM.

The agent gets the relevant graph context and decides what to do.

async function agent(context: any) {
  const prompt = `
You are Maya, a sales employee.
Your job is to follow up with leads.

Event:
${JSON.stringify(context.event)}

Graph:
${JSON.stringify(context.relationships)}

Decide the next action.
Return JSON:
{
  "action": "...",
  "reason": "...",
  "runAt": "..."
}
`;

  return llm.generateObject(prompt);
}

For Sarah's message, Maya might return:

{
  "action": "schedule_followup",
  "reason": "Sarah requested a follow-up next Tuesday.",
  "runAt": "2026-09-01T09:00:00Z"
}

Then we execute it:

async function execute(decision: any) {
  switch (decision.action) {
    case "schedule_followup":
      schedule({
        employeeId: "maya",
        runAt: new Date(decision.runAt),
        event: {
          id: crypto.randomUUID(),
          type: "followup.due",
          data: decision,
        },
      });
      return {
        success: true,
      };
    case "send_email":
      return sendEmail(decision);
    default:
      throw new Error(`Unknown action: ${decision.action}`);
  }
}

Finally, record what happened:

function recordResult(employeeId: string, decision: any, result: any) {
  graph.addNode({
    id: crypto.randomUUID(),
    type: "agent_action",
    data: {
      employeeId,
      decision,
      result,
      createdAt: new Date(),
    },
  });
}

Now the employee has memory.

Not necessarily memory as a giant conversation transcript.

Memory as state and relationships.

7. Put It Together

Let's walk through the entire workflow.

Sarah replies:

Sounds interesting. Follow up next Tuesday.

  1. Email arrives
Gmail
  ↓
email.replied
  1. Roster finds the relevant lead
Email
  ↓
related_to
  ↓
Lead #123
  1. Roster finds the owner
Lead #123
  ↓
owned_by
  ↓
Maya
  1. Maya wakes up
Maya
  ↓
Read Graph
  ↓
Understand Context
  1. Maya reasons

Sarah wants a follow-up next Tuesday.

  1. Maya schedules it
Task
  ↓
scheduled_for
  ↓
Tuesday 9:00 AM
  1. Maya sleeps 💤

  2. Tuesday arrives

Scheduler
  ↓
followup.due
  ↓
Wake Maya
  1. Maya reads the graph
Maya
  ↓
Lead #123
  ↓
Sarah
  ↓
Previous Conversation
  1. Maya sends the email
Maya
  ↓
send_email()
  ↓
Sarah
  1. Graph updates
Task #123
status = completed

Email #43
status = sent

Lead #123
last_contacted = today

Then:

Maya
  ↓
Sleep

That's a tiny AI employee.

8. The Bigger Idea

The architecture is surprisingly simple:

                   ┌─────────────┐
                   │    Events   │
                   └──────┬──────┘
                          ↓
                   ┌─────────────┐
                   │    Graph    │
                   │             │
                   │ State       │
                   │ Relations   │
                   │ History     │
                   └──────┬──────┘
                          ↓
                   ┌─────────────┐
                   │ AI Employee │
                   └──────┬──────┘
                          ↓
                  ┌───────┴───────┐
                  ↓               ↓
                Tools         Scheduler
                  │               │
                  └───────┬───────┘
                          ↓
                        Events
                          │
                          └────→ Graph

The important part is the final arrow:

Agent
  ↓
Action
  ↓
Event
  ↓
Graph
  ↓
Next Decision

The agent changes the world.

The graph records the change.

The next time the employee wakes up, it doesn't start over.

It continues.

The Takeaway

I think the future of AI employees looks less like:

Prompt → LLM → Tool

and more like:

World
  ↓
Graph
  ↓
Agent
  ↓
Action
  ↓
Event
  ↓
Graph

The LLM provides reasoning.

The tools provide capabilities.

The scheduler provides time.

Events provide wake-ups.

The graph provides continuity.

That's the interesting part.

We're not just building agents that can do things.

We're building software that can own work.

That's what Roster is for.

Try Roster

If the same follow-ups, handoffs, and waiting loops keep eating your week, give them to an AI employee.

Try Roster →

DE
Source

This article was originally published by DEV Community and written by Bobby Hall Jr .

Read original article on DEV Community
Back to Discover

Reading List