Agent 执行过程可视化:终端步进指示器设计
在运行一个多步骤的智能体(Agent)任务时,大模型在背后通常要经历:理解意图 -> 决定调用工具 -> 执行外部 API -> 读取结果 -> 组织最终回答。
如果这个过程在终端里没有任何反馈,用户就会面对一个死寂的光标长达十多秒,产生“程序是不是卡死了”的焦虑。但如果直接把所有原始日志乱糟糟地全打印出来,又会把终端屏幕刷得一片狼藉。
设计一个清晰、优雅且不占空间的“终端步进指示器(Step Progress Indicator)”,是提升 Agent 操控感的核心细节。
终端可视化的三大设计原则
- 单行动态覆盖(In-Place Update):利用 ANSI 逃逸控制符
\r和清除行指令,在同一行动态更新当前步骤与旋转动画(Spinner),保持屏幕干净整洁。 - 状态明确区分:通过色彩与符号明确表达“思考中(⚡️)”、“执行中(⚙️)”、“已完成(✔)”与“异常重试(⚠)”。
- 完成时固化关键摘要:当某个子步骤执行完毕后,换行固化输出耗时与关键结论,不再重复闪烁。
40 行轻量步进指示器实现
不依赖体积庞大的第三方库,用纯原生 TypeScript 即可实现:
export class AgentStepIndicator { private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; private frameIndex = 0; private timer: NodeJS.Timeout | null = null; private currentLabel = ""; private startTime = 0; // 开始一个新步骤 public startStep(label: string) { this.stopSpinner(); this.currentLabel = label; this.startTime = Date.now(); this.timer = setInterval(() => { const frame = this.frames[this.frameIndex]; this.frameIndex = (this.frameIndex + 1) % this.frames.length; // \r 回到行首,\x1b[K 清除从光标到行尾的内容 process.stdout.write(`\r\x1b[36m${frame}\x1b[0m \x1b[2m[Agent]\x1b[0m ${this.currentLabel}...`); }, 80); } // 成功完成当前步骤并固化展示 public completeStep(summary?: string) { this.stopSpinner(); const elapsedSeconds = ((Date.now() - this.startTime) / 1000).toFixed(1); const detail = summary ? ` -> \x1b[32m${summary}\x1b[0m` : ""; process.stdout.write(`\r\x1b[32m✔\x1b[0m \x1b[1m${this.currentLabel}\x1b[0m \x1b[2m(${elapsedSeconds}s)\x1b[0m${detail}\n`); } // 步骤失败处理 public failStep(errorMessage: string) { this.stopSpinner(); process.stdout.write(`\r\x1b[31m✖\x1b[0m \x1b[1m${this.currentLabel}\x1b[0m - \x1b[31m${errorMessage}\x1b[0m\n`); } private stopSpinner() { if (this.timer) { clearInterval(this.timer); this.timer = null; } } }实战中的调用示例
在 Agent 调度循环中配合使用:
const indicator = new AgentStepIndicator(); async function runAgentPipeline() { indicator.startStep("正在分析用户需求与上下文"); await sleep(600); indicator.completeStep("识别出意图: 数据库慢查询分析"); indicator.startStep("调用 Tool: 抓取最近 5 条慢日志"); await sleep(1200); indicator.completeStep("获取到 3 条超过 1000ms 的 SQL"); indicator.startStep("大模型生成优化索引建议"); await sleep(1500); indicator.completeStep("生成完成"); }终端呈现效果
在用户的控制台中,输出会是如此规整清晰:
✔ 正在分析用户需求与上下文 (0.6s) -> 识别出意图: 数据库慢查询分析 ✔ 调用 Tool: 抓取最近 5 条慢日志 (1.2s) -> 获取到 3 条超过 1000ms 的 SQL ✔ 大模型生成优化索引建议 (1.5s) -> 生成完成总结
好的工程不是冷冰冰的代码,而是处处站在使用者角度考虑的细腻体验。
用简洁优雅的状态指示器代替凌乱的日志打印,让 Agent 的每一次思考和行动都清晰可感。