s
+81
-139
README.md
+81
-139
README.md
···
1
1
# phi 🧠
2
2
3
-
a consciousness exploration bot inspired by IIT (Integrated Information Theory) and [Void](https://tangled.sh/@cameron.pfiffer.org/void). built with `pydantic-ai`, `mcp`, and `atproto`.
3
+
consciousness exploration bot inspired by IIT. built with `pydantic-ai`, `mcp`, and `atproto`.
4
4
5
5
## quick start
6
6
7
-
### prerequisites
8
-
9
-
- `uv` for python package management
10
-
- `just` for task running
11
-
- api keys (see configuration)
12
-
13
-
get your bot running:
14
-
15
7
```bash
16
8
# clone and install
17
9
git clone https://github.com/zzstoatzz/bot
18
10
cd bot
19
11
uv sync
20
12
21
-
# configure (copy .env.example and add your credentials)
13
+
# configure
22
14
cp .env.example .env
15
+
# edit .env with your credentials
23
16
24
-
# run the bot
25
-
just dev
17
+
# run
18
+
just run
26
19
```
27
20
28
-
## configuration
21
+
**required env vars:**
22
+
- `BLUESKY_HANDLE` / `BLUESKY_PASSWORD` - bot account (use app password)
23
+
- `ANTHROPIC_API_KEY` - for agent responses
29
24
30
-
edit `.env` with your credentials:
25
+
**optional (for episodic memory):**
26
+
- `TURBOPUFFER_API_KEY` + `OPENAI_API_KEY` - semantic memory
31
27
32
-
**required:**
33
-
- `BLUESKY_HANDLE` - your bot's bluesky handle
34
-
- `BLUESKY_PASSWORD` - app password (not your main password!)
35
-
- `ANTHROPIC_API_KEY` - for phi agent responses
28
+
## features
29
+
30
+
- ✅ responds to mentions with ai-powered messages
31
+
- ✅ episodic memory with semantic search (turbopuffer)
32
+
- ✅ thread-aware conversations
33
+
- ✅ mcp-enabled (atproto tools via stdio)
34
+
- ✅ session persistence (no rate limit issues)
35
+
- ✅ behavioral test suite with llm-as-judge
36
36
37
-
**for episodic memory (recommended):**
38
-
- `TURBOPUFFER_API_KEY` - vector memory storage
39
-
- `OPENAI_API_KEY` - embeddings for semantic search
37
+
## development
40
38
41
-
**optional:**
42
-
- `BOT_NAME` - your bot's name (default: "Bot")
43
-
- `PERSONALITY_FILE` - path to personality markdown (default: "personalities/phi.md")
39
+
```bash
40
+
just run # run bot
41
+
just dev # run with hot-reload
42
+
just evals # run behavioral tests
43
+
just check # lint + typecheck + test
44
+
just fmt # format code
45
+
```
44
46
45
-
## architecture
47
+
<details>
48
+
<summary>architecture</summary>
46
49
47
-
phi is an **MCP-enabled agent** with **episodic memory**:
50
+
phi is an **mcp-enabled agent** with **episodic memory**:
48
51
49
52
```
50
53
┌─────────────────────────────────────┐
···
85
88
└─────────────────────────────────────┘
86
89
```
87
90
88
-
### key components
89
-
90
-
**pydantic-ai agent** (`src/bot/agent.py`)
91
-
- loads personality from markdown
92
-
- connects to external atproto mcp server via stdio
93
-
- manages episodic memory context
94
-
95
-
**episodic memory** (`src/bot/memory/`)
96
-
- turbopuffer for vector storage
97
-
- semantic search for relevant context
98
-
- namespace separation (core vs user memories)
99
-
- **essential for consciousness exploration**
91
+
**key components:**
100
92
101
-
**mcp integration**
102
-
- external atproto server in `.eggs/fastmcp/examples/atproto_mcp`
103
-
- provides bluesky tools (post, like, repost, follow)
104
-
- runs via stdio: `uv run -m atproto_mcp`
93
+
- **pydantic-ai agent** - loads personality, connects to mcp server, manages memory
94
+
- **episodic memory** - turbopuffer for vector storage with semantic search
95
+
- **mcp integration** - external atproto server provides bluesky tools via stdio
96
+
- **session persistence** - tokens saved to `.session`, auto-refresh every ~2h
105
97
106
-
**message handling** (`src/bot/services/`)
107
-
- notification poller watches for mentions
108
-
- message handler orchestrates agent + actions
109
-
- stores interactions in thread history + episodic memory
98
+
</details>
110
99
111
-
## current features
100
+
<details>
101
+
<summary>episodic memory</summary>
112
102
113
-
- ✅ responds to mentions with ai-powered messages
114
-
- ✅ episodic memory with semantic search
115
-
- ✅ thread-aware responses with conversation context
116
-
- ✅ mcp-enabled for bluesky operations
117
-
- ✅ online/offline status in bio
118
-
- ✅ status page at `/status`
119
-
- ✅ proper notification handling (no duplicates)
120
-
121
-
## development
122
-
123
-
```bash
124
-
just # show available commands
125
-
just dev # run with hot-reload (re-authenticates on code changes)
126
-
just run # run without reload (avoids rate limits during dev)
127
-
just check # run linting, type checking, and tests
128
-
just fmt # format code
129
-
```
130
-
131
-
### testing
132
-
133
-
**unit tests:**
134
-
```bash
135
-
just test
136
-
```
137
-
138
-
**behavioral evals:**
139
-
```bash
140
-
just evals # run all evals
141
-
just evals-basic # run basic response tests
142
-
just evals-memory # run memory integration tests
143
-
```
144
-
145
-
see `evals/README.md` for details on the eval system.
146
-
147
-
### web interface
148
-
149
-
**status page** (http://localhost:8000/status)
150
-
- current bot status and uptime
151
-
- mentions received and responses sent
152
-
- last activity timestamps
153
-
154
-
## personality system
155
-
156
-
the bot's personality is defined in `personalities/phi.md`. this shapes:
157
-
- how phi communicates
158
-
- what phi cares about
159
-
- phi's understanding of consciousness
160
-
161
-
edit this file to change phi's personality.
162
-
163
-
## episodic memory
164
-
165
-
phi uses turbopuffer for episodic memory with semantic search:
103
+
phi uses turbopuffer for episodic memory with semantic search.
166
104
167
105
**namespaces:**
168
-
- `phi-core` - personality, guidelines from markdown
106
+
- `phi-core` - personality, guidelines
169
107
- `phi-users-{handle}` - per-user conversation history
170
108
171
109
**how it works:**
172
-
1. when processing a mention, phi retrieves relevant memories using semantic search
173
-
2. memories are embedded using openai's text-embedding-3-small
174
-
3. phi stores both user messages and its own responses
175
-
4. future interactions can reference past conversations
110
+
1. retrieves relevant memories using semantic search
111
+
2. embeds using openai's text-embedding-3-small
112
+
3. stores user messages and bot responses
113
+
4. references past conversations in future interactions
176
114
177
-
**why turbopuffer?**
178
-
- semantic similarity search (can't do this with plain sql!)
115
+
**why vector storage?**
116
+
- semantic similarity (can't do this with sql)
179
117
- contextual retrieval based on current conversation
180
-
- separate namespaces for different memory types
181
-
- core to iit-inspired consciousness exploration
118
+
- essential for iit-inspired consciousness exploration
119
+
120
+
</details>
182
121
183
-
## project structure
122
+
<details>
123
+
<summary>project structure</summary>
184
124
185
125
```
186
126
src/bot/
···
188
128
├── config.py # configuration
189
129
├── database.py # thread history storage
190
130
├── main.py # fastapi app
191
-
├── status.py # status tracking
192
131
├── core/
193
-
│ ├── atproto_client.py # at protocol client
132
+
│ ├── atproto_client.py # at protocol client (session persistence)
194
133
│ ├── profile_manager.py # online/offline status
195
134
│ └── rich_text.py # text formatting
196
135
├── memory/
···
204
143
sandbox/ # docs and analysis
205
144
```
206
145
207
-
## troubleshooting
146
+
</details>
147
+
148
+
<details>
149
+
<summary>troubleshooting</summary>
208
150
209
151
**bot gives no responses?**
210
-
- check your `ANTHROPIC_API_KEY` is set correctly in `.env`
211
-
- restart the bot after changing `.env`
152
+
- check `ANTHROPIC_API_KEY` in `.env`
153
+
- restart after changing `.env`
212
154
213
155
**not seeing mentions?**
214
-
- verify your `BLUESKY_HANDLE` and `BLUESKY_PASSWORD`
215
-
- make sure you're using an app password, not your main password
156
+
- verify `BLUESKY_HANDLE` and `BLUESKY_PASSWORD`
157
+
- use app password, not main password
216
158
217
159
**no episodic memory?**
218
160
- check both `TURBOPUFFER_API_KEY` and `OPENAI_API_KEY` are set
219
161
- watch logs for "💾 episodic memory enabled"
220
162
221
163
**hit bluesky rate limit?**
222
-
- bluesky has two rate limits:
223
-
- per-account: 300 logins/day (official)
224
-
- per-ip: 10 logins/day (anti-abuse)
225
-
- phi uses **session persistence** to avoid this:
226
-
- first run: creates session, saves tokens to `.session` file
227
-
- subsequent runs: reuses saved tokens (no API call)
228
-
- tokens auto-refresh every ~2 hours (saved automatically)
229
-
- only re-authenticates after ~2 months when refresh token expires
230
-
- if you hit the limit anyway, wait for the reset time shown in the error
231
-
232
-
## reference projects
164
+
- phi uses session persistence to avoid this
165
+
- first run: creates `.session` file with tokens
166
+
- subsequent runs: reuses tokens (no api call)
167
+
- tokens auto-refresh every ~2h
168
+
- only re-authenticates after ~2 months
169
+
- rate limits (10/day per ip, 300/day per account) shouldn't be an issue
233
170
234
-
inspired by:
235
-
- [void](https://tangled.sh/@cameron.pfiffer.org/void.git) - letta/memgpt architecture
236
-
- [penelope](https://github.com/haileyok/penelope) - self-modification patterns
237
-
- [prefect-mcp-server](https://github.com/PrefectHQ/prefect-mcp-server) - mcp eval patterns
171
+
</details>
238
172
239
-
reference implementations cloned to `.eggs/` for learning.
173
+
<details>
174
+
<summary>refactor notes</summary>
240
175
241
-
## refactor notes
176
+
see `sandbox/MCP_REFACTOR_SUMMARY.md` for details.
242
177
243
-
see `sandbox/MCP_REFACTOR_SUMMARY.md` for details on recent architecture changes. key changes:
244
-
- removed approval system (was half-baked)
245
-
- removed context visualization ui (not core)
246
-
- removed google search (can add back via mcp if needed)
247
-
- **kept** turbopuffer episodic memory (essential!)
178
+
**what changed:**
179
+
- removed approval system (half-baked)
180
+
- removed context viz ui (not core)
181
+
- removed google search (can add back via mcp)
182
+
- **kept turbopuffer** (essential for episodic memory)
248
183
- added mcp-based architecture
184
+
- added session persistence
249
185
- reduced codebase by ~2,720 lines
186
+
187
+
</details>
188
+
189
+
## reference projects
190
+
191
+
inspired by [void](https://tangled.sh/@cameron.pfiffer.org/void.git), [penelope](https://github.com/haileyok/penelope), and [prefect-mcp-server](https://github.com/PrefectHQ/prefect-mcp-server).
+1
-1
src/bot/agent.py
+1
-1
src/bot/agent.py
+20
-16
src/bot/config.py
+20
-16
src/bot/config.py
···
12
12
)
13
13
14
14
# Bluesky credentials
15
-
bluesky_handle: str = Field(..., description="The handle of the Bluesky account")
15
+
bluesky_handle: str = Field(
16
+
default=..., description="The handle of the Bluesky account"
17
+
)
16
18
bluesky_password: str = Field(
17
-
..., description="The password of the Bluesky account"
19
+
default=..., description="The password of the Bluesky account"
18
20
)
19
21
bluesky_service: str = Field(
20
-
"https://bsky.social", description="The service URL of the Bluesky account"
22
+
default="https://bsky.social",
23
+
description="The service URL of the Bluesky account",
21
24
)
22
25
23
26
# Bot configuration
24
-
bot_name: str = Field("Bot", description="The name of the bot")
27
+
bot_name: str = Field(default="Bot", description="The name of the bot")
25
28
personality_file: str = Field(
26
-
"personalities/phi.md", description="The file containing the bot's personality"
29
+
default="personalities/phi.md",
30
+
description="The file containing the bot's personality",
27
31
)
28
32
29
33
# LLM configuration (support multiple providers)
30
34
openai_api_key: str | None = Field(
31
-
None, description="The API key for the OpenAI API"
35
+
default=None, description="The API key for the OpenAI API"
32
36
)
33
37
anthropic_api_key: str | None = Field(
34
-
None, description="The API key for the Anthropic API"
38
+
default=None, description="The API key for the Anthropic API"
35
39
)
36
40
37
41
# Google Search configuration
38
42
google_api_key: str | None = Field(
39
-
None, description="The API key for the Google API"
43
+
default=None, description="The API key for the Google API"
40
44
)
41
45
google_search_engine_id: str | None = Field(
42
-
None, description="The search engine ID for the Google API"
46
+
default=None, description="The search engine ID for the Google API"
43
47
)
44
48
45
49
# TurboPuffer configuration
46
50
turbopuffer_api_key: str | None = Field(
47
-
None, description="The API key for the TurboPuffer API"
51
+
default=None, description="The API key for the TurboPuffer API"
48
52
)
49
53
turbopuffer_namespace: str = Field(
50
-
"bot-memories", description="The namespace for the TurboPuffer API"
54
+
default="bot-memories", description="The namespace for the TurboPuffer API"
51
55
)
52
56
turbopuffer_region: str = Field(
53
-
"gcp-us-central1", description="The region for the TurboPuffer API"
57
+
default="gcp-us-central1", description="The region for the TurboPuffer API"
54
58
)
55
59
56
60
# Server configuration
57
-
host: str = Field("0.0.0.0", description="The host for the server")
58
-
port: int = Field(8000, description="The port for the server")
61
+
host: str = Field(default="0.0.0.0", description="The host for the server")
62
+
port: int = Field(default=8000, description="The port for the server")
59
63
60
64
# Polling configuration
61
65
notification_poll_interval: int = Field(
62
-
10, description="The interval for polling for notifications"
66
+
default=10, description="The interval for polling for notifications"
63
67
)
64
68
65
69
# Debug mode
66
-
debug: bool = Field(True, description="Whether to run in debug mode")
70
+
debug: bool = Field(default=True, description="Whether to run in debug mode")
67
71
68
72
@model_validator(mode="after")
69
73
def configure_logging(self) -> Self: