Skip to content

Nachrichtentypen und convertToLlm ​

源码版本v0.73.1

messages.ts fügt dem AgentMessage von pi-agent-core vier eigene Nachrichtentypen hinzu: bashExecution, custom, branchSummary, compactionSummary. Der darunterliegende Agent kennt nur user/assistant/toolResult, convertToLlm übersetzt diese vier eigenen Typen in Message[] und filtert bash-Ausführungsergebnisse mit excludeFromContext heraus. Die Erweiterung erfolgt über das CustomAgentMessages-Interface per declaration merging, auf Typ- und auf Runtime-Ebene ausgerichtet.

Verantwortung ​

  1. Typerweiterung: Mit declare module werden dem CustomAgentMessages-Interface vier Rollen hinzugefügt, siehe packages/coding-agent/src/core/messages.ts:69-77.
  2. bash-Ausführungsnachricht: Das Ergebnis eines !-Befehls mit command, output, exitCode, cancelled, truncated, fullOutputPath, optionales excludeFromContext (für !!-Präfix). Siehe packages/coding-agent/src/core/messages.ts:29-40.
  3. custom-Nachricht: Von einer Extension über sendMessage injizierte Nachricht, customType unterscheidet den Typ, display steuert, ob sie in der UI angezeigt wird, details ist eine optionale strukturierte Datenform. Siehe packages/coding-agent/src/core/messages.ts:46-53.
  4. branch / compaction summary: Branch-Zusammenfassung beim Zurückkehren auf den Hauptstrang, Kompressions-Zusammenfassung nach der Kompression, beide mit <summary>-Tag gewrappt, siehe packages/coding-agent/src/core/messages.ts:55-67.
  5. Fabrikfunktionen: createBranchSummaryMessage / createCompactionSummaryMessage / createCustomMessage wandeln String-timestamp in numerische Millisekunden um. Siehe packages/coding-agent/src/core/messages.ts:100-138.
  6. convertToLlm: Wandelt eigene Nachrichten in Message[] um, bash wird über bashExecutionToText zu einer user-Nachricht, branch/compaction summary werden in <summary>-Tags gewrappt, bash mit excludeFromContext wird direkt mit undefined herausgefiltert. Siehe packages/coding-agent/src/core/messages.ts:148-195.

Entwurfsmotivation ​

Warum nicht einfach user-Nachrichten wiederverwenden? Weil die UI-Schicht zwischen "das ist bash-Ausgabe" und "das ist Nutzereingabe" unterscheiden muss - Render-Stil, Einklappbarkeit, erneutes Senden sind unterschiedlich. Der LLM braucht aber nur den Text zu sehen - convertToLlm klappt vor dem Senden an den Provider alle eigenen Typen zu user-Nachrichten flach, das Modell weiß gar nicht, dass es custom-Typen gibt. So bleibt die semantische Reichhaltigkeit für die UI erhalten, ohne den Modellkontext zu verschmutzen.

bashExecutionToText formatiert bash-Ausgabe als Markdown-Codeblock, mit Ran \command`-Präfix und Exit-Code-Suffix, damit der LLM das gleiche Format sieht wie der Nutzer in der UI. bash mit !!-Präfix wird über excludeFromContext: true` komplett aus dem LLM-Kontext entfernt - das ist die aktive Nutzerwahl "diesen Schritt soll das Modell nicht sehen", üblich bei sensiblen Operationen oder lautem Output.

branchSummary und compactionSummary werden mit <summary>-XML-Tags gewrappt, das ist das von Anthropic empfohlene Format, das Modell behandelt den Inhalt eher als geschlossene Einheit statt als verteilte Absätze.

Wichtige Dateien ​

convertToLlm nutzt switch + never für erschöpfende Prüfung, ein vergessener neuer Typ führt zu einem Compile-Fehler:

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 wickelt das Ausführungsergebnis in einen Markdown-Codeblock mit Exit-Code und Truncate-Hinweis:

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;
}

Datenfluss ​

Pfad einer Nachricht von der Entstehung zum LLM:

Grenzen und Fehler ​

  • excludeFromContext: bash-Ergebnis mit !!-Präfix wird in convertToLlm komplett gefiltert, das Modell sieht es nicht, aber die UI zeigt es noch, siehe packages/coding-agent/src/core/messages.ts:152-156.
  • custom content zwei Formen: CustomMessage.content kann string oder Array<TextContent | ImageContent> sein, bei string wird es in ein einzelnes TextContent-Array umgewandelt, siehe packages/coding-agent/src/core/messages.ts:162-168.
  • timestamp String zu Zahl: Die drei create-Funktionen nutzen new Date(timestamp).getTime(), wenn schon ein numerischer timestamp reinkommt, ergibt das NaN, der Aufrufer muss ISO-Strings übergeben, siehe packages/coding-agent/src/core/messages.ts:105-106.
  • blockImages-Wrapper-Schicht: createAgentSession wickelt convertToLlm noch in eine convertToLlmWithBlockImages-Schicht ein, die settings dynamisch liest und Bilder durch Platzhaltertext ersetzt, siehe den Grenz-Abschnitt in createAgentSession Zusammenbau.
  • Kompression nutzt dieselbe Funktion: Sowohl transformToLlm von Agent als auch generateSummary der Kompression nutzen convertToLlm, damit der bei der Kompression sichtbare Kontext mit dem beim Senden an den Provider übereinstimmt.

Zusammenfassung ​

messages.ts fügt über TypeScript declaration merging AgentMessage vier eigene Typen hinzu, convertToLlm klappt sie vor dem Senden an den Provider zu user-Nachrichten flach, die UI-Schicht behält die Originaltypen für differenziertes Rendern. Beim Zusammenbau wird diese Funktion von createAgentSession noch in eine blockImages-Filter-Schicht gewrappt, siehe createAgentSession Zusammenbau; die Details, wie der Kompressionsprozess eine CompactionSummaryMessage erzeugt, im compaction-Verzeichnis.