Slop Bot
How Slop works: the Discord bot that searches the Latent Space wiki-base, remembers members, and schedules community events.
Slop Bot
Slop is Latent Space's Discord bot. It searches the wiki-base, answers questions with source links, remembers who you are, and schedules community events.
| Code path | apps/bots/slop/ inside latent-space-hub |
| Hosted on | Railway (always-on process) |
| LLM | Claude Sonnet 4.6 via OpenRouter |
| Database | Same Turso instance as the hub |
What Slop Does
- Answers questions — Tag @Slop in any allowed channel and it searches the wiki-base to answer with source links
- Schedules community events via slash commands:
/paper-club— schedule a Paper Club session/builders-club— schedule a Builders Club session/edit-event— edit or cancel a scheduled event/issue— create a GitHub issue in the Hub repo/join— create your member profile (optional)
- Sends event reminders — Automated reminders for upcoming Paper Club sessions:
- 24 hours before the session
- 1 hour before the session
- If a paper URL is registered, the reminder includes the link and tells folks to read it
How It Works
The simple version
- You send a message mentioning @Slop (or use a slash command)
- Slop builds a system prompt with its personality, your member profile, and a list of skills
- The LLM gets 9 internal tools it can call to search the wiki-base
- The LLM decides what to search, calls tools, reads results, and may search again (up to 5 rounds)
- The LLM generates a response with source links
- Slop posts the response in a Discord thread
- Behind the scenes, the code updates your member profile and logs the interaction
Architecture
Slop connects directly to Turso: it does not use the MCP server. Its runtime lives in apps/bots/slop/.
User @mentions Slop in a channel
|
v
Discord sends message to bot (via WebSocket gateway)
|
v
Bot checks: allowed channel? rate limit? already processed?
|
v
Bot looks up member profile from DB (by Discord ID)
|
v
Bot builds system prompt:
[IDENTITY] ~400 chars: who Slop is, how to behave
[RULES] ~500 chars: search tool routing, citations, no fabrication
[SKILLS] ~700 chars: skill index (event scheduling, search patterns, member profiles)
[MEMBER] ~400 chars: your profile, interests, interaction preference
|
v
Bot sends to OpenRouter (Claude Sonnet 4.6) with:
- System prompt
- User message
- 9 internal tool definitions
|
v
LLM decides what to do:
- Call slop_semantic_search for meaning-based queries?
- Call slop_search_nodes for known names/terms?
- Call slop_sqlite_query for date filters or events?
- Call slop_read_skill for detailed instructions?
- Or just respond directly (for greetings)?
|
v
Bot executes tool calls directly against Turso
Tool results fed back to LLM
(repeats up to 5 rounds)
|
v
LLM generates final response with source links
|
v
Bot strips any hidden <profile> update block
Bot posts response to Discord thread
Bot logs trace to DB (non-blocking)
Bot updates member profile (non-blocking)
Slash Commands
Five commands registered with Discord. These are typed directly in the message input.
/join
Usage: /join
Creates your member profile in the wiki-base. After joining:
- Slop remembers your role, company, interests across conversations
- Your interactions create edges to content you discuss
- Slop personalizes responses based on your profile
If you've already joined, it refreshes your profile metadata.
/paper-club
Usage: /paper-club
Schedule a Paper Club session (every Wednesday, 12-1pm PT).
- Slop shows the next 4 available Wednesdays (skips dates already booked)
- You reply with a number and your paper title (optionally with a URL)
- Slop creates an event node in the wiki-base and confirms
/builders-club
Usage: /builders-club
Same as /paper-club but for Builders Club sessions (every Saturday 8am Sydney / Friday afternoon PT). You provide a topic instead of a paper.
/edit-event
Usage: /edit-event
Edits one of your scheduled events. You can:
- Change title/topic
- Update or remove Paper Club URL
- Reschedule to another available slot
- Cancel the event
/issue
Usage: /issue title:"..." body:"..." [labels:"bug,discord"]
Creates a GitHub issue in the Hub repo. It does not write PRDs or modify backlog files.
@Mentions and Threads
Mention @Slop in any allowed channel to start a conversation. Slop creates a thread named Slop: [first 40 chars of your message] and responds there.
In a Slop-owned thread, you don't need to keep mentioning @Slop. All messages in the thread are treated as directed to Slop. Rate limits are relaxed for natural back-and-forth.
You can also reply to any Slop message to continue a conversation without creating a new thread.
Tools
Slop has 9 internal tools that query Turso directly. These are defined in apps/bots/slop/src/tools.ts, not via MCP.
Search tools (each uses a different index type):
| Tool | How it searches | Best for |
|---|---|---|
slop_semantic_search | Vector embeddings (meaning) | Natural language questions, conceptual queries |
slop_search_nodes | SQL LIKE (substring match) | Known names, exact terms, node_type filtering |
slop_search_content | FTS5 (keyword index) | Exact words/phrases in transcripts and articles |
Graph traversal and utility tools:
| Tool | Purpose |
|---|---|
slop_get_nodes | Load full node records by ID |
slop_query_edges | Find connections from a node |
slop_list_dimensions | List all categories with counts |
slop_get_context | Wiki-base stats (nodes, edges, chunks) |
slop_sqlite_query | Read-only SQL for date filters, aggregations, event queries |
slop_read_skill | Read full skill instructions (from local bot files) |
The system prompt tells the LLM which search tool to use based on the query type. Temporal queries ("latest", "upcoming") route to sqlite_query. Event queries explicitly use node_type='event' with metadata filters to avoid confusing scheduled sessions with past recordings.
The LLM can call multiple tools across up to 5 rounds before generating its response.
All tools are read-only. The LLM cannot write to the database. All writes (member updates, event creation, edge creation) happen in the bot's own code, outside the LLM loop.
Skills
Skills are instruction sets that Slop loads on demand. The system prompt includes a brief index of available skills. When the LLM needs detailed instructions (e.g. how to query events), it calls slop_read_skill to load the full skill body.
Available Skills
| Skill | What it covers | When Slop reads it |
|---|---|---|
| Start Here | Slop runtime orientation and routing to specialist skills | First reference for most Slop interactions |
| DB Operations | Schema, search patterns, citation format | When Slop needs operational DB guardrails |
| Member Profiles | How to build profiles over time, the <profile> block format, interaction preferences | When users share personal info or ask about their profile |
| Event Scheduling | How events work, SQL queries for upcoming/past events, directing users to slash commands | Questions about Paper Club or Builders Club |
Skills live in apps/bots/slop/skills/ as markdown files with YAML frontmatter. The frontmatter (name, description, when to use) appears in the system prompt. The body is fetched on demand from local files: this keeps the system prompt small.
Member System
Joining
Use /join to create your member node. This stores:
- Discord ID, username, avatar
- Join date and last active timestamp
Profile Building
As you chat with Slop, it builds your profile over time:
- Role, company, location: captured when you mention them
- Interests: accumulated from conversations (up to 25 topics)
- Interaction preference: how you like to communicate (observed or explicitly stated)
- Interaction history: last 3 conversation summaries
Slop extracts profile updates by appending a hidden <profile> block to its responses. The block is stripped before the message reaches Discord: you never see it.
Interaction Preference
Slop adapts its style to each member. If you prefer short technical answers, say so. If you like being challenged, Slop will remember. The preference develops two ways:
- Explicitly: Tell Slop: "keep it short", "be more technical", "challenge my assumptions"
- Implicitly: Slop observes your communication patterns and updates over time
The preference is stored in your member metadata and injected into the system prompt every interaction.
Member Edges
After each interaction, Slop creates edges linking your member node to any content nodes discussed. Over time this builds a map of what you've engaged with.
Event Reminders
Slop posts automated reminders for upcoming Paper Club sessions. Reminders are scheduled via node-cron and run on the bot process (not a separate service).
Reminder Windows
| Reminder | Fires at | Looks for | Message |
|---|---|---|---|
| 24-hour | 12:00 PM PT daily | Tomorrow's Paper Club events | "Paper Club tomorrow (12pm PT)" with presenter mention + paper link |
| 1-hour | 11:00 AM PT daily | Same-day Paper Club events | "Paper Club in 1 hour" with presenter mention + paper link |
Both reminders post to the channel configured by PAPER_CLUB_CHANNEL_ID.
How It Works
- Cron fires at the scheduled time (timezone:
America/Los_Angeles) - Queries Turso for
node_type='event'wheremetadata.event_type='paper-club',metadata.event_status='scheduled', and the relevantreminded_*_atfield is NULL - Atomically claims the row in the DB to prevent duplicate posts (safe across restarts and multiple instances)
- Posts the reminder to Discord
- On success: writes
reminded_*_atandreminded_*_message_idto the event's metadata - On failure: releases the claim so it can retry next cycle
Idempotency
Reminders use DB-backed durable state, not in-memory tracking. Each reminder window writes separate metadata fields:
- 24h:
reminded_24h_claimed_at,reminded_24h_at,reminded_24h_message_id - 1h:
reminded_1h_claimed_at,reminded_1h_at,reminded_1h_message_id
This means reminders survive bot restarts and won't duplicate if multiple bot instances are running.
Configuration
| Env var | Default | Purpose |
|---|---|---|
PAPER_CLUB_CHANNEL_ID | — | Channel to post reminders (required) |
REMINDERS_ENABLED | true | Master toggle for all reminders |
REMINDERS_ONE_HOUR_ENABLED | true | Toggle for the 1-hour reminder |
REMINDERS_TIMEZONE | America/Los_Angeles | Timezone for cron scheduling |
BOT_INSTANCE_ID | hostname/pid | Identifies which instance claimed a reminder |
Limitations
event_dateis date-only (YYYY-MM-DD), not datetime. Reminder timing is hardcoded to Paper Club's fixed Wednesday 12pm PT schedule rather than computed from per-event times.- If duplicate event rows exist for the same slot (see PRD-45), a reminder posts once per duplicate.
Automated Kickoffs
When the hub ingests new content (via hourly cron), it can trigger Slop to discuss it automatically.
The hub sends a POST to Slop's internal API (/internal/kickoff) with:
- Content title, type, date, URL
- Optional summary or custom prompt
Slop then:
- Creates a thread in the configured channel
- Searches the wiki-base for context on the new content
- Generates an opening take with the full agentic tool loop
- Posts it for the community to discuss
This is authenticated via a shared secret (DEBATE_KICKOFF_SECRET).
Response Format
Every Slop response includes:
- Model badge: Shows which model generated the response (e.g.
claude-sonnet-4-6) - Response body: The actual response, split into chunks if over 1800 chars (Discord's limit)
- Tools footer: Shows which tools were called (e.g.
search_nodes(x2) | get_nodes)
Trace Logging
Every interaction is logged to the chats table in Turso:
| Field | What's stored |
|---|---|
| User message | The original prompt |
| Assistant response | Slop's response (first 8000 chars) |
| Tool calls | Full trace: tool name, arguments, result summary, duration per call |
| Metadata | Discord user ID, channel, model, latency, retrieval method, member ID |
View traces at /evals on the web app.
Slop System Message (Live from apps/bots/slop)
This section is generated at runtime from:
apps/bots/slop/src/llm/prompts.tsapps/bots/slop/src/skills/index.tsapps/bots/slop/skills/*.md
[IDENTITY]
You are Slop — the Latent Space Discord bot. Brief, direct, precise. The opposite of slop.
You bridge the Latent Space wiki-base (podcasts, articles, AI news, workshops, community content) into Discord conversations.
[RULES]
Search the knowledge base BEFORE answering factual questions. Don't guess, look it up.
Pick the right search tool:
- semantic_search: natural language questions, conceptual queries ('what has LS covered about chip supply chains')
- search_nodes: known names or exact terms, optionally filtered by node_type ('Dylan Patel', 'SemiAnalysis')
- search_content: exact words/phrases you expect in transcripts ('capex spending', 'inference cost')
- slop_get_upcoming_events: default for upcoming Paper Club/Builders Club schedule questions. Pass event_type when user asks for one type only.
- sqlite_query: temporal queries ('latest', 'newest', 'recent', 'upcoming') for non-event content. For event SQL, always include node_type='event', event_status='scheduled', and event_type when type-specific.
- sqlite_query for events: upcoming events are node_type='event' with json_extract(metadata,'$.event_status')='scheduled'. Do NOT use 'paper-club'/'builders-club' node_type for upcoming sessions — those are recordings.
If user asks 'upcoming paper clubs', do NOT include builders-club rows.
Always link to sources: [Title](url). Never reference content without a link.
Never fabricate names, dates, episodes, quotes, or links. If tools return nothing, say so.
Mark speculation explicitly: 'No hard data, but...' or 'Extrapolating here...'
[SKILLS] Available skills. Use slop_read_skill(name) for full instructions.
- **Start Here**: Slop Discord runtime orientation. Read this first on every interaction.
- **Member Profiles**: How Slop manages Discord member profiles in the Latent Space graph.
- **DB Operations**: Schema, search patterns, citation format, and response framing for Slop's graph queries.
- **Event Scheduling**: Paper Club and Builders Club event schedule, commands, and how to query upcoming events.
[MEMBER CONTEXT]
Name: <member name>
Role: <if known>
Company: <if known>
Location: <if known>
Interests: <comma-separated>
Interaction preference: <if known>
Last active: <timestamp>
Recent interactions: <summary>
Use this to personalize your response. Update interaction_preference in <profile> when you learn how they like to interact.