A Slack bot backed by Ollama gives your team an AI assistant that lives in the tools they already use, costs nothing per message, and keeps all conversation data on your infrastructure. Users can tag the bot in any channel or DM it directly, and it responds using your local Ollama model. This guide walks through building a fully functional Ollama Slack bot — handling mentions, direct messages, conversation threading, and streaming responses — using Slack’s Bolt SDK.
What You’ll Build
By the end of this guide you’ll have a bot that: responds when mentioned in channels (@yourbot what is X), handles DMs directly, maintains conversation history within Slack threads (so multi-turn conversations work naturally), and uses any Ollama model as the inference backend. The whole thing runs as a Python process on the same machine as Ollama — no cloud infrastructure required.
Prerequisites
You need: Ollama running locally with at least one model downloaded, a Slack workspace where you can install apps (you need admin rights or approval), and Python 3.9+. Install the dependencies:
pip install slack-bolt slack-sdk ollama python-dotenv
Creating the Slack App
Go to api.slack.com/apps and click “Create New App” → “From scratch.” Give it a name and select your workspace. Then:
- OAuth & Permissions: Add the following Bot Token Scopes:
app_mentions:read,channels:history,chat:write,im:history,im:read,im:write,reactions:write - Install to workspace and copy the Bot User OAuth Token (starts with
xoxb-) - Basic Information → App Credentials: Copy the Signing Secret
- Event Subscriptions: Enable events and subscribe to:
app_mention,message.im. The Request URL needs to be reachable by Slack — use ngrok for local development:ngrok http 3000, then set URL tohttps://your-ngrok-url.ngrok.io/slack/events
Save your tokens in a .env file:
SLACK_BOT_TOKEN=xoxb-your-token-here
SLACK_SIGNING_SECRET=your-signing-secret-here
OLLAMA_MODEL=llama3.2
The Bot Code
import os
import re
from dotenv import load_dotenv
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import ollama
load_dotenv()
app = App(token=os.environ["SLACK_BOT_TOKEN"])
MODEL = os.getenv("OLLAMA_MODEL", "llama3.2")
def get_thread_history(client, channel: str, thread_ts: str, bot_id: str) -> list:
"""Fetch conversation history from a Slack thread."""
messages = []
try:
result = client.conversations_replies(channel=channel, ts=thread_ts)
for msg in result["messages"]:
if msg.get("bot_id") == bot_id:
messages.append({"role": "assistant", "content": msg["text"]})
elif msg.get("text") and not msg.get("bot_id"):
# Strip bot mention from user messages
text = re.sub(r"<@[A-Z0-9]+>s*", "", msg["text"]).strip()
if text:
messages.append({"role": "user", "content": text})
except Exception as e:
print(f"Error fetching thread: {e}")
return messages
@app.event("app_mention")
def handle_mention(event, say, client):
"""Handle @bot mentions in channels."""
channel = event["channel"]
thread_ts = event.get("thread_ts", event["ts"])
user_text = re.sub(r"<@[A-Z0-9]+>s*", "", event["text"]).strip()
if not user_text:
say(text="What can I help you with?", thread_ts=thread_ts)
return
# Show typing indicator
client.reactions_add(channel=channel, timestamp=event["ts"], name="thinking_face")
# Build conversation history from thread
bot_info = client.auth_test()
bot_id = bot_info["bot_id"]
history = get_thread_history(client, channel, thread_ts, bot_id)
# Ensure current message is included
if not history or history[-1].get("content") != user_text:
history.append({"role": "user", "content": user_text})
messages = [{"role": "system", "content": "You are a helpful assistant. Be concise."}] + history
try:
response = ollama.chat(model=MODEL, messages=messages)
reply = response["message"]["content"]
say(text=reply, thread_ts=thread_ts)
except Exception as e:
say(text=f"Sorry, I hit an error: {str(e)}", thread_ts=thread_ts)
finally:
client.reactions_remove(channel=channel, timestamp=event["ts"], name="thinking_face")
@app.event("message")
def handle_dm(event, say, client):
"""Handle direct messages."""
if event.get("channel_type") != "im" or event.get("bot_id"):
return
user_text = event.get("text", "").strip()
if not user_text:
return
messages = [
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": user_text}
]
try:
response = ollama.chat(model=MODEL, messages=messages)
say(response["message"]["content"])
except Exception as e:
say(f"Error: {str(e)}")
if __name__ == "__main__":
# Use Socket Mode (no public URL needed for events)
handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
handler.start()
Figure 1 — Ollama Slack Bot Architecture
Socket Mode vs HTTP Mode
The code above uses Socket Mode, which is the recommended approach for local bots. In Socket Mode, your bot connects to Slack via a persistent WebSocket rather than receiving HTTP webhooks — this means you don’t need a public URL, no ngrok, no port forwarding. Your bot initiates the connection to Slack, so it works behind firewalls and NAT without any network configuration.
To use Socket Mode, you need an additional App-Level Token. In your Slack app settings, go to Basic Information → App-Level Tokens → Generate Token. Give it the connections:write scope and copy the token (starts with xapp-). Add it to your .env file as SLACK_APP_TOKEN. Socket Mode is significantly simpler for local deployments than managing ngrok or a reverse proxy — use it unless you specifically need webhook support.
Adding a System Prompt for Your Team’s Use Case
The system prompt defines your bot’s personality and capabilities. A few examples tuned for common team scenarios:
# Engineering team assistant
SYSTEM_PROMPT = """You are a helpful assistant for a software engineering team.
You have expertise in Python, cloud infrastructure, and data engineering.
When asked about code, provide working examples. Be direct and technical.
If you're not sure about something, say so clearly rather than guessing."""
# Customer support assistant with company context
SYSTEM_PROMPT = """You are a support assistant for Acme Corp.
Our product is a B2B SaaS platform. Key policies:
- Refunds within 30 days, no questions asked
- Support hours: Mon-Fri 9am-6pm ET
- Enterprise plans include dedicated support
Answer customer questions helpfully and escalate complex issues to the human team."""
# Documentation assistant
SYSTEM_PROMPT = """You help team members navigate internal documentation.
Give concise, specific answers. Always cite the relevant doc section if you know it.
If you don't know the answer, say so — don't guess at policies or procedures."""
Customise the system prompt to match your team’s actual needs. The bot’s usefulness depends entirely on how well the system prompt aligns with the questions it will receive — a generic “helpful assistant” prompt works, but a domain-specific prompt that understands your team’s context and policies produces significantly better answers.
Handling Conversation Context in Threads
The get_thread_history function reconstructs conversation history from a Slack thread. This is what enables multi-turn conversations — when a user follows up in the same thread, the bot reads the full thread history and includes it as context for the new response. The bot effectively has memory within each thread, which is the natural Slack interaction model: one thread = one conversation. Across different threads or channels, each conversation starts fresh.
One consideration: long threads accumulate a lot of context. A thread with 50 messages might exceed your model’s context window if each message is substantial. Handle this gracefully by limiting how many thread messages you fetch, or by summarising earlier thread content. For most practical team use, threads rarely exceed 15–20 exchanges before the conversation naturally concludes, keeping context manageable.
Running the Bot as a Service
For reliable operation, run the bot as a persistent service rather than a manual Python process. On Linux with systemd:
[Unit]
Description=Ollama Slack Bot
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/path/to/bot
ExecStart=/path/to/venv/bin/python bot.py
Restart=always
RestartSec=10
EnvironmentFile=/path/to/bot/.env
[Install]
WantedBy=multi-user.target
Save as /etc/systemd/system/ollama-slack-bot.service, then enable and start: sudo systemctl enable ollama-slack-bot && sudo systemctl start ollama-slack-bot. The bot now starts automatically on boot and restarts if it crashes. On macOS, a launchd plist achieves the same; on Windows, a Task Scheduler entry or NSSM service wrapper handles it.
Figure 2 — Features to Add After Your Basic Bot Works
Privacy and Security Considerations
An Ollama Slack bot in production has important data flow characteristics to understand. The bot connects to Slack’s API servers — this means Slack itself sees the messages (as it always does for any Slack communication), but the message content is only sent to Ollama for inference locally, not to any external AI API. For teams that previously used cloud AI integrations (Claude for Slack, ChatGPT Enterprise), switching to an Ollama-backed bot keeps the same Slack data exposure while removing the secondary exposure to AI provider systems. The bot code and Ollama run on your infrastructure. Message content processed by the bot never reaches OpenAI, Anthropic, or any other external AI provider — only Slack’s own servers see it, as they do for all Slack messages. Document this clearly for your team and security reviewers: the privacy model is “Slack-level privacy plus local AI inference,” not “all messages sent to OpenAI.”
Choosing the Right Model for a Team Bot
The model choice for a team Slack bot involves trade-offs that don’t exist for personal use. Response latency matters more than in solo use — when a colleague asks the bot a question in a channel, they’re watching and waiting, and a 30-second response feels rude even if the output quality is excellent. For a team bot, optimise for response time at acceptable quality rather than maximum quality at any latency. A 7B model generating 40–60 tokens per second on a mid-range GPU produces responses in 3–8 seconds for typical Slack messages — fast enough to feel responsive. A 70B model might produce better answers but at 6–12 tokens per second with loading time, the user experience deteriorates. For most team use cases (answering questions, explaining concepts, helping draft text), a well-configured 7–8B model is the right choice. Use Llama 3.1 8B, Qwen 2.5 7B, or Mistral Small 3 as your baseline and upgrade only if you identify specific task types where the larger model’s quality is clearly necessary. Also consider the concurrent load: a team of 20 with multiple people using the bot simultaneously will hit Ollama’s single-worker default limit. Set OLLAMA_NUM_PARALLEL=2 or 3 if you see queuing issues, and make sure the host machine has enough RAM to support multiple parallel KV caches at your chosen context length.
Making the Bot Useful: What Works Well
The most successful team Ollama bots focus on a specific, well-defined use case rather than trying to be a general-purpose assistant. A bot with a clear purpose — “helps engineers understand our codebase,” “answers questions about company policies,” “drafts customer communication in our voice” — is more useful and gets more adoption than a bot that can technically do anything but doesn’t do any one thing particularly well. The system prompt and, where relevant, RAG over a curated document set make this specialisation possible. A documentation assistant that has access to your team’s Confluence or Notion content via RAG, for example, will answer internal questions far more accurately than a general model trying to infer from conversation context. The Slack bot infrastructure built in this guide is the foundation — the value comes from what you load it with and how carefully you define its purpose for your specific team’s needs.
Iterating and Improving Over Time
Once your Slack bot is running, the feedback loop for improvement is faster than for most software projects — you can see exactly how your team uses it, which questions it handles well, and where it falls short. Log the conversations (with team awareness, since Slack messages are visible to admins anyway) and periodically review them for patterns: questions the bot answers incorrectly, topics it’s asked about that your system prompt doesn’t address, and formats that confuse it. Each review session usually surfaces a handful of clear improvements to the system prompt or model configuration that make a noticeable difference. The bot that’s running three months after you first set it up should be significantly better than the day-one version, not through code changes but through prompt refinement based on real usage patterns. This iterative improvement loop — deploy, observe, refine — is what separates a useful team tool from a proof-of-concept that gets abandoned after the novelty wears off.