Lab 06 — n8n Automations Calling AI Services — AI Infrastructure for Real Business Problems
Last updated 10/17/2026
Course › Hands-on track › Lab 06

Lab 06 — n8n Automations Calling AI Services

n8n 自動化串接AI服務 · Hands-on track: AI Infrastructure for Real Business Problems

Code: labs/06/ · Uses: data/reviews.csv, data/tickets.csv, data/leads.csv, docs/wholesale.md

Business Problem — 商業問題

Three streams of work arrive at Kettle & Kiln every day, and today someone reads all of them by hand:

The owner already uses n8n for order notifications. She wants AI to slot into those workflows without n8n holding the prompts or business rules. Our split:

Architecture — 系統架構

flowchart LR
    SRC[Shopify / review app / form] -->|POST /webhook/review-received| N8N
    subgraph N8N["n8n :5678 (workflow per stream)"]
        W[Webhook] --> H[HTTP Request<br/>X-API-Key, retry ×3] --> LOG[HTTP: /v1/events] --> IF{IF needs_reply / urgent / hot}
        IF -- true --> SL[Slack incoming webhook<br/>onError: continue]
        IF -- false --> RS[Respond to Webhook]
        SL --> RS
    end
    H --> SVC[ai-services :8006<br/>/v1/reviews/analyze<br/>/v1/tickets/triage<br/>/v1/leads/score]
    SVC --> LLM[llmkit → model]
    SVC --> DB[(SQLite: idempotency cache + events)]

What lives in code, not prompts (ai_services/logic.py):

Prerequisites — 先決條件

Step-by-Step Setup — 逐步設定

cd hands-on/labs/06
cp .env.example .env
python -c "import secrets;print(secrets.token_urlsafe(32))"   # → SERVICE_API_KEY
python -c "import secrets;print(secrets.token_urlsafe(32))"   # → N8N_ENCRYPTION_KEY (back it up!)
python n8n/build_workflows.py                                  # (re)generates n8n/workflows/*.json
docker compose up -d --build                                   # ai-services + n8n
docker compose --profile tools run --rm n8n-import             # imports the 3 workflows

Open http://localhost:5678, create the owner account, then activate (n8n 2.x: Publish) the three workflows. Their production webhook URLs are:

POST http://localhost:5678/webhook/review-received
POST http://localhost:5678/webhook/ticket-received
POST http://localhost:5678/webhook/lead-received

Replay the sample data:

uv venv --python 3.12 && source .venv/bin/activate
uv pip install -e ../common -r requirements.txt
python replay.py --target n8n          # every review, ticket, lead through n8n
python replay.py --target services     # straight to ai-services (debug without n8n)
curl -s localhost:8006/v1/events -H "X-API-Key: $SERVICE_API_KEY" | python -m json.tool

Without Docker, run the service with uvicorn ai_services.main:app --port 8006. Start n8n with N8N_BLOCK_ENV_ACCESS_IN_NODE=false AI_SERVICES_URL=http://localhost:8006 SERVICE_API_KEY=... npx n8n start, and import the workflows with npx n8n import:workflow --separate --input=n8n/workflows.

Workflows as code. The JSON is generated by build_workflows.py (webhook v2, HTTP Request v4.2 with retries, IF v2, Respond to Webhook). Review diffs in git instead of clicking through the UI. If you edit in the UI, export and commit.

Secrets in n8n. For simplicity the workflows read $env.SERVICE_API_KEY, which requires N8N_BLOCK_ENV_ACCESS_IN_NODE=false. In production, prefer an n8n Header Auth credential on the HTTP node, which is encrypted with N8N_ENCRYPTION_KEY, and keep env access blocked.

How to Test — 測試方法

LLM_PROVIDER=mock pytest -q            # 9 tests

The tests cover:

What was run on the build box — 實測紀錄

Cost & Performance Notes — 成本與效能

Common Failures & Fixes — 常見錯誤與修正

Symptom Cause Fix
Webhook 404 workflow not active/published, or using /webhook-test/ URL without "listening" activate/publish; production path is /webhook/<path>
access to env vars denied n8n blocks $env by default N8N_BLOCK_ENV_ACCESS_IN_NODE=false, or switch to a Header Auth credential
401 from ai-services key mismatch same SERVICE_API_KEY in both containers; no quotes/whitespace
n8n can't reach localhost:8006 inside Docker, localhost is n8n's container use http://ai-services:8006 (compose service name)
Timeouts slow model raise HTTP node timeout, faster model, or async: respond 202 and post results back
Credentials unreadable after redeploy N8N_ENCRYPTION_KEY changed/lost restore the original key; back it up with the volume
Duplicate Slack alerts retries without idempotency keep the service idempotent (it is); don't add random fields to the payload

Stretch Exercise — 延伸練習

  1. Add a 4th workflow: Schedule Trigger (daily 8:00 America/Los_Angeles) → GET /v1/events → summarise yesterday's negative reviews into one Slack digest.
  2. Add a human-approval step for review replies: Slack message with Approve/Reject buttons, or n8n's Wait node + form, and only then post the reply.

Deliverable Checklist — 交付清單

↑