Step 3 — Output: AI-Enhanced Digest & DM Delivery
This final step completes the POC loop with intelligence. It proves the full AI pipeline by generating per-item summaries using the Symfony AI Bundle and delivering a formatted digest via Slack DM. Each knowledge item is condensed into 2–3 sentences followed by a direct link to its Notion page.
TIP
The AI provider stack introduced here is the same stack the MVP uses for all AI operations (summaries, content cleaning, etc.). Building it now saves significant rework later.
User Story
Story 3.1 — AI-Powered Digest
As a user, I want to generate a digest of recent knowledge for a list and receive a beautifully formatted DM where each item is summarized in 2–3 sentences with a link to its Notion page.
Example Output (Slack DM):
📊 Digest — LIJST: Design & UX
2 nieuwe items in de afgelopen 7 dagen
━━━━━━━━━━━━━━━━━━━━━━
📝 Accessibility Best Practices 2026
Nieuwe WCAG 3.0 richtlijnen benadrukken contrast-
en leesbaarheid. De focus verschuift van compliance
naar bruikbare inclusiviteit.
🔗 Bekijk in Notion
━━━━━━━━━━━━━━━━━━━━━━
📝 Dark Mode Design Patterns
Donkere achtergronden verbeteren de leesbaarheid op
OLED-schermen en verminderen batterijverbruik. Goede
implementatie vereist aangepaste kleurenpaletten.
🔗 Bekijk in Notion
━━━━━━━━━━━━━━━━━━━━━━Architecture Flow
This flow introduces the AI Provider Layer into the generation pipeline:
The AI Provider Stack
The POC introduces the full Symfony AI provider stack. This is the same stack the MVP extends — no throwaway code.
Key Design Decisions
| Decision | Rationale |
|---|---|
AiProviderInterface | Provider-agnostic contract. MVP can swap OpenAI for Anthropic without touching business logic. |
SymfonyAiProvider | Uses symfony/ai-bundle PlatformInterface directly. No custom gateway or auth override — clean standard integration. |
| Mock mode support | SymfonyAiProvider has a $mockMode flag. When true, returns deterministic text without calling OpenAI. Essential for testing. |
| Agent selection | Temperature-based agent routing (creative/balanced/precise). POC uses balanced for digest summaries. |
Backend Files (9)
AI Provider Infrastructure (5 files)
These are the foundation for all future AI operations in the MVP (content cleaning, full summaries, prompt templates, etc.).
| # | File | Layer | Purpose | ~LOC |
|---|---|---|---|---|
| 1 | Service/Ai/AiProviderInterface.php | Contract | generateCompletion(prompt, options): AiResponse, getName(), getModel(), estimateCost(), getMaxTokens() | 59 |
| 2 | Service/Ai/SymfonyAi/SymfonyAiProvider.php | Implementation | Implements interface via Symfony AI PlatformInterface. Agent selection (creative/balanced/precise), cost calculation, mock mode. No custom gateway/auth override. | 180 |
| 3 | Service/Ai/AiResponse.php | DTO | Immutable result: content, promptTokens, completionTokens, cost, model, metadata | 43 |
| 4 | DTO/Ai/AiOptionsDto.php | DTO | temperature, maxTokens, topP, frequencyPenalty, presencePenalty, seed, model, timeout | 50 |
| 5 | DTO/Ai/AiMetadataDto.php | DTO | durationMs, finishReason, provider, systemFingerprint, mock, extra | 30 |
Digest Domain (4 files)
| # | File | Layer | Purpose | ~LOC |
|---|---|---|---|---|
| 6 | Service/Digest/DigestAiSummaryGenerator.php | Domain Service | generateItemSummary(Knowledge): string — builds a prompt from the knowledge content and calls AiProviderInterface. Hardcoded Dutch prompt template (POC only). Fallback: returns truncated content if AI fails. | 100 |
| 7 | Service/Digest/DigestQueryService.php | Query Service | findKnowledgeForDigest(categoryId, dateRange): Knowledge[], parseDateRange(string): DateRangeDto. Extends BaseQueryService. | 80 |
| 8 | Service/Digest/DigestOrchestrator.php | Orchestrator | generateDigest(categoryId, dateRange) — queries items via DigestQueryService, generates per-item AI summaries via DigestAiSummaryGenerator, builds response with Notion URLs from Knowledge::notionId. Extends BaseOrchestrator. | 130 |
| 9 | Controller/DigestController.php | Presentation | POST /api/digests/generate — accepts categoryId + dateRange, delegates to orchestrator. | 50 |
IMPORTANT
Simplified for POC: The DigestAiSummaryGenerator uses a hardcoded prompt template, not PromptTemplate entities or PromptBuilderService. Those are MVP features.
POC Prompt Template (Hardcoded)
The generator uses this embedded prompt — no database-backed templates needed:
Vat het volgende kennisitem samen in 2-3 beknopte zinnen in het Nederlands.
Focus op de kernboodschap en praktische relevantie.
Titel: {{title}}
Inhoud: {{content}}
Samenvatting:Slack Files (3)
| # | File | Responsibilities | ~LOC |
|---|---|---|---|
| 1 | views/modals/digest/generateDigest.ts | Modal UI: Category dropdown, Days numeric input. | 60 |
| 2 | services/digest.ts | generateDigest(categoryId, dateRange) — POST /api/digests/generate. | 40 |
| 3 | handlers/knowledge/itemActions.ts | handleGenerateDigest() — extract modal values, call backend, build Block Kit DM with: Header → per-item AI summary + Notion link → statistics footer. This handler IS the delivery mechanism. | 150 |
Block Kit DM Structure
The Slack handler builds these blocks from the backend response:
┌─────────────────────────────────────────┐
│ 📊 Digest — {categoryName} │ ← Header block
│ {itemCount} items · afgelopen {days}d │ ← Section block
├─────────────────────────────────────────┤
│ ───────────────────────────────── │ ← Divider
│ │
│ 📝 *{item.title}* │ ← Section block
│ {item.aiSummary} │ ← 2-3 sentences from AI
│ 🔗 <{notionUrl}|Bekijk in Notion> │ ← Notion link
│ _Tags: {tags}_ │ ← Context block
│ │
│ ───────────────────────────────── │ ← Divider
│ ... repeat for each item ... │
└─────────────────────────────────────────┘Updating Existing Code
The following files from Step 1 are updated:
app.ts: Register the digest view submission callbackviews/home/dynamicHomeTab.ts: Add "Digest Maken" button to the Quick Actions row
Notion URL Construction
Each knowledge item has a notionId field (populated when synced to Notion). The Notion URL is constructed as:
https://notion.so/{notionId with dashes removed}If notionId is null (item not yet synced), the link is omitted and only the AI summary is shown.
Verification Checklist
- [ ] Click "Digest Maken" on the App Home
- [ ] Select a list and set timeframe (e.g., 7 days)
- [ ] Receive a DM from the bot
- [ ] Each item in the DM has a 2–3 sentence AI-generated summary (not raw content)
- [ ] Each item with a
notionIdincludes a clickable Notion link - [ ] If no items exist for the period → polite "Geen items gevonden" message
- [ ] If AI fails (API key missing / mock mode) → fallback shows truncated raw content instead
Step 3 LOC Summary
| Component | Files | ~LOC |
|---|---|---|
| AI Provider stack | 5 | 362 |
| Digest services | 2 | 210 |
| Digest controller | 1 | 50 |
| Slack modal/service | 2 | 100 |
| Slack handler | 1 | 150 |
| Total | 12 | ~872 |
NOTE
Step 3 is larger than originally planned (~460 LOC → ~872 LOC) due to the AI provider stack. This is intentional — the 5 AI files are MVP foundational infrastructure, not throwaway POC code.