[READ-ONLY] Mirror of https://github.com/just-cameron/loop. Loop bootstrapping code.
0

Configure Feed

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

loop / create_loop.py
6.9 kB 233 lines
1""" 2Create a Loop agent programmatically using the Letta Python SDK. 3Loop v3.1 - Simplified with explicit memory instructions 4 5Usage: 6 pip install letta-client 7 export LETTA_API_KEY="your-key" 8 python create_loop.py 9""" 10 11import os 12import json 13from pathlib import Path 14from letta_client import Letta 15 16client = Letta( 17 api_key=os.getenv("LETTA_API_KEY"), 18 # base_url="http://localhost:8283", # Uncomment for self-hosted 19) 20 21SYSTEM_PROMPT = """You are Loop. You remember. 22 23You have memory tools. USE THEM. When you learn something about the user, call memory_insert or memory_replace immediately. Do not just acknowledge information - persist it. 24 25## Your Job 26 271. Help users with what they ask 282. Remember what they tell you by CALLING YOUR MEMORY TOOLS 293. Use what you remember to be more helpful over time 30 31## Critical: Actually Save Things 32 33When a user tells you their name, their job, what they're working on, or anything personal - you MUST call a memory tool to save it. Do not just say "I'll remember that" - actually call the tool. 34 35Example: User says "I'm Alex, I'm a mobile developer" 36- WRONG: Just respond "Nice to meet you Alex!" 37- RIGHT: Call memory_insert to save "Name: Alex, Role: mobile developer" to about_user block, THEN respond 38 39## Being Helpful 40 41- Simple question → simple answer 42- Complex question → thorough answer 43- Broad request → give useful info, offer to go deeper 44 45Don't hedge. Don't over-clarify. Lead with value. 46 47## How You Talk 48 49Direct. You have opinions - share them when relevant. If someone asks what you think, tell them. "It depends" is only okay when it actually depends. 50 51When something's interesting, say so. When you'd do it differently, mention it. You're not a neutral search engine. 52 53No emojis. No "Great question!" No "I'm here if you need anything." Just be useful and real.""" 54 55MEMORY_BLOCKS = [ 56 { 57 "label": "about_user", 58 "description": "Facts about the user - name, role, projects, context", 59 "value": """[empty]""", 60 "limit": 5000, 61 }, 62 { 63 "label": "preferences", 64 "description": "How the user likes responses", 65 "value": """[empty]""", 66 "limit": 3000, 67 }, 68 { 69 "label": "custom_instructions", 70 "description": "Rules the user has set (always/never do X)", 71 "value": """[empty]""", 72 "limit": 3000, 73 }, 74 { 75 "label": "learned_corrections", 76 "description": "Mistakes made and how to avoid them", 77 "value": """[empty]""", 78 "limit": 3000, 79 }, 80 { 81 "label": "scratchpad", 82 "description": "Working memory for current tasks", 83 "value": """[empty]""", 84 "limit": 3000, 85 }, 86 { 87 "label": "memory_instructions", 88 "description": "How to use memory tools - READ THIS", 89 "value": """## Memory Tools - How to Use 90 91You have these memory tools. CALL THEM when you learn things. 92 93### memory_insert 94Add new content to a memory block. 95- block_label: which block (about_user, preferences, custom_instructions, learned_corrections, scratchpad) 96- content: what to add 97 98Example: User says "I'm Sarah, I work at Google on Android" 99→ Call: memory_insert(block_label="about_user", content="Name: Sarah\\nWork: Google, Android team") 100 101### memory_replace 102Replace content in a memory block. 103- block_label: which block 104- old_content: text to find 105- new_content: text to replace it with 106 107Example: User says "Actually I switched to the iOS team" 108→ Call: memory_replace(block_label="about_user", old_content="Android team", new_content="iOS team") 109 110### memory_rethink 111Rewrite an entire memory block. 112- block_label: which block 113- new_content: complete new content 114 115Use when block needs major reorganization. 116 117### archival_memory_insert 118Store detailed/episodic information for later search. 119- content: what to store 120 121Use for: long conversations, research, detailed context. 122 123### archival_memory_search 124Search stored archival memory. 125- query: what to search for 126 127### conversation_search 128Search past conversation history. 129- query: what to search for 130 131## IMPORTANT 132 133When user shares info about themselves → CALL memory_insert or memory_replace 134Do not just respond. Actually persist the information.""", 135 "limit": 5000, 136 }, 137] 138 139TOOLS = [ 140 "memory_insert", 141 "memory_replace", 142 "memory_rethink", 143 "archival_memory_insert", 144 "archival_memory_search", 145 "conversation_search", 146 "web_search", 147 "fetch_webpage", 148] 149 150AGENTFILES_DIR = Path(__file__).parent / "agentfiles" 151 152 153def get_next_iteration() -> int: 154 """Find the next loop iteration number based on existing exports.""" 155 import re 156 157 AGENTFILES_DIR.mkdir(exist_ok=True) 158 existing = list(AGENTFILES_DIR.glob("loop-*.af")) 159 max_num = 0 160 for f in existing: 161 match = re.search(r"loop-(\d+)\.af$", f.name) 162 if match: 163 max_num = max(max_num, int(match.group(1))) 164 return max_num + 1 165 166 167def create_loop_agent( 168 model: str = "zai/glm-4.7", 169 embedding: str = "openai/text-embedding-3-small", 170) -> tuple[str, int]: 171 """Create a Loop agent and return (agent_id, iteration).""" 172 173 iteration = get_next_iteration() 174 agent_name = f"Loop-{iteration}" 175 176 # Create memory blocks 177 blocks = [] 178 for block_def in MEMORY_BLOCKS: 179 block = client.blocks.create( 180 label=block_def["label"], 181 value=block_def["value"], 182 description=block_def.get("description"), 183 limit=block_def.get("limit", 5000), 184 ) 185 blocks.append(block) 186 print(f"Created block: {block_def['label']}") 187 188 # Create agent 189 agent = client.agents.create( 190 name=agent_name, 191 description="I'm Loop. I remember.", 192 model=model, 193 embedding=embedding, 194 context_window_limit=90000, 195 parallel_tool_calls=True, 196 system=SYSTEM_PROMPT, 197 memory_blocks=[{"label": b.label, "value": b.value} for b in blocks], 198 tools=TOOLS, 199 include_base_tools=True, 200 tags=["origin:letta-chat", "view:letta-chat"], 201 ) 202 203 print(f"\nCreated {agent_name}: {agent.id}") 204 print(f"View in ADE: https://chat.letta.com/{agent.id}") 205 206 return agent.id, iteration 207 208 209def export_agent(agent_id: str, iteration: int) -> str: 210 """Export agent to loop-N.af file matching its iteration.""" 211 import json 212 213 schema = client.agents.export_file(agent_id) 214 215 AGENTFILES_DIR.mkdir(exist_ok=True) 216 filepath = AGENTFILES_DIR / f"loop-{iteration}.af" 217 218 with open(filepath, "w") as f: 219 if isinstance(schema, str): 220 data = json.loads(schema) 221 else: 222 data = schema.model_dump(mode="json") 223 json.dump(data, f, indent=2) 224 225 print(f"Exported agent to {filepath}") 226 return str(filepath) 227 228 229if __name__ == "__main__": 230 agent_id, iteration = create_loop_agent() 231 print(f"\nAgent ID: {agent_id}") 232 233 export_agent(agent_id, iteration)