DEV Community

Cover image for How to Build an AI Employee With a Knowledge Graph (Not Just Another Agent)
Bobby Hall Jr
Bobby Hall Jr

Posted on

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Let's build a tiny version.

Table of Contents


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
Enter fullscreen mode Exit fullscreen mode

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),
      }));
  }
}
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

Our graph now knows:

Maya ──owns──→ Lead #123
Enter fullscreen mode Exit fullscreen mode

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.",
  },
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We just answered:

Who should wake up?

The flow becomes:

Email Reply
    ↓
Event
    ↓
Find Lead
    ↓
Find Owner
    ↓
Wake Maya
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

The important part is the sequence:

Wake
 ↓
Read Graph
 ↓
Reason
 ↓
Act
 ↓
Record
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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",
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now we have two ways to wake an employee:

Customer Reply ──────┐
                     │
Approval Granted ────┼──→ Wake Employee
                     │
Schedule Due ────────┘
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

For Sarah's message, Maya might return:

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

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}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

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(),
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  1. Roster finds the relevant lead
Email
  ↓
related_to
  ↓
Lead #123
Enter fullscreen mode Exit fullscreen mode
  1. Roster finds the owner
Lead #123
  ↓
owned_by
  ↓
Maya
Enter fullscreen mode Exit fullscreen mode
  1. Maya wakes up
Maya
  ↓
Read Graph
  ↓
Understand Context
Enter fullscreen mode Exit fullscreen mode
  1. Maya reasons

Sarah wants a follow-up next Tuesday.

  1. Maya schedules it
Task
  ↓
scheduled_for
  ↓
Tuesday 9:00 AM
Enter fullscreen mode Exit fullscreen mode
  1. Maya sleeps 💤

  2. Tuesday arrives

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

Email #43
status = sent

Lead #123
last_contacted = today
Enter fullscreen mode Exit fullscreen mode

Then:

Maya
  ↓
Sleep
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The important part is the final arrow:

Agent
  ↓
Action
  ↓
Event
  ↓
Graph
  ↓
Next Decision
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

and more like:

World
  ↓
Graph
  ↓
Agent
  ↓
Action
  ↓
Event
  ↓
Graph
Enter fullscreen mode Exit fullscreen mode

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 →

Top comments (0)