Kvika Team·· 7 min read

Extracting Tasks From Teams Chat Without Drowning in Noise

HTML strip in code, classification in the LLM, batch size eight, 14-day lookback — a Teams pipeline designed for false negatives over invented tasks.

microsoft-teamsaitasksengineering

Teams messages mix action items, status updates, jokes, and "LGTM" replies. Treating every message as a task source creates garbage. Ignoring Teams creates the same leak as email — commitments that never reach Jira or a task list.

Our Teams extraction pipeline scans recent messages, strips HTML noise in code, batches the remainder to the LLM, and persists tasks with stable source IDs. The design bias is false negatives over invented work.

Chat is not a task queue

Unlike email, chat lacks subject lines and explicit threading hierarchy. Context lives in replies, @mentions, and channel names. Extraction needs message metadata — team, channel, author, timestamp — in the prompt so the model knows who owes whom what.

Hygiene in code, judgment in the model

prepareTeamsMessage strips HTML and drops empty or ultra-short bodies (under three characters). We deliberately do not regex-classify "LGTM" vs "please review" in TypeScript — that becomes a maintenance nightmare. The LLM decides actionability; code only removes unusable input.

Prompt instruction: false negatives are better than inventing tasks. Match the conservative posture of email extraction.

Pipeline stages

1. Filter. Max 40 messages, 14-day lookback. Older chat is stale for task capture.

2. Prepare. Strip HTML, collect skip reasons for stats (empty, too_short).

3. Batch LLM. Chunks of eight messages per call. Include user profile (name, email) so the model knows who "you" is versus assignees.

4. Persist. Create tasks with source: TEAMS, sourceId from message identity, sourceData with confidence and extraction method.

typescript
const stats = {
  messagesScanned, messagesSkipped, messagesActionable,
  tasksExtracted, tasksCreated, duplicatesSkipped,
  skippedReasons: { empty: 2, too_short: 5 },
  llmBatches: 4,
}

Expose stats in the API response so the UI can toast "3 tasks from 12 messages, 8 skipped as noise."

Dedupe by message ID

Teams dedupe checks userId + source TEAMS + sourceId only — one task row per message ID on retry. Unlike email, we do not also match title; one message should not spawn duplicate rows on pipeline re-run.

Teams vs email extraction

Email uses temp IDs in batch prompts and title-based dedupe within the same message. Teams uses native message IDs directly. Email defers sync cursors; Teams ingestion is separate from mail sync entirely.

Both require AI consent and rate limiting. Both link tasks back to source context — Teams stores team/channel in sourceData for display even when deep links are imperfect.

Related: email extraction pipeline · inbox task capture. Join the Kvika beta.

Kvika Team

Kvika unifies your calendar, email, and tasks across Google and Microsoft. Join the beta at kvika.work/waitlist.