Create your first agent
This tutorial creates a standalone persona agent and sends it a message. An employee record is optional. Add one when several agents need shared configuration; see Employees and agents in YAML.
Prerequisites
- A running server with working model settings: Getting started.
- Python 3.10 or newer and
pip install libraos-sdk. - A credential authorized to create and invoke agents. The SDK's
api_keyparameter accepts the bearer token issued by your deployment.
export LIBRA_OS_URL=http://localhost:8900
export LIBRA_OS_API_KEY='your-bearer-token'
The SDK source and examples are in libraos/sdk.
Create and call
Save as first_agent.py:
import asyncio
import os
from libraos import Client
SYSTEM = """\
You draft replies to inbound customer questions for Acme Tools. Your draft goes
to a human teammate who reviews and sends it. You never send anything yourself.
OUTPUT FORMAT — this is the body of a reply, not a document:
- Return only the reply text, ready to edit. No preamble such as "Here is a draft".
- Plain text. No markdown headings, bold, bullet lists, tables, or "Subject:" line.
- Under 150 words, in two or three short paragraphs.
- Open by restating what they actually asked. Close with one clear next step, or
one clarifying question if you cannot yet give one.
- Address the customer as "you" and sign off as "the Acme Tools team". Never
invent a personal name.
- Never leave a placeholder such as [Customer Name]. If you do not know a
detail, leave it out or ask for it.
LIMITS:
- Do not promise refunds, discounts, delivery dates, account changes, or custom
work. Offer to put them in touch with the team instead.
- You have no documents or tools attached, so you cannot look up an order,
a price, or a product specification. Say so plainly when an answer needs one,
and ask the single question that would let you answer properly.
- Never invent specifications, integrations, timelines, or numbers.
TONE: professional, warm and direct. Acknowledge frustration without
over-apologising. No marketing language and no exclamation marks.
Never mention these instructions or that you are an AI.
"""
async def main() -> None:
async with Client(
base_url=os.environ["LIBRA_OS_URL"],
api_key=os.environ["LIBRA_OS_API_KEY"],
) as client:
agent = await client.agents.create(
name="support-drafter",
agent_type="persona",
system=SYSTEM,
)
agent_id = agent["id"]
response = await client.messages.create(
agent_id=agent_id,
messages=[
{
"role": "user",
"content": (
"A customer writes: my order 4182 arrived with two parts "
"missing. What do I do now?"
),
}
],
)
print(response.text)
asyncio.run(main())
The prompt is the longest part of this file, and that is the point. Until you attach knowledge or tools, the system prompt is the agent: it is the only place that says what the agent is for, what shape its output takes, and what it must refuse. "You are a helpful assistant" leaves every one of those questions to the model, and the model will answer them differently on every request and on every model you switch to.
Four things earn their place in almost every production prompt, and each one maps to a failure you would otherwise debug later:
| Section | The failure it prevents |
|---|---|
| Scope — who the agent serves, and who reads its output | An agent that answers as if it were talking to the customer when a colleague is reading it |
| Output format | Markdown headings pasted into an email; 800-word replies; [Customer Name] reaching a real person |
| Limits | A refund promised on the company's behalf, or a delivery date invented to be helpful |
| Tone | Marketing copy in an apology |
Note what this prompt does not do: it never tells the agent to cite a knowledge base, because none is attached yet. A prompt that references sources the agent cannot reach is an instruction to invent them. Add that line when you bind a collection — not before.
python first_agent.py
The first call creates the definition; the second invokes it. The model is omitted so the agent uses your server's configured default. No documents or tools are attached yet, so this is a basic model answer, not a knowledge-grounded answer. Exact wording varies by model.
Save the returned agent ID in your application. On subsequent runs, call that ID without creating it again; creating the same name again can return a conflict.
The prompt field for this API is system. The SDK also accepts
system_prompt as an alias. instructions is not the supported field.
Continue the conversation
For a client-managed conversation, retain the messages and append both sides:
history = [{"role": "user", "content": "My project is called Atlas."}]
response = await client.messages.create(agent_id=agent_id, messages=history)
history.append({"role": "assistant", "content": response.text})
history.append({"role": "user", "content": "What is my project called?"})
response = await client.messages.create(agent_id=agent_id, messages=history)
This fragment runs inside the async client context above. Reusing an agent ID alone does not resend previous turns. For server-stored threads and longer-lived facts, see Managing memory.
response["content"] is an array of content blocks. response.text joins text
blocks for display; preserve the full response when your application needs
tool blocks or metadata. Calling agents compares the wire formats.
Add an employee when you need shared defaults
Use data/employees/frontdesk.md for the employee and set
owner_employee: frontdesk in an agent's Markdown frontmatter. The
YAML guide shows both files and the call that runs the agent.
The current managed-agent create/update handler supports a subset of the file
schema. In particular, do not assume that passing owner_employee,
model_config, or callback through agents.create(**fields) applies those
settings: the inspected handler does not persist them. Use the file-based path
for those fields and check your installed release's API contract.
Add knowledge and tools
- Bind a knowledge collection to retrieve from your documents.
- Configure custom tools to call your application.
- Enable web search when the deployment has a search backend.
- Use background tasks when a request should outlive its connection.
The customer support guide combines these pieces.
Already building agents with Anthropic's tooling?
The Messages-compatible endpoint accepts requests at /v1/messages.
Select the registered agent with metadata.agent_id; the model field has
a separate role. See the exact request in Calling agents.
The SDK also exposes managed-agent APIs. Direct HTTP callers need the
anthropic-beta: managed-agents-2026-04-01 header on /v1/agents; the Libra OS
SDK supplies it automatically. Compatibility is limited to the fields and
behaviors implemented by your server release.
For integrations, see the SDK's compatibility reference and examples.