Skip to content

Tipos de mensaje y convertToLlm

源码版本v0.73.1

messages.ts añade cuatro tipos de mensaje personalizado al AgentMessage de pi-agent-core: bashExecution, custom, branchSummary, compactionSummary. El Agent subyacente sólo reconoce user/assistant/toolResult; convertToLlm traduce estos cuatro tipos personalizados a Message[] y filtra los resultados de bash con excludeFromContext. La extensión se hace por declaración merging sobre la interfaz CustomAgentMessages, alineando tipo y runtime.

Responsabilidades

  1. Extensión de tipos: con declare module añade cuatro roles a CustomAgentMessages. Ver packages/coding-agent/src/core/messages.ts:69-77.
  2. Mensaje bash execution: el resultado de un comando !, con command, output, exitCode, cancelled, truncated, fullOutputPath, y excludeFromContext opcional (para el prefijo !!). Ver packages/coding-agent/src/core/messages.ts:29-40.
  3. Mensaje custom: inyectado por extensiones vía sendMessage; customType distingue el tipo, display controla si se muestra en la UI, details es data estructurada opcional. Ver packages/coding-agent/src/core/messages.ts:46-53.
  4. Resumen de branch / compaction: al volver de un fork se inserta un resumen de branch, al compactar se inserta un resumen de compactación; ambos envueltos en etiquetas <summary>. Ver packages/coding-agent/src/core/messages.ts:55-67.
  5. Fábricas: createBranchSummaryMessage / createCompactionSummaryMessage / createCustomMessage convierten un timestamp string a milisegundos numéricos. Ver packages/coding-agent/src/core/messages.ts:100-138.
  6. convertToLlm: traduce los mensajes personalizados a Message[]; bash usa bashExecutionToText y se convierte en user message; branch/compaction se envuelven en <summary>; los bash con excludeFromContext se filtran devolviendo undefined. Ver packages/coding-agent/src/core/messages.ts:148-195.

Motivación de diseño

¿Por qué no reutilizar directamente el mensaje user? Porque la UI necesita distinguir "esto es salida de bash" de "esto es entrada del usuario": estilo de renderizado, plegado, reenvío. Pero el LLM sólo necesita ver texto: convertToLlm aplana todos los tipos personalizados a user message antes de llamar al provider, y el modelo no sabe que existen los tipos custom. Así se preserva la riqueza semántica en la UI sin contaminar el contexto del modelo.

bashExecutionToText formatea la salida de bash como bloque de código Markdown, con prefijo Ran \command`y código de salida como sufijo, de modo que el LLM ve un formato parecido al que ve el usuario. Los bash con prefijo!!se eliminan completamente del contexto del LLM víaexcludeFromContext: true`: es la elección del usuario de "que el modelo no vea este paso", típico en operaciones sensibles o salidas ruidosas.

branchSummary y compactionSummary se envuelven en etiquetas XML <summary>; es el formato que Anthropic recomienda, y el modelo tiende a tratar el contenido de la etiqueta como una unidad completa en vez de dispersarlo en párrafos.

Archivos clave

convertToLlm usa switch + cheque exhaustivo never; si se añade un tipo nuevo y se olvida, falla en compilación:

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 envuelve la salida en un bloque Markdown, con código de salida y aviso de truncado:

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

Flujo de datos

Camino de los mensajes hasta el LLM:

Límites y fallos

  • excludeFromContext: los resultados bash con prefijo !! se filtran en convertToLlm; el modelo no los ve, pero la UI los sigue mostrando. Ver packages/coding-agent/src/core/messages.ts:152-156.
  • content dual en custom: CustomMessage.content puede ser string o Array<TextContent | ImageContent>; si es string se convierte en array de un TextContent. Ver packages/coding-agent/src/core/messages.ts:162-168.
  • timestamp string a número: las tres funciones create usan new Date(timestamp).getTime(); si ya viene como timestamp numérico, devuelven NaN; el llamador debe pasar ISO string. Ver packages/coding-agent/src/core/messages.ts:105-106.
  • Envoltura blockImages: createAgentSession envuelve convertToLlm con convertToLlmWithBlockImages, que lee settings dinámicamente y reemplaza imágenes por placeholder. Ver la sección de límites de ensamblaje createAgentSession.
  • Compactación usa la misma función: tanto transformToLlm del Agent como generateSummary de compactación usan convertToLlm, para que el contexto que ve la compactación coincida con el que se envía al provider.

Resumen

messages.ts usa declaration merging de TypeScript para añadir cuatro tipos personalizados a AgentMessage; convertToLlm los aplana a user message antes de llamar al provider, y la UI conserva el tipo original para renderizado diferenciado. Al ensamblar, esta función se envuelve con la capa blockImages en ensamblaje createAgentSession; los detalles de CompactionSummaryMessage generado por la compactación están en el directorio compaction.