Skip to content

訊息類型與 convertToLlm

源码版本v0.73.1

messages.tspi-agent-coreAgentMessage 加四種自訂訊息類型:bashExecutioncustombranchSummarycompactionSummary。底層 Agent 只認 user/assistant/toolResult,convertToLlm 把這四種自訂類型翻譯成 Message[],過濾掉 excludeFromContext 的 bash 執行結果。宣告合併透過 CustomAgentMessages 介面擴充,類型層和執行時層都對齊。

職責

  1. 類型擴充:用 declare moduleCustomAgentMessages 介面加四個 role,見 packages/coding-agent/src/core/messages.ts:69-77
  2. bash 執行訊息:! 命令的執行結果,帶 commandoutputexitCodecancelledtruncatedfullOutputPath,可選 excludeFromContext(對應 !! 前綴)。見 packages/coding-agent/src/core/messages.ts:29-40
  3. custom 訊息:擴充透過 sendMessage 注入的訊息,customType 區分類型,display 控制是否在 UI 顯示,details 是可選結構化資料。見 packages/coding-agent/src/core/messages.ts:46-53
  4. branch / compaction summary:fork 回主線時插入分支摘要,壓縮後插入壓縮摘要,都用 <summary> 標籤包裹,見 packages/coding-agent/src/core/messages.ts:55-67
  5. 工廠函式:createBranchSummaryMessage / createCompactionSummaryMessage / createCustomMessage 把字串 timestamp 轉成數字毫秒。見 packages/coding-agent/src/core/messages.ts:100-138
  6. convertToLlm:把自訂訊息轉成 Message[],bash 用 bashExecutionToText 轉成 user 訊息,branch/compaction summary 包 <summary> 標籤,excludeFromContext 的 bash 直接回傳 undefined 過濾掉。見 packages/coding-agent/src/core/messages.ts:148-195

設計動機

為什麼不直接重用 user 訊息?因為 UI 層需要區分「這是 bash 執行輸出」和「這是使用者輸入」,渲染樣式、可摺疊、可重發都不同。但 LLM 只需要看到文字——convertToLlm 在送 provider 前把所有自訂類型拍平成 user 訊息,模型完全不知道有 custom 類型存在。這樣既保留了 UI 的語意豐富性,又不污染模型上下文。

bashExecutionToText 把 bash 輸出格式化成 Markdown 程式碼區塊,加上 Ran \command` 前綴和退出碼後綴,讓 LLM 看到的格式和使用者在 UI 看到的差不多。!!前綴的 bash 透過excludeFromContext: true` 完全從 LLM 上下文移除——這是使用者主動選擇「這步執行不讓模型看到」,常見於敏感操作或噪音輸出。

branchSummarycompactionSummary<summary> XML 標籤包裹,這是 Anthropic 推薦的格式,模型會更傾向把標籤內容當成完整單元對待,而不是分散成段落。

關鍵檔案

convertToLlmswitch + never 窮盡檢查,新增類型忘記處理會編譯報錯:

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 把執行結果包成 Markdown 程式碼區塊,帶上退出碼和截斷提示:

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

資料流

訊息從產生到 LLM 的路徑:

邊界與失敗

  • excludeFromContext:!! 前綴的 bash 執行結果在 convertToLlm 裡被完全過濾,模型看不到,但 UI 仍展示,見 packages/coding-agent/src/core/messages.ts:152-156
  • custom content 雙形態:CustomMessage.content 可以是 string 或 Array<TextContent | ImageContent>,string 時轉成單元素 TextContent 陣列,見 packages/coding-agent/src/core/messages.ts:162-168
  • timestamp 字串轉數字:三個 create 函式都用 new Date(timestamp).getTime(),傳入已是數字 timestamp 會得到 NaN,呼叫方需保證傳 ISO 字串,見 packages/coding-agent/src/core/messages.ts:105-106
  • blockImages 包裝層:createAgentSessionconvertToLlm 外再包一層 convertToLlmWithBlockImages,動態讀 settings 替換圖片為佔位文字,見 createAgentSession 組裝 的邊界段。
  • compaction 用同一函式:AgenttransformToLlm 和 compaction 的 generateSummary 都用 convertToLlm,保證壓縮時看到的上下文和送 provider 時一致。

小結

messages.ts 用 TypeScript 宣告合併給 AgentMessage 加四種自訂類型,convertToLlm 在送 provider 前把它們拍平成 user 訊息,UI 層保留原類型用於差異化渲染。組裝時這個函式被 createAgentSession 包一層 blockImages 過濾,看 createAgentSession 組裝;壓縮流程產生 CompactionSummaryMessage 的細節在 compaction 目錄。