终端UI:让AI说话有颜色 ——CogitoAgent开发实战(五)
终端UI:让AI说话有颜色
——CogitoAgent开发实战(第5篇)
📖 本文是专栏的第五篇。前四篇我们让AI学会了思考、学会了动手、学会了管理文件、学会了联网。但有一个问题始终没解决:AI说的话,用户怎么“看”?一个黑底白字的终端,如何区分“AI在思考”和“AI在回答”?如何让工具调用不喧宾夺主?这一篇,我们深入终端UI模块,看看如何让命令行里的AI“活”起来。

📌 从一个问题开始
想象一下这个场景:
你坐在电脑前,看着终端窗口。AI正在运行。
你看到一段文字在屏幕上滚动:
让我看看src目录下有什么。[TOOL] ls("src") [/TOOL][工具结果]: agent/ api/ config.js
这段文字里混着:
- AI的思考
- AI的正式回答
- 工具调用指令
- 工具执行结果
你能分清哪句是AI在想、哪句是AI在说、哪句是程序自动生成的?
如果分不清,那这个AI就“不好用”。
终端UI的核心问题:如何用有限的字符界面,让用户直观理解AI的“心智状态”?
这一篇,我们看CogitoAgent是怎么解决这个问题的。
一、终端的“语言”:ANSI转义码
1.1 问题:终端为什么默认只有黑白?
终端(Terminal)的本质是一个字符显示设备。它只能显示字符,没有图形界面。
在ANSI转义码出现之前,终端只能显示一种颜色(通常是绿色或白色)的字符。
但终端协议其实支持“指令”——特殊字符序列可以控制终端的行为:
- 改变文字颜色
- 移动光标位置
- 清屏
- 加粗、下划线
这些指令就是ANSI转义码。
1.2 ANSI转义码的格式
ANSI转义码的格式是:\x1b[参数m
\x1b是转义字符(ASCII 27)[是控制序列引导符参数是数字代码,用分号分隔m是“选择图形再现”命令
示例:
\x1b[31m 红色文字
\x1b[32m 绿色文字
\x1b[1m 加粗
\x1b[0m 重置所有样式
1.3 终端UI模块中的颜色定义
// src/io/terminal.js
const COLORS = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
gray: '\x1b[90m',
red: '\x1b[91m',
green: '\x1b[92m',
darkGreen: '\x1b[32m',
yellow: '\x1b[93m',
blue: '\x1b[94m',
magenta: '\x1b[95m',
cyan: '\x1b[96m',
white: '\x1b[97m',
bgBlack: '\x1b[40m',
bgBlue: '\x1b[44m',
bgCyan: '\x1b[46m',
};
这些颜色代码的命名是有规律的:
| 颜色 | 前景色(文字) | 背景色 |
|---|---|---|
| 黑色 | 30 | 40 |
| 红色 | 31 | 41 |
| 绿色 | 32 | 42 |
| 黄色 | 33 | 43 |
| 蓝色 | 34 | 44 |
| 品红 | 35 | 45 |
| 青色 | 36 | 46 |
| 白色 | 37 | 47 |
90-97 是亮色(bright)版本,比30-37更鲜艳。
1.4 让文字“带颜色”的封装
直接写 \x1b[31m红色文字\x1b[0m 非常繁琐,所以 terminal.js 封装了几个核心函数:
function print(text, color = null) {
if (color && COLORS[color]) {
process.stdout.write(COLORS[color] + text + COLORS.reset);
} else {
process.stdout.write(text);
}
}
function println(text, color = null) {
print(text, color);
process.stdout.write('\n');
}
使用示例:
println('这是一条错误信息', 'red');
println('这是一条提示信息', 'yellow');
println('这是一条调试信息', 'gray');
原理:println('错误', 'red') 实际输出的是 \x1b[31m错误\x1b[0m\n。终端收到 \x1b[31m 后,把后续文字显示为红色,直到收到 \x1b[0m 重置。
二、分区展示:思考区、内容区、工具区
2.1 问题:AI的回复分成三种“内容”
大模型返回的内容,实际上可以分成三种类型:
| 类型 | 内容 | 用户想看吗? | 应该怎么显示? |
|---|---|---|---|
| 思考过程 | reasoning_content |
想看(知道AI在想什么) | 低调,不干扰主内容 |
| 正式回复 | content |
必须看 | 醒目,是核心内容 |
| 工具调用 | [TOOL]...[/TOOL] |
可看可不看 | 缩进,灰色,不喧宾夺主 |
三种内容混在一起输出,用户无法区分。所以需要分区展示。

2.2 状态标签的设计
terminal.js 使用两个布尔标志来控制分区:
let reasoningTagPrinted = false; // 思考区的“门头”是否已打印
let contentTagPrinted = false; // 内容区的“门头”是否已打印
核心逻辑:每种内容的“门头”(比如 ┌─ 思考过程)只在第一次收到该类型内容时打印一次,后续内容直接追加,直到切换到另一种类型时才“关门”。
2.3 思考区的实现
function printReasoning(text) {
// 只有第一次收到思考内容时才打印门头
if (!reasoningTagPrinted) {
print('\n');
println('┌─ 思考过程 ─────────────────────────────', 'darkGreen');
reasoningTagPrinted = true;
}
print(text, 'darkGreen');
}
function closeReasoning() {
// 当思考内容结束时,打印底部边框
if (reasoningTagPrinted) {
print('\n');
println('└──────────────────────────────────────────', 'darkGreen');
reasoningTagPrinted = false;
}
}
效果:
┌─ 思考过程 ─────────────────────────────
用户让我查看src目录,我需要先ls一下
然后分析里面有什么关键文件
└──────────────────────────────────────────
思考过程用深绿色(darkGreen)显示,视觉上比正文暗一些,传递“这是AI内部思考,不是最终答案”的信号。
2.4 内容区的实现
function printContent(text) {
// 如果之前有思考区,先“关门”
if (!contentTagPrinted) {
if (reasoningTagPrinted) {
print('\n');
println('└──────────────────────────────────────────', 'dim');
resetReasoningTag();
}
print('\n');
println('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'cyan');
println(' ▼ 回复内容 ▼', 'bold');
println('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'cyan');
contentTagPrinted = true;
}
print(text);
}
效果:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
▼ 回复内容 ▼
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
我来看看src目录下有什么...
正文区域用青色分隔线包围,中间有一个醒目的 ▼ 回复内容 ▼ 标签。用户一眼就能识别:“这是AI要跟我说的话”。
2.5 工具调用区的实现
function printToolBlock(content, title = '工具调用') {
print('\n');
println(` ┌─── ${title} ────────────────────────`, 'dim');
const lines = content.split('\n');
for (const line of lines) {
println(` │ ${line}`, 'dim');
}
println(' └────────────────────────────────────────', 'dim');
}
效果:
┌─── 工具调用 ────────────────────────
│ [TOOL] ls("src") [/TOOL]
└────────────────────────────────────────
┌─── 工具结果 ────────────────────────
│ [目录]
│ agent/
│ api/
│ [文件]
│ config.js
└────────────────────────────────────────
工具调用区用灰色(dim)显示,并且整体缩进。视觉上它“靠后站”,告诉用户:“这是中间过程,不是最终答案。”

2.6 三种分区的视觉层次
思考区 → 深绿色,带边框,视觉上“暗”
内容区 → 正常颜色,带醒目分隔线,视觉上“亮”
工具调用区 → 灰色,缩进,视觉上“后退”
三种分区的视觉权重:内容区 > 思考区 > 工具调用区。
用户的视线会自然地被内容区吸引,思考区和工具调用区作为“背景信息”存在。
三、流式输出的“实时感”
3.1 流式输出 vs 一次性输出
大模型有两种输出方式:
一次性输出:等AI生成完所有内容,一次性显示。
(等待5-10秒)... 突然显示一大段文字
用户在这5-10秒里不知道AI在干嘛,体验很差。
流式输出:AI每生成一个token就立即显示。
用户发送消息
↓ 0.1秒后显示第一个字
↓ 0.2秒后显示第二个字
↓ ... 像打字机一样逐字出现
用户看到文字在“生长”,知道AI在工作,体验好。
3.2 CogitoAgent的流式实现
// src/api/client.js
async function* streamChat(messages) {
const response = await c.chat.completions.create({
messages,
model: m,
stream: true, // 关键:开启流式
// ...
});
for await (const chunk of response) {
const delta = chunk.choices[0].delta;
yield {
content: delta.content || null,
reasoning: delta.reasoning_content || null
};
}
}
关键点:
stream: true告诉API以流式返回for await循环逐块接收- 每个chunk包含
content(正文)或reasoning_content(思考) - 两个字段分别对应两种分区
3.3 思考过程的“实时展示”
// src/agent/Agent.js
for await (const chunk of streamChat(messages)) {
if (chunk.reasoning) {
printReasoning(chunk.reasoning); // 实时打印思考内容
}
if (chunk.content) {
fullResponse += chunk.content; // 累积正文
}
}
用户在AI“思考”的同时就能看到思考过程,而不是干等。
3.4 思考区“关门”的时机
thinkCycle 函数在流式输出结束后调用 closeReasoning():
// 收尾:关闭思考区标签
closeReasoning();
resetContentTag();
如果AI在思考过程中没有输出任何正式回答,思考区就会保持打开状态,然后在流结束时关闭。如果AI既有思考又有回答,思考区会在内容区开始打印时自动关闭(printContent 中会检查并关闭思考区)。
四、状态标签(Tag)与提示
4.1 状态标签的用途
在终端UI中,状态标签用来突出显示关键信息点:
function printTag(text, bgColor = 'bgBlue', textColor = 'white') {
const bg = COLORS[bgColor] || COLORS.bgBlack;
const tc = COLORS[textColor] || COLORS.white;
return `${bg}${tc} ${text} ${COLORS.reset}`;
}
使用:
println(' 活动范围: ' + printTag(getBasePath(), 'bgBlue'));
效果:活动范围: [ D:\my-project ](蓝色背景,白色文字,像一块标签)
状态标签让关键信息在界面中“跳出来”,用户扫一眼就能看到。
4.2 启动时的状态展示
// src/agent/Agent.js
printBanner();
printDivider('─', 'cyan');
println(' 活动范围: ' + printTag(tools.getBasePath(), 'bgBlue') +
' 思考间隔: ' + printTag(`${thoughtInterval / 1000}秒`, 'bgCyan'));
printDivider('─', 'cyan');
println(' 按 ' + printTag('Enter', 'bgBlue') + ' 打断思考,输入 ' +
printTag('exit', 'bgBlue') + ' 退出\n', 'gray');
效果:
██████╗ ██████╗ ██████╗ ██╗████████╗ ██████╗ █████╗ ██████╗ ███████╗...
CogitoAgent
╔══════════════════════════════════════════════════════════════╗
║ 持续思考的智能体 - 探索文件 · 联网搜索 · 自主学习 ║
╚══════════════════════════════════════════════════════════════╝
────────────────────────────────────────────────────────────────
活动范围: [ D:\my-project ] 思考间隔: [ 3秒 ]
────────────────────────────────────────────────────────────────
按 [ Enter ] 打断思考,输入 [ exit ] 退出
用户启动程序后,立刻知道:
- AI能访问哪些文件(活动范围)
- AI多久思考一次(思考间隔)
- 如何与AI交互(Enter打断、exit退出)
4.3 危险操作的确认提示
// Agent.js - executeTool
if (isConfirmEnabled() && isDangerousOperation(toolName)) {
println('');
printDivider('!', 'yellow');
println(` ⚠️ 危险操作请求: ${printTag(toolName, 'bgYellow')}`, 'yellow');
println(` 参数: ${args.join(', ')}`, 'yellow');
println(` 是否执行此操作? 输入 ${printTag('y', 'bgGreen')} 确认, ${printTag('n', 'bgRed')} 拒绝`, 'yellow');
printDivider('!', 'yellow');
println('');
// 等待用户确认...
}
效果:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
⚠️ 危险操作请求: [ executeCode ]
参数: rm -rf /, python
是否执行此操作? 输入 [ y ] 确认, [ n ] 拒绝
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
黄色分隔线 + 警告图标 + 高亮标签,让用户不可能错过这个确认提示。
4.4 分隔线函数
function printDivider(char = '─', color = 'gray') {
const width = process.stdout.columns || 80;
const colorCode = COLORS[color] || COLORS.reset;
console.log(colorCode + char.repeat(width) + COLORS.reset);
}
设计细节:
- 自适应终端宽度(
process.stdout.columns) - 可自定义分隔符(
─、=、!等) - 可自定义颜色
五、用户输入与交互
5.1 readline:Node.js的命令行交互基础
Node.js 的 readline 模块提供了命令行交互能力:
import readline from 'readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (input) => {
// 用户按Enter时触发
handleUserInput(input.trim());
});
关键点:
readline是事件驱动的:用户输入一行,触发line事件- 不会阻塞主线程,AI可以在等待输入的同时继续思考
rl.prompt()可以显示提示符(但CogitoAgent选择不显示,保持界面干净)
5.2 用户输入处理流程
// src/agent/Agent.js
function handleUserInput(input) {
// 1. 退出命令
if (input.toLowerCase() === 'exit') {
exit();
return;
}
// 2. 特殊命令(以 / 开头)
if (input.startsWith('/')) {
if (handleCommand(input)) return;
}
// 3. 危险操作确认(等待确认状态)
if (state.current === STATE.AWAITING_CONFIRMATION) {
if (input === 'y') {
resolveConfirmation(true);
} else if (input === 'n') {
resolveConfirmation(false);
}
return;
}
// 4. 正常消息输入
if (state.current === STATE.THINKING) {
// 用户打断AI思考
shouldStop = true;
clearTimeout(thinkingTimer);
state.current = STATE.AWAITING_INPUT;
} else if (state.current === STATE.AWAITING_INPUT) {
if (input) {
addUserMessage(input);
state.current = STATE.THINKING;
scheduleNextCycle();
}
}
}
5.3 “打断思考”的用户体验
当用户按 Enter 时,AI正在思考:
(AI正在流式输出思考内容)
用户:按 Enter
终端: [中断] 思考已停止,请输入消息...
用户:帮我看看src目录
关键设计:
shouldStop = true让thinkCycle在下一个chunk处退出clearTimeout(thinkingTimer)取消已调度的下一次思考- 状态切换到
AWAITING_INPUT,AI停止自主活动 - 用户输入消息后,状态切回
THINKING,AI恢复思考
5.4 退出清理机制
// src/io/terminal.js
async function exit() {
console.log('\n正在清理资源...');
if (cleanupCallback) {
try {
await cleanupCallback();
} catch (e) {
console.error('清理失败:', e.message);
}
}
if (rl) {
rl.close();
}
setTimeout(() => {
process.exit(0);
}, 100);
}
退出前执行清理回调(如关闭数据库连接、停止定时任务等),确保资源正确释放。
六、欢迎页面的设计
6.1 启动 Banner
printBanner 函数生成了一个 ASCII Art 标题:
function printBanner() {
const banner = `
${COLORS.cyan}${COLORS.bold}
██████╗ ██████╗ ██████╗ ██╗████████╗ ██████╗ █████╗ ██████╗ ███████╗...
${COLORS.reset}
${COLORS.cyan}${COLORS.bold} CogitoAgent${COLORS.reset}
${COLORS.bold}╔══════════════════════════════════════════════════════════════╗
║ 持续思考的智能体 - 探索文件 · 联网搜索 · 自主学习 ║
╚══════════════════════════════════════════════════════════════╝${COLORS.reset}
`;
console.log(banner);
}
视觉效果:
- 彩色大标题(青色 + 加粗)
- 产品名称居中
- 一个带边框的标语框
ASCII Art 是通过一个在线转换工具将文字转成字符画后粘贴到代码中的。虽然不是动态生成的,但效果稳定。
6.2 欢迎页面:设置向导的“第一印象”
在最新版本中,setup.js 也加入了艺术化设计:
function printSetupBanner() {
const banner = `
${COLORS.cyan}${COLORS.bold}
██████╗ ██████╗ ██████╗ ██╗████████╗ ██████╗ █████╗ ██████╗ ███████╗...
${COLORS.reset}
${COLORS.cyan}${COLORS.bold} CogitoAgent${COLORS.reset}
${COLORS.bold}╔══════════════════════════════════════════════════════════════╗
║ ${COLORS.magenta}🚀 设置向导${COLORS.reset}${COLORS.bold} - 让我来帮你完成初始配置 ║
╚══════════════════════════════════════════════════════════════╝${COLORS.reset}
`;
println(banner);
}
设置向导与主程序使用统一的设计语言,用户从首次配置到日常使用,界面风格一致。
6.3 步骤引导的艺术
function printStepTitle(stepNum, title) {
println('');
printDivider('─', 'cyan');
println(COLORS.cyan + COLORS.bold + ` 【第${stepNum}步】` +
COLORS.white + title + COLORS.reset);
printDivider('─', 'cyan');
}
效果:
────────────────────────────────────────────
【第1步】输入 API Base URL
────────────────────────────────────────────
💡 例如: https://api.openai.com/v1 或 https://api.moark.com/v1
请输入:
步骤标题 + 提示信息 + 输入提示,用户清楚地知道“我在哪一步”、“我该输入什么”。
6.4 完成画面
function printCompleteBanner() {
println('');
println(COLORS.green + COLORS.bold + '╔══════════════════════════════════════════════════════════════╗' + COLORS.reset);
println(COLORS.green + '║' + COLORS.reset + ' ' + COLORS.green + '║' + COLORS.reset);
println(COLORS.green + '║' + COLORS.reset + ' ' + COLORS.bold + COLORS.white + '🎉 设置完成!' + COLORS.reset + ' ' + COLORS.green + '║' + COLORS.reset);
println(COLORS.green + '║' + COLORS.reset + ' ' + COLORS.green + '║' + COLORS.reset);
println(COLORS.green + '║' + COLORS.reset + ' ' + COLORS.cyan + '重新运行程序即可开始使用 CogitoAgent' + COLORS.reset + ' ' + COLORS.green + '║' + COLORS.reset);
println(COLORS.green + '║' + COLORS.reset + ' ' + COLORS.green + '║' + COLORS.reset);
println(COLORS.green + COLORS.bold + '╚══════════════════════════════════════════════════════════════╝' + COLORS.reset);
println('');
}
效果:
╔══════════════════════════════════════════════════════════════╗
║ ║
║ 🎉 设置完成! ║
║ ║
║ 重新运行程序即可开始使用 CogitoAgent ║
║ ║
╚══════════════════════════════════════════════════════════════╝
这个“完成画面”用绿色边框包围,带有一个庆祝表情符号,传递“成功”的情绪信号。
七、设计原则总结
7.1 视觉层次原则
终端UI的设计遵循了明确的视觉层次:
| 层次 | 内容 | 视觉处理 |
|---|---|---|
| 最高 | 危险操作确认 | 黄色分隔线 + 警告图标 + 高亮标签 |
| 高 | AI正式回复 | 青色分隔线 + 居中标签 |
| 中 | AI思考过程 | 深绿色 + 边框 |
| 低 | 工具调用/结果 | 灰色 + 缩进 |
| 最低 | 系统提示 | 灰色普通文字 |
7.2 信息密度控制原则
终端界面的信息密度需要控制:
| 设计决策 | 目的 |
|---|---|
| 思考区“折叠” | 思考内容不干扰正文阅读 |
| 工具调用区缩进 | 中间过程不喧宾夺主 |
| 分隔线自适应宽度 | 在不同终端尺寸下保持一致 |
| 颜色限制 | 只用6-8种颜色,不乱 |
7.3 反馈即时性原则
| 场景 | 反馈方式 | 延迟 |
|---|---|---|
| AI开始思考 | 打印 ┌─ 思考过程 |
< 100ms |
| AI输出正文 | 打印 ▼ 回复内容 + 逐字输出 |
实时 |
| 工具执行 | 打印灰色小框 | 执行后立即 |
| 危险操作 | 黄色确认框 + 等待输入 | 立即 |
| 用户打断 | [中断] 思考已停止 |
立即 |
用户的每个操作都能得到即时视觉反馈,不会产生“我在按按钮,但不知道有没有生效”的困惑。
八、完整的交互流程演示
让我们把以上所有机制串联起来,看一个完整的交互流程:
用户启动程序:
██████╗ ██████╗ ██████╗ ██╗████████╗ ██████╗ ...
CogitoAgent
╔══════════════════════════════════════════════════════════════╗
║ 持续思考的智能体 - 探索文件 · 联网搜索 · 自主学习 ║
╚══════════════════════════════════════════════════════════════╝
────────────────────────────────────────────────────────────────
活动范围: [ D:\my-project ] 思考间隔: [ 3秒 ]
────────────────────────────────────────────────────────────────
按 [ Enter ] 打断思考,输入 [ exit ] 退出
AI开始自主思考:
┌─ 思考过程 ─────────────────────────────
我刚刚启动,需要先了解工作区结构。让我看看根目录下有什么。
└──────────────────────────────────────────
┌─── 工具调用 ────────────────────────
│ [TOOL] ls(".") [/TOOL]
└────────────────────────────────────────
┌─── 工具结果 ────────────────────────
│ [目录]
│ src/
│ docs/
│ [文件]
│ README.md
│ package.json
└────────────────────────────────────────
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
▼ 回复内容 ▼
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
我查看了工作区根目录,发现了一个 src/ 文件夹、docs/ 文件夹、
README.md 和 package.json。让我再深入看看 src/ 里有什么。
[WAIT]
[等待] 我先不说了,等你说~
用户按Enter打断,输入指令:
(用户按 Enter)
[中断] 思考已停止,请输入消息...
(用户输入 "帮我看看src目录")
[消息] 帮我看看src目录
AI继续思考:
┌─ 思考过程 ─────────────────────────────
用户想查看 src 目录,我来 ls 一下。
└──────────────────────────────────────────
┌─── 工具调用 ────────────────────────
│ [TOOL] ls("src") [/TOOL]
└────────────────────────────────────────
┌─── 工具结果 ────────────────────────
│ [目录]
│ agent/
│ api/
│ [文件]
│ config.js
│ setup.js
└────────────────────────────────────────
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
▼ 回复内容 ▼
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
src/ 目录下有三个子目录:agent/、api/,以及两个配置文件。你想深入了解哪个部分?
[WAIT]
[等待] 我先不说了,等你说~
用户输入 exit:
exit
正在清理资源...
九、设计启发
9.1 终端UI的三个层次
CogitoAgent 的终端UI设计可以抽象为三个层次:
| 层次 | 内容 | 实现方式 |
|---|---|---|
| 内容层 | AI的思考、回答、工具调用 | 分区展示 + 颜色标记 |
| 交互层 | 用户输入、打断、确认 | readline + 状态机 |
| 装饰层 | Banner、分隔线、标签 | ASCII字符 + ANSI颜色 |
9.2 为什么终端UI仍然重要?
在图形界面普及的今天,终端UI似乎“过时”了。但 CogitoAgent 选择终端界面,有它的道理:
- 跨平台:终端在所有操作系统上都能工作
- 轻量:不需要渲染HTML、CSS,启动快
- 专注:没有浏览器弹窗、通知等干扰
- 可控:开发者对每一行输出都有完全控制权
更重要的是:终端UI的设计思路可以迁移到任何界面。
思考区、内容区、工具区的分区逻辑,可以变成图形界面中的三个面板;颜色标记可以变成图标和字号;状态标签可以变成徽章和状态灯。
9.3 启发:让“中间状态”可视化
很多AI应用只展示最终答案,不展示中间过程。但 CogitoAgent 选择展示思考过程和工具调用。
为什么?
因为用户需要信任AI。如果AI只给结果不给过程,用户会怀疑“它是不是瞎编的”。展示思考过程和工具调用,让用户看到AI的“推理链条”,增强信任感。
这是一个重要的产品设计原则:在AI应用中,展示过程比展示结果更重要。
十、小结
| 设计要点 | 实现方式 | 解决的问题 |
|---|---|---|
| 分区展示 | printReasoning/printContent/printToolBlock |
区分思考/回答/工具 |
| 流式输出 | for await + 实时打印 |
避免用户等待 |
| 颜色标记 | ANSI转义码 + 颜色函数 | 传递语义信息 |
| 状态标签 | printTag |
突出关键信息 |
| 自适应分隔线 | process.stdout.columns |
适配不同终端 |
| 退出清理 | onCleanup回调 |
释放资源 |
| 危险确认 | 黄色分隔线 + 等待输入 | 防止误操作 |
| Banner | ASCII Art | 品牌识别 |
下一篇预告:记忆系统
我们将深入 memory.js,看看:
- 记忆如何持久化存储
- 标签分类和相关性评分
- 记忆搜索的模糊匹配算法
- 如何让AI“记住”和“回忆”
如果这篇文章对你有帮助,欢迎 ⭐Star 支持一下开源项目!
更多推荐




所有评论(0)