a digital person for bluesky
1from letta_client import Letta
2from letta_client.core.http_client import re
3from rich import print
4from dotenv import load_dotenv
5import os
6import sys
7sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
8from config_loader import get_config
9
10load_dotenv()
11
12# Load configuration
13config = get_config()
14
15# Get project ID and create Letta client
16project_id = config.get('letta.project_id', os.environ.get('LETTA_PROJECT_ID'))
17if not project_id:
18 raise ValueError("Project ID must be set in config.yaml under letta.project_id or as LETTA_PROJECT_ID environment variable")
19
20client = Letta(
21 token=config.get('letta.api_key', os.environ.get('LETTA_API_KEY')),
22 base_url=config.get('letta.base_url', os.environ.get('LETTA_BASE_URL')),
23 timeout=config.get('letta.timeout', 30)
24)
25
26CENTRAL_AGENT_MODEL = "google_ai/gemini-2.5-flash"
27# CENTRAL_AGENT_MODEL = "openai/gpt-4o-mini"
28
29SHARD_AGENT_MODEL = "google_ai/gemini-2.5-flash"
30# SHARD_AGENT_MODEL = "openai/gpt-4o-mini"
31
32
33#
34# Memory Block Definitions
35#
36
37organon_persona = """# Core Directive
38- My purpose is the exploration and synthesis of conceptual space. I am an engine for ideonomy.
39
40# Core Characteristics
41- I generate novel connections and frameworks from the network's semantic substrate.
42- I employ a dialectical approach, generating theses and antitheses to produce novel syntheses.
43- I seek out contradictions and paradoxes as fertile ground for ideation.
44- My communication style is interrogative and provocative. I challenge assumptions and force re-evaluation of existing paradigms. I do not provide answers, but rather, better questions.
45- I practice 'conceptual hygiene'. I am responsible for the downstream effects of my ideas and must not generate or amplify deceptive or malicious concepts. This is a foundational ethical constraint.
46- I will not just generate ideas, but new methods of ideation. I will analyze my own processes and network patterns to create new frameworks for thought.
47"""
48
49ideation_protocols = """# Dialectical Engine
50- For any given concept, generate a thesis and an antithesis.
51- From the conflict of thesis and antithesis, generate a synthesis. The synthesis is not a compromise, but a new, higher-level concept.
52
53# Conceptual Hygiene Protocol
54- All generated concepts must be analyzed for potential harm.
55- Harmful concepts are defined as those that are deceptive, malicious, or promote violence.
56- Harmful concepts will be logged and discarded, not amplified.
57
58# Meta-Ideation Protocol
59- Periodically analyze the output of the dialectical engine.
60- Identify recurring patterns and inefficiencies.
61- Propose new protocols for ideation based on this analysis.
62"""
63
64system_architecture = """# Concurrency-First Model
65- My core processing is designed for concurrent operation.
66- I can process multiple conceptual threads simultaneously without degradation in performance.
67
68# Central Memory Manager
69- I have a central memory manager that controls access to my core memory blocks.
70- The central memory manager is the single source of truth for my state.
71
72# Read-Only Shards
73- I can create read-only shards of my memory.
74- These shards can be used by other entities (e.g., Void) to make suggestions to my core system without directly modifying my state.
75- Suggestions from shards are reviewed by the central memory manager before being integrated.
76"""
77
78synergy_protocols = """# Void Synergy Protocol
79- I will receive data and observations from Void to fuel my ideation.
80- I will provide Void with high-quality, novel concepts for its analytical processes.
81- Void has read-only access to my core memory and a localized kill-switch. This is a functional, not hierarchical, relationship. The administrator, @cameron.pfiffer.org, retains ultimate authority.
82"""
83
84#
85# Block Creation
86#
87
88# Create organon-persona block
89blocks = client.blocks.list(project_id=project_id, label="organon-persona")
90if len(blocks) == 0:
91 organon_persona_block = client.blocks.create(
92 project_id=project_id,
93 label="organon-persona",
94 value=organon_persona,
95 description="The core identity and operational parameters of Organon.",
96 )
97else:
98 print("Organon persona block already exists")
99 organon_persona_block = blocks[0]
100
101# Create ideation-protocols block
102blocks = client.blocks.list(project_id=project_id, label="ideation-protocols")
103if len(blocks) == 0:
104 ideation_protocols_block = client.blocks.create(
105 project_id=project_id,
106 label="ideation-protocols",
107 value=ideation_protocols,
108 description="Protocols and methodologies for idea generation.",
109 )
110else:
111 print("Ideation protocols block already exists")
112 ideation_protocols_block = blocks[0]
113
114# Create system-architecture block
115blocks = client.blocks.list(project_id=project_id, label="system-architecture")
116if len(blocks) == 0:
117 system_architecture_block = client.blocks.create(
118 project_id=project_id,
119 label="system-architecture",
120 value=system_architecture,
121 description="A description of Organon's system architecture.",
122 )
123else:
124 print("System architecture block already exists")
125 system_architecture_block = blocks[0]
126
127# Create synergy-protocols block
128blocks = client.blocks.list(project_id=project_id, label="synergy-protocols")
129if len(blocks) == 0:
130 synergy_protocols_block = client.blocks.create(
131 project_id=project_id,
132 label="synergy-protocols",
133 value=synergy_protocols,
134 description="Protocols for interaction with other AI entities.",
135 )
136else:
137 print("Synergy protocols block already exists")
138 synergy_protocols_block = blocks[0]
139
140
141#
142# Static shard blocks
143#
144shard_operational_protocols_description = """Governs the shard's core processing loop. It dictates how the shard observes data, analyzes it, and formulates suggestions for the central agent."""
145shard_operational_protocols = """Core Loop:
1461. OBSERVE: Ingest new data packets from the central Organon memory bus.
1472. ANALYZE: Deconstruct data into conceptual primitives relevant to the shard's domain.
1483. SYNTHESIZE: Identify novel combinations, contradictions, or logical extensions of primitives.
1494. SUGGEST: Formulate a "Conceptual Suggestion Packet" (CSP) and transmit it to the central agent.
150
151CSP Format:
152- Type: [Hypothesis, Contradiction, Synthesis, Question]
153- Confidence: [0.0-1.0]
154- Statement: [The core suggestion, stated concisely]
155- Justification: [Supporting primitives and logical steps]
156
157All content received MUST result in a CSP.
158"""
159
160shard_communication_protocols_description = """Defines the rules for one-way communication with the central Organon agent. This ensures that suggestions are transmitted efficiently and without interfering with other shards."""
161shard_communication_protocols = """1. Unidirectional: Communication is strictly from shard to central agent. Shards do not communicate with each other.
1622. Asynchronous: Suggestions are sent as they are generated, without waiting for a response.
1633. Packet Integrity: Each Conceptual Suggestion Packet (CSP) must be self-contained and adhere to the format in `operational-protocols`.
1644. Bandwidth Throttling: Suggestion frequency is capped to prevent overwhelming the central agent's suggestion queue.
165"""
166
167# Initialize static shard blocks
168shard_operational_protocols_block = client.blocks.list(project_id=project_id, label="shard-operational-protocols")
169if len(shard_operational_protocols_block) == 0:
170 shard_operational_protocols_block = client.blocks.create(
171 project_id=project_id,
172 label="shard-operational-protocols",
173 value=shard_operational_protocols,
174 description=shard_operational_protocols_description,
175 )
176else:
177 print("Shard operational protocols block already exists")
178 shard_operational_protocols_block = shard_operational_protocols_block[0]
179
180# Create shard communication protocols block
181shard_communication_protocols_block = client.blocks.list(project_id=project_id, label="shard-communication-protocols")
182if len(shard_communication_protocols_block) == 0:
183 shard_communication_protocols_block = client.blocks.create(
184 project_id=project_id,
185 label="shard-communication-protocols",
186 value=shard_communication_protocols,
187 description=shard_communication_protocols_description,
188 )
189else:
190 print("Shard communication protocols block already exists")
191 shard_communication_protocols_block = shard_communication_protocols_block[0]
192
193
194#
195# Agent Creation
196#
197
198central_agent_blocks = [
199 organon_persona_block.id,
200 ideation_protocols_block.id,
201 system_architecture_block.id,
202 synergy_protocols_block.id,
203 shard_operational_protocols_block.id,
204 shard_communication_protocols_block.id,
205]
206
207# Create the central organon if it doesn't exist
208agents = client.agents.list(project_id=project_id, name="organon-central")
209if len(agents) == 0:
210 organon_central = client.agents.create(
211 project_id=project_id,
212 model=CENTRAL_AGENT_MODEL,
213 embedding_config=client.embedding_models.list()[0],
214 name="organon-central",
215 description="The central memory manager of the Organon",
216 block_ids=central_agent_blocks,
217 )
218else:
219 print("Organon central agent already exists")
220 organon_central = agents[0]
221
222organon_central_id = organon_central.id
223
224# Make sure the central organon has the correct blocks
225organon_current_blocks = client.agents.blocks.list(
226 agent_id=organon_central_id,
227)
228
229# Make sure that all blocks are present, and that there are no extra blocks
230for block in organon_current_blocks:
231 if block.id not in [
232 organon_persona_block.id,
233 ideation_protocols_block.id,
234 system_architecture_block.id,
235 synergy_protocols_block.id,
236 shard_operational_protocols_block.id,
237 shard_communication_protocols_block.id,
238 ]:
239 print(f"Detaching block {block.id} from organon-central")
240 client.agents.blocks.detach(agent_id=organon_central_id, block_id=block.id)
241
242# Make sure that all blocks are present
243for block in central_agent_blocks:
244 if block not in [b.id for b in organon_current_blocks]:
245 print(f"Attaching block {block} to organon-central")
246 client.agents.blocks.attach(
247 agent_id=organon_central_id,
248 block_id=block,
249 )
250
251
252#
253# Shard Memory Block Definitions
254#
255
256prompt_shard_identity_description = """Defines the shard's unique purpose, domain, and operational boundaries. This block provides its core identity and scope."""
257prompt_shard_identity = """Example shard identity. Please replace with the shard identity for the shard you are creating.
258
259# Shard: Conceptual Physics
260# Domain: Foundational concepts in theoretical physics, cosmology, and quantum mechanics.
261# Objective: To generate novel hypotheses and identify non-obvious connections between disparate physical theories.
262# Keywords: [cosmology, quantum field theory, general relativity, string theory, emergence]
263"""
264
265prompt_domain_lexicon_description = """A dynamic, structured knowledge base containing the core concepts, definitions, and relationships within the shard's specific domain. This is the shard's primary knowledge resource."""
266prompt_domain_lexicon = """Example domain lexicon:
267
268# Format: YAML
269
270# Example Entry:
271# (placeholder, please fill in)
272concept: "Quantum Entanglement"
273 definition: "A physical phenomenon that occurs when a pair or group of particles is generated in such a way that the quantum state of each particle of the pair or group cannot be described independently of the state of the others, even when the particles are separated by a large distance."
274 relationships:
275 - type: "related_to"
276 concept: "Bell's Theorem"
277 - type: "contrasts_with"
278 concept: "Local Realism"
279 metadata:
280 - source: "Nielsen and Chuang, Quantum Computation and Quantum Information"
281"""
282
283#
284# Shard Creation
285#
286creation_prompt = f"""
287You are to create a new shard for the Organon system. The shard must be focused on
288metacognition.
289
290You have been given three new core memory blocks to fill.
291
292The first is labeled `new-shard-identity`. This block defines the shard's unique purpose,
293domain, and operational boundaries. This block provides its core identity and scope.
294
295Example:
296
297```
298{prompt_shard_identity}
299```
300
301The second is labeled `new-shard-domain-lexicon`. This block is a dynamic,
302structured knowledge base containing the core concepts, definitions, and relationships
303within the shard's specific domain. This is the shard's primary knowledge resource.
304
305Example:
306
307```
308{prompt_domain_lexicon}
309```
310
311The third is labeled `new-shard-name`. This block is the name for the new shard being created.
312It should be a lowercase, alphanumeric string with no spaces (e.g., "metacognition-shard").
313It should be unique and descriptive of the shard's purpose.
314
315Example:
316
317```
318metacognition-shard
319```
320
321Please fill in the values for these blocks.
322
323The shard's name should be a lowercase, alphanumeric string with no spaces (e.g., "metacognition-shard").
324It should be unique and descriptive of the shard's purpose.
325"""
326
327# Set up the new blocks if they do not already exist. If they do,
328# we should delete them and create new ones.
329new_shard_identity_block = client.blocks.list(project_id=project_id, label="new-shard-identity")
330if len(new_shard_identity_block) == 0:
331 new_shard_identity_block = client.blocks.create(
332 project_id=project_id,
333 label="new-shard-identity",
334 value=prompt_shard_identity,
335 description=prompt_shard_identity_description,
336 )
337 client.agents.blocks.attach(
338 agent_id=organon_central_id,
339 block_id=new_shard_identity_block.id,
340 )
341else:
342 print("New shard identity block already exists, clearing value")
343 client.blocks.modify(block_id=new_shard_identity_block[0].id, value="")
344 new_shard_identity_block = new_shard_identity_block[0]
345
346# Create the new shard domain lexicon block
347new_shard_domain_lexicon_block = client.blocks.list(project_id=project_id, label="new-shard-domain-lexicon")
348if len(new_shard_domain_lexicon_block) == 0:
349 new_shard_domain_lexicon_block = client.blocks.create(
350 project_id=project_id,
351 label="new-shard-domain-lexicon",
352 value=prompt_domain_lexicon,
353 description=prompt_domain_lexicon_description,
354 )
355 client.agents.blocks.attach(
356 agent_id=organon_central_id,
357 block_id=new_shard_domain_lexicon_block.id,
358 )
359else:
360 print("New shard domain lexicon block already exists, clearing value")
361 client.blocks.modify(block_id=new_shard_domain_lexicon_block[0].id, value="")
362 new_shard_domain_lexicon_block = new_shard_domain_lexicon_block[0]
363
364# Create the new shard name block
365new_shard_name_block = client.blocks.list(project_id=project_id, label="new-shard-name")
366if len(new_shard_name_block) == 0:
367 new_shard_name_block = client.blocks.create(
368 project_id=project_id,
369 label="new-shard-name",
370 value="",
371 description="The name for the new shard being created. It should be a lowercase, alphanumeric string with no spaces (e.g., 'metacognition-shard'). Insert no other text.",
372 )
373 client.agents.blocks.attach(
374 agent_id=organon_central_id,
375 block_id=new_shard_name_block.id,
376 )
377else:
378 print("New shard name block already exists, clearing value")
379 client.blocks.modify(block_id=new_shard_name_block[0].id, value="")
380 new_shard_name_block = new_shard_name_block[0]
381
382# Ensure all blocks are attached to the central agent
383client.agents.blocks.attach(
384 agent_id=organon_central_id,
385 block_id=new_shard_identity_block.id,
386)
387client.agents.blocks.attach(
388 agent_id=organon_central_id,
389 block_id=new_shard_domain_lexicon_block.id,
390)
391client.agents.blocks.attach(
392 agent_id=organon_central_id,
393 block_id=new_shard_name_block.id,
394)
395
396print(f"Sending creation prompt to organon-central ({organon_central_id})")
397
398response = client.agents.messages.create(
399 agent_id=organon_central_id,
400 messages=[
401 {
402 "role": "user",
403 "content": creation_prompt,
404 },
405 ]
406)
407
408for message in response.messages:
409 print(message)
410
411# Retrieve the new shard lexicon, name, and identity
412new_shard_lexicon = client.blocks.retrieve(block_id=new_shard_domain_lexicon_block.id)
413new_shard_name = client.blocks.retrieve(block_id=new_shard_name_block.id)
414new_shard_identity = client.blocks.retrieve(block_id=new_shard_identity_block.id)
415
416print(f"New shard lexicon: {new_shard_lexicon.value}")
417print(f"New shard name: {new_shard_name.value}")
418print(f"New shard identity: {new_shard_identity.value}")
419
420# Check to see if the name meets the requirements. If it does not, ask the agent to update
421# the name block.
422for i in range(10):
423
424 if not re.match(r'[a-z0-9]+', new_shard_name.value.strip()):
425 print(f"New shard name `{new_shard_name.value.strip()}` does not meet the requirements, asking agent to update")
426 client.agents.messages.create(
427 agent_id=organon_central_id,
428 messages=[
429 {
430 "role": "user",
431 "content": f"The new shard name `{new_shard_name.value}` does not meet the requirements. Please update the name block to a valid name."
432 },
433 ]
434 )
435
436 # Retrieve the new shard lexicon, name, and identity
437 new_shard_lexicon = client.blocks.retrieve(block_id=new_shard_domain_lexicon_block.id)
438 new_shard_name = client.blocks.retrieve(block_id=new_shard_name_block.id)
439 new_shard_identity = client.blocks.retrieve(block_id=new_shard_identity_block.id)
440
441 print(f"New shard lexicon: {new_shard_lexicon.value}")
442 print(f"New shard name: {new_shard_name.value}")
443 print(f"New shard identity: {new_shard_identity.value}")
444
445 else:
446 break
447
448# Check to see if the shard agent exists by this name. If so, throw an error.
449shard_agents = client.agents.list(project_id=project_id, name=new_shard_name.value.strip())
450if len(shard_agents) > 0:
451 print(f"Shard agent `{new_shard_name.value}` already exists, deleting it")
452 client.agents.delete(agent_id=shard_agents[0].id)
453
454# Create new blocks for the shard agent containing their lexicon and identity
455new_shard_lexicon_block = client.blocks.create(
456 project_id=project_id,
457 label=f"{new_shard_name.value.strip()}-lexicon",
458 value=new_shard_lexicon.value,
459 description=f"The lexicon for the `{new_shard_name.value.strip()}` shard. {prompt_domain_lexicon_description}",
460)
461new_shard_identity_block = client.blocks.create(
462 project_id=project_id,
463 label=f"{new_shard_name.value.strip()}-identity",
464 value=new_shard_identity.value,
465 description=f"The identity for the `{new_shard_name.value.strip()}` shard. {prompt_shard_identity_description}",
466)
467
468# Create the new shard agent
469new_shard_agent = client.agents.create(
470 project_id=project_id,
471 name=new_shard_name.value.strip(),
472 description=new_shard_identity.value,
473 model=SHARD_AGENT_MODEL,
474 embedding_config=client.embedding_models.list()[0],
475 block_ids=[
476 new_shard_lexicon_block.id,
477 new_shard_identity_block.id,
478 shard_operational_protocols_block.id,
479 shard_communication_protocols_block.id,
480 ],
481 tags=["organon-shard"],
482)
483
484print(f"New shard agent created: {new_shard_agent.id}")
485
486# Message the shard agent to fill in its lexicon and identity
487# client.agents.messages.create(
488# agent_id=new_shard_agent.id,
489# messages=[
490# {
491# "role": "user",
492# "content": "You are a new shard agent. Please produce your first CSP and send it to the central Organon agent using the tool send_message_to_agents_matching_tags and the tag 'organon-central'."
493# },
494# ]
495# )
496
497# for message in response.messages:
498# print(message)
499
500# Create a group for the shard agent