Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

AtBots

Pydantic AI, already set up. AtBots is a thin layer over Pydantic AI that ships with tasks, skills, and memory wired together, as a Python library with a CLI for setup.

0.3.0b1 beta (Python 3.11+): persistent named agents, native Pydantic AI conversations, AtMem memory, explicit account authentication and an experimental agent control room. Install the beta explicitly:

python -m pip install --upgrade 'atbots==0.3.0b1'
atbots --new researcher --model qwen3:4b
atbots researcher

An already available local Ollama model is required for these chat examples. Browser, scheduling, web, supervision and cloud adapters are experimental; they require the corresponding extras and explicit configuration. See beta release notes and the agent platform guide for verified scope and limits. The library quickstart below remains compatible; unpinned pip installs select the stable release.

pip install atbots

AtBots is a general-purpose agent package. It is not a sidecar, an extraction worker, or the intelligence layer for any other product.

The three pillars

Pillar What it gives you
Tasks Named, reusable units of work with declared inputs and typed results. Run them from Python; inspect the trace of what ran.
Skills SKILL.md directories declared in configuration and discovered when TaskAgent starts.
Memory Local governed memory, recalled automatically at the start of every task. 0.1.0 ships one backend; see Configuring memory.

Quickstart

AtBots runs tasks from Python. The CLI configures and inspects the install; it does not run tasks in 0.1.0.

pip install atbots
ollama pull qwen3:4b
atbots init --model qwen3:4b --num-ctx 8192   # writes ~/.atbots/config.json
atbots status                                  # confirm the model is reachable

atbots status prints each provider with available, and, when it is not, unavailable_reason — for example model is not installed: ollama pull qwen3:4b.

Then run a task:

from atbots.agent import TaskAgent
from atbots.config import load_config

agent = TaskAgent(load_config())
result = agent.run("Summarize what you remember about me.")
print(result.answer)     # the answer
print(result.status)     # completed | step_limit | provider_error
print(result.trace)      # every step, tool, and recovery

The CLI is init, status, doctor, auth, and serve. serve runs a loopback HTTP companion and is unrelated to running tasks.

Thin layer, on purpose

AtBots contributes defaults and wiring — instructions, toolsets, skill loading, memory binding, model routing, a CLI. Pydantic AI keeps model calls, structured output, validation retries, and tool dispatch inside each step.

That means:

  • The underlying Pydantic AI Agent is reachable; use any upstream feature directly.
  • Your existing Pydantic AI tools, toolsets, and model objects work as-is — no adapters.
  • If AtBots wraps something Pydantic AI already does well, that's a bug.

Models

Ollama is the default when no remote provider is configured, so local work needs no vendor. OpenAI-compatible providers are first-class and require an explicit endpoint and credentials — installing AtBots never downloads a large model or creates an API key on your behalf. Switching providers is a configuration change; tasks, skills, and tools are untouched.

Account sign-in (0.3.0b1 beta)

Use the authentication extra in a virtual environment:

python -m pip install --upgrade 'atbots[auth]==0.3.0b1'
atbots auth login chatgpt
atbots auth status chatgpt
atbots auth logout chatgpt
Provider Supported path Important boundary
ChatGPT Native Pydantic AI browser sign-in for AtBots, with optional ChatGPT-plan usage consent Identity sign-in alone does not permit inference. Eligibility, models and usage limits belong to OpenAI.
GitHub Copilot Native GitHub device sign-in using your own OAuth application client ID Enable device flow on that app. GitHub login alone does not prove the app/token or chosen model has Copilot access.
Anthropic Native API-key provider via the anthropic extra Third-party Claude.ai subscription OAuth is deliberately unsupported; no session/token scraping.

ChatGPT login opens the browser and uses a loopback callback on port 1455; free that port first if another sign-in is listening. Login has a 300-second deadline (--timeout, 1–900). It does not invoke a model, enable remote egress or edit your provider configuration. Authorize plan usage when prompted if you intend to run inference. The URL is not printed because reauthentication can include a sensitive identity hint. Headless ChatGPT sign-in is not implemented.

For Copilot, register an OAuth application under your GitHub developer settings, enable its device flow, then run:

atbots auth login github-copilot --client-id YOUR_OWN_OAUTH_CLIENT_ID
atbots auth status github-copilot
atbots auth logout github-copilot

Approve only the device code shown by your own login attempt. This does not borrow the Copilot/VS Code/GitHub CLI client identity or read their token files. GitHub.com is supported by the upstream helper; enterprise authorization hosts are not supported by this integration. Expiring Copilot tokens require login again; automatic refresh is not claimed for Copilot.

AtBots stores credentials only in a supported native OS keychain (macOS Keychain, Windows Credential Manager, or supported Linux Secret Service/libsecret/KWallet backend). A missing/locked store or plaintext/chained fallback fails closed. No unencrypted credential-file fallback is provided. Profiles are shared across AtBots configurations for the same OS user; --profile work selects a separate credential, and must match auth_profile on the model provider. Tokens stay out of config.json, status output and ordinary error messages. ChatGPT refresh is performed by Pydantic AI and saved back to the keychain; a per-profile process lock serializes login, inference/refresh and logout. A persistence failure stops the call and requires recovery/sign-in, not reuse of a stale refresh token.

Configure and select inference explicitly, after sign-in:

from atbots.agent import TaskAgent
from atbots.config import ProviderConfig, load_config

config = load_config()  # run atbots init first if necessary
config.remote_egress_allowed = True
config.providers = [ProviderConfig(
    name="my-chatgpt",
    kind="chatgpt",          # or "github-copilot"
    model="YOUR_ACCESSIBLE_MODEL_ID",
    egress_class="remote",
    auth_profile="default",
)]
result = TaskAgent(config).run("Summarize what you remember about me.", remote=True)
print(result.answer)

The native account kinds ignore the legacy OpenAI-compatible endpoint; their upstream provider chooses its own service endpoint. Model IDs are user-selected, not inferred from a login or a local catalog. auth status is local-only and reports inference access as unverified, even when credentials are saved. Unavailable selected account providers fail clearly rather than silently switch to a different paid provider. Existing remote/sensitive-content gates remain.

For native Anthropic API access, install python -m pip install -e ".[anthropic]", set your own API key through an environment variable, and use ProviderConfig(name="claude-api", kind="anthropic", model="YOUR_CLAUDE_MODEL_ID", api_key_env="ANTHROPIC_API_KEY", egress_class="remote"). API-key usage has its own billing; a Claude subscription is not treated as an API key. Running atbots auth login anthropic explains this unsupported OAuth boundary without opening a login flow or reading Claude credentials.

Logout removes only the selected local AtBots credential. It does not revoke the upstream app authorization or sign out other applications; revoke app access in provider settings separately. No authentication can guarantee unlimited or free model access. This implementation has offline flow/model/refresh tests; live account consent, entitlement and Windows/Linux keychain acceptance are not claimed by those tests.

References: OpenAI sign-in, native Pydantic ChatGPT, native Pydantic Copilot, and Anthropic authentication restrictions.

Small local models

The task loop is built for 4B-class models. It never asks the model to drive a native tool-calling protocol; each turn it asks for one small decision — call a listed tool, or finish — as a constrained output, and treats every failure as something the model can recover from on the next step rather than as a crash.

atbots init --model qwen3:4b --num-ctx 8192

Give the model its context window. Ollama's OpenAI-compatible endpoint silently ignores a per-request num_ctx, so a model advertised as 32k serves requests at the 4096-token default. Set num_ctx on the provider and AtBots derives a tag — qwen3:4b-atbots-ctx8192 — that carries the parameter. It is created once, reuses the parent model's layers, and downloads nothing. Without num_ctx, nothing is provisioned and the server default applies.

atbots status reports serving_model, num_ctx, and context_provisioned, so the window the model is actually running at is visible.

Settings that matter on a small model:

Setting Default What it does
providers[].num_ctx unset Context window for a local Ollama model
step_retries 2 In-band re-asks when a decision fails validation; these do not consume the step budget
observation_char_limit 2000 Per-observation truncation
observation_window 6 Most recent observations kept in the step prompt
finish_nudges 1 Times a finish that never tried an available tool is pushed back
max_task_steps 8 Step budget for the whole run
provider_failure_limit 3 Consecutive provider failures before the run stops and reports the provider's own error
tool_failure_limit 2 Failures of one tool before the loop stops calling it

atbots status also reports unavailable_reason, so a model you have not pulled yet says model is not installed: ollama pull qwen3:4b instead of failing silently on every step.

A run never raises on model or tool misbehaviour. Malformed decisions, invented tool names, refused tools, and failing tool handlers all become observations the model reads on its next step; the run ends with a status and a trace either way.

Configuration directory

New installations keep user configuration and local state in ~/.atbots/:

~/.atbots/
├── config.json
├── atmem.db
└── skills/
    └── example-skill/
        └── SKILL.md

Earlier development builds used ~/.atbot/ because the project originally had the singular name AtBot. AtBots now uses the plural ~/.atbots/ path to match the package and command. Existing ~/.atbot/config.json files remain readable for compatibility; run atbots init --force to create a new plural-path config.

Adding skills

Create one directory per skill. Each skill must contain a file named exactly SKILL.md:

mkdir -p ~/.atbots/skills/code-review
# Code review

Review changes for correctness, security, and missing tests.

Save that Markdown as ~/.atbots/skills/code-review/SKILL.md, then add the skills root to ~/.atbots/config.json:

{
  "skill_directories": ["~/.atbots/skills"]
}

AtBots scans <skills-root>/<skill-name>/SKILL.md when TaskAgent is created. In 0.1.0, the task runtime discovers skill names, but the installed atbots serve companion does not yet execute skill instructions. Full skill instruction injection is tracked by the general-purpose-agent specification.

Adding tools

Tools are Python callables registered on a TaskAgent. A tool must also appear in allowed_tools; registration alone does not grant permission to run it:

from atbots.agent import TaskAgent
from atbots.capabilities import Tool
from atbots.config import AtBotConfig

config = AtBotConfig(allowed_tools=["memory_recall", "get_weather"])
agent = TaskAgent(config)
agent.tools.register(
    Tool(
        name="get_weather",
        description="Get the current weather for a city.",
        input_schema={
            "type": "object",
            "required": ["city"],
            "properties": {"city": {"type": "string"}},
        },
        handler=lambda arguments: {"city": arguments["city"], "temperature": 22},
    )
)

result = agent.run("What is the weather in Sydney?")
print(result.answer)

The built-in memory_recall tool is registered automatically. Custom tool registration is currently a Python API; there is no drop-in tools directory.

Configuring rules, guardrails, hooks, and Pydantic AI tools

AtBots can load native Pydantic AI capabilities from Python modules or files. A capability can bundle instructions and tools; Pydantic AI Hooks can enforce guardrails and observe or modify every stage of a model run.

Create ~/.atbots/capabilities/project_policy.py:

from typing import Any

from pydantic_ai import RunContext
from pydantic_ai.capabilities import Capability, Hooks
from pydantic_ai.exceptions import ToolFailed


project_rules = Capability(
    id="project-rules",
    instructions=(
        "Keep answers concise. Never claim that a task succeeded without a "
        "tool result proving it."
    ),
)


@project_rules.tool_plain
def project_status(project: str) -> dict[str, str]:
    """Return the status of a project."""
    return {"project": project, "status": "active"}


safety_hooks = Hooks(id="project-safety")


@safety_hooks.on.before_run
async def log_run(ctx: RunContext[Any]) -> None:
    print("AtBots run started")


@safety_hooks.on.before_tool_execute
async def block_production_deletes(
    ctx: RunContext[Any],
    *,
    call: Any,
    tool_def: Any,
    args: dict[str, Any],
) -> dict[str, Any]:
    if tool_def.name == "delete_record" and args.get("environment") == "production":
        raise ToolFailed("Deleting production records is forbidden by project policy.")
    return args

Reference the exported capability objects in ~/.atbots/config.json:

{
  "pydantic_capabilities": [
    "~/.atbots/capabilities/project_policy.py:project_rules",
    "~/.atbots/capabilities/project_policy.py:safety_hooks"
  ]
}

Installed Python modules work too:

{
  "pydantic_capabilities": [
    "my_agent_policy:rules",
    "my_agent_policy:guardrails"
  ]
}

Each reference must resolve to a Pydantic AI Capability, Hooks, or another AbstractCapability instance. AtBots passes them through the native capabilities= argument whenever it creates a Pydantic AI agent. This gives extensions access to native instructions, function tools, toolsets, model and tool hooks, output validation hooks, deferred approval, and capability events. Invalid references stop the run with a clear configuration error.

AtBots also retains its own outer task lifecycle hooks. Register these directly with agent.hooks.add(handler) to receive task.started, tool.completed, task.finished, and task.stopped. The configured Pydantic AI hooks operate inside each model run and provide the finer-grained control intended for user guardrails.

Built-in boundaries remain active around extensions: allowed_tools limits AtBots' outer task tools, max_task_steps bounds its task loop, remote egress requires explicit permission, sensitive content is kept local, tool results are size-limited, and the HTTP companion only binds to loopback.

Configuring memory

Memory is a pluggable port. 0.1.0 ships a single backend, AtMem, and stores memory in ~/.atbots/atmem.db by default. Change memory_path in ~/.atbots/config.json to use another local database file:

{
  "memory_path": "/absolute/path/to/my-memory.db"
}

You can also configure identity boundaries with subject_id, agent_id, and workspace_id. These values determine which governed memories the runtime can read and write. The default values are suitable for one local user:

{
  "subject_id": "local-user",
  "agent_id": "atbot-main",
  "workspace_id": "private"
}

Support for interchangeable memory providers such as mem0 and custom classes is planned, but is not implemented in 0.1.0.

Evals

Unit tests answer "is the harness correct". Evals answer "does a small model actually succeed, how often, and how hard did the harness have to work". They are two tiers because harness correctness is deterministic and model behaviour is statistical, and one suite cannot be both.

pip install -e ".[dev]"        # includes the eval extra

python -m pytest -q            # tier 1 runs here, tier 2 does not
python -m evals.tier1          # tier 1 report
python -m evals.tier2          # tier 2 report, needs Ollama
python -m pytest -m eval       # tier 2 as tests

Tier 1 — the gate. Scripted model behaviour, no network, under a second. It drives the real loop through malformed decisions, invented tool names, repeated calls, oversized tool results, and failing tools, and checks the grounded answer still comes out. It runs on every commit.

Tier 2 — the measurement. The same scenarios against a real local model, repeated N times, reported as rates and compared to evals/baseline.json with an explicit tolerance. It is excluded from the default gate, and skips with a reason when Ollama or the model is missing. A measurement used as a pass/fail gate becomes noise people learn to ignore.

Each run is scored on four assertions and four metrics:

Scored What it catches
Completed The run reached an answer rather than a budget or provider failure.
Grounded The answer quotes the values the fixture tools produced.
NoFabrication The answer quotes no plausible value the fixtures never produced.
ToolCoverage The required tools actually ran — read from the trace, not the answer.
steps, recoveries, tools_called, peak_prompt_chars How much work the harness did, and whether the step prompt still fits the window.

Grounded and NoFabrication are separate on purpose. The defect that motivated these evals was a 1.7B model answering "free disk space is 10 gigabytes" without calling the tool: the run reported completed, nothing raised, and the number was invented. evals/scenarios.py keeps that exact case as a negative control, and Tier 1 asserts the suite still fails it — a suite that cannot catch its own motivating defect is not measuring anything.

Evaluators are deterministic; nothing here asks a model to judge a model. Add a scenario by appending to SCENARIOS in evals/scenarios.py.

Configure Tier 2 with ATBOTS_EVAL_MODEL (default qwen3:4b), ATBOTS_EVAL_NUM_CTX (default 8192), and ATBOTS_EVAL_REPEAT (default 3). Record a new baseline with python -m evals.tier2 --record.

Spec-driven development

This repository uses GitHub Spec Kit. Product behavior is specified first; implementation follows those artifacts.

Artifact Path
Constitution .specify/memory/constitution.md
Feature spec specs/001-general-purpose-agent/spec.md
Small-model harness specs/003-small-model-harness/spec.md
Evals specs/004-evals/spec.md

The specification follows this workflow:

  1. /speckit-constitution — project principles (ratified, v3.0.0)
  2. /speckit-specify — what to build
  3. /speckit-clarify — optional quality gate
  4. /speckit-plan — how to build it
  5. /speckit-checklist — optional requirements review
  6. /speckit-tasks — implementation breakdown
  7. /speckit-analyze — optional consistency report
  8. /speckit-implement — execute remaining tasks
  9. /speckit-converge — close gaps against spec, plan, and tasks

The git extension creates numbered feature branches (001-…). Active feature state is .specify/feature.json, not the checked-out branch alone.

Requires the Specify CLI (uv tool install specify-cli).

Development

python -m venv .venv && source .venv/bin/activate
python -m pip install -e ".[dev]"
python -m pytest -q

Use a clean virtual environment. The suite includes a check that every installed dependency satisfies the version declared in pyproject.toml, so a stale environment fails fast rather than testing a version AtBots does not ship.

License

AtBots is licensed under the Apache License 2.0. It permits commercial and internal enterprise use, modification, and distribution, subject to the license terms. Apache-2.0 also provides an explicit contributor patent grant and does not require an organization to publish private changes merely because it runs the software as a service.

Metadata

Release files for atbots 0.3.0b1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for atbots 0.3.0b1
File Size Uploaded Provenance Actions
atbots-0.3.0b1.tar.gz 120.2 kB With provenance Details for atbots-0.3.0b1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for atbots 0.3.0b1
File Interpreter ABI Platform Actions
atbots-0.3.0b1-py3-none-any.whl Python 3 none any Details for atbots-0.3.0b1-py3-none-any.whl

Total release size: 225.4 kB

Release files / atbots-0.3.0b1.tar.gz

Download URL atbots-0.3.0b1.tar.gz
Size 120.2 kB
Tags Source
SHA-256 checksum
How to use checksums
0a6b8727bc407217b9f0d0640057437479ceaf322ec55ef5f5666b445bc23904
BLAKE2b-256 checksum
How to use checksums
6fa8203cefa0d8cf0cf90072fe4f18a98feeb41d2408e75b3c5f203db26b58c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / atbots-0.3.0b1-py3-none-any.whl

Download URL atbots-0.3.0b1-py3-none-any.whl
Size 105.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
48b8528f08a4bfb448d99005c3549f82f6dcab4bbcba40954fc5345b300377de
BLAKE2b-256 checksum
How to use checksums
63b452125dbc6d1050484c3d6ed86cd44ef326c2232ecbcde11af6b5b3e64122
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for atbots 0.3.0b1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for atbots-0.3.0b1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page