跳转到主要内容
SimpleLLMFunc 提供开箱即用的 @tui 装饰器,基于 textual 和事件流构建。你可以把它直接叠加在 @llm_chat 上,让 Agent 具备完整的终端输入循环、流式渲染和工具调用可视化能力。
@tui 依赖事件流。请在 @llm_chat 中设置 enable_event=True,并建议同时开启 stream=True 以获得更好的流式渲染体验。

快速开始

1

安装依赖

textual 已作为框架依赖提供。如果你是在已有环境中升级,请重新安装依赖:
poetry install
2

叠加 @tui 和 @llm_chat

from SimpleLLMFunc import llm_chat, tui

@tui()
@llm_chat(
    llm_interface=llm,
    toolkit=[...],
    stream=True,
    enable_event=True,
)
async def agent(message: str, history=None):
    """Your agent prompt."""

if __name__ == "__main__":
    agent()
3

运行并进入聊天循环

直接执行脚本即可启动 TUI、输入循环和事件流渲染。

参数识别规则

@tui 会自动识别输入参数:
  • historychat_history 作为历史参数
  • 其余第一个参数作为用户输入

UI 能力

  • 用户消息与模型消息交替渲染
  • 模型流式输出实时刷新,并支持 Markdown 渲染
  • 流式输出期间消息区自动跟随到底部
  • reasoning delta 会在模型支持时以灰色文本展示
  • 工具调用开始时展示结构化参数,而不是裸 JSON 字符串
  • 工具执行期间可消费 CustomEvent 并实时更新输出
  • 工具结束后展示结果、耗时和状态
  • 当工具触发 input() 时,输入框会切换为工具输入模式
  • 新输入会优先回填给该工具请求
  • fork 任务会基于 origin.fork_id 自动拆分为独立列
  • 主链路与子链路事件可以稳定分流显示

中断当前回复

当 Agent 仍在生成回复时再次发送消息,TUI 会自动触发中断并开启新回合:
  • 当前回合会被终止,停止流式输出并取消工具调用
  • 新消息会自动加上中断提示语:"我要打断你的回复。"
这适合快速纠偏或打断长回复。如果你需要更细粒度的控制,可手动传入 AbortSignal。详见 中断与取消

交互与退出

  • 发送消息:输入后按 Enter
  • 当存在待处理的工具输入请求时,Enter 会优先把输入提交给该请求
  • 强制发送新一轮聊天:/chat <message>
  • 复制完整转录:/copyCtrl+Y
  • 退出命令:/exit/quit/q
  • 快捷键退出:Ctrl+Q,同时保留 Ctrl+C

自定义 Tool 事件 Hook

@tui 支持通过 custom_event_hook 注入自定义事件解析逻辑:
from SimpleLLMFunc.hooks.events import CustomEvent
from SimpleLLMFunc.utils.tui import ToolEventRenderUpdate, ToolRenderSnapshot


def my_hook(
    event: CustomEvent,
    snapshot: ToolRenderSnapshot,
) -> ToolEventRenderUpdate | None:
    if event.event_name != "batch_progress" or not isinstance(event.data, dict):
        return None

    return ToolEventRenderUpdate(
        append_output=f"progress={event.data['percent']}%\n"
    )


@tui(custom_event_hook=[my_hook])
@llm_chat(..., enable_event=True, stream=True)
async def agent(message: str, history=None):
    ...
框架内置了部分常见工具事件的默认解析,例如 PyRepl 的 kernel_stdoutkernel_stderrkernel_input_request

运行示例

参考示例:examples/tui_chat_example.py