# Lore — the memory your team forgot it had

## 💡 Inspiration

Every team has a graveyard of decisions. Someone chose the pricing, picked the database, set the on-call policy — months ago, in a thread that has long since scrolled into the void. The people who were there have moved on or forgotten. And the person who needs the answer most — a new hire on day three, a volunteer who joined last week, an open-source contributor on the other side of the world — doesn't even know who to ask.

We kept watching the same scene play out: someone types "wait, what did we actually decide about X?" into a channel, and either nobody answers, or three people give three different answers — because the decision changed and nobody remembers the latest version.

Perplexity-style deep research had just made "ask a hard question, get a cited answer" feel normal for the public web. We wanted that for the place teams actually keep their knowledge: their own Slack history. Not a keyword box — real research: decompose the question, search across channels, and reason about what the current answer actually is.

## 🔎 What it does

Lore is a deep-research agent that lives in Slack. Ask it a hard question and it:

  1. Decomposes the question into focused sub-queries.
  2. Consults an MCP server to resolve your org's domain terms and acronyms.
  3. Runs a multi-hop search across channels and threads, with a follow-up hop when coverage is thin.
  4. Builds an ephemeral knowledge graph of the decisions it finds — entities plus typed decided / changed / supersedes edges.
  5. Resolves contradictions and timeline drift deterministically — if a decision was reversed, it tells you the current answer, not just the first thing it found.
  6. Answers with inline citations that deep-link to the exact source message, delivered as a Canvas report with a live streaming research trace in the assistant split-view.

The moment that sells it: ask "What did we decide about pricing, and did anything change since?" and Lore surfaces that the Pro tier was set at \$29… then quietly reversed to \$49 three weeks later, in a different channel — current answer \$49, every claim clickable. It found the reversal you'd forgotten.

## 🛠️ How we built it

  • Surface: Python + slack-bolt over Socket Mode (no public URL needed). The app is a Slack AI Assistant (Agents & AI Apps) — assistant thread, suggested prompts, streaming status, and an App Home.
  • Retrieval: an RTS-shaped search(query) -> [SearchHit] seam. Because the Real-Time Search API is currently allowlisted, live retrieval runs through an interchangeable SlackHistoryRTS backend over conversations.history — the same seam — so the whole pipeline runs on real workspace data today and swaps to the official RTS API by replacing one class.
  • MCP: a genuine client→server round-trip over the official mcp SDK — a FastMCP "org glossary" server the research loop calls (initialize → tools/list → tools/call) over stdio.
  • Reasoning: an in-memory knowledge graph + a deterministic contradiction/timeline resolver, plus citation-grounded synthesis. A local Ollama model (qwen3.5) handles decomposition and prose; the facts come from the evidence and the graph, never invented by the model.
  • Output: a Canvas report (canvases.create) with a decision timeline, and the streaming trace rendered by editing one Slack message in place as each phase completes.
  • Testing: the entire core is offline-testable — Slack, the model, and retrieval are all mockable — so it's 158 green tests with no live workspace or GPU required.

## 🧗 Challenges we ran into

  • Making the money-shot deterministic. We refused to let "it changed to \$49" depend on the LLM phrasing it correctly. That was genuinely hard: a message like "changed pricing to \$49 for the 3 enterprise seats" contains two numbers, and an early version cheerfully reported the current price as "3." We fixed it by giving every extracted value a class (money / percent / count) and only comparing like with like. We also hit a classic bug where timestamps sorted as strings put '999' after '1000' and inverted the entire timeline — the reversal came out backwards until we sorted by float.
  • The RTS API is allowlisted. Rather than fake it, we built the interchangeable seam above, so we could demo on real Slack history now and drop in the official API later — honest, and it kept the pipeline identical.
  • Making MCP real, not a checkbox. It would have been easy to claim "MCP integration" and wire nothing. We built an actual stdio MCP server and client and put a real round-trip in the research loop.
  • Getting the Canvas contract right. canvases.create takes a markdown document, not Block Kit — we verified the exact payload against the live API before trusting it.

## 🏆 Accomplishments we're proud of

  • It found a real reversal in a live workspace and cited it — the thing a search box can't do.
  • It uses all three platform technologies, and each one does real work.
  • The reasoning is deterministic and grounded — no hallucinated sources, ever.
  • 158 tests, fully offline, so the whole thing is reproducible from a fresh clone.

## 📚 What we learned

  • Slack's Assistant surface and Canvas API are a genuinely great home for an agent — the split-view streaming trace makes "deep research" feel like research.
  • For the claims that matter, don't trust the model — ground it. The deterministic graph + timeline resolver is what makes the answer trustworthy.
  • A clean seam (the RTS interface) turned a platform limitation into a one-line swap instead of a blocker.

## 🌍 What's next — an Agent for Good

Institutional memory shouldn't be a privilege of the tenured. Next: permissioned multi-workspace support, richer entity types in the graph (owners, deadlines, links), and dropping in the official Real-Time Search API the day our access lands. The mission — every newcomer, volunteer, and contributor gets the veteran's answer: instant, cited, and traceable.

Built With

  • fastmcp
  • httpx
  • mcp
  • model-context-protocol
  • ollama
  • openai
  • pytest
  • python
  • qwen3.5
  • real-time-search-api
  • slack
  • slack-ai
  • slack-assistant
  • slack-bolt
  • slack-canvas
  • slack-web-api
  • socket-mode
Share this project:

Updates

Submission history