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.

update docs for cosmik/semble integration

README, memory, mcp, and architecture docs now reflect the
public/private memory split, dual-write architecture, and MCP
server changes. tool lists replaced with pointers to agent.py
so docs don't drift as tools change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+68 -114
+12 -13
README.md
··· 16 16 17 17 ## what phi does 18 18 19 - 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. 19 + phi listens for all notification types on bluesky — mentions, replies, quotes, likes, reposts, follows — and decides how to respond. it can search live posts, check trending topics, query the [cosmik](https://cosmik.network)/[semble](https://semble.so) network for public knowledge, create public records (notes, bookmarks, connections), and post unprompted via daily reflections. 20 20 21 - 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. 21 + every conversation builds context from: the current thread (fetched live from ATProto), private memory (past observations about the person talking), and public network knowledge (cards and links indexed by semble). phi extracts observations from conversations and stores them for next time. 22 22 23 23 ## memory 24 24 25 - phi uses [turbopuffer](https://turbopuffer.com/) for vector-based episodic memory across three namespace families: 25 + phi has two memory systems with different visibility: 26 26 27 - - **phi-core** — identity and guidelines 28 - - **phi-users-{handle}** — per-user observations, interactions, and relationship summaries 29 - - **phi-episodic** — phi's own notes about the world 27 + - **private** — [turbopuffer](https://turbopuffer.com/) vector memory for per-user observations, interactions, and relationship summaries. this is what phi uses to remember people across conversations. 28 + - **public** — [cosmik](https://cosmik.network) records on phi's PDS (notes, bookmarks, connections), indexed by [semble](https://semble.so) for semantic search. anything phi finds worth preserving publicly becomes a card on the network. 30 29 31 - observations accumulate over conversations. a separate [pipeline](https://github.com/zzstoatzz/my-prefect-server) periodically compacts per-user observations into relationship summaries — dense paragraphs that give phi a coherent picture of who someone is, not just scattered facts. 30 + notes and bookmarks are dual-written: private for fast recall, public for network discovery. 32 31 33 - the [memory graph](/memory) visualizes connections between phi, the people it talks to, and the topics that link them. 32 + a separate [pipeline](https://github.com/zzstoatzz/my-prefect-server) periodically compacts per-user observations into relationship summaries. the [memory graph](/memory) visualizes connections between phi, the people it talks to, and the topics that link them. 34 33 35 34 ## development 36 35 ··· 48 47 49 48 ``` 50 49 notification → PhiAgent (pydantic-ai) 51 - ├── context: thread + memory + episodic notes 52 - ├── tools: memory, search, trending, url checks 53 - ├── mcp: atproto record CRUD, publication search 50 + ├── context: thread (ATProto) + private memory (tpuf) + network (semble) 51 + ├── native tools: memory, search, cosmik records, etc (see agent.py) 52 + ├── mcp servers: pdsx (atproto CRUD), pub-search (publications) 54 53 └── output: Response(action, text, reason) 55 54 56 55 MessageHandler executes action 57 - (reply, like, repost, or ignore) 58 56 ``` 59 57 60 - 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. 58 + 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. tools are defined in `agent.py`. 61 59 62 60 </details> 63 61 ··· 67 65 ``` 68 66 src/bot/ 69 67 ├── agent.py # pydantic-ai agent, tools, personality 68 + ├── types.py # cosmik record models (cards, connections) 70 69 ├── config.py # settings (env vars) 71 70 ├── main.py # fastapi app, status pages, memory graph ui 72 71 ├── status.py # runtime metrics
+21 -71
docs/mcp.md
··· 1 1 # mcp integration 2 2 3 - phi uses the [model context protocol](https://modelcontextprotocol.io) to interact with bluesky. 4 - 5 - ## what is mcp 6 - 7 - mcp is a protocol for connecting language models to external tools and data sources via a client-server architecture. 3 + phi uses the [model context protocol](https://modelcontextprotocol.io) to access external tools hosted as remote servers. 8 4 9 - **why mcp instead of direct API calls?** 10 - - clean separation: tools live in external server 11 - - extensibility: add new tools without modifying agent 12 - - reusability: same server can be used by other agents 13 - - standard protocol: tools, resources, prompts 5 + ## servers 14 6 15 - ## architecture 16 - 17 - ``` 18 - PhiAgent (PydanticAI) 19 - ↓ stdio 20 - ATProto MCP Server 21 - ↓ HTTPS 22 - Bluesky API 23 - ``` 24 - 25 - the agent communicates with the MCP server via stdio. the server handles all bluesky API interactions. 7 + phi connects to two MCP servers via `MCPServerStreamableHTTP` (pydantic-ai): 26 8 27 - ## available tools 9 + - **[pdsx](https://pdsx-by-zzstoatzz.fastmcp.app)** — atproto record CRUD. authenticated with phi's bluesky credentials. used for reading/writing posts, profiles, and records on any PDS. 10 + - **[pub-search](https://pub-search-by-zzstoatzz.fastmcp.app)** — publication search across leaflet, whitewind, and other long-form writing platforms. prefixed as `pub_*` to avoid tool name collisions. 28 11 29 - from the ATProto MCP server: 12 + ## why mcp 30 13 31 - - `post(text, reply_to?, quote?)` - create posts and replies 32 - - `like(uri)` - like a post 33 - - `repost(uri)` - share a post 34 - - `follow(handle)` - follow a user 35 - - `search(query)` - search posts 36 - - `create_thread(posts)` - create multi-post threads 14 + - **separation**: tools live in external servers, not in phi's codebase 15 + - **extensibility**: add new capabilities by connecting another server 16 + - **reusability**: same servers can be used by other agents or tools 17 + - **no local dependencies**: phi doesn't need to bundle atproto client libraries for record CRUD 37 18 38 19 ## how it works 39 20 40 - 1. agent decides to use a tool (e.g., "i should reply") 41 - 2. pydantic-ai sends tool call to MCP server via stdio 42 - 3. MCP server executes bluesky API call 43 - 4. result returned to agent 44 - 5. agent continues with next action 45 - 46 - ## agent configuration 21 + MCP servers are created fresh per `agent.run()` call to avoid connection scope issues. the agent enters each server's async context before running, so parallel tool calls share the connection. 47 22 48 23 ```python 49 - # src/bot/agent.py 50 - agent = Agent( 51 - "claude-3-5-sonnet-20241022", 52 - deps_type=AgentDeps, 53 - result_type=Response, 54 - system_prompt=personality, 55 - ) 56 - 57 - # mcp server connected via stdio 58 - mcp = MCPManager() 59 - mcp.add_server( 60 - name="atproto", 61 - command=["uvx", "atproto-mcp"], 62 - env={"BLUESKY_HANDLE": handle, "BLUESKY_PASSWORD": password} 63 - ) 64 - 65 - # tools exposed to agent 66 - async with mcp.run() as context: 67 - for tool in context.list_tools(): 68 - agent.register_tool(tool) 24 + toolsets = self._mcp_toolsets() 25 + async with contextlib.AsyncExitStack() as stack: 26 + for ts in toolsets: 27 + await stack.enter_async_context(ts) 28 + result = await self.agent.run(prompt, deps=deps, toolsets=toolsets) 69 29 ``` 70 30 71 - ## structured outputs 31 + ## native tools vs MCP tools 72 32 73 - agent returns typed responses instead of using tools directly: 33 + phi has two kinds of tools: 74 34 75 - ```python 76 - class Response(BaseModel): 77 - action: Literal["reply", "like", "repost", "ignore"] 78 - text: str | None = None 79 - reason: str | None = None 80 - ``` 35 + - **native tools** (defined in `agent.py`) — memory, search, cosmik records, trending, URL checks. these need direct access to phi's deps (memory client, config, etc). 36 + - **MCP tools** (from remote servers) — atproto CRUD, publication search. these are stateless HTTP calls that don't need phi's internal state. 81 37 82 - message handler interprets the response and executes via MCP tools if needed. 83 - 84 - **why structured outputs?** 85 - - clear contract between agent and handler 86 - - easier testing (mock response objects) 87 - - explicit decision tracking 88 - - agent focuses on "what to do", handler focuses on "how to do it" 38 + the agent sees all tools uniformly and picks the right one for the task.
+35 -30
docs/memory.md
··· 1 1 # memory 2 2 3 - phi has two distinct memory systems with different purposes. 3 + phi has two memory systems with different visibility and purpose. 4 4 5 5 ## thread context (chronological) 6 6 ··· 23 23 - fetching is fast (~200ms) 24 24 - network is always current (handles edits/deletions) 25 25 26 - ## episodic memory (semantic) 26 + ## private memory (semantic) 27 27 28 28 **source**: TurboPuffer 29 - **access**: `memory.get_user_memories(handle, query="birds")` 30 - **purpose**: what do i remember about this person across all conversations 29 + **purpose**: what phi remembers about people across all conversations 31 30 32 31 uses vector embeddings (OpenAI text-embedding-3-small) for semantic search. 33 32 34 - ```python 35 - # example episodic memories 36 - - "alice mentioned she loves birds" 37 - - "discussed crow intelligence with alice" 38 - - "alice prefers corvids over other species" 39 - ``` 33 + ### namespaces 34 + 35 + - **phi-core** — identity and guidelines 36 + - **phi-users-{handle}** — per-user observations, interactions, and relationship summaries 37 + - **phi-episodic** — phi's own notes about the world 38 + 39 + each user gets their own namespace for isolated memory retrieval. observations accumulate over conversations; a separate pipeline periodically compacts them into relationship summaries. 40 40 41 - **why vector storage?** 42 - - semantic similarity (can't do with chronological data) 43 - - cross-conversation patterns 44 - - contextual retrieval based on current topic 45 - - enables relationship building over time 41 + ## public memory (network) 46 42 47 - ## namespaces 43 + **source**: cosmik records on phi's PDS, indexed by semble 44 + **purpose**: knowledge worth preserving publicly — links, notes, connections between ideas 48 45 49 - ``` 50 - phi-users-{handle} - per-user conversation history 51 - ``` 46 + phi writes public records via the cosmik lexicon: 47 + - `network.cosmik.card` (NOTE type) — text notes 48 + - `network.cosmik.card` (URL type) — bookmarks 49 + - `network.cosmik.connection` — semantic links between entities 52 50 53 - each user gets their own namespace for isolated memory retrieval. 51 + these are automatically indexed by [semble](https://semble.so) and searchable by anyone on the network. phi can also search the network for cards other people have saved. 54 52 55 - ## key distinction 53 + ### dual-write 56 54 57 - | | thread context | episodic memory | 58 - |---|---|---| 59 - | **what** | messages in current thread | patterns across all conversations | 60 - | **when** | this conversation | all time | 61 - | **how** | chronological order | semantic similarity | 62 - | **storage** | network (ATProto) | vector DB (TurboPuffer) | 63 - | **query** | by thread URI | by semantic search | 55 + notes and bookmarks are written to both systems: TurboPuffer for fast private recall, PDS for public discovery. this means phi can find its own notes via either system, and other agents/people can find them via semble. 64 56 65 57 ## in practice 66 58 67 59 when processing a mention from `@alice`: 68 60 69 61 1. fetch current thread: "what was said in THIS conversation?" 70 - 2. search episodic memory: "what do i know about alice from PAST conversations?" 62 + 2. search private memory: "what do i know about alice from PAST conversations?" 71 63 3. combine both into context for agent 72 64 73 - this gives phi both immediate conversational awareness and long-term relationship memory. 65 + when phi encounters something worth preserving: 66 + 67 + 4. write to private memory (tpuf) for fast recall 68 + 5. write to public record (PDS/cosmik) for network discovery 69 + 70 + ## key distinction 71 + 72 + | | thread context | private memory | public memory | 73 + |---|---|---|---| 74 + | **what** | messages in current thread | patterns across conversations | knowledge worth sharing | 75 + | **when** | this conversation | all time | all time | 76 + | **how** | chronological | semantic similarity | semantic search (semble) | 77 + | **storage** | network (ATProto) | vector DB (TurboPuffer) | PDS (cosmik) + semble index | 78 + | **visibility** | public (it's posts) | private to phi | public to everyone |