Natural Language Tool State
背景
这条观点来自一张关于 Claude bash 命令风格的截图。核心说法是:
- Claude 生成 bash 命令时常会先写一行注释,再写实际命令。
- 它也常用
echo、&& echo "success" || echo "failed"这类方式,把 exit code 翻译成自然语言。 - 这些看似多余的文本,其实是在利用自然语言作为模型更容易消费的状态表达。
- 设计 agent tool use 时,可以让 tool input 先承载 description / intent,让 tool output 返回自然语言状态,而不是只返回裸数据。
考证结论
这个观点的方向是对的,但需要拆成三层来看。
第一层:bash 注释确实会被 shell 忽略,但会留在模型已经生成的上下文里。GNU Bash 手册说明,非交互 shell 里以 # 开头的词会让该词及本行后续字符被忽略。因此,如果模型先生成一行注释,再生成命令,注释不会改变 shell 执行,却会在模型自己的生成轨迹里先固定意图。
第二层:自然语言中间态对模型行动有帮助,这有论文和工程文档支持。Chain-of-Thought 说明中间推理步骤能提升复杂推理;ReAct 进一步把 reasoning trace 和 action 交错起来,让推理轨迹帮助模型跟踪、更新计划并处理异常。这能解释为什么“先用一句自然语言说清楚要做什么”常常能让后面的 tool call 更稳。
第三层:tool description 和 tool output 的自然语言质量确实影响模型表现。Anthropic 文档明确说,工具 description 要解释工具做什么、何时使用、参数含义和限制,并称详细 description 是影响 tool performance 的重要因素。OpenAI function calling 文档也说明工具定义会注入到模型上下文里,function output 通常以字符串返回,格式可以是 JSON、error code 或 plain text,模型会按需要解释。
所以,更准确的说法不是“自然语言一定比结构化数据更高级”,而是:
对 LLM 来说,结构化状态负责可执行性,自然语言状态负责可解释性和下一步行动先验。
需要校正的点
截图里提到 OpenCode bash tool 有 description 参数。这个例子需要校正。
我查了 opencode-ai/opencode 当前源码(2026-05-22,commit 73ee493)和 README:内置 bash tool 的参数是 command 和 timeout,没有独立的 per-call description 字段。它确实有一个很长的工具级 description,并且 command 参数本身也有 description;系统 prompt 还要求模型在运行非平凡 bash 命令前说明命令做什么、为什么运行。
这说明 OpenCode 的真实设计更接近:
- 在工具 schema / tool description 里提前塞足语义。
- 在 assistant 正文里让模型说明非平凡命令的目的。
- bash tool input 本身仍保持
command为主。
如果我们自己设计 custom bash tool,仍然可以加入 description / intent 字段,但不能把这说成当前 OpenCode 内置 bash tool 的既有事实。
设计建议
Tool input
对高风险、长耗时、可变更系统状态的工具,建议在 tool input 里显式加入 intent 或 description 字段:
{
"intent": "检查 dev server 是否被 3000 端口占用,并找出占用进程",
"command": "lsof -i :3000"
}
这个字段的作用不是给 shell 看,而是让模型在生成 command 前先把动作意图压成一句稳定的自然语言。它相当于 tool-call 层面的 mini ReAct thought,但更适合被 runtime 记录、审计、审批。
Tool output
不要只返回:
{"exit_code": 1}
更好的返回是结构化状态加自然语言摘要:
{
"status": "failed",
"exit_code": 1,
"summary": "部署失败:3000 端口已被 nginx(pid 8432) 占用。",
"suggested_next_steps": ["改用其他端口", "确认后停止占用进程"]
}
这样 runtime 仍能依赖 status / exit_code 做确定性控制,模型也能从 summary 和 suggested_next_steps 里直接得到下一步行动的高概率路径。
Bash echo 模式的坑
cmd && echo "success" || echo "failed" 对模型友好,但对 runtime 不一定友好。因为最后执行的是 echo,整个 shell 命令的最终 exit code 可能变成 0,从而掩盖原命令失败。
更稳的做法是由 tool wrapper 捕获原始 exit code,然后同时返回:
- 原始
stdout/stderr - 原始
exit_code - 归一化
status - 面向模型的自然语言
summary - 可选
suggested_next_steps
也就是说,不要用 echo 去替代状态机;要让 wrapper 把机器状态翻译成人类/模型都能读的状态。
可发展的文章角度
- 标题方向:
为什么 Agent Tool Output 不该只返回 exit_code - 主线问题:工具结果到底是机器状态,还是给模型继续生成的上下文?
- 关键对照:裸状态码、自然语言摘要、结构化 diagnosis 三者各自服务谁
- 可接到已有方向:tool call role design、Trace Log、multi-agent veto boundary
- 结尾落点:好的 tool result 应该同时能被程序判断、被人审计、被模型继续行动
参考来源
- GNU Bash Manual, Comments
- Wei et al., Chain-of-Thought Prompting Elicits Reasoning in Large Language Models
- Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models
- Anthropic Claude Docs, Define tools
- Anthropic Claude Docs, Handle tool calls
- Anthropic Claude Docs, Bash tool
- OpenAI API Docs, Function calling
- Claude Code Docs, Hooks reference
- OpenCode, Tools docs
- OpenCode source checked locally from
opencode-ai/opencodecommit73ee493, especiallyinternal/llm/tools/bash.goandinternal/llm/prompt/coder.go