Aller au contenu principal

ADR-032 : Transformation des Réponses — Pattern d'Adapter Pluggable

Metadata

ChampValeur
StatutAccepté
Date2026-02-06
LinearN/A

Contexte

Les réponses API des backends sont souvent :

  • Trop verbeuses pour les fenêtres de contexte des LLM
  • Structurées pour les machines, pas pour les agents IA
  • Incohérentes entre différents fournisseurs d'API

Le MCP Gateway doit transformer les réponses en formats adaptés aux LLM tout en respectant les budgets de tokens.

Le Problème

« Comment rendre les réponses API arbitraires consommables par l'IA sans coder en dur les transformations par API ? »

Décision

Implémenter un Moteur de Transformation de Réponses avec des adapters pluggables pour différents formats de réponse.

Architecture

┌────────────────────────────────────────────────────────────────┐
│ TransformEngine │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Field │ │ Truncation │ │ Pagination │ │
│ │ Selection │ │ │ │ │ │
│ │ │ │ - Max chars │ │ - Page size │ │
│ │ - Include │ │ - Ellipsis │ │ - Cursor │ │
│ │ - Exclude │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Adapters: │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Notion │ │ Linear │ │ Capper │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ │ │ │ │ │ │ │
│ │ Blocks → │ │ Issues → │ │ Token │ │
│ │ Markdown │ │ Summary │ │ Budget │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└────────────────────────────────────────────────────────────────┘

Opérations de Transformation

Sélection de Champs

Extraire uniquement les champs pertinents :

config = TransformConfig(
include_fields=["id", "title", "status", "created_at"],
exclude_fields=["internal_id", "raw_data"],
)

Troncature

Limiter la taille de la réponse :

config = TransformConfig(
max_chars=4000,
truncation_marker="... [truncated]",
)

Capper (Budget de Tokens)

Appliquer des limites de tokens pour le contexte LLM :

class CapperAdapter:
def transform(self, response: dict, max_tokens: int) -> dict:
"""Cap response to fit token budget."""
current_tokens = count_tokens(response)
if current_tokens <= max_tokens:
return response

# Progressive summarization
return self._summarize_to_budget(response, max_tokens)

Adapters

Adapter Notion

Transforme les réponses de blocs Notion :

class NotionAdapter:
def transform(self, blocks: list[dict]) -> str:
"""Convert Notion blocks to Markdown."""
markdown = []
for block in blocks:
if block["type"] == "paragraph":
markdown.append(block["paragraph"]["rich_text"][0]["plain_text"])
elif block["type"] == "heading_1":
markdown.append(f"# {block['heading_1']['rich_text'][0]['plain_text']}")
return "\n\n".join(markdown)

Adapter Linear

Transforme les réponses d'issues Linear :

class LinearAdapter:
def transform(self, issue: dict) -> dict:
"""Extract key issue fields."""
return {
"id": issue["identifier"],
"title": issue["title"],
"status": issue["state"]["name"],
"assignee": issue.get("assignee", {}).get("name"),
"priority": issue["priority"],
}

Schéma de Configuration

transform:
enabled: true
default_max_tokens: 4000
adapters:
notion:
enabled: true
output_format: markdown
linear:
enabled: true
include_comments: false
capper:
enabled: true
model: gpt-4 # For token counting

Conséquences

Positives

  • Adapté aux LLM — Les réponses s'intègrent dans les fenêtres de contexte
  • Extensible — Nouveaux adapters sans modification du moteur
  • Configurable — Règles de transformation par outil
  • Cohérent — Format de sortie uniforme

Négatives

  • Perte d'information — La troncature supprime des données
  • Maintenance des adapters — Chaque API nécessite un adapter
  • Comptage de tokens — Nécessite une logique spécifique au modèle

Références