Skip to content

Types de messages et convertToLlm

源码版本v0.73.1

messages.ts ajoute quatre types de messages personnalisés à l'AgentMessage de pi-agent-core : bashExecution, custom, branchSummary, compactionSummary. L'Agent sous-jacent ne reconnaît que user/assistant/toolResult, et convertToLlm traduit ces quatre types personnalisés en Message[], en filtrant les résultats d'exécution bash marqués excludeFromContext. L'extension par fusion de déclarations (declaration merging) se fait via l'interface CustomAgentMessages, alignant couche de types et couche d'exécution.

Responsabilités

  1. Extension de types : utilise declare module pour ajouter quatre rôles à l'interface CustomAgentMessages, voir packages/coding-agent/src/core/messages.ts:69-77.
  2. Messages d'exécution bash : résultats des commandes !, avec command, output, exitCode, cancelled, truncated, fullOutputPath, et excludeFromContext optionnel (correspond au préfixe !!). Voir packages/coding-agent/src/core/messages.ts:29-40.
  3. Messages custom : messages injectés via sendMessage par les extensions, customType distingue les types, display contrôle l'affichage UI, details est une donnée structurée optionnelle. Voir packages/coding-agent/src/core/messages.ts:46-53.
  4. Branch / compaction summary : résumé de branche inséré quand un fork revient à la branche principale, résumé de compaction inséré après compression, tous deux enveloppés dans des balises <summary>. Voir packages/coding-agent/src/core/messages.ts:55-67.
  5. Fonctions factory : createBranchSummaryMessage / createCompactionSummaryMessage / createCustomMessage convertissent un timestamp chaîne en millisecondes numériques. Voir packages/coding-agent/src/core/messages.ts:100-138.
  6. convertToLlm : convertit les messages personnalisés en Message[] ; bash utilise bashExecutionToText pour produire un message user, branch/compaction summary sont enveloppés dans <summary>, et les bash excludeFromContext retournent undefined pour être filtrés. Voir packages/coding-agent/src/core/messages.ts:148-195.

Motifs de conception

Pourquoi ne pas réutiliser directement les messages user ? Parce que la couche UI a besoin de distinguer « c'est une sortie d'exécution bash » d'« c'est une entrée utilisateur » : le rendu, la possibilité de repli et la réémission diffèrent. Mais le LLM n'a besoin de voir que du texte — convertToLlm aplatit tous les types personnalisés en messages user avant l'envoi au provider, le modèle n'a aucune conscience de l'existence des types custom. On préserve ainsi la richesse sémantique pour l'UI sans polluer le contexte du modèle.

bashExecutionToText formate la sortie bash en bloc de code Markdown, avec préfixe Ran \command`et suffixe contenant le code de sortie, pour que le LLM voie un format proche de ce que l'utilisateur voit dans l'UI. Les commandes bash préfixées!!sont totalement retirées du contexte LLM viaexcludeFromContext: true` — c'est l'utilisateur qui choisit activement « cette exécution, le modèle ne la voit pas », typiquement pour opérations sensibles ou sorties bruitées.

branchSummary et compactionSummary sont enveloppés dans des balises XML <summary>, un format recommandé par Anthropic : le modèle a tendance à traiter le contenu de la balise comme une unité complète plutôt que de le disperser en paragraphes.

Fichiers clés

convertToLlm utilise un switch + vérification d'exhaustivité via never : oublier de traiter un nouveau type provoque une erreur de compilation :

typescript
// packages/coding-agent/src/core/messages.ts:148-195
export function convertToLlm(messages: AgentMessage[]): Message[] {
	return messages
		.map((m): Message | undefined => {
			switch (m.role) {
				case "bashExecution":
					if (m.excludeFromContext) {
						return undefined;
					}
					return {
						role: "user",
						content: [{ type: "text", text: bashExecutionToText(m) }],
						timestamp: m.timestamp,
					};
				case "custom": {
					const content = typeof m.content === "string" ? [{ type: "text" as const, text: m.content }] : m.content;
					return { role: "user", content, timestamp: m.timestamp };
				}
				// ... branchSummary / compactionSummary ...
				case "user":
				case "assistant":
				case "toolResult":
					return m;
				default:
					const _exhaustiveCheck: never = m;
					return undefined;
			}
		})
		.filter((m) => m !== undefined);
}

bashExecutionToText enveloppe le résultat d'exécution dans un bloc de code Markdown, avec code de sortie et indication de tronquage :

typescript
// packages/coding-agent/src/core/messages.ts:82-98
export function bashExecutionToText(msg: BashExecutionMessage): string {
	let text = `Ran \`${msg.command}\`\n`;
	if (msg.output) {
		text += `\`\`\`\n${msg.output}\n\`\`\``;
	} else {
		text += "(no output)";
	}
	if (msg.cancelled) {
		text += "\n\n(command cancelled)";
	} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {
		text += `\n\nCommand exited with code ${msg.exitCode}`;
	}
	if (msg.truncated && msg.fullOutputPath) {
		text += `\n\n[Output truncated. Full output: ${msg.fullOutputPath}]`;
	}
	return text;
}

Flux de données

Le chemin d'un message jusqu'au LLM :

Limites et cas d'échec

  • excludeFromContext : les résultats d'exécution bash préfixés !! sont totalement filtrés dans convertToLlm, le modèle ne les voit pas, mais l'UI les affiche toujours. Voir packages/coding-agent/src/core/messages.ts:152-156.
  • Deux formes de custom content : CustomMessage.content peut être une string ou Array<TextContent | ImageContent> ; quand c'est une string, elle est convertie en tableau à un seul TextContent. Voir packages/coding-agent/src/core/messages.ts:162-168.
  • Conversion string vers nombre du timestamp : les trois fonctions create* utilisent new Date(timestamp).getTime() ; si on passe un timestamp déjà numérique, on obtient NaN — l'appelant doit garantir une chaîne ISO. Voir packages/coding-agent/src/core/messages.ts:105-106.
  • Couche blockImages : createAgentSession enveloppe convertToLlm dans convertToLlmWithBlockImages, qui lit dynamiquement les settings pour remplacer les images par du texte placeholder. Voir la section des limites dans assemblage createAgentSession.
  • Même fonction pour la compaction : transformToLlm de l'Agent et generateSummary de la compaction utilisent toutes deux convertToLlm, garantissant que le contexte vu pendant la compaction est identique à celui envoyé au provider.

Synthèse

messages.ts utilise la fusion de déclarations TypeScript pour ajouter quatre types personnalisés à AgentMessage, et convertToLlm les aplatit en messages user avant l'envoi au provider, tandis que la couche UI conserve les types d'origine pour un rendu différencié. À l'assemblage, cette fonction est enveloppée par createAgentSession d'une couche de filtrage blockImages, voir assemblage createAgentSession ; les détails de CompactionSummaryMessage produits par le flux de compaction sont dans le répertoire compaction.