第 04 章
ThreadItem 详解
理解 AI 的输出 — ThreadItem 详解
这章把 AI 产出的 7 种"东西"讲透:文字回复、命令执行、文件修改、MCP 调用、Web 搜索、待办事项、错误。
AI 不只是"说话"
跟普通聊天机器人不同,Codex 是一个 Agent——它不光会说话,还会干活。让它"帮你看看项目有什么问题",它可能会:
- 先执行
ls看看有什么文件 - 再执行
cat package.json读配置 - 然后执行
npm test跑测试 - 最后给你一段文字总结
这 4 步产出了不同类型的东西:3 个命令执行 + 1 个文字回复。在 SDK 里,这些"东西"统一叫 ThreadItem。
7 种 Item 一览
| 类型 | type 字段 |
大白话 | 典型场景 |
|---|---|---|---|
| 文字回复 | agent_message |
AI 说的话 | 回答问题、给出建议 |
| 推理摘要 | reasoning |
AI 脑子里想的 | 展示思考过程 |
| 执行命令 | command_execution |
跑 shell 命令 | ls、cat、npm test |
| 文件修改 | file_change |
改了哪些文件 | 创建/修改/删除文件 |
| MCP 工具 | mcp_tool_call |
调用外部工具 | 数据库查询、API 调用 |
| Web 搜索 | web_search |
上网搜资料 | 查文档、搜 bug |
| 待办事项 | todo_list |
AI 给自己列的计划 | 复杂任务的工作清单 |
| 错误 | error |
非致命错误 | 某步失败但整体继续 |
下面逐个拆解。
AgentMessageItem — AI 说的话
这是最常见的 Item,就是 AI 的文字回复。
typescripttype AgentMessageItem = {
id: string;
type: "agent_message";
text: string; // 回复内容
};重点:
turn.finalResponse就是从最后一个agent_message的text取的- 如果用了
outputSchema(结构化输出),text里面是 JSON 字符串
typescript// 使用示例
if (item.type === "agent_message") {
console.log("AI 说:", item.text);
}ReasoningItem — AI 在想什么
AI 有时候会先"想一想"再回答。想的过程就是 ReasoningItem。
typescripttype ReasoningItem = {
id: string;
type: "reasoning";
text: string; // 推理摘要
};不是每次都有这个 Item,取决于模型和
modelReasoningEffort配置。
typescriptif (item.type === "reasoning") {
console.log("思考中:", item.text);
}CommandExecutionItem — 跑命令
AI 需要获取信息或执行操作时,会跑 shell 命令。这是 Codex 最强大的能力之一。
typescripttype CommandExecutionItem = {
id: string;
type: "command_execution";
command: string; // 执行的命令,如 "ls -la"
aggregated_output: string; // stdout + stderr 合并输出
exit_code?: number; // 退出码(运行中时没有)
status: "in_progress" | "completed" | "failed";
};三个状态:
in_progress:命令还在跑(item.started事件时)completed:跑完了,exit_code为 0failed:跑完了但出错,exit_code非 0
typescriptif (item.type === "command_execution") {
if (item.status === "completed") {
console.log(`命令成功: ${item.command}`);
console.log(` 输出: ${item.aggregated_output.slice(0, 200)}`);
} else if (item.status === "failed") {
console.log(`命令失败: ${item.command} (退出码: ${item.exit_code})`);
}
}FileChangeItem — 改文件
AI 创建、修改或删除文件时,会产出这个 Item。
typescripttype FileChangeItem = {
id: string;
type: "file_change";
changes: Array<{
path: string; // 文件路径
kind: "add" | "update" | "delete"; // 新建 / 修改 / 删除
}>;
status: "completed" | "failed";
};注意:一个 FileChangeItem 可能包含多个文件变更(一次 patch 改了好几个文件)。
typescriptif (item.type === "file_change") {
for (const change of item.changes) {
const actionMap = { add: "新建", update: "修改", delete: "删除" };
console.log(`${actionMap[change.kind]}: ${change.path}`);
}
}McpToolCallItem — 调外部工具
MCP(Model Context Protocol)让 AI 能调用外部工具——数据库、API、自定义服务等。
typescripttype McpToolCallItem = {
id: string;
type: "mcp_tool_call";
server: string; // MCP 服务器名称
tool: string; // 工具名称
arguments: unknown; // 传给工具的参数
result?: { // 工具返回的结果
content: McpContentBlock[];
structured_content: unknown;
};
error?: { message: string }; // 如果调用失败
status: "in_progress" | "completed" | "failed";
};typescriptif (item.type === "mcp_tool_call") {
console.log(`调用工具: ${item.server}/${item.tool}`);
if (item.status === "completed" && item.result) {
console.log(" 结果:", JSON.stringify(item.result.structured_content));
}
if (item.error) {
console.log(" 错误:", item.error.message);
}
}其他 Item
WebSearchItem — 搜索
typescripttype WebSearchItem = { id: string; type: "web_search"; query: string };AI 上网搜东西时产出。需要在 ThreadOptions 里启用 webSearchMode: "live"。
TodoListItem — 待办清单
typescripttype TodoListItem = {
id: string;
type: "todo_list";
items: Array<{ text: string; completed: boolean }>;
};AI 处理复杂任务时,会先列个计划。这个 Item 会通过 item.updated 事件不断更新(完成一项勾一项)。
typescriptif (item.type === "todo_list") {
console.log("工作计划:");
for (const todo of item.items) {
const mark = todo.completed ? "[x]" : "[ ]";
console.log(` ${mark} ${todo.text}`);
}
}ErrorItem — 非致命错误
typescripttype ErrorItem = { id: string; type: "error"; message: string };AI 遇到了问题,但不影响整体执行。比如某个文件读不了,跳过继续。
实战:智能日志记录器
把所有 Item 类型综合起来,做一个能按类型分类记录的日志器:
typescript// item-logger.ts
import { Codex } from "@openai/codex-sdk";
import type { ThreadItem } from "@openai/codex-sdk";
import { appendFileSync, writeFileSync } from "fs";
// 初始化日志文件
writeFileSync("messages.log", "");
writeFileSync("commands.log", "");
writeFileSync("changes.log", "");
function logItem(item: ThreadItem): void {
const timestamp = new Date().toISOString();
switch (item.type) {
case "agent_message":
appendFileSync("messages.log", `[${timestamp}] ${item.text}\n\n`);
console.log("已记录 AI 回复");
break;
case "command_execution":
if (item.status !== "in_progress") {
const log = `[${timestamp}] $ ${item.command}\nExit: ${item.exit_code}\n${item.aggregated_output}\n---\n`;
appendFileSync("commands.log", log);
console.log(`已记录命令: ${item.command}`);
}
break;
case "file_change":
for (const change of item.changes) {
appendFileSync("changes.log", `[${timestamp}] ${change.kind}: ${change.path}\n`);
}
console.log(`已记录 ${item.changes.length} 个文件变更`);
break;
case "mcp_tool_call":
console.log(`MCP 调用: ${item.server}/${item.tool}`);
break;
case "todo_list":
const done = item.items.filter(t => t.completed).length;
console.log(`计划进度: ${done}/${item.items.length}`);
break;
case "reasoning":
console.log(`推理中...`);
break;
case "error":
console.log(`错误: ${item.message}`);
break;
}
}
async function main() {
const codex = new Codex();
const thread = codex.startThread();
const prompt = process.argv[2] || "分析当前项目的结构和代码质量";
console.log(`任务: ${prompt}\n`);
const { events } = await thread.runStreamed(prompt);
for await (const event of events) {
if (event.type === "item.completed") {
logItem(event.item);
} else if (event.type === "item.updated") {
// todo_list 更新时也记录
if (event.item.type === "todo_list") logItem(event.item);
}
}
console.log("\n完成!日志已保存到 messages.log、commands.log、changes.log");
}
main().catch(console.error);运行后会生成三个日志文件,分门别类记录 AI 的所有操作。
小结
- ThreadItem 是 AI 产出的"东西",有 7 种类型
- 最常用的三种:
agent_message(回复)、command_execution(命令)、file_change(文件) - 通过
item.type判断类型,用 switch-case 分别处理 item.status告诉你这件事做完了没有(in_progress / completed / failed)- 流式模式下,同一个 item 会经历 started -> updated -> completed 三个阶段
下一章:结构化输出 — 让 AI 乖乖返回 JSON,不再是自由文本。