Michel Epeepe.michel@gmail.com

Applied AI Engineer · Choreograph / WPP

Michel Epe

I joined the support team at Choreograph and ended up building the AI tools the team runs on.

Fragmented information, knowledge that lives with one person and goes nowhere, processes that can't keep up: I built systems that fix exactly that, and other teams keep asking for the same thing.

Projects
Production

AskOpti

Powerful AI capabilities were already there, but getting to them meant installing tools, navigating a terminal, and jumping through access hoops. Most people just didn't bother. AskOpti brought those same capabilities into Slack, where the whole team already lives. Three teams use it daily, and more keep asking to get onboarded.

View case study →Close ↑
PythonClaude Sonnet + HaikuSlack BoltMCP

Multi-agent orchestration

Entry points

Slack
CC Plugin
Teams (soon)

Orchestrator

Claude Sonnetreasons · routes · synthesizes

Sub-agents (parallel)

Claude Haiku
Claude Haiku
Claude Haiku

MCP tools

Zendesk
Knowledge Base
Atlassian

The Problem

Support, implementation, and enablement teams needed fast answers from product data spread across Zendesk, a customer-facing knowledge base, and Atlassian. Finding one answer meant checking all three tools by hand, so a single query could eat up minutes.

What I Built

A multi-agent Slack bot. The orchestrator (Claude Sonnet) receives a question and reasons about which sources to query. Sub-agents (Claude Haiku) execute the tool calls in parallel through MCP connections. Results get synthesized into a structured answer posted back to the thread.

The system is interface-agnostic. The same agent backend powers a Slack bot, a Claude Code plugin, and a Teams bot in development. Each entry point has different capabilities but shares the same architecture and tool connections.

The Hard Parts

  1. 1.

    Latency. The first version averaged 250 seconds. I made five targeted changes: upgraded the sub-agent model, cut orchestrator round limits from 5 to 3, cut sub-agent limits from 8 to 4, increased the concurrent semaphore from 2 to 4, and tightened the investigation contract format. P50 dropped to 10-12s. P95 went from 149s to 35-45s.

  2. 2.

    Testing model changes safely. Built a shadow testing system that runs experimental configurations in parallel with production without surfacing results to users. New configs get validated against real queries before promotion.

  3. 3.

    Interface parity. The same agent backend serves Slack, Claude Code, and Teams, each with different capabilities and tool access. Getting the abstraction right meant any new interface is an addition, not a rewrite of what already works.

57savg response (was 250s)
P50 10-12slatency
P95 35-45swas 149s
3 teamsusing it in production
Production

KB Automation

Every support team has the same problem: thousands of tickets, no time to capture what's in them, and documentation that's outdated before it's finished. Multiple people tried to solve this at Choreograph and moved on. I built a pipeline that does it automatically, with a human reviewer at the end instead of the beginning. The knowledge base now grows on its own.

View case study →Close ↑
PythonClaude 3.7 Sonnetsentence-transformersPrometheusTerraform

4-stage pipeline · two-LLM design

Stage 1 · Ingest

Fetch ticketsZendesk + external
Scrub PIIPresidio

Stage 2 · Cluster

sentence-transformerslocal · cheap · no LLM
k-means / HDBSCANscikit-learn

Stage 3 · Interpret

Claude Sonnetdecides if cluster warrants article

Stage 4 · Generate

Claude Sonnetwrites article
30+ validatorscircuit breaker · retry · loop detect

The Problem

Thousands of support tickets per product, per quarter. Valuable signal buried in unstructured text. No way to query it. Teams writing knowledge base articles by hand, missing patterns that only become visible across hundreds of tickets at once.

I was living this problem every day as a support specialist, which meant I knew exactly where the friction was before I wrote a line of code.

What I Built

A 4-stage pipeline. Stage 1: fetch tickets, scrub PII with Presidio before any LLM touches the text. Stage 2: cluster with sentence-transformers and k-means/HDBSCAN. No LLM here, just a cheap embedding model running locally. Stage 3: Claude evaluates each cluster. Stage 4: Claude generates the article, with validation at every step.

Splitting the work across two models was deliberate. sentence-transformers do the heavy lifting of grouping 5,000 tickets, and Claude only ever sees the pre-grouped clusters. That keeps the cost at $1-3 per run no matter how many tickets come in.

The Hard Parts

  1. 1.

    Article generation rate. Started at 9%. Claude kept deciding clusters did not warrant articles. Fixed by tuning cluster quality thresholds and rewriting the interpretation prompt. Reached 90%+.

  2. 2.

    Hallucination prevention. An LLM writing factual content about real products at a real company is a liability, so I built 30+ regex validators, a circuit breaker that halts the pipeline after repeated failures, retry logic with tightened prompts, and a circular-loop detector. So far that has held the hallucination rate at 0% in production.

  3. 3.

    Observability. Instrumented 25+ Prometheus metrics across all four stages, built Grafana dashboards, wrote 30+ alert rules. Added a checkpoint/resume system so a failed run picks up where it left off.

90%+generation rate (was 9%)
0%hallucination rate
~200articles per run
$1-3cost per run
Published

Swarm

When multiple AI agents work in parallel, they can overwrite each other's work without knowing it. Swarm solves this by mapping which files each agent will touch before anything runs, and only allowing parallel execution where it's actually safe. Built it for my own projects, published it on npm so others could use it too.

View case study →Close ↑
TypeScript 5.7SQLiteClaude CLI

DAG execution ordering

Task list

Task Atouches: config, auth
Task Btouches: routes
Task Ctouches: config
Task Dtouches: tests

Analysis

File overlap mapA + C both touch config
Topological sortbuild execution DAG

Execution

Layer 1 (parallel)A, B, D have no conflicts
Layer 2 (after A)C waits for A to finish

The Problem

Running multiple Claude workers in parallel is fast. But workers editing the same file produce corrupted output. And when a long run fails halfway through, you lose all progress and start over.

Building AskOpti and KB Automation in parallel made this concrete. I needed a coordination layer that was smarter than "run one at a time" but safer than "run everything at once."

What I Built

A TypeScript framework that analyzes a task list before any execution begins. It maps which files each task will touch, finds overlaps, and builds a directed acyclic graph. Tasks with no shared files run in parallel. Tasks that share files run sequentially.

Human-in-the-loop checkpoints gate high-risk operations. SQLite backs the memory layer so workers resume across sessions without repeating completed work.

The Hard Parts

  1. 1.

    Pre-execution file analysis. The system needs to infer which files a task will touch before running it. I used task description parsing and file path heuristics, tuned against actual file changes from real runs.

  2. 2.

    Risk-based gating. A conflict on a config file is more dangerous than one on a test file. The risk model gates parallel execution based on file type and location, not just detected overlap.

2production systems built with it
npmpublished as @michelepe/swarm
Shipped

CC Plugin

Support agents forget steps under pressure. Not because they don't know the process, but because the queue is long and the documentation is buried somewhere. This plugin brings the right investigation steps directly into the workflow, triggered by natural language during a ticket. Less back and forth, fewer missed checks, faster resolution.

View case study →Close ↑
Prompt engineeringMCPJSON configs

Investigation contract · confidence routing

Input

Support ticket+ platform detection

Supervisor

Decomposetask worker pattern

Workers (parallel)

Zendesk query
KB query
Atlassian query

Confidence routing

HIGH → /draft
MED → /draft or /escalate
LOW → /request

The Problem

Support and implementation teams use Claude Code but querying Zendesk, the knowledge base, and Atlassian for a specific ticket required writing custom prompts every time. No shared workflow, no consistency, no way to distribute institutional knowledge about how to investigate properly.

What I Built

A Claude Code marketplace plugin with six investigation skills. There is no runtime code at all; the whole system is prompt engineering and JSON configuration.

Skills follow a task worker pattern: a supervisor skill decomposes the investigation, parallel workers query different sources through MCP connections, results get synthesized following a strict investigation contract format.

The Hard Parts

  1. 1.

    Hallucination prevention through format constraints. The investigation contract forces the model to declare confidence before making any claim. High confidence routes to drafting, low confidence routes to escalation. The format constraints end up doing the same job that validators handle in a traditional system, without a line of code.

  2. 2.

    Platform routing. 12 ad platform configs, each with detection keywords and known error codes in JSON. The model reads the ticket, identifies the platform, and routes to the right config. No code, just structured data and prompt instructions that reference it.

  3. 3.

    Model routing rules. Skills include explicit instructions: no Opus or Claude 4.6 for tool calls. Keeps costs down without sacrificing quality. Baked into the skill definitions so future contributors cannot override it accidentally.

About

I joined Choreograph's support team and ended up building the AI tools the team runs on. KB Automation shipped first. Then AskOpti. Then Swarm. Each one started as a problem I was living myself.

My background is in healthcare and mental health, not computer science. What that gave me is a habit of putting myself in someone else's shoes before I write a line of code. Who is this person, what are they trying to do, what gets in their way? That shows up in the work: AskOpti exists because powerful AI tools were too hard to access for most people on the team. The plugin exists because support agents forget steps under pressure, not because they're bad at their jobs.

I'm looking for teams that want to move fast on AI and need someone who can own it end to end.

epe.michel@gmail.com →