AIHero
    阅读约 3 分钟

    如何在 AFK Ralph 中流式查看 Claude Code 输出

    运行 AFK Ralph 时实时流式查看 Claude Code。学习使用 jq 获取 Claude 输出,而不是盯着空白屏幕。

    Matt Pocock
    Matt Pocock
    本页目录

    本指南面向已经尝试用 Claude Code 运行 Ralph、却遇到这一恼人问题的人:运行 AFK 脚本后,只能盯着空白屏幕。

    如果从未听说过 Ralph,请从这里开始:

    问题:空白屏幕

    如果希望离开键盘后 Ralph 继续运行,可能会使用下面这样的脚本:

    #!/bin/bash
    set -e
    if [ -z "$1" ]; then
    echo "Usage: $0 <iterations>"
    exit 1
    fi
    for ((i=1; i<=$1; i++)); do
    result=$(docker sandbox run --credentials host claude \
    --print \
    "<your prompt here>")
    if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
    echo "Ralph complete after $i iterations."
    exit 0
    fi
    done

    令人沮丧的问题是:使用 --print 标记运行 Claude 时,完全没有流式输出,终端一片空白。

    离开后,你完全不知道发生了什么:Claude 在工作吗?卡住了吗?是不是出了问题?只有结束时才会知道。

    AFK Ralph 的理想状态是兼得两者:既能实时看到 Claude 在做什么,又能在离开时让它继续运行。

    解决方案:使用 jq 流式输出

    Claude 可以输出 stream-json 格式,实时提供每一条消息,但输出极其冗长,难以阅读。

    通过组合 stream-json with jq 过滤,可以只提取有用信息并实时流式显示到终端,同时捕获最终结果,以检查 <promise>COMPLETE</promise> 标记。

    下面是完整脚本:

    #!/bin/bash
    set -e
    if [ -z "$1" ]; then
    echo "Usage: $0 <iterations>"
    exit 1
    fi
    # jq filter to extract streaming text from assistant messages
    stream_text='select(.type == "assistant").message.content[]? | select(.type == "text").text // empty | gsub("\n"; "\r\n") | . + "\r\n\n"'
    # jq filter to extract final result
    final_result='select(.type == "result").result // empty'
    for ((i=1; i<=$1; i++)); do
    tmpfile=$(mktemp)
    trap "rm -f $tmpfile" EXIT
    docker sandbox run --credentials host claude \
    --verbose \
    --print \
    --output-format stream-json \
    "<your prompt here>" \
    | grep --line-buffered '^{' \
    | tee "$tmpfile" \
    | jq --unbuffered -rj "$stream_text"
    result=$(jq -r "$final_result" "$tmpfile")
    if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
    echo "Ralph complete after $i iterations."
    exit 0
    fi
    done

    脚本接收一个参数:要运行的迭代次数。

    逐步解析脚本结构

    拆解流过滤器

    流过滤器会执行几项重要操作:

    1. 选择 assistant 消息: select(.type == "assistant") 只获取 Claude 的响应
    2. 提取文本内容: .message.content[]? | select(.type == "text").text 只取出文本部分
    3. 修复行尾: gsub("\n"; "\r\n") 把换行替换为回车加换行
    4. 添加间距: . + "\r\n\n" 在消息之间插入额外空白

    替换回车可以修复光标无法正确返回行首字符的问题。

    数据管道

    数据在脚本中的流动方式如下:

    Docker 会流式输出 stream-json 格式的数据,但其中还夹杂一些无意义的非 JSON 行。 grep --line-buffered '^{' 过滤器可以确保只处理有效 JSON 行。

    随后, tee "$tmpfile" 命令会在不中断数据流的情况下,把全部内容写入临时文件。稍后需要用该文件检查 Claude 是否完成。

    最后, jq --unbuffered -rj "$stream_text" 应用流过滤器,并在终端中实时显示文本。

    总结

    我希望不久后就能删除本文,因为 Claude Code 会推出一项功能,在流式显示响应的同时仍能捕获最终输出。

    OpenCode 已经支持这项能力,因此不需要为 OpenCode 写类似文章。在 Claude Code 支持之前,这仍是运行 AFK Ralph 时实时查看 Claude 流式输出的可行方案。