In this tutorial, you’ll learn how to resurrect one of the first chatbots in
computer science history—the famous Eliza, originally created in the 1960s!
This not only pays homage to computing’s past but also demonstrates how the
Agent User Interaction Protocol (AG-UI) can integrate seamlessly with any
protocol. Let’s get started.
Prerequisites
Make sure to have the following installed:
Running Eliza via Docker
Let’s run Eliza in a Docker container and connect to her via telnet
.
# Pull and run the Eliza Telnet server
docker build -t eliza:latest https://docs.ag-ui.io/quickstart/eliza/Dockerfile
docker run --rm -it -p 2323:2323 eliza:latest
Once the container is running, we can connect to Eliza.
You should see Eliza greet you with:
$ telnet localhost 2323
Trying ::1...
Connected to localhost.
Escape character is '^]'.
=====================================================
ELIZA - The first chatbot (1966) by Joseph Weizenbaum
Running via Telnet - Type 'quit' to exit
=====================================================
Hello, I'm Eliza. How can I help you today?
> How are you?
What if I were ?
> What if you were?
What does this speculation lead to ?
> I am not sure to be honest.
Do you enjoy being not sure to be honest ?
> quit
Goodbye. It was nice talking to you!
Connection closed by foreign host.
Type quit
to exit Telnet. Congratulations! You’ve just brought Eliza to life.
Now let’s connect her to the Agent User Interaction Protocol.
Bridge Eliza with AG-UI
Now that we have our Eliza server running, let’s show how AG-UI can connect to
any protocol—in this case, the somewhat arcane Telnet protocol.
Project Setup
Let’s create a new project for our AG-UI bridge:
# Create a directory for your project
mkdir awp-eliza && cd awp-eliza
npm init -y
npm install @ag-ui/client telnetlib
npm install -D typescript ts-node @types/node @types/telnetlib
npx tsc --init --target ES2020 --module CommonJS --strict --esModuleInterop --skipLibCheck --outDir dist --rootDir src
mkdir src
AG-UI Recap
The Agent User Interaction Protocol is a framework that lets you connect agents
to clients in a structured, event-driven way. Agents implement the
AbstractAgent
class from @ag-ui/client
and emit events for each piece of
content, progress, etc.
ElizaAgent
We’ll create a special agent class, ElizaAgent
, which:
- Launches a Telnet client to connect to our running Eliza server on
localhost:2323.
- Sends the user’s last message to Eliza.
- Waits for the response, then emits AG-UI events to deliver that text back.
Create a file called src/eliza-agent.ts
:
// src/eliza-agent.ts
import {
AbstractAgent,
RunAgent,
RunAgentInput,
EventType,
BaseEvent,
RunStartedEventSchema,
TextMessageStartEventSchema,
TextMessageContentEventSchema,
TextMessageEndEventSchema,
RunFinishedEventSchema,
} from "@ag-ui/client"
import { Observable } from "rxjs"
import * as telnetlib from "telnetlib"
export class ElizaAgent extends AbstractAgent {
protected run(input: RunAgentInput): RunAgent {
// AG-UI calls `run` each time it wants to generate a response.
return () => {
// Extract user's latest message
const lastUserMessage =
[...input.messages].reverse().find((msg) => msg.role === "user")
?.content || "Hello, Eliza"
return new Observable<BaseEvent>((observer) => {
// 1) Emit RUN_STARTED
observer.next(
RunStartedEventSchema.parse({
type: EventType.RUN_STARTED,
threadId: input.threadId,
runId: input.runId,
})
)
// Create a new telnet client
const client = telnetlib.createConnection(
{
host: "127.0.0.1",
port: 2323,
},
() => {
console.log("Connected to Eliza telnet server")
}
)
// Wait for initial greeting and prompt
client.once("data", () => {
// Once we receive the greeting, send the user message
client.write(lastUserMessage + "\n")
// Wait for the response from Eliza
client.once("data", (data) => {
// Get the response, removing the prompt
let response = data.toString().trim()
// Strip the trailing prompt if present
if (response.endsWith(">")) {
response = response.substring(0, response.length - 1).trim()
}
// Generate a message ID
const messageId = Date.now().toString()
// Emit typical AG-UI message events
observer.next(
TextMessageStartEventSchema.parse({
type: EventType.TEXT_MESSAGE_START,
messageId,
})
)
observer.next(
TextMessageContentEventSchema.parse({
type: EventType.TEXT_MESSAGE_CONTENT,
messageId,
delta: response,
})
)
observer.next(
TextMessageEndEventSchema.parse({
type: EventType.TEXT_MESSAGE_END,
messageId,
})
)
// Emit RUN_FINISHED and complete
observer.next(
RunFinishedEventSchema.parse({
type: EventType.RUN_FINISHED,
threadId: input.threadId,
runId: input.runId,
})
)
// Close the telnet connection
client.end()
observer.complete()
})
})
// Handle errors
client.on("error", (err) => {
console.error("Telnet client error:", err)
observer.error(err)
})
// Return a cleanup function
return () => {
if (client) {
client.end()
}
}
})
}
}
}
Explanation
-
Connection We create a TelnetClient and connect to our Eliza Telnet
server.
-
User Message We find the last user message from the AG-UI input. If none
exists, we default to “Hello, Eliza”.
-
Emitting Events
RUN_STARTED
means the agent started processing the request.
TEXT_MESSAGE_START
/ TEXT_MESSAGE_CONTENT
/ TEXT_MESSAGE_END
together
send the agent’s textual response to the AG-UI pipeline.
RUN_FINISHED
means this run is over.
-
Round-Trip
- Write the user message to Eliza over Telnet.
- Listen for the response from Eliza.
- Emit that response as AG-UI events.
Putting It All Together
Create a main entry point file src/index.ts
that sets up our AG-UI agent:
// src/index.ts
import { ElizaAgent } from "./eliza-agent"
// Create an instance of our agent
const elizaAgent = new ElizaAgent()
elizaAgent.messages = [
{ id: "1", role: "user", content: "I feel anxious about my job." },
]
// Example: Make a request to Eliza through AG-UI
const run = elizaAgent.runAgent({
runId: "run1",
})
Run the application:
Bridging AG-UI to Any Protocol
This example demonstrates how to bridge AG-UI with an existing protocol. The key
components are:
- AG-UI Agent Implementation: Create a class that extends
AbstractAgent
and implements the run
method.
- Implement
run()
: Set up an agent or connect to an existing protocol.
- Manage agent input: Send the right input to the agent (in our case, it’s
the user’s last message).
- Send events: Handle the agent’s response and emit AG-UI events.
You can apply this pattern to connect AG-UI to virtually any protocol or
service:
- HTTP/REST APIs
- WebSockets
- MQTT for IoT devices
- any other protocol
By following this pattern of creating protocol adapters around your AG-UI
agents, you can make your AI agents available through any communication channel
while maintaining a consistent agent implementation.
Conclusion
Congratulations! You’ve resurrected the first historical chatbot by serving
Eliza over Telnet and bridged it to the Agent User Interaction Protocol. This
tutorial demonstrates how easy it is to adopt AG-UI with any existing
protocol—be it Telnet, WebSocket, custom REST calls, or something entirely else.