Delfhos
Tutorials

Learning-oriented guides that take you through a series of steps to complete a project. Follow them in order — explanations of why things work come later, in Explanation.

Tutorial 1 — Your First Agent

Get from zero to a running AI agent in five minutes. You will install Delfhos, set an API key, run a task, and read the Response.

What you'll build

An agent that searches the web and returns a structured answer with cost and timing telemetry.

1

Install Delfhos

Requires Python 3.9 or newer.

bash
pip install delfhos
2

Set your API key

Delfhos works with Google Gemini, OpenAI, and Anthropic. Create a .env file:

bash
# .env
GOOGLE_API_KEY=your_gemini_key_here
3

Create and run an agent

Pass tools and an llm, then call agent.run().

python
from delfhos import Agent, WebSearch

agent = Agent(
    tools=[WebSearch(llm="gemini-3.1-flash-lite-preview")],
    llm="gemini-3.1-flash-lite-preview",
)

result = agent.run("What are the top 3 AI trends right now?")

print(result.text)
print(f"Cost: ${result.cost_usd:.4f}")
print(f"Duration: {'{'}result.duration_ms{'}'}ms")

agent.stop()
4

Read the Response

Every run returns a Response dataclass.

FieldTypeDescription
textstrFinal answer text
statusboolTrue = success, False = failure
errorstr | NoneError message if status is False
cost_usdfloat | NoneEstimated USD cost
duration_msintWall-clock time in milliseconds
traceAnyFull execution trace object
What just happened?
  1. Your task was sent to the LLM for code generation
  2. The LLM generated Python code that calls WebSearch
  3. That code executed in a sandboxed environment
  4. The result was returned to the LLM to compose a final answer
  5. A Response object was returned with the answer, cost, and timing

Tutorial 2 — Connecting to Gmail

Connect an agent to a real Gmail account so it can read and send emails, with human approval required before sending.

What you'll build

An agent connected to Gmail that summarizes unread emails and pauses for your approval before sending anything.

1

Get OAuth credentials

  1. Go to the Google Cloud Console and create or select a project
  2. Enable the Gmail API
  3. Go to Credentials → Create Credentials → OAuth client ID
  4. Choose Desktop app, download the JSON file
  5. Save it as client_secrets.json in your project folder
2

Build the agent

The first run opens a browser for OAuth consent. After you click Allow, the token is saved at ~/.config/oauth_gmail.json and reused automatically.

python
from delfhos import Agent, Gmail

gmail = Gmail(
    oauth_credentials="client_secrets.json",
    allow=["read", "send"],     # Only these two actions
    confirm=["send"],           # Require approval before sending
)

agent = Agent(
    tools=[gmail],
    llm="gpt-5.4",
)

result = agent.run("Summarize the 5 most recent unread emails in my inbox.")
print(result.text)

agent.stop()
3

Try sending an email (with approval)

python
result = agent.run("Send a short greeting email to alice@example.com")

Because confirm=["send"] is set, the terminal pauses:

╔══════════════════════════════════╗
║ ACTION APPROVAL REQUIRED ║
╠══════════════════════════════════╣
║ Tool: gmail.send ║
║ To: alice@example.com ║
> Approve
Reject

Tutorial 3 — Custom Tools

Expose your own Python functions as agent tools using the @tool decorator. The LLM uses the function name, docstring, and type hints to call it correctly.

1

Define a tool

Three things are required: the @tool decorator, type hints on every parameter and the return value, and a docstring.

python
from delfhos import Agent, tool

@tool
def calculate_discount(price: float, discount_pct: float) -> float:
    """Calculate the final price after applying a percentage discount."""
    return price * (1 - discount_pct / 100)
2

Register and run

python
agent = Agent(
    tools=[calculate_discount],
    llm="claude-sonnet-4-6",
)

result = agent.run("What is the final price of a $250 item with a 15% discount?")
print(result.text)
# → "The final price after a 15% discount is $212.50."
agent.stop()
3

Add error handling with ToolException

Raise ToolException to send a recoverable error back to the LLM instead of crashing.

python
from delfhos import tool, ToolException

@tool(confirm=False)
def get_user_tier(user_id: str) -> str:
    """Look up a user's subscription tier: free, pro, or enterprise."""
    users = {"u001": "pro", "u002": "enterprise"}
    if user_id not in users:
        raise ToolException(f"User '{'{'}user_id{'}'}' not found in the system.")
    return users[user_id]
4

Async tools

Tools can be async. Delfhos handles both sync and async transparently.

python
import aiohttp

@tool
async def fetch_price(symbol: str) -> float:
    """Fetch the current stock price for a ticker symbol."""
    async with aiohttp.ClientSession() as session:
        async with session.get(f"https://api.example.com/price/{'{'}symbol{'}'}") as r:
            data = await r.json()
            return data["price"]

Tutorial 4 — Chat Mode and Memory

Build a persistent, context-aware agent. Chat keeps conversation history within a session; Memory persists facts across restarts.

1

Enable session memory with Chat

Pass a Chat instance in the Agent constructor. The agent remembers context across multiple run() calls.

python
from delfhos import Agent, Chat, Gmail

agent = Agent(
    tools=[Gmail(oauth_credentials="client_secrets.json")],
    llm="gpt-5.4",
    chat=Chat(
        keep=10,
        summarize=True,
        summarizer_llm="claude-opus-4-7",
    ),
)

agent.run("Who emailed me this morning?")
agent.run("Reply to the first one saying I'll follow up tomorrow")

agent.stop()
2

Start an interactive terminal session

python
agent.run_chat()   # Launches an interactive REPL in your terminal
CommandAction
/helpShow available commands
/exitExit the chat session
/stopStop the agent (restarts on next input)
/clearClear the terminal screen
3

Add persistent long-term memory

Memory survives across program restarts. Facts are retrieved by semantic similarity before each task.

python
from delfhos import Agent, Chat, Memory, Gmail

memory = Memory(namespace="alice_support_agent")

memory.save("""
Primary contact: Alice Chen, alice@acme.com
Company: Acme Corp, Enterprise tier
Account manager: Bob (bob@ourco.com)
Preferred contact: email, not Slack
""")

agent = Agent(
    tools=[Gmail(oauth_credentials="client_secrets.json")],
    llm="claude-sonnet-4-6",
    chat=Chat(summarizer_llm="claude-opus-4-7"),
    memory=memory,
)

agent.run("Draft a check-in email to our primary contact")
# The agent uses memory to know Alice's email without being told again