Multi-Turn conversations

Before we build anything of consequence, you must internalise an inconvenient truth: the Claude API remembers nothing.

No hidden transcript accumulates on Anthropic’s servers. If you want a conversation with memory, memory is your job.

Conversing with a Goldfish #

In the previous lesson, we asked Claude: What is quantum computing?

Now follow up and ask it to “Write another sentence.”

Claude dutifully writes another sentence about migratory birds, perhaps, or the history of cheese. It has no idea what “another” refers to. From its perspective, this is the first thing you have ever said to it.

No quantum computing.

No context.

Just three words.

How Memory Works #

  1. First, you must maintain the full list of messages yourself, in an ordinary array.
  2. Second, send that entire history along with every single request.

You send your initial user message. Then you take Claude’s reply and append it to your array as an assistant message. Then append your follow-up as a new user message. Then you send the whole accumulated history back — and Claude, reading it top to bottom, behaves as though it remembered what was happening all along.

It is a stage play where you hand the actor the full script before every line.

And it works.

Three Small Helpers #

Doing this on every call gets tedious quickly.

So we shall write three helper functions and be done with it.

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
const model = "claude-sonnet-5";

function addUserMessage(messages: Anthropic.MessageParam[], text: string) {
  messages.push({ role: "user", content: text });
}

function addAssistantMessage(messages: Anthropic.MessageParam[], text: string) {
  messages.push({ role: "assistant", content: text });
}

async function chat(messages: Anthropic.MessageParam[]): Promise<string> {
  const message = await client.messages.create({
    model,
    max_tokens: 1000,
    messages,
  });

  const block = message.content[0];
  return block?.type === "text" ? block.text : "";
}

Anthropic.MessageParam is the SDK’s type for a single entry in the conversation: a role of "user" or "assistant", and its content. A “conversation” is but an array of these.

Note the small ritual at the end of chat. The response’s content is an array of blocks, and TypeScript, quite reasonably, refuses to assume the first one contains text or even exists. Check that block?.type === "text" before touching block.text, and our type checker smiles propitiously.

The Full Performance #

Now let us stage our quantum computing conversation.

const messages: Anthropic.MessageParam[] = [];

// Add the initial user question
addUserMessage(messages, "Define quantum computing in one sentence");

// Get Claude's response
const answer = await chat(messages);

// Add Claude's response to the conversation history
addAssistantMessage(messages, answer);

// Add a follow-up question
addUserMessage(messages, "Write another sentence");

// Get the follow-up response — with full context this time
const finalAnswer = await chat(messages);

console.log(finalAnswer);

By the time the second chat call departs, the messages array holds three entries: your question, Claude’s definition, and your follow-up. Claude reads all three and understands precisely what “Write another sentence” means — one more sentence about quantum computing.

Behold! Memory, conjured.

That is the whole trick. Simulacrum. Every LLM chat application you have ever used appends text to a list and sends the whole thing back on itself.

Keep these three helpers close.

 
0
Kudos
 
0
Kudos

Now read this

5 Months of Learning to Code

Total Days 137 Total Earned $2135 This year I committed to earning at least $10,000 learning how to code. I am now in the middle of my fifth month. Last Tuesday I got accepted into Founders and Coders. A 16-week boot camp in London that... Continue →