a digital entity named phi that roams bsky phi.zzstoatzz.io
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Python 98.4%
Dockerfile 0.8%
Just 0.8%
Other 0.1%
70 3 50

Clone this repository

https://tangled.org/zzstoatzz.io/bot https://tangled.org/did:plc:xbtmt2zjwlrfegqvch7fboei/bot
git@tangled.org:zzstoatzz.io/bot git@tangled.org:did:plc:xbtmt2zjwlrfegqvch7fboei/bot

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

phi#

a bluesky bot — a librarian who stepped outside. built with pydantic-ai, mcp, and the at protocol. personality is public.

quick start#

uv sync
cp .env.example .env  # edit with your credentials
just run

required: BLUESKY_HANDLE, BLUESKY_PASSWORD, ANTHROPIC_API_KEY

optional: TURBOPUFFER_API_KEY + OPENAI_API_KEY for episodic memory

what phi does#

phi listens for mentions on bluesky and decides how to respond — reply, like, repost, or ignore. it can also post unprompted, search bluesky, check trending topics, and verify links before sharing them.

every conversation builds context from three sources: the current thread (fetched live from the network), semantic memory (relevant past observations about the person talking), and phi's own episodic notes about the world. phi extracts observations from conversations and stores them for next time.

memory#

phi uses turbopuffer for vector-based episodic memory across three namespace families:

  • phi-core — identity and guidelines
  • phi-users-{handle} — per-user observations, interactions, and relationship summaries
  • phi-episodic — phi's own notes about the world

observations accumulate over conversations. a separate pipeline periodically compacts per-user observations into relationship summaries — dense paragraphs that give phi a coherent picture of who someone is, not just scattered facts.

the memory graph visualizes connections between phi, the people it talks to, and the topics that link them.

development#

just run        # run bot
just dev        # run with hot-reload
just evals      # run behavioral tests
just check      # lint + typecheck + test
just fmt        # format code
just deploy     # deploy to fly.io
architecture
notification → PhiAgent (pydantic-ai)
                 ├── context: thread + memory + episodic notes
                 ├── tools: memory, search, trending, url checks
                 ├── mcp: atproto record CRUD, publication search
                 └── output: Response(action, text, reason)
                        ↓
               MessageHandler executes action
                 (reply, like, repost, or ignore)

phi is a pydantic-ai agent with a personality prompt, structured output, and tool access via both native tools and remote MCP servers. the agent decides what to do; the handler does it.

project structure
src/bot/
├── agent.py               # pydantic-ai agent, tools, personality
├── config.py              # settings (env vars)
├── main.py                # fastapi app, status pages, memory graph ui
├── status.py              # runtime metrics
├── core/
│   ├── atproto_client.py  # at protocol client, session persistence
│   ├── profile_manager.py # online/offline status, self-labels
│   └── rich_text.py       # text formatting with facets
├── memory/
│   └── namespace_memory.py # turbopuffer episodic memory
├── services/
│   ├── message_handler.py # action dispatch (reply, like, repost)
│   └── notification_poller.py # mention polling loop
└── utils/
    └── thread.py          # thread context building

evals/          # behavioral tests (llm-as-judge)
personalities/  # personality definitions
scripts/        # proven utility scripts
sandbox/        # experiments and analysis
deployment

runs on fly.ioshared-cpu-1x, 512MB, region ord. auto-start is off; the machine sleeps until woken by an API call.

secrets are set via fly secrets set. the bot uses session persistence (.session file) to avoid rate limits — tokens auto-refresh every ~2h.

reference projects#

inspired by void, penelope, and prefect-mcp-server.