phi — a bluesky bot. python + pydantic-ai + atproto + turbopuffer + cosmik/semble. fastapi for the small web surface (status pages, memory graph). ## development - `just run` / `just dev` (hot-reload) / `just deploy` (manual fly.io) / `just release X` (tag vX, CI deploys) - `just check` — lint + typecheck + test - `just evals` — behavioral tests (llm-as-judge) - `just loq-relax ` — relax line limit for a file. never edit loq.toml manually or compress code to fit - work from repo root ## python style - 3.10+ typing (`T | None`, `list[T]`) - prefer functional over OOP - imports at the top — no deferred imports unless circular - never use `pytest.mark.asyncio` - tool params: `Annotated[T, Field(description=...)]` so the LLM sees what each param does ## project structure ``` src/bot/ ├── agent.py # pydantic-ai agent + dynamic system prompts ├── config.py # settings (env vars) ├── main.py # fastapi app, status pages, memory graph ├── status.py # runtime metrics ├── core/ # atproto client, profile, mentionable, goals, self_state ├── memory/ # turbopuffer namespaces + extraction/reconciliation/review pipelines ├── services/ # notification polling + message handler ├── tools/ # native pydantic-ai tools (posting, search, memory, goals, blog, etc.) └── utils/ # thread context, text formatting personalities/ # personality definitions (public; phi.md is the live one) evals/ # behavioral tests scripts/ # proven utility scripts sandbox/ # experiments (graduate to scripts/ once proven) .eggs/ # cloned reference projects ``` ## key architecture - one agent loop, many entry points (notifications batch, scheduled musing, daily reflection, relay check). all end in `agent.run()` with different `PhiDeps`. - actions happen as tool calls inside the run, not via structured output. the agent's return value is a brief summary string for logging. - personality is separate from operational rules. tool docstrings carry per-tool guidance, not the system prompt. - memory: turbopuffer namespaces (`phi-users-{handle}`, `phi-episodic`). intent state on PDS under `io.zzstoatzz.phi.*` (goals, mention consent, legacy queue). - owner-gated mutations (`follow_user`, `propose_goal_change`, `manage_mentionable`, `create_feed`) flow through a like-as-approval mechanism: phi posts an authorization request, owner likes it, next batch lets the action through. - MCP servers: pdsx (atproto record CRUD), pub-search (publication search). connected via `MCPServerStreamableHTTP`, fresh per `agent.run()`. - web grounding via tavily for recency claims (`web_search`). ## documentation deeper reference in `docs/`: - `architecture.md` — entry points, scheduling, why this shape - `memory.md` — the four kinds of state and how they compose - `system-prompt.md` — block-by-block reference for what's in phi's context - `mcp.md` — MCP integration - `testing.md` — testing philosophy ## deployment fly.io app `zzstoatzz-phi`. CI deploys on `v*` tag push (tangled `.tangled/workflows/deploy.yml`). `just release X.Y.Z` tags + pushes; `just deploy` runs fly deploy directly when CI is backed up. push to both `origin` (tangled) and `github` mirror. secrets via `fly secrets set` or `fly secrets import` (pipe `grep ^KEY .env` into it to keep values off the terminal).