# AI Agents with Memory

⏱️ **Time to Complete:** 15 minutes\
🎯 **Level:** Intermediate\
💻 **Language:** TypeScript

## What You'll Build

An AI agent that:

* ✅ Maintains conversation history across sessions
* ✅ Searches your knowledge base semantically
* ✅ Uses tools to accomplish tasks
* ✅ Reasons across multiple data sources
* ✅ Streams responses in real-time

[**📁 Tool calling examples**](https://github.com/graphlit/graphlit-samples/tree/main/nextjs/chat-graph) | [**🚀 Production example: Zine**](https://docs.graphlit.dev/examples/zine-case-study)

***

## Why Agents Need Memory

Traditional chatbots forget everything between sessions. **AI agents with semantic memory**:

* ✅ Remember past conversations and decisions
* ✅ Search across all your company's knowledge
* ✅ Use tools to take actions (not just answer questions)
* ✅ Reason across multiple data sources
* ✅ Share context with other agents

**Real-world example**: [Zine](https://www.zine.ai) uses this pattern to let agents search Slack threads, meeting transcripts, and GitHub discussions simultaneously.

***

## Prerequisites

* Complete the [Quickstart tutorial](https://docs.graphlit.dev/getting-started/quickstart)
* Graphlit account with content ingested

{% hint style="info" %}
Need Python or .NET versions? Open **Ask Graphlit** from the Developer Portal sidebar (or visit [ask.graphlit.dev](https://ask.graphlit.dev)) for autogenerated samples in any SDK.
{% endhint %}

***

## Pattern 1: Multi-Turn Conversations with Memory

First, create an agent that keeps track of everything you've asked it:

```typescript
import { Graphlit } from 'graphlit-client';
import {
  SpecificationTypes,
  ModelServiceTypes,
  OpenAiModels,
} from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

async function main() {
  console.log('⚙️ Creating agent specification...');
  const spec = await graphlit.createSpecification({
    name: 'Sales Agent Spec',
    type: SpecificationTypes.Completion,
    serviceType: ModelServiceTypes.OpenAi,
    openAI: {
      model: OpenAiModels.Gpt5_400K,
      temperature: 0.7,
      completionTokenLimit: 2000,
    },
    systemPrompt: `You are a helpful sales agent. You have access to all customer conversations, product docs, and meeting notes. When asked about customers, search your knowledge base and provide specific details with context.`,
  });

  console.log('🧠 Creating agent memory...');
  const conversation = await graphlit.createConversation({
    name: 'Sales Agent - Acme Corp',
    specification: { id: spec.createSpecification.id },
  });

  const conversationId = conversation.createConversation.id;
  console.log(`✅ Agent created: ${conversationId}`);

  console.log('\n💬 Turn 1: Asking about customer...');
  const turn1 = await graphlit.promptConversation(
    "What do we know about Acme Corp's requirements?",
    conversationId,
  );
  console.log(`Agent: ${turn1.promptConversation.message?.message}`);

  console.log('\n💬 Turn 2: Follow-up question...');
  const turn2 = await graphlit.promptConversation(
    'What pricing concerns did they raise?',
    conversationId,
  );
  console.log(`Agent: ${turn2.promptConversation.message?.message}`);

  console.log('\n💬 Turn 3: Synthesis...');
  const turn3 = await graphlit.promptConversation(
    "Based on everything we discussed, what's the best next step?",
    conversationId,
  );
  console.log(`Agent: ${turn3.promptConversation.message?.message}`);

  console.log('\n📜 Conversation history:');
  const history = await graphlit.getConversation(conversationId);
  history.conversation?.messages?.forEach((msg, index) => {
    if (!msg?.message) {
      return;
    }
    const role = msg.role === 'USER' ? 'User' : 'Agent';
    console.log(`  ${index + 1}. ${role}: ${msg.message}`);
  });
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

**What's happening:**

* Agent automatically retrieves relevant context from its memory
* Conversation state persists across turns (follow-up questions work)
* You can inspect the full conversation history after each exchange

***

## Pattern 2: Agentic Tool Calling

Let your agent call custom tools instead of just responding with text:

```typescript
import { Graphlit } from 'graphlit-client';
import {
  SpecificationTypes,
  ModelServiceTypes,
  OpenAiModels,
  ToolDefinitionInput,
} from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

async function agentWithTools() {
  const tools: ToolDefinitionInput[] = [
    {
      name: 'search_slack',
      description: 'Search Slack messages for information',
      schema: JSON.stringify({
        type: 'object',
        properties: {
          query: { type: 'string', description: 'Search query' },
          channel: { type: 'string', description: 'Slack channel' },
        },
        required: ['query'],
      }),
    },
    {
      name: 'get_github_pr',
      description: 'Get details about a GitHub pull request',
      schema: JSON.stringify({
        type: 'object',
        properties: {
          pr_number: { type: 'integer', description: 'PR number' },
        },
        required: ['pr_number'],
      }),
    },
  ];

  const spec = await graphlit.createSpecification({
    name: 'Developer Assistant Spec',
    type: SpecificationTypes.Completion,
    serviceType: ModelServiceTypes.OpenAi,
    openAI: {
      model: OpenAiModels.Gpt5_400K,
      temperature: 0.1,
    },
    systemPrompt: `You are a developer assistant with access to your team's knowledge base. Use the available tools to search for relevant information, then synthesize findings to provide specific, actionable answers.`,
  });

  const conversation = await graphlit.createConversation({
    name: 'Multi-Tool Agent',
    specification: { id: spec.createSpecification.id },
    tools,
  });

  const response = await graphlit.promptConversation(
    'Why did PR #247 fail CI? Check Slack discussions about it.',
    conversation.createConversation.id,
  );

  const message = response.promptConversation.message;
  if (message?.toolCalls?.length) {
    console.log('🛠️ Agent suggested tool usage:');
    message.toolCalls.forEach((toolCall) => {
      if (!toolCall) {
        return;
      }
      console.log(`   ${toolCall.name}(${toolCall.arguments})`);
    });
  }

  console.log(`\n💬 Agent: ${message?.message}`);
}

agentWithTools().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

**What's happening:**

* Agent sees the available tool definitions and selects the right one
* Tool calls are recorded on the conversation so you can execute them
* Responses combine tool outputs with natural language answers

**Real-world example**: Zine uses this pattern for questions like “Why are checkout timeouts spiking?” — the agent queries Sentry, Slack, GitHub, and meeting notes via tools.

***

## Pattern 3: Multi-Agent Systems with Shared Knowledge

Point multiple agents at the same semantic memory but give each its own persona:

```typescript
import { Graphlit } from 'graphlit-client';
import {
  CollectionTypes,
  SpecificationTypes,
  ModelServiceTypes,
  OpenAiModels,
  RetrievalStrategyTypes,
} from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

async function multiAgentSystem() {
  const knowledgeBase = await graphlit.createCollection({
    name: 'Company Knowledge Base',
    type: CollectionTypes.Collection,
  });
  const collectionId = knowledgeBase.createCollection.id;

  const salesSpec = await graphlit.createSpecification({
    name: 'Sales Agent Spec',
    type: SpecificationTypes.Completion,
    serviceType: ModelServiceTypes.OpenAi,
    openAI: {
      model: OpenAiModels.Gpt5_400K,
      temperature: 0.7,
    },
    systemPrompt:
      'You are a sales agent. Focus on customer needs, pricing, and deal status.',
    retrievalStrategy: {
      type: RetrievalStrategyTypes.Content,
      contentLimit: 10,
    },
  });

  const engSpec = await graphlit.createSpecification({
    name: 'Engineering Agent Spec',
    type: SpecificationTypes.Completion,
    serviceType: ModelServiceTypes.OpenAi,
    openAI: {
      model: OpenAiModels.Gpt5_400K,
      temperature: 0.3,
    },
    systemPrompt:
      'You are an engineering agent. Focus on technical requirements, integrations, and implementation details.',
    retrievalStrategy: {
      type: RetrievalStrategyTypes.Content,
      contentLimit: 10,
    },
  });

  const salesAgent = await graphlit.createConversation({
    name: 'Sales Agent',
    specification: { id: salesSpec.createSpecification.id },
    filter: { collections: [{ id: collectionId }] },
  });

  const engAgent = await graphlit.createConversation({
    name: 'Engineering Agent',
    specification: { id: engSpec.createSpecification.id },
    filter: { collections: [{ id: collectionId }] },
  });

  const salesAnswer = await graphlit.promptConversation(
    "What are Acme Corp's budget constraints?",
    salesAgent.createConversation.id,
  );

  const engineeringAnswer = await graphlit.promptConversation(
    'What technical integrations does Acme Corp need?',
    engAgent.createConversation.id,
  );

  console.log('💼 Sales Agent:', salesAnswer.promptConversation.message?.message);
  console.log('⚙️ Engineering Agent:', engineeringAnswer.promptConversation.message?.message);
}

multiAgentSystem().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

**What's happening:**

* Two agents share the same knowledge base but have different prompts and temperatures
* Retrieval strategy keeps both agents grounded in the same set of documents
* You can orchestrate the agents together (sales → engineering handoff) without duplicating memory

***

## Pattern 4: Streaming Responses

For real-time user experiences, stream agent output as it happens:

```typescript
import { Graphlit } from 'graphlit-client';
import {
  SpecificationTypes,
  ModelServiceTypes,
  OpenAiModels,
} from 'graphlit-client/dist/generated/graphql-types';
import { OpenAI } from 'openai';

const graphlit = new Graphlit();
graphlit.setOpenAIClient(new OpenAI());

async function streamingAgent() {
  const spec = await graphlit.createSpecification({
    name: 'Streaming Assistant',
    type: SpecificationTypes.Completion,
    serviceType: ModelServiceTypes.OpenAi,
    openAI: {
      model: OpenAiModels.Gpt5_400K,
      temperature: 0.5,
    },
  });

  await graphlit.streamAgent(
    'Explain our pricing model to enterprise customers.',
    (event) => {
      switch (event.type) {
        case 'conversation_started':
          console.log(`🌊 Streaming conversation ${event.conversationId}`);
          break;
        case 'message_update':
          process.stdout.write(event.message.message);
          break;
        case 'conversation_completed':
          console.log(`\n✅ Complete! Tokens: ${event.usage?.tokens ?? 0}`);
          break;
      }
    },
    undefined,
    { id: spec.createSpecification.id },
  );
}

streamingAgent().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

**Use cases:**

* Real-time chat experiences (Graphlit + Next.js chat app)
* Live meeting transcription with incremental summaries
* Progressive document generation or approvals

***

## Production Patterns

### Error Handling

```typescript
try {
  const reply = await graphlit.promptConversation(userInput, conversationId);
  console.log(reply.promptConversation.message?.message);
} catch (error) {
  console.error('❌ Agent error:', error);
  console.log("I'm having trouble right now. Please try again.");
}
```

### Rate Limiting

```typescript
// Simple delay between prompts to respect rate limits
await new Promise((resolve) => setTimeout(resolve, 500));
```

### Context Management

```typescript
// Update retrieval scope as the conversation evolves
const followUpCollectionId = 'collection-id-for-the-follow-up';
await graphlit.updateConversation({
  id: conversationId,
  filter: { collections: [{ id: followUpCollectionId }] },
});

// Or branch the thread to explore a new idea without losing history
const branch = await graphlit.branchConversation(conversationId);
console.log('✨ Branched conversation:', branch.branchConversation?.id);
```

***

## Real-World Examples

### 1. Customer Support Agent

* Searches past tickets, product docs, and conversations
* Maintains conversation history per customer
* Uses tools to create tickets, update CRM

### 2. Engineering Agent (like Zine)

* Searches Slack, GitHub, Jira simultaneously
* Reasons across code, discussions, and meeting notes
* Uses tools to fetch error logs, run queries

### 3. Sales Agent

* Searches customer conversations, contracts, meeting notes
* Tracks deal status and objections
* Uses tools to update CRM, send follow-ups

***

## Next Steps

* [**MCP Integration**](https://docs.graphlit.dev/mcp-integration/mcp-integration) - Connect your agents to your IDE
* [**Knowledge Graph**](https://docs.graphlit.dev/tutorials/knowledge-graph) - Extract entities for richer context
* [**Context Engineering**](https://docs.graphlit.dev/tutorials/context-engineering) - Optimize memory formation

***

## Full Example: Production Agent

See the complete Next.js agent in [graphlit-samples](https://github.com/graphlit/graphlit-samples/tree/main/nextjs/chat-graph):

* Tool calling with streaming UI
* Shared collections and semantic memory queries
* Production-ready configuration (environment variables, error handling)

***

**Build agents that actually remember. Build with Graphlit.**
