A Slack-native AI assistant for your AWS data lake. Datalfred lets your team query data and investigate ingestion pipelines in plain English, directly from Slack.
Ask @datalfred what tables are in the sales database? and get an answer. Ask why did last night's ingestion fail? and get logs, root cause, and a one-click redrive — without leaving Slack, opening the AWS console, or writing SQL.
Built on Strands Agents and AWS Bedrock. Deployed as a single Lambda. Terraform included.
- Query the data lake — explores the Glue Data Catalog, writes the SQL, runs it on Athena, returns the result.
- Investigate pipelines — inspects Step Functions, ECS, EMR Serverless and CloudWatch logs to explain why a job failed.
- Operate (carefully) — can redrive failed Step Function executions or toggle EventBridge schedules, but only when a user explicitly asks.
- Stays cheap — pick between three Bedrock model tiers (Claude Opus 4.7 / Sonnet 4.6 / Haiku 4.5); the agent reports token cost after every conversation.
- Stays safe — Slack signature validation, user allowlist, and an automatic kill-switch if the bot starts receiving a flood of invalid requests (a sign someone is probing the endpoint).
Slack ──► Lambda URL ──► main agent ──┬─► data analyst (Glue + Athena)
└─► run guy (Step Functions / ECS / EMR / CloudWatch)
│
└─► AWS Bedrock (Claude)
- Main agent (
code/chatbot/main_agent.py) — orchestrates the conversation, routes to sub-agents, keeps a 20-message sliding window, persists sessions to S3, tracks token cost. - Sub-agents (
code/chatbot/sub_agents/) —data_analystfor SQL/catalog work,run_guyfor AWS introspection. Each is a Strands@toolwith its own system prompt. - Slack layer (
code/chatbot/slack.py) — HMAC signature check, threaded replies, event filtering. - Failsafe Lambda (
code/chatbot_failsafe/main.py) — a second, isolated Lambda that disables the main one if a CloudWatch alarm detects abuse (by setting its reserved concurrency to 0).
You need an AWS account with Bedrock access (Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 enabled in eu-west-1), Terraform, Docker, and Poetry 2.x.
cd iac
cp terraform.tfvars.example terraform.tfvars # then edit it
cp backend.hcl.example backend.hcl # then edit it
terraform init -backend-config=backend.hcl
terraform workspace new prod
terraform apply
terraform output lambda_function_url # you'll need this for SlackTerraform builds the Docker image, pushes it to ECR, provisions the Lambda, Bedrock inference profiles, S3 bucket, Athena workgroup, CloudWatch alarms, and the failsafe.
Go to api.slack.com/apps → Create New App → From manifest, paste the manifest below, replacing $APPLICATION_NAME and $AWS_LAMBDA_FUNCTION_URL:
{
"display_information": { "name": "$APPLICATION_NAME" },
"features": {
"app_home": { "messages_tab_enabled": true, "messages_tab_read_only_enabled": false },
"bot_user": { "display_name": "$APPLICATION_NAME" }
},
"oauth_config": {
"scopes": { "bot": [
"app_mentions:read", "chat:write", "chat:write.customize", "commands",
"im:read", "im:write", "im:history", "mpim:history",
"incoming-webhook", "reactions:read", "reactions:write"
]}
},
"settings": {
"event_subscriptions": {
"request_url": "$AWS_LAMBDA_FUNCTION_URL",
"bot_events": ["app_mention", "message.im"]
}
}
}Install the app, then store its credentials in Secrets Manager under the name {project_name}_slack_alerting_prod:
{
"token": "xoxb-...",
"signing_secret": "...",
"slack_channel_id": "C01234567"
}DM the bot or mention it in a channel where it's invited. Access is restricted to the Slack user IDs listed in authorized_slack_users — leave that variable empty to allow any Slack user.
Prefix any message with /model large|medium|small to override the model tier for that single message — e.g. @datalfred /model large explain this failure in depth. Without the prefix, the bot defaults to small (Haiku 4.5).
cd code
poetry install --with agent
poetry run chatbot -p poc -s prod -m large # interactive
poetry run chatbot -p poc -s prod -m medium -up "list databases" # one-shot
poetry run chatbot -p poc -s prod -id my-session # resumable session (persisted to S3)Flags: -m large|medium|small picks the Bedrock tier (default small), -d prints sub-agent debug output.
| Var (Lambda env) | Set by | Purpose |
|---|---|---|
PROJECT_NAME, STAGE_NAME |
Terraform | Resource naming: {project}_chatbot_{stage}_* |
SLACK_SECRET_ARN |
Terraform | ARN of the Secrets Manager secret above |
AUTHORIZED_SLACK_USERS |
terraform.tfvars |
Comma-separated Slack user IDs; empty = open to all |
Three Bedrock inference profiles are provisioned per stage (*_large, *_medium, *_small); swap models in iac/bedrock_inference_profile.tf. Pricing used for cost estimation is hardcoded in main_agent.py — update it there if Bedrock prices change.
- Read-only by default. The
run_guyagent's system prompt forbids any mutation. The only allowed writes are Step Function redrives and EventBridge schedule toggles, both requiring an explicit user request. - Slack signature validation on every request, with HMAC and timestamp checks.
- User allowlist via
AUTHORIZED_SLACK_USERS(optional — leave empty to allow any Slack user). - Kill switch. If the bot receives more than 100 invalid-signature requests in an hour (a sign someone is probing the endpoint), a CloudWatch alarm triggers a separate failsafe Lambda that disables the main bot by setting its reserved concurrency to 0. Operators get an email.
- Timeout failsafe. The Lambda aborts when fewer than 3 minutes of its 15-minute budget remain, posting a graceful message to Slack instead of timing out mid-tool-call.
- No retry on tool errors (except SQL syntax fixes), to prevent runaway costs from agent loops.
code/
├── chatbot/ # main Lambda + CLI
│ ├── main_agent.py
│ ├── lambda_entrypoint.py
│ ├── slack.py
│ └── sub_agents/{data_analyst,run_guy}.py
├── chatbot_failsafe/main.py
├── pyproject.toml
└── Dockerfile
iac/ # Terraform (eu-west-1, S3+DynamoDB backend, workspace = stage)
- AWS region is
eu-west-1by default — change iniac/terraform.tf. - The Terraform
defaultworkspace producesstage_name=default. Always create an explicit workspace before applying. - Sessions are keyed by Slack user ID (one history per user, shared across threads).
- CI is GitLab-only; the public repo is a mirror.
- Cost numbers are estimates, not billing-grade.
MIT. See LICENSE.