从零实现 GeekAgent —— Day7 轻量 TUI 与用量面板
内容提要
本文介绍GeekAgent开发第七天,实现轻量TUI界面与用量面板。通过引入pi-tui库,程序接管终端,左侧显示消息流,右侧常驻面板展示模型、会话、上下文占用及本轮/累计tokens。接口回传usage数据,流式期间用字符估算。支持输入编辑、工具确认,退出恢复原终端。代码新增1115行,验证功能正常,为后续权限管理打基础。
延伸解读
为什么需要常驻面板
前六天的终端只有滚动日志,tokens 花销和上下文占用都是“暗账”。本文指出,流式接口其实可以在请求末尾回传 usage,只要带上 stream_options: { include_usage: true },但此前从未使用。常驻面板让这些数据一直可见,用户可以在上下文接近窗口上限时主动 /compact,而不是等模型变慢或失败。
引入 UI 库的取舍
这是项目第一次引入 UI 依赖,原因是自己实现终端界面要处理中英文宽度、输入与刷新冲突、全屏切换恢复等复杂问题。选择 pi-tui 换来类型安全和维护,但代价是依赖体积变大,且内置了暂时用不到的 Markdown 组件。作者明确说明该库只被 tui.ts 使用,不会渗入核心逻辑,体现了模块化设计。
tokens 数据的双重来源
面板上的 tokens 数字并非单一来源:本轮和累计以接口回传的 usage 为准,但流式期间拿不到真实值,先用字符估算顶替,真实值一到立即覆盖。上下文占用则完全依赖估算,因为 history 序列化后按字符估算,压缩后分子回落,能直观看到 /compact 的效果。作者强调“真金白银的数字永远看接口”,估算只用于参考。
明确未做的功能
文章列出了 Day 7 明确没做的三件事:管道/重定向时直接退出,不降级为纯文本模式;输入框不支持历史浏览,主区只保留最近 1000 行;模型输出的 Markdown 不渲染,原样显示。这些限制为后续开发留出空间,也提醒读者当前版本并非完整产品,而是迭代过程中的一个阶段。
Q&A
GeekAgent Day7 为什么需要引入 TUI 界面?
因为前六天的终端只有日志流,无法常驻显示本轮/累计 tokens 和上下文占用比例,而这两项数据对控制成本和主动压缩很重要。TUI 提供了一块固定的屏幕区域来展示这些信息,也为后续 todos、用量、记忆等展示型能力打下基础。
GeekAgent 的 TUI 界面布局是怎样的?
TUI 采用双栏布局:左侧是滚动消息流,右侧是常驻面板(宽度 28 列),底部是输入行。终端宽度不足 60 列时自动隐藏右侧面板。面板显示模型、会话、上下文占用(token 数和百分比)、本轮 tokens 和累计 tokens。
GeekAgent 如何获取和估算 token 用量?
真实 token 用量来自接口回传的 usage 字段,流式请求需带上 stream_options: { include_usage: true },最后一个 chunk 会带 usage;非流式请求也会回传。流式期间拿不到真实值,用 estimateTokens 按字符估算:中文 1 字约 1 token,英文约 4 字符 1 token,混合取 2 字符 1 token。真实值到达后立即覆盖估算值。
GeekAgent 的 TUI 如何支持工具确认?
工具执行需要确认时,输入行提示符从 'You › ' 变为 '[y/N] ',用户输入 y 或 n 回车后,确认结果通过 promise 交还给等待中的工具调用,并在主区记录一行黄色日志,如 '问题 → y'。
GeekAgent Day7 的代码量变化和新增文件是什么?
Day7 共 8 个源文件 1115 行,比 Day6 的 820 行净增 295 行。新增 tui.ts(216 行),index.ts 从 151 行增至 203 行,chat.ts 增加 27 行。
GeekAgent 的 TUI 如何实现流式输出的平滑显示?
流式输出时,模型每次吐出一两个词,使用 appendInline 方法将增量追加到上一行同色文本末尾,避免回答被拆成碎片;当遇到工具调用或历史压缩等不同颜色时另起新行。同时 ScrollView 的 follow: 'end' 让输出始终跟随底部。
GeekAgent Day7 明确没有实现哪些功能?
没有实现管道/重定向降级(直接退出)、历史浏览(不能用 ↑ 找回上一条输入,主区只保留最近 1000 行)、markdown 渲染(模型输出仍带 ** 和反引号)。
GeekAgent 的下一步计划是什么?
下一步将实现权限模型与目录隔离,把哪些工具免确认、哪些必须问、哪些禁止,以及文件访问的根目录,配置到第一份配置文件中。