跳转到内容

03 - TUI 终端界面架构

学习目标

本章聚焦 Hermes Agent 的 TUI 终端界面架构。你将了解:

  • TypeScript Ink 前端与 Python JSON-RPC 后端的进程分离
  • 换行分隔的 JSON-RPC over stdio 传输协议
  • Slash 命令的三级分发流
  • Dashboard 的 PTY 桥接与 xterm.js 渲染

前置知识

无特定前置知识。


项目实践

进程分离架构

Hermes TUI 采用前后端进程分离架构:

职责划分

  • TypeScript(Ink):控制屏幕渲染、用户输入、动画效果、补全
  • Python(tui_gateway):会话管理、工具调用、模型交互、审批处理

这种分离使得前端可以使用 React 生态(组件、状态管理),后端保持 Python 的 Agent 运行时。

传输层:换行分隔的 JSON-RPC over stdio

tui_gateway/transport.py 实现 stdio JSON-RPC 传输层:

  • 每条 JSON-RPC 消息以换行符(\n)分隔
  • 双向通信:Node → Python(用户输入、命令)和 Python → Node(流式输出、工具活动)
  • 消息格式:标准 JSON-RPC 2.0(method、params、id、result)

tui_gateway/ 目录(8 个文件):

文件职责
server.pyTUI 网关服务器主入口
transport.pystdio JSON-RPC 传输层
event_publisher.py事件发布到前端
render.py渲染数据格式化
slash_worker.pySlash 命令子进程
ws.pyWebSocket 备用传输
entry.py进程入口点

Slash 命令三级分发流

TUI 中的 Slash 命令(如 /skin/reset/session)经过三级分发:

  1. 本地处理:简单命令(如清屏)由前端直接执行
  2. slash.exec:大部分命令通过 slash.exec 分发到后端
  3. _SlashWorker 子进程:重型命令在独立子进程中执行,不阻塞主进程
  4. command.dispatch 回退:未匹配的命令回退到通用分发器

Dashboard:PTY 桥接 + xterm.js

hermes dashboard 命令启动 Dashboard 界面:

  • PTY 桥接:后端创建伪终端,前端通过 JSON-RPC 获取终端输出
  • xterm.js WebGL 渲染器:前端使用 xterm.js 的 WebGL 加速器渲染终端输出
  • 支持实时日志流、Agent 活动监控

开发命令

命令功能
npm run devwatch 模式开发,自动重新构建
npm run build完整构建,生产打包

问题与规避

stdio JSON-RPC 消息边界

问题:如果 JSON 消息中包含换行符(如字符串值中的 \n),会导致消息解析错误。

对策:每条消息序列化为单行 JSON,字符串中的换行符被转义为 \n。接收端按行分割即可得到完整消息。

前后端版本不匹配

问题:前端期望的 JSON-RPC 格式与后端输出的格式不一致。

对策:JSON-RPC 2.0 是标准协议,前后端都遵循相同的 method/params/id/result 格式。新增方法时向后兼容。


设计取舍

React TUI + Python 后端 vs 纯 Python TUI

方案优势代价
React TUI + Python 后端(Hermes)前端可用 React 生态、UI 更丰富、状态管理成熟需要 Node.js 运行时、进程间通信开销
纯 Python TUI(Rich/Textual)单进程、部署简单UI 组件有限、动画能力弱

Hermes 选择 React TUI 是因为其目标用户需要丰富的交互体验(审批弹窗、会话选择器、实时流式输出),这在纯 Python TUI 中难以实现。


参考来源

  • 源码验证:ui-tui/package.jsontui_gateway/ 目录、AGENTS.md TUI 章节