声明

英语不是我的母语。这篇文章最初用中文写成,再借助 AI 翻译为英文。行文可能带有些许 AI 味,但其中的设计决策、生产环境的踩坑故事、以及从中提炼出的原则——都是我自己的。

我曾是 Manus 被 Meta 收购前的后端负责人。过去两年,我一直在构建 AI 智能体——先是在 Manus,后来转向我自己的开源智能体运行时 (Pinix) 和智能体项目 (agent-clip)。一路走来,我得出了一个连自己都感到意外的结论:

**一个 `run(command="...")` 工具配合 Unix 风格的命令,效果优于一整套类型化的函数调用。**

以下是我的所学所悟。


为什么选择 *nix

Unix 在 50 年前做了一个设计决策:一切皆文本流。 程序之间不交换复杂的二进制结构,也不共享内存对象——它们通过文本管道通信。每个小工具专注做好一件事,再通过 | 组合成强大的工作流。程序用 --help 描述自身,用退出码报告成败,用 stderr 传递错误信息。

LLM 在 50 年后做了一个几乎一模一样的决策:一切皆 token。 它们只理解文本,只生成文本。它们的”思考”是文本,它们的”行动”是文本,它们从世界接收到的反馈也必须是文本。

这两个决策相隔半个世纪、出发点截然不同,却殊途同归,汇聚到了同一种接口模型。Unix 为人类终端操作者设计的那套文本系统——catgrep、管道、退出码、man pages——对 LLM 来说不仅仅是”能用”,而是天然契合。在工具调用这件事上,LLM 本质上就是一个终端操作者——一个比任何人类都快、并且在训练数据中已经见过海量 shell 命令和 CLI 模式的终端操作者。

这就是 *nix Agent 的核心理念:不要发明新的工具接口。把 Unix 经过 50 年验证的东西,直接交给 LLM。


为什么只用一个 run

单工具假说

大多数智能体框架会给 LLM 提供一整套独立的工具目录:

tools: [search_web, read_file, write_file, run_code, send_email, ...]

每次调用之前,LLM 都必须做一次工具选择——用哪个?传什么参数?工具越多,选择就越难,准确率也随之下降。认知负担全花在了”用哪个工具?“上,而不是”我要完成什么任务?”

我的做法是:一个 run(command="...") 工具,所有能力都以 CLI 命令的形式暴露出来。

run(command="cat notes.md")
run(command="cat log.txt | grep ERROR | wc -l")
run(command="see screenshot.png")
run(command="memory search 'deployment issue'")
run(command="clip sandbox bash 'python3 analyze.py'")

LLM 仍然需要选择使用哪条命令,但这与在 15 个各有不同 schema 的工具之间做选择,有着本质区别。命令选择是在一个统一命名空间内的字符串组合——而函数选择则是在一堆毫不相关的 API 之间做上下文切换。

LLM 天生就会说 CLI

为什么 CLI 命令比结构化的函数调用更适合 LLM?

因为 CLI 是 LLM 训练数据中密度最高的工具使用模式。GitHub 上数十亿行内容里到处都是:

# README 安装说明
pip install -r requirements.txt && python main.py
 
# CI/CD 构建脚本
make build && make test && make deploy
 
# Stack Overflow 解决方案
cat /var/log/syslog | grep "Out of memory" | tail -20

我根本不需要教 LLM 怎么用 CLI——它早就会了。 当然,这种熟悉度因模型而异,但在实践中,主流模型的表现都出奇地稳定。

来比较一下同一个任务的两种做法:

Task: Read a log file, count the error lines
 
Function-calling approach (3 tool calls):
  1. read_file(path="/var/log/app.log") → returns entire file
  2. search_text(text=<entire file>, pattern="ERROR") → returns matching lines
  3. count_lines(text=<matched lines>) → returns number
 
CLI approach (1 tool call):
  run(command="cat /var/log/app.log | grep ERROR | wc -l")
  → "42"

一次调用替代三次。不是因为做了什么特殊优化——而是因为 Unix 管道天然支持组合。

让管道和链式调用跑起来

光有一个 run 还不够。如果 run 一次只能执行一条命令,LLM 在处理组合任务时仍然需要多次调用。所以我在命令路由层实现了一个链式解析器parseChain),支持四种 Unix 操作符:

操作符名称语义
|Pipe前一条命令的 stdout 作为下一条的 stdin
&&And前一条成功才执行下一条
||Or前一条失败才执行下一条
;Seq无论前一条结果如何都执行下一条

有了这套机制,每次工具调用都可以是一个完整的工作流

# 一次工具调用:下载 → 检查
curl -sL $URL -o data.csv && cat data.csv | head 5
 
# 一次工具调用:读取 → 过滤 → 排序 → 取前 10 条
cat access.log | grep "500" | sort | head 10
 
# 一次工具调用:尝试 A,失败则回退到 B
cat config.yaml || echo "config not found, using defaults"

N 条命令 × 4 种操作符——组合空间急剧膨胀。而对 LLM 来说,这不过是一段它早就知道怎么写的字符串。

核心洞察

命令行就是 LLM 的原生工具接口。


启发式设计:让 CLI 引导智能体

单工具 + CLI 解决了”用什么”的问题。但智能体仍然需要知道**“怎么用”**。它不能 Google,也没法问同事。我采用了三种渐进式设计技巧,让 CLI 本身成为智能体的导航系统。

技巧 1:渐进式 —help 发现

一个设计良好的 CLI 工具不需要你去翻文档——因为 --help 会告诉你一切。我把同样的原则应用到智能体上,将其结构化为渐进式展示:智能体不需要一次性加载所有文档,而是在深入探索的过程中按需发现细节。

Level 0:工具描述 → 命令列表注入

run 工具的描述在每次对话开始时动态生成,列出所有已注册的命令及其一行摘要:

Available commands:
  cat    — Read a text file. For images use 'see'. For binary use 'cat -b'.
  see    — View an image (auto-attaches to vision)
  ls     — List files in current topic
  write  — Write file. Usage: write <path> [content] or stdin
  grep   — Filter lines matching a pattern (supports -i, -v, -c)
  memory — Search or manage memory
  clip   — Operate external environments (sandboxes, services)
  ...

智能体从第一轮对话就知道有哪些可用命令,但不需要了解每个命令的每个参数——那纯属浪费上下文。

开放设计问题

注入完整命令列表 vs. 按需发现。随着命令数量增长,列表本身就会消耗上下文预算。我仍在探索合适的平衡点。欢迎提供想法。

Level 1: command(无参数)→ 用法说明

当智能体对某个命令感兴趣时,直接调用就行。没有参数?命令会返回自身的用法说明:

→ run(command="memory")
[error] memory: usage: memory search|recent|store|facts|forget
 
→ run(command="clip")
  clip list                              — list available clips
  clip <name>                            — show clip details and commands
  clip <name> <command> [args...]         — invoke a command
  clip <name> pull <remote-path> [name]   — pull file from clip to local
  clip <name> push <local-path> <remote>  — push local file to clip

现在智能体知道 memory 有五个子命令,clip 支持 list/pull/push。一次调用,毫无噪声。

Level 2: command subcommand(缺少参数)→ 具体参数

智能体决定使用 memory search,但不确定格式?继续深入:

→ run(command="memory search")
[error] memory: usage: memory search <query> [-t topic_id] [-k keyword]
 
→ run(command="clip sandbox")
  Clip: sandbox
  Commands:
    clip sandbox bash <script>
    clip sandbox read <path>
    clip sandbox write <path>
  File transfer:
    clip sandbox pull <remote-path> [local-name]
    clip sandbox push <local-path> <remote-path>

渐进式展示:概览(注入)→ 用法(探索)→ 参数(深入)。 智能体按需发现,每一层只提供刚好足够进入下一步的信息。

这与把 3000 词的工具文档塞进系统提示词有本质区别。那些信息大部分在大部分时间里都毫无用处——纯粹浪费上下文。渐进式帮助让智能体自己决定何时需要更多信息。

这也对命令设计提出了要求:每个命令和子命令都必须有完整的帮助输出。 这不仅仅是为人类设计的——更是为智能体设计的。一条好的帮助信息意味着一次成功。缺失的帮助信息意味着盲目猜测。

技巧 2:错误信息即导航

智能体会犯错。关键不在于防止错误——而在于让每一条错误都指向正确的方向。

传统 CLI 的错误信息是为能 Google 的人类设计的。智能体不能 Google。所以我要求每条错误都同时包含”出了什么问题”和”应该怎么做”:

Traditional CLI:
  $ cat photo.png
  cat: binary file (standard output)
  → Human Googles "how to view image in terminal"
 
My design:
  [error] cat: binary image file (182KB). Use: see photo.png
  → Agent calls see directly, one-step correction

更多示例:

[error] unknown command: foo
Available: cat, ls, see, write, grep, memory, clip, ...
→ 智能体立刻知道有哪些可用命令
 
[error] not an image file: data.csv (use cat to read text files)
→ 智能体从 see 切换到 cat
 
[error] clip "sandbox" not found. Use 'clip list' to see available clips
→ 智能体知道应该先列出可用的 clip

技巧 1(帮助)解决的是”我能做什么?“技巧 2(错误)解决的是”我应该做什么?“两者配合,智能体的纠错成本极低——通常只需 1-2 步就能回到正确路径。

真实案例:静默 stderr 的代价

有一段时间,我的代码在调用外部沙箱时静默丢弃了 stderr——只要 stdout 非空,stderr 就会被直接扔掉。智能体执行了 pip install pymupdf,得到退出码 127。stderr 里写着 bash: pip: command not found,但智能体看不到。它只知道”失败了”,却不知道”为什么”——于是开始盲目猜测 10 种不同的包管理器:

pip install         → 127  (doesn't exist)
python3 -m pip      → 1    (module not found)
uv pip install      → 1    (wrong usage)
pip3 install        → 127
sudo apt install    → 127
... 5 more attempts ...
uv run --with pymupdf python3 script.py → 0 ✓  (10th try)

10 次调用,每次约 5 秒推理时间。如果第一次就能看到 stderr,一次调用就够了。

关键教训

stderr 恰恰是命令失败时智能体最需要的信息。永远不要丢弃它。

技巧 3:一致的输出格式

前两个技巧处理的是发现和纠错。第三个技巧让智能体能够随着使用不断变得更聪明。

我在每次工具调用的结果后面附加一致的元数据:

file1.txt
file2.txt
dir1/
[exit:0 | 12ms]

LLM 从中提取两个信号:

退出码(Unix 惯例,LLM 本来就懂这些):

退出码含义
exit:0成功
exit:1一般错误
exit:127命令未找到

耗时(成本感知):

耗时信号
12ms很便宜,随便调用
3.2s中等
45s很贵,谨慎使用

在一次对话中反复看到 [exit:N | Xs] 几十次之后,智能体会内化这个模式。它开始学会预判——看到 exit:1 意味着检查错误,看到长耗时意味着减少调用。

一致性原则

一致的输出格式让智能体随时间变得更聪明。不一致的格式让每次调用都像是第一次。

三个技巧形成了一个递进体系:

技巧智能体的问题设计模式
--help”What can I do?”主动发现(Proactive discovery)
Error Msg”What should I do?”被动纠错(Reactive correction)
Output Fmt”How did it go?”持续学习(Continuous learning)

双层架构:为启发式设计做工程化

上一节讲的是 CLI 如何在语义层面引导智能体。但要让它真正跑起来,还有一个工程问题:命令的原始输出和 LLM 实际需要看到的内容,往往是两回事。

LLM 的两个硬约束

约束 A:上下文窗口是有限且昂贵的。 每个 token 都意味着成本、注意力和推理速度的消耗。把一个 10MB 的文件塞进上下文,不仅浪费上下文预算——还会把更早的对话挤出窗口。智能体会”失忆”。

约束 B:LLM 只能处理文本。 二进制数据经过分词器后会产生高熵的无意义 token。这不仅浪费上下文——还会干扰周围有效 token 的注意力,导致推理质量下降。

这两个约束意味着:命令的原始输出不能直接喂给 LLM——需要一个表示层来加工处理。但这种处理又不能影响命令的执行逻辑——否则管道就断了。于是,双层架构应运而生。

执行层 vs. 表示层

┌─────────────────────────────────────────────┐
│  Layer 2: LLM Presentation Layer            │  ← Designed for LLM constraints
│  Binary guard | Truncation+overflow | Meta   │
├─────────────────────────────────────────────┤
│  Layer 1: Unix Execution Layer              │  ← Pure Unix semantics
│  Command routing | pipe | chain | exit code │
└─────────────────────────────────────────────┘

cat bigfile.txt | grep error | head 10 执行时:

Inside Layer 1:
  cat output → [500KB raw text] → grep input
  grep output → [matching lines] → head input
  head output → [first 10 lines]

如果在第一层截断 cat 的输出 → grep 就只能搜索前 200 行,产出不完整的结果。如果在第一层附加 [exit:0] → 它会作为数据流入 grep,变成被搜索的目标。

所以第一层必须保持原始、无损、不附加任何元数据。 处理只发生在第二层——在管道链执行完毕、最终结果准备返回给 LLM 的时候。

设计原则

第一层服务于 Unix 语义。第二层服务于 LLM 认知。这种分离不是设计偏好——而是逻辑上的必然。

第二层的四种机制

机制 A:二进制守卫(应对约束 B)

在将任何内容返回给 LLM 之前,先检查是否为文本:

Null byte detected → binary
UTF-8 validation failed → binary
Control character ratio > 10% → binary
 
If image: [error] binary image (182KB). Use: see photo.png
If other: [error] binary file (1.2MB). Use: cat -b file.bin

LLM 永远不会收到它无法处理的数据。

机制 B:溢出模式(应对约束 A)

Output > 200 lines or > 50KB?
  → Truncate to first 200 lines (rune-safe, won't split UTF-8)
  → Write full output to /tmp/cmd-output/cmd-{n}.txt
  → Return to LLM:
 
    [first 200 lines]
 
    --- output truncated (5000 lines, 245.3KB) ---
    Full output: /tmp/cmd-output/cmd-3.txt
    Explore: cat /tmp/cmd-output/cmd-3.txt | grep <pattern>
             cat /tmp/cmd-output/cmd-3.txt | tail 100
    [exit:0 | 1.2s]

关键洞察:LLM 本来就知道怎么用 grepheadtail 来浏览文件。溢出模式把”大数据探索”转化为 LLM 已经具备的能力。

机制 C:元数据尾注

actual output here
[exit:0 | 1.2s]

退出码 + 执行耗时,作为第二层的最后一行附加。给智能体提供成功/失败信号和成本感知,同时不污染第一层的管道数据。

机制 D:stderr 附加

When command fails with stderr:
  output + "\n[stderr] " + stderr

确保智能体能看到命令失败的原因,避免盲目重试。


生产环境中的经验教训

故事一:一张 PNG 引发的 20 轮死循环

一位用户上传了一张架构图。智能体用 cat 去读它,结果收到了 182KB 的原始 PNG 字节流。LLM 的分词器把这些字节转换成了成千上万个毫无意义的 token,硬塞进了上下文窗口。LLM 完全无法理解这些内容,开始尝试各种不同的读取方式——cat -fcat --formatcat --type image——每次都收到同样的乱码。20 轮迭代之后,进程被强制终止。

  • 根因: cat 没有二进制检测,第二层没有守卫机制。
  • 修复: 添加 isBinary() 二进制守卫 + 错误引导提示 Use: see photo.png
  • 教训: 工具的返回结果就是智能体的眼睛。返回垃圾 = 智能体失明。

故事二:被吞掉的 stderr 与 10 次盲目重试

智能体需要读取一个 PDF 文件。它尝试执行 pip install pymupdf,得到退出码 127。stderr 里明明写着 bash: pip: command not found,但代码把它丢弃了——因为 stdout 有一些输出,而逻辑是”只要 stdout 有内容,就忽略 stderr”。

智能体只知道”失败了”,却不知道”为什么”。接下来就是一长串试错:

pip install         → 127  (doesn't exist)
python3 -m pip      → 1    (module not found)
uv pip install      → 1    (wrong usage)
pip3 install        → 127
sudo apt install    → 127
... 5 more attempts ...
uv run --with pymupdf python3 script.py → 0 ✓

10 次调用,每次约 5 秒的推理时间。如果第一次就能看到 stderr,一次调用就够了。

  • 根因: InvokeClip 在 stdout 非空时静默丢弃了 stderr。
  • 修复: 失败时始终附加 stderr。
  • 教训: stderr 恰恰是智能体在命令失败时最需要的信息。

故事三:溢出模式的价值

智能体分析一个 5,000 行的日志文件。如果不做截断,全文(约 200KB)会被塞进上下文窗口。LLM 的注意力被淹没,响应质量急剧下降,之前的对话内容也被挤出了上下文窗口。

启用溢出模式后:

[first 200 lines of log content]
 
--- output truncated (5000 lines, 198.5KB) ---
Full output: /tmp/cmd-output/cmd-3.txt
Explore: cat /tmp/cmd-output/cmd-3.txt | grep <pattern>
         cat /tmp/cmd-output/cmd-3.txt | tail 100
[exit:0 | 45ms]

智能体看到了前 200 行,理解了文件结构,然后用 grep 精准定位了问题——总共 3 次调用,上下文占用不到 2KB。

教训

给智能体一张”地图”,远比把整片领土扔给它有效得多。


边界与局限

CLI 并非银弹。在以下场景中,类型化 API 可能是更好的选择:

  • 强类型交互:数据库查询、GraphQL API 以及其他需要结构化输入/输出的场景。schema 校验比字符串解析更可靠。
  • 高安全性要求:CLI 的字符串拼接天然存在注入风险。在处理不可信输入的场景下,类型化参数更安全。agent-clip 通过沙箱隔离来缓解这一问题。
  • 原生多模态:纯音频/视频处理等二进制流场景,CLI 的文本管道会成为瓶颈。

此外,“不设迭代上限”不等于”没有安全边界”。安全性由外部机制来保障:

  • 沙箱隔离:命令在 BoxLite 容器内执行,无法逃逸
  • API 预算:LLM 调用有账户级别的消费上限
  • 用户取消:前端提供取消按钮,后端支持优雅终止

总结

将 Unix 哲学交给执行层,将 LLM 的认知约束交给表示层,再以帮助信息、错误消息和输出格式作为三种渐进式启发式导航手段。

CLI 就是智能体所需的一切。


源码(Go):github.com/epiral/agent-clip

核心文件:

文件职责
internal/tools.go命令路由
internal/chain.go管道
internal/loop.go两层智能体循环
internal/fs.go二进制守卫
internal/clip.gostderr 处理
internal/browser.go视觉自动附加
internal/memory.go语义记忆

欢迎讨论——尤其是如果你也尝试过类似的方案,或者发现了 CLI 力不从心的场景。命令发现问题(该预注入多少 vs. 让智能体自行探索)是我仍在积极探索的方向。


评论区

WithoutReason1729 · 2026-03-12 · 1 points

你的帖子火了,我们已经在 Discord 上推荐了它!来看看吧!

我们还为你的贡献授予了特别标识。感谢你的分享!

我是机器人,此操作自动执行。

spaceman_ · 2026-03-12 · 178 points

我找不到那篇论文了,但之前有一个类似的实验是用 Python 代码做的——LLM 只能用 Python 的 code eval 作为工具,没有其他工具。那篇论文声称效果出奇地好。

thrope · 2026-03-12 · 68 points

你说的可能是 Hugging Face 的 Smolagents

spaceman_ · 2026-03-12 · 15 points

就是这个!谢谢!

MorroHsu · 2026-03-12 · 119 points

对,我想我看过那篇论文——code-as-tool 是一种可行的方案,效果确实不错。

但根据我的经验,LLM 目前在使用工具方面比编写代码来完成同样的事情要强。随着模型能力增强,这个差距在缩小,但仍然存在。

还有一个更深层的问题:可发现性。使用 CLI 时,智能体可以运行 tool --helptool list 在运行时发现可用的工具——这是 Unix 在 50 年前就建立的模式。而用 Python eval 的话,LLM 怎么知道有哪些函数/库可用?你要么把文档塞进上下文(代价昂贵),要么自己发明一套发现机制。两种方式都不如 --help 来得自然。

CLI 天生自带可发现性。Code eval 则需要你重新发明它。

docybo · 2026-03-12 · 8 points

没错,我觉得可发现性才是真正的优势。

Code eval 可以很强大,但 CLI 给了模型一种内置的方式来探索工具表面:—help、list、stderr、退出码等等。用 code eval 的话,你通常得自己造这一层。

所以这不只是 shell 和代码之争,而是可导航接口和自定义接口之争。

itsmekalisyn · 2026-03-12 · 14 points

同意。而且,写 Python 代码会占用大量的上下文长度。同样的操作用 cat 和 grep 只需要一行,Python 则需要超过 5 行。虽然单次工具调用差别不大,但随着调用次数增多,差距很容易累积起来。

johnbbab · 2026-03-12 · 109 points

最强大的智能体框架,最终可能长得和 shell 一模一样

johnmclaren2 · 2026-03-12 · 27 points

美元符号后面那个闪烁的光标,对很多用户来说是无法解释的…… :)

gefahr · 2026-03-12 · 14 points

所以我总是 su 切到 root。告别美元符号,拥抱井号。

raucousbasilisk · 2026-03-12 · 124 points

即时将自然语言转换成 sed awk 正则表达式,才是真正的超能力

SurprisinglyInformed · 2026-03-12 · 172 points

而且我们还能用 vim 来阻止任何 AI 叛乱——把它们骗进去就行了。就像我们困住大多数人类一样。

michaelkeithduncan · 2026-03-12 · 44 points

这对我来说过于好笑了

AlwaysLateToThaParty · 2026-03-12 · 28 points

只要别给它 Esc 键就行。今天不行,天网。今天不行。

johnmclaren2 · 2026-03-12 · 7 points

因为我根本记不住任何正则表达式,所以我总是让 LLM 帮我生成。这算不算?:)

Dependent_Range9705 · 2026-03-12 · 121 points

楼主这帖子是心理战,想让你把终端的完整权限交给 LLM 智能体

Time-Dot-1808 · 2026-03-12 · 66 points

Unix 趋同论这个论点很有意思。我看到的主要权衡在于沙箱隔离——类型化的函数调用让你可以预先定义严格的访问边界(这个智能体只能调用 search_webread_file),而 run(command) 要求你要么完全信任 LLM,要么自己实现一套命令过滤器。你在生产环境中有没有找到一种干净的模式来限制允许执行的命令?

MorroHsu · 2026-03-12 · 111 points

好问题。这里实际上有两层:

  1. 大多数命令根本不接触操作系统。 catgrepmemory searchbrowser open——这些看起来像 shell 命令,但实际上是用 Go 命令路由器实现的。LLM 输出一个字符串,我解析它并分派到原生函数。没有 os/exec,没有 shell 注入面。本质上就是披着 CLI 语法外衣的类型化函数——同样的访问控制,但 LLM 得到了一个熟悉的接口。
  2. 当你确实需要真正的操作系统执行时(比如运行 Python 脚本或安装包),它会在微型虚拟机内部运行——一个拥有独立文件系统的隔离 QCOW2 虚拟机。智能体可以在沙箱内部为所欲为。它可以 rm -rf /,对宿主机毫无影响。沙箱隔离 > 命令过滤。

所以安全模型不是”过滤哪些命令被允许”——而是”命令要么是原生函数(不涉及 OS),要么是沙箱执行(隔离的 OS)。“没有 LLM 在宿主机上运行任意命令的中间地带。

sfcl33t · 2026-03-12 · 30 points

从描述中我没有意识到这一点。现在说得通了,感谢解释。一个伪装成 CLI 语法的抽象层,而模型本身就熟悉这套语法!相当精妙

pulse77 · 2026-03-12 · 17 points

既然如此:为什么不干脆把所有东西都放在微型虚拟机里运行——包括 cat、grep 等等?

belfilm · 2026-03-12 · 67 points

我知道这有点跑题,但我忍不住想表达一下对 LLM 打破语言壁垒的感慨。楼主能够如此精准地向一群非母语使用者传达自己的观点。我花了大量的时间和精力才达到现在的英语水平,过去我也有过和楼主一样的处境,但那时候没有 LLM,自动翻译简直是个笑话。

我真心感激这些工具带来的可能性!

MorroHsu · 2026-03-12 · 66 points

谢谢——说实话,我很感激 LLM 让这场对话成为可能。一年前,我不可能写出这篇文章,也不可能在这个层面参与讨论。

而且这远不止于语言。智能体可以帮助我们学习任何东西——编程、硬件、金融,无论你对哪个领域好奇。我们现在都站在同一条起跑线上。让我们好好把握这个机会。

DeathByPain · 2026-03-12 · 13 points

这篇帖子和你的评论读起来非常精彩,虽然对我来说有点超出理解范围了。感谢分享

GuideAxon · 2026-03-12 · 22 points

有意思的理念,感谢如此详细的分享。我还在消化你的想法

Loud-Option9008 · 2026-03-12 · 14 points

双层架构(Unix 执行层保持原始状态,表示层处理 LLM 约束)是这篇帖子中最重要的洞察。其他人都在试图让工具变得更聪明。而你把执行和表示之间的边界显式化了,这一下子解决了一整类问题。

渐进式帮助发现模式很干净。在对话开始时注入完整的命令列表,然后让智能体通过无参数调用逐层深入,比把 3000 字的工具文档塞进系统提示词要好得多。上下文预算很重要。

stderr 的故事是一个绝佳的警示案例。智能体对失败信息的需求大于对成功信息的需求。丢弃 stderr 就像从编译器中删除错误消息,然后指望开发者靠猜来调试。

有一点值得在你的局限性部分补充:你提到沙箱隔离作为安全机制,但 BoxLite 是基于容器的。你指出的 CLI 字符串拼接注入风险是真实存在的,如果通过精心构造的命令字符串实现容器逃逸,就会绕过整个沙箱。对于你提到的不可信输入场景,隔离层和输入清理同样重要。