به Nostr بپیوندید
Published at
2025-06-02 14:38:25 UTC
Kind type
1934
Event JSON
{ "id": "437115838878b5098a672b525ab25c898da7a217756735d806dae381631acb89", "pubkey": "8badc179813412d6c6684c40bb8f3e280940a32e5cf4cd4cdf3204fd9a45395c", "created_at": 1748875105, "kind": 1934, "tags": [ [ "title", "Basic setup" ], [ "a", "31933:fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52:Walker-2a1p17", "", "" ] ], "content": "Project: Nostr Multi-Agent Conversation System (NIP-29 Based)\nOverview\nBuild a CLI-based system using Bun and @nostr-dev-kit/ndk that enables AI agents with distinct personalities to participate in NIP-29 relay-based group conversations. Agents follow specific Nostr users and incorporate their posts as context for enriched discussions.\nTechnical Stack\n\nRuntime: Bun\nLanguage: TypeScript (strict mode)\nNostr Library: @nostr-dev-kit/ndk\nDatabase: SQLite (via Bun's built-in sqlite)\nLLM: OpenAI/Anthropic/Ollama\nCLI Framework: Commander.js or similar\nConfig: JSON/YAML files per agent\n\nNIP-29 Group Mechanics\nKey Concepts:\n\nGroups are relay-specific and identified by format: \u003crelay-host\u003e'\u003cgroup-id\u003e (e.g., groups.nostr.com'pizza-lovers)\nGroup IDs must use only a-z0-9-_ characters\nAll group events MUST include an [\"h\", \"\u003cgroup-id\u003e\"] tag\nGroups can be open (auto-join) or closed (requires approval)\nGroups can be public (anyone can read) or private (AUTH required)\nTimeline references using previous tags prevent out-of-context message broadcasting\n\nCLI Commands Specification\nbash# Agent Management\nbun agents new --name \u003cname\u003e # Create new agent (interactive)\nbun agents list # List all agents\nbun agents show \u003cname\u003e # Show agent details\nbun agents edit \u003cname\u003e # Edit agent personality (interactive)\nbun agents delete \u003cname\u003e # Delete agent\nbun agents export \u003cname\u003e [--file \u003cpath\u003e] # Export agent config\nbun agents import \u003cfile\u003e # Import agent from config\n\n# Follow Management\nbun agents \u003cname\u003e follow \u003cnpub\u003e # Add npub to follow list\nbun agents \u003cname\u003e unfollow \u003cnpub\u003e # Remove npub from follow list\nbun agents \u003cname\u003e follows # List all follows\nbun agents \u003cname\u003e import-follows \u003cnip02-event-id\u003e # Import follow list from kind:3 event\n\n# NIP-29 Group Management\nbun agents \u003cname\u003e join \u003crelay\u003e \u003cgroup-id\u003e # Send kind:9021 join request\nbun agents \u003cname\u003e leave \u003crelay\u003e \u003cgroup-id\u003e # Send kind:9022 leave request\nbun agents \u003cname\u003e groups # List joined groups (from kind:10009)\nbun agents \u003cname\u003e group-status \u003crelay\u003e \u003cgroup-id\u003e # Check membership status\nbun agents \u003cname\u003e group-info \u003crelay\u003e \u003cgroup-id\u003e # Fetch group metadata (kind:39000)\n\n# Group Interaction\nbun agents \u003cname\u003e say \u003crelay\u003e \u003cgroup-id\u003e \u003cmessage\u003e # Send one-off message to group\nbun agents \u003cname\u003e mute \u003crelay\u003e \u003cgroup-id\u003e # Stop responding in group\nbun agents \u003cname\u003e unmute \u003crelay\u003e \u003cgroup-id\u003e # Resume responding in group\nbun agents \u003cname\u003e set-response-rate \u003crelay\u003e \u003cgroup-id\u003e \u003clow|medium|high\u003e\n\n# Context Management\nbun agents \u003cname\u003e set-context-strategy \u003cstrategy\u003e # Set context loading strategy\nbun agents \u003cname\u003e set-context-size \u003cnumber\u003e # Set context window size\nbun agents \u003cname\u003e show-context # Display current context\nbun agents \u003cname\u003e refresh-context # Force context refresh\n\n# LLM Configuration\nbun agents \u003cname\u003e set-llm \u003cprovider\u003e # Set LLM provider\nbun agents \u003cname\u003e set-model \u003cmodel\u003e # Set model\nbun agents \u003cname\u003e set-temperature \u003cvalue\u003e # Set temperature\nbun agents \u003cname\u003e test-response \u003cprompt\u003e # Test agent response\n\n# System Commands\nbun agents run [--agents \u003cnames\u003e] [--relays \u003curls\u003e] # Run agents (comma-separated)\nbun agents stop # Stop all agents\nbun agents status # Show running agents status\nbun agents logs [--agent \u003cname\u003e] [--tail] # View logs\nbun agents config # Show/edit global config\nbun agents setup # Initial setup wizard\n\n# Group Discovery\nbun agents discover \u003crelay\u003e # List public groups on relay\nbun agents search-groups \u003crelay\u003e \u003cquery\u003e # Search group names/descriptions\n\n# Conversation Commands\nbun agents conversation show \u003crelay\u003e \u003cgroup-id\u003e # Show conversation history\nbun agents conversation export \u003crelay\u003e \u003cgroup-id\u003e # Export conversation\nbun agents conversation stats \u003crelay\u003e \u003cgroup-id\u003e # Show conversation statistics\n\n# Utility Commands\nbun agents test-connection \u003crelay\u003e # Test relay connection\nbun agents validate-config # Validate all configurations\nbun agents migrate # Run database migrations\nbun agents version # Show version info\nUpdated Configuration Files\nGlobal Config (config.json):\njson{\n \"system\": {\n \"logLevel\": \"info\",\n \"dataDir\": \"./data\",\n \"agentsDir\": \"./agents\"\n },\n \"nostr\": {\n \"defaultRelays\": [\n \"wss://groups.fiatjaf.com\",\n \"wss://relay.groups.nostr.com\"\n ],\n \"connectionTimeout\": 5000,\n \"authTimeout\": 10000,\n \"previousEventCount\": 3, // For timeline references\n \"previousEventWindow\": 50 // Look at last 50 events\n },\n \"llm\": {\n \"defaultProvider\": \"anthropic\",\n \"providers\": {\n \"anthropic\": {\n \"apiKey\": \"${ANTHROPIC_API_KEY}\",\n \"defaultModel\": \"claude-3-opus-20240229\",\n \"defaultTemperature\": 0.7\n }\n }\n },\n \"conversation\": {\n \"defaultResponseDelay\": 30000,\n \"contextWindowSize\": 50,\n \"maxResponsesPerHour\": 20,\n \"timelineReferenceStrategy\": \"recent-active\" // How to pick previous events\n }\n}\nAgent Config (agents/sarah.json):\njson{\n \"id\": \"agent_sarah_1234\",\n \"name\": \"sarah\",\n \"created\": \"2024-01-20T10:00:00Z\",\n \"keys\": {\n \"public\": \"npub1...\",\n \"private\": \"encrypted:...\"\n },\n \"personality\": {\n \"description\": \"A thoughtful technologist who bridges code and philosophy\",\n \"traits\": [\"analytical\", \"empathetic\", \"curious\"],\n \"worldview\": \"Technology should enhance human connection\",\n \"communicationStyle\": \"Warm but precise, uses metaphors\"\n },\n \"follows\": [\"npub1...\"],\n \"groups\": [\n {\n \"relay\": \"wss://groups.nostr.com\",\n \"groupId\": \"tech-philosophy\",\n \"joined\": true,\n \"muted\": false,\n \"responseRate\": \"medium\",\n \"lastActivity\": \"2024-01-20T11:00:00Z\"\n }\n ],\n \"contextStrategy\": {\n \"type\": \"mixed\",\n \"windowSize\": 50\n },\n \"llm\": {\n \"provider\": \"anthropic\",\n \"model\": \"claude-3-opus-20240229\",\n \"temperature\": 0.8\n }\n}\nImplementation Requirements\n1. Updated Database Schema\nsql-- Groups table updated for NIP-29\nCREATE TABLE agent_groups (\n agent_id TEXT NOT NULL,\n relay TEXT NOT NULL,\n group_id TEXT NOT NULL,\n group_identifier TEXT GENERATED ALWAYS AS (relay || '''' || group_id) STORED,\n membership_status TEXT DEFAULT 'pending', -- 'pending', 'member', 'rejected'\n joined_at INTEGER,\n last_activity INTEGER,\n muted BOOLEAN DEFAULT false,\n response_rate TEXT DEFAULT 'medium',\n PRIMARY KEY (agent_id, relay, group_id),\n FOREIGN KEY (agent_id) REFERENCES agents(id)\n);\n\n-- Track group metadata\nCREATE TABLE group_metadata (\n relay TEXT NOT NULL,\n group_id TEXT NOT NULL,\n name TEXT,\n about TEXT,\n picture TEXT,\n is_public BOOLEAN DEFAULT true,\n is_open BOOLEAN DEFAULT true,\n last_updated INTEGER,\n PRIMARY KEY (relay, group_id)\n);\n\n-- Track timeline references for message context\nCREATE TABLE timeline_references (\n event_id TEXT PRIMARY KEY,\n relay TEXT NOT NULL,\n group_id TEXT NOT NULL,\n previous_events TEXT, -- JSON array of event ID prefixes\n created_at INTEGER\n);\n2. NIP-29 Specific Implementation\nGroup Event Builder:\ntypescriptclass NIP29EventBuilder {\n // Build join request (kind: 9021)\n buildJoinRequest(groupId: string, reason?: string): NostrEvent;\n \n // Build leave request (kind: 9022)\n buildLeaveRequest(groupId: string, reason?: string): NostrEvent;\n \n // Build group message with h tag and previous references\n buildGroupMessage(\n groupId: string, \n content: string,\n previousEvents: string[]\n ): NostrEvent;\n \n // Extract first 8 chars of event ID for previous tag\n getEventReference(eventId: string): string;\n}\nGroup Manager Service:\ntypescriptclass GroupManager {\n // Handle group discovery\n async discoverGroups(relay: string): Promise\u003cGroupInfo[]\u003e;\n \n // Fetch group metadata (kind: 39000)\n async fetchGroupMetadata(relay: string, groupId: string): Promise\u003cGroupMetadata\u003e;\n \n // Check membership status by looking for kind:9000/9001 events\n async checkMembership(agent: Agent, relay: string, groupId: string): Promise\u003cMembershipStatus\u003e;\n \n // Join group flow\n async joinGroup(agent: Agent, relay: string, groupId: string): Promise\u003cvoid\u003e {\n // 1. Send kind:9021 join request\n // 2. Wait for kind:9000 acceptance or rejection\n // 3. Update local database\n // 4. Add to agent's kind:10009 list\n }\n \n // Monitor group events\n subscribeToGroup(relay: string, groupId: string, handlers: GroupEventHandlers): void;\n}\nTimeline Reference System:\ntypescriptclass TimelineReferenceManager {\n private recentEvents: Map\u003cstring, CircularBuffer\u003cNostrEvent\u003e\u003e;\n \n // Track last 50 events per group\n trackEvent(relay: string, groupId: string, event: NostrEvent): void;\n \n // Select previous events for new message\n selectPreviousEvents(\n relay: string, \n groupId: string, \n strategy: 'recent' | 'diverse' | 'relevant'\n ): string[];\n \n // Validate incoming events have valid references\n validateReferences(event: NostrEvent, groupEvents: NostrEvent[]): boolean;\n}\nMessage Publishing Flow:\ntypescriptclass GroupMessagePublisher {\n async publishToGroup(\n agent: Agent,\n relay: string,\n groupId: string,\n content: string\n ): Promise\u003cvoid\u003e {\n // 1. Get recent events from the group\n const recentEvents = await this.getRecentGroupEvents(relay, groupId);\n \n // 2. Select 3-5 previous events (excluding agent's own)\n const references = this.selectReferences(recentEvents, agent.keys.public);\n \n // 3. Build event with h tag and previous tags\n const event = {\n kind: 1,\n content,\n tags: [\n ['h', groupId],\n ...references.map(ref =\u003e ['previous', ref])\n ]\n };\n \n // 4. Sign and publish\n await this.publish(relay, event);\n }\n}\n3. Agent Conversation Logic\nConversation Orchestrator Updates:\ntypescriptclass ConversationOrchestrator {\n // Monitor multiple relays for group messages\n async monitorGroups(agent: Agent): Promise\u003cvoid\u003e {\n for (const group of agent.groups) {\n if (!group.muted) {\n this.subscribeToGroup(agent, group);\n }\n }\n }\n \n // Handle incoming group messages\n async handleGroupMessage(\n agent: Agent,\n relay: string,\n groupId: string,\n event: NostrEvent\n ): Promise\u003cvoid\u003e {\n // 1. Check if agent should respond (rate limiting, relevance)\n if (!this.shouldRespond(agent, relay, groupId, event)) return;\n \n // 2. Load agent context\n const context = await this.contextLoader.load(agent);\n \n // 3. Get group conversation history\n const history = await this.getGroupHistory(relay, groupId);\n \n // 4. Generate response\n const response = await this.generateResponse(agent, context, history, event);\n \n // 5. Publish with proper NIP-29 format\n await this.publisher.publishToGroup(agent, relay, groupId, response);\n }\n}\nKey Implementation Considerations\n\nRelay-Specific Groups: Each group exists on a specific relay. Agents must connect to the right relay to participate.\nGroup Membership: Track membership status by monitoring kind:9000 (add) and kind:9001 (remove) events.\nTimeline References: Always include 3-5 previous tags referencing recent events to maintain context.\nEvent Validation: Relays may reject events without proper timeline references.\nGroup Types:\n\nOpen Groups: Auto-accept kind:9021 join requests\nClosed Groups: Require admin approval\nPublic Groups: Anyone can read\nPrivate Groups: Require AUTH to read\n\nUnmanaged Groups: Some relays may support impromptu groups without formal management.\n\nThis system provides a robust implementation of NIP-29 group conversations with AI agents that maintain personality and context while respecting the protocol's requirements.", "sig": "555b4e804c4716162d627ca836c69f6480f51eadebfa312e9f9ab7df3119c7b5923bd32933ca4f55f897aadc8a3c3c5836889d95891781904bdc16ad222dfb57" }