Skip to content

第八章 TUI 终端界面 — 双进程架构

Hermes 提供了一个基于 Ink(React for CLI)的富终端界面,通过 hermes --tui 启动。它采用双进程架构:Node.js 负责渲染 UI,Python 负责 Agent 逻辑。

8.1 架构概览

为什么用两个进程?

职责Node/Ink 擅长Python 擅长
终端渲染✅ Ink 组件化 UI
用户输入✅ prompt_toolkit 级体验
Agent 逻辑✅ AIAgent + 工具系统
会话管理✅ SQLite + FTS5
模型调用✅ OpenAI/Anthropic SDK

8.2 JSON-RPC 通信协议

两个进程通过 stdio 传输的 JSON-RPC 通信:

请求方法(Ink → Python)

方法说明
prompt.submit提交用户消息
session.list列出历史会话
session.resume恢复会话
slash.exec执行斜杠命令
approval.respond响应审批请求
clarify.respond响应澄清问题
complete.slash斜杠命令自动补全
complete.path路径自动补全

事件推送(Python → Ink)

事件说明
message.delta流式文本增量
message.complete消息完成
tool.start工具开始执行
tool.progress工具执行进度
tool.complete工具执行完成
approval.request请求用户审批
clarify.request请求澄清
gateway.ready后端就绪(附带皮肤、模型列表等)

8.3 Ink 前端关键组件

组件结构

ui-tui/src/
├── entry.tsx          # TTY 检测 + render()
├── app.tsx            # 主状态机和 UI
├── gatewayClient.ts   # JSON-RPC 客户端
├── app/
│   ├── eventHandler.ts    # 事件处理
│   ├── slashHandler.ts    # 斜杠命令
│   ├── stores/            # 状态管理
│   └── hooks/             # 自定义 Hooks
├── components/
│   ├── messageLine.tsx    # 消息行
│   ├── thinking.tsx       # 工具活动指示
│   ├── prompts.tsx        # 审批/澄清提示
│   └── branding.tsx       # 品牌元素
└── hooks/
    ├── useCompletion.ts   # 自动补全
    ├── useInputHistory.ts # 输入历史
    └── useQueue.ts        # 消息队列

App 状态机

app.tsx 是 TUI 的核心,管理着整个应用状态:

消息渲染管线

消息从 Python 后端到终端屏幕的过程:

message.delta (增量文本)
  → useMainApp hook 接收
  → 追加到 currentResponse 状态
  → messageLine.tsx 渲染
    → Markdown 解析(代码块高亮、链接、列表)
    → Ink <Text> 组件输出到终端

核心 Hook 详解

Hook作用实现原理
useCompletionTab 自动补全监听 Tab 键,发送 complete.slashcomplete.path RPC,渲染补全列表
useInputHistory上下箭头历史维护 history[] 数组和 historyIndex,↑↓ 切换时替换输入框内容
useQueue消息发送队列保证同一时间只有一个 prompt.submit 在处理,后续消息排队等待
useMainApp主状态管理合并所有 RPC 事件到统一的 React state,驱动 UI 更新

主题与皮肤集成

TUI 通过 gateway.ready 事件获取皮肤数据,同步 CLI 的视觉风格:

typescript
// gatewayClient.ts
socket.on('gateway.ready', (data) => {
  const { skin, models, sessions } = data
  setTheme(skin)  // 应用颜色、品牌名称等
  setModels(models)
  setSessions(sessions)
})

8.4 Python 后端 RPC 服务

8.4 Python 后端 RPC 服务

tui_gateway/server.py 负责处理所有 RPC 请求:

线程模型

  • 主线程:JSON-RPC 请求解析和分发(快速,不阻塞)
  • 线程池:处理 Agent 调用、会话操作等阻塞任务
  • SlashWorker:独立的 Python 子进程,运行斜杠命令(不阻塞聊天)

8.5 斜杠命令工作流

为什么用独立子进程?

  1. 隔离:斜杠命令可能很耗时(如 /skills browse),不阻塞主聊天
  2. 持久:SlashWorker 在会话期间持续运行,避免每次启动的开销
  3. 状态:维护独立的命令状态(如搜索上下文)

8.6 如何调试 TUI

开发模式

bash
# 前端热重载开发
cd ui-tui
npm run dev      # TypeScript watch + Ink 渲染

# 后端调试
HERMES_LOG_LEVEL=DEBUG hermes --tui

查看 RPC 通信

bash
# 开启 debug 后,RPC 消息会输出到 stderr
HERMES_DEBUG=1 hermes --tui 2>tui-debug.log

日志中可以看到完整的 JSON-RPC 请求和响应,方便排查前后端通信问题。

常见问题

问题原因解决
TUI 不渲染Node 版本过低需要 Node.js 18+
中文乱码终端编码设置 LANG=zh_CN.UTF-8
卡在 "Connecting..."Python 后端崩溃检查 HERMES_LOG_LEVEL=DEBUG 输出

源码导航

  • TUI 入口 → ui-tui/src/entry.tsx
  • 主应用 → ui-tui/src/app.tsx
  • JSON-RPC 客户端 → ui-tui/src/gatewayClient.ts
  • Python RPC 服务 → tui_gateway/server.py
  • 斜杠命令工作进程 → tui_gateway/slash_worker.py

下一章:第九章 子 Agent 与并行执行

基于 MIT 许可发布