10 · TUI、RPC 与服务化边界¶
10.1 界面不应该重新实现 Agent 逻辑¶
Pi 的 interactive、print、JSON、RPC 都使用相同 AgentSession 机制。差异在输入、显示和控制协议。自己的网页也应该接会话事件,不在前端另写一个「看到 toolCall 就执行」循环。
10.2 TUI 的设计¶
pi-tui Component 的核心 render(width): string[] 输出已排版行,handleInput 处理输入,invalidate 使缓存失效。Container 组合子组件。
终端没有浏览器 DOM。全量清屏重绘会闪烁,也浪费输出;differential rendering 比较当前与历史行,只重绘变化段。文本宽度还要处理中文、emoji、ANSI escapes、图片占位、换行和光标。
tui.ts 共享输入、焦点、overlay 和组件逻辑;tui-main-screen.ts 与 tui-alt-screen.ts 处理普通屏幕和全屏模式的不同渲染边界;terminal.ts 管理真正 stdin/stdout 和终端协议。
为什么业务和 TUI 要分开:同一次模型流可以驱动终端或 Web UI,而不会改变工具执行。你的自有 Agent 不需要写 TUI 才能使用 Pi。
10.3 流式 UI 需要区别状态¶
| 状态 | 显示建议 |
|---|---|
| assistant partial | 追加文本,不作为已完成证据 |
| tool start | 工具名称和安全可展示参数 |
| tool update | 暂时输出和进展 |
| tool end | 成功 / 失败,并标结果 |
| auto retry | 显示重试,避免看起来重复提交 |
| compaction | 显示上下文整理而非业务完成 |
| agent_settled | 可提示完成,允许下一次普通输入 |
UI 日志要脱敏;原始工具参数可能包含密钥、个人资料和文件内容。不要默认把所有 provider payload 显示给其他租户。
10.4 CLI RPC 是 JSONL,不是 HTTP¶
启动:
stdin 每行一个 command JSON;stdout 有 response 和异步 session events;stderr 是诊断。
response 按 id 对应请求,不能按出现顺序猜。prompt success 的 disposition 只说明 started / queued / handled,不等于任务完成。handled 没有启动模型运行,不应等一个永远不会到的 agent_settled。
例如普通 prompt 的响应为:
started 说明通过前置处理且已开始运行,queued 说明已排队,handled 说明扩展/input 直接消费;后者可能没有模型运行。应在发送 prompt 之前注册事件监听,再结合 disposition 管理等待。官方 RpcClient.promptAndWait() 并不替你处理 handled 的全部完成语义,可能被扩展直接消费的输入应使用有 disposition 分支的宿主流程。
stdout 必须持续读取并处理 backpressure,stdin 写也要尊重 drain。JSON 字符串可以包含 U+2028 / U+2029,不能用会把这些字符当换行的 generic readline 方式分帧;正确按 LF 字节切分。
10.5 Python 等语言怎么接¶
用 subprocess 启动 Pi,独立线程/异步任务持续读 stdout,按 request ID 管理 futures,收 message_update 驱动界面。关闭 stdin 请求有序退出,必要时宿主 deadline 中止进程。
别把 shell 脚本 echo task | pi 当等价 RPC client;它没有请求相关、队列、错误或完成语义。TypeScript 子进程客户端可以用官方 RpcClient;同进程应用优先 SDK。
10.6 pi-protocol / client / server 是另一套实验机制¶
这些包不是 CLI JSONL RPC 的换名。1.0 中 pi-protocol 的 PROTOCOL_VERSION 为 8,使用四字节 big-endian 长度 + definite-length CBOR item。
路由区分:
- server target:serverId。
- session target:serverId + sessionId + attachmentId。
- requests / responses 相关。
- cancellation、subscription updates、attachment change 分开。
Session ID 指持久对象,attachment ID 指一次 live presentation capability。客户端断开后旧 attachment 不能继续控制新附件,这避免 stale UI 路由到错误会话。
10.7 协议只校验 envelope,业务由 Chord 与应用校验¶
Pi protocol 保证 strict JSON 的 opaque payload;Chord 管 serviceId / member / args、服务目录、状态 snapshot 和 delta;应用决定服务业务含义。
对连接粘包和分片,decoder 接受 arbitrary chunks,不能假设一次 socket data 就是一帧。默认 payload 限制 16 MiB,嵌套层数 64,容器项数量也有限制。
这是一种职责拆分:帧解码不懂「删除哪个会话」;路由器不加载你的业务 facet contract;服务和应用执行语义检查。
10.8 不是可以直接暴露公网的已认证服务¶
protocol 和 server 文档明确声明实验 transport 未实现 peer authentication。Unix socket 也需要目录权限与调用者约束。不要直接挂 Nginx 然后把它描述为多用户 SaaS 后台。
如果你要自己的 Web 产品,先用稳定 SDK,宿主实现登录、会话 ownership、queue、SSE/WebSocket 状态推送和工具 policy。实验 server 适合继续研究,不作为本课程直接部署的 Agent 服务。
本次部署的是静态课程网站,浏览者不能通过它触发服务器 Agent 或 shell。