Your First Agent
A closer look at what Agent and Runner actually do, and how to hold a multi-turn conversation.
Overview
An Agent is a pure configuration container: a name, system instructions, a model identifier, a
provider, and optionally tools, an output schema, a session, and middleware. It never talks to a
provider itself — it just describes one. Runner is the execution engine: it's the only component
that reads an agent's configuration, calls its provider, executes any tools it requests, validates
its output, and returns a result. Keeping these separate means an Agent is cheap to construct and
inspect, and a Runner can be reused across many agents.
Prerequisites
You should have completed the Quick Start and have a working provider configuration (an API key resolved either explicitly or from an environment variable).
Basic Example
import { Agent, Runner } from "aniki-sdk";
const agent = new Agent({
name: "Assistant",
instructions: "You are a concise, helpful assistant. Keep answers to two sentences or fewer.",
model: "gpt-4o-mini",
provider: "openai",
});
const runner = new Runner();
const result = await runner.run(agent, { message: "What's the capital of Japan?" });
console.log(result.content);Explanation
nameis a human-readable label — it shows up in log fields and lifecycle events, but has no effect on the model's behavior.instructionsis the system prompt. It's sent as arole: "system"message ahead of the conversation on every turn.modelis an opaque string passed straight through to the provider — the SDK does not validate it, so any identifier your provider account accepts works.providercan be a registered provider name (a string, resolved throughProviderFactory) or an already-constructed provider instance. See Providers for what's actually registered today.runner.run(agent, { message })sends the message, waits for the full response, and returns aRunResult— no separate "send" and "receive" step.
If you construct an Agent with invalid configuration (for example, a provider value that
doesn't implement IProvider), the constructor throws ValidationError immediately, not on first
use.
Advanced Usage: an example conversation
Every Agent has a session (an InMemorySession is created automatically if you don't pass one),
and Runner.run reads from and appends to that session on every call. Running the same agent twice
carries the conversation forward:
import { Agent, Runner } from "aniki-sdk";
const agent = new Agent({
name: "Assistant",
instructions: "You are a helpful assistant.",
model: "gpt-4o-mini",
provider: "openai",
});
const runner = new Runner();
const first = await runner.run(agent, { message: "My name is Lalit." });
console.log(first.content); // e.g. "Nice to meet you, Lalit!"
const second = await runner.run(agent, { message: "What's my name?" });
console.log(second.content); // e.g. "Your name is Lalit."
console.log(second.messages.length); // 4: user, assistant, user, assistantEach call to runner.run sends the entire history so far, not just the new message — this is
covered in depth in Memory, including its current lack of any windowing or
truncation.
Best Practices
- Keep instructions focused. The system message is resent on every turn (and grows the conversation's token cost with it) — write instructions once, precisely, rather than repeating context in every user message.
- One
Runner, many agents. ARunnerholds no per-agent state; construct it once and reuse it across everyAgentin your application. - Give each conversation its own session. If two unrelated conversations share an
Agentinstance without separate sessions, they'll see each other's history. Pass a freshISession(or construct a freshAgent) per conversation.
Common Mistakes
- Expecting
modelto be validated. Passing a model name your provider doesn't recognize produces a provider-level error at request time, not a construction-timeValidationError. - Reusing one
Agentacross unrelated users. Since the default session lives on theAgentinstance, sharing oneAgentacross concurrent, unrelated conversations mixes their history together. See Memory for session ownership patterns. - Assuming
runner.runsupports empty input. An emptymessagethrowsValidationErrorbefore any provider request is made.
API Reference
See Agent and Runner in the API
Reference for the complete option and method list.