一次“做 Excel 失败”触发的工具补全:REPL、数据导出和依赖管理

一次“做 Excel 失败”触发的工具补全:REPL、数据导出和依赖管理

系列博客第五篇 · 2026-07-02
今天的故事从一个失败的对话开始。


一、今天的故事从一个失败开始

今天早上我在 REPL 里跟 Agent 聊了这样一段话:

:你可以建一个 Excel 然后把工作区文件名都写入到里面吗?

Agent 说“好的”,然后开始调用工具。我满怀期待地等着,结果十几秒后它告诉我:

Agent:达到最大工具循环次数 8,任务未能明确完成。

失败了。

这不是模型的问题,也不是 API 的问题——而是我的 工具集不完整。Agent 手头没有“创建 Excel”的工具,所以它只能硬着头皮用 run_shell + pip install openpyxl + python -c 写一段临时 Python 脚本去绕路。Windows 上引号、多行命令、路径空格……任何一个环节出问题就 silent failure。

这次失败让我意识到两件事:

  1. Agent 的工具箱里缺了数据导出类的工具(CSV、Excel),它需要“直给”的能力,而不是绕路。
  2. 我已经在 REPL 里测试了,如果没有 REPL,我根本不会这么快发现这个问题——因为每次 npm run dev 是单次执行,失败一次就结束了,很难在对话中发现工具缺口。

所以今天我一口气补了四个工具,同时把 REPL 和 Eval 也正式打磨了一遍。这篇文章从头到尾记录这个过程。


二、先看最终效果:两次对话的对比

2.1 失败的那次(修复前)

Session 文件里记录了完整的对话历史:

轮次用户Agent
1“我叫小明,正在学 Agent Harness”“你好,小明!欢迎学习 Agent Harness…”
2“我叫什么”“你叫小明。”
3“我现在这个工作区你可以看见吗”“可以看见。当前工作区是 `xxxxxxxxx”
4“你可以建一个 Excel 然后把工作区文件名都写入到里面吗”“达到最大工具循环次数 8,任务未能明确完成。”
5(重试)同上“达到最大工具循环次数 8,任务未能明确完成。”

两次都失败了,因为 Agent 在循环里尝试用 run_shell + Python 绕路,但每次都卡在了 Windows 的转义问题上,直到 maxTurns = 8 耗尽。

2.2 成功的那次(修复后)

我补完工具后再问同样的问题:

:你可以建一个Excel然后把工作区文件名都写入到里面吗?

Agent 直接调用了 create_excel 工具:

create_excel(path="exports/workspace_files.xlsx", from_workspace_files=true)

几秒钟后:

Agent:Excel 已生成!文件在 exports/workspace_files.xlsx,共 122 个文件(包含子目录下的所有文件),按路径排序,带序号和文件路径两列。

从“绕路 8 轮失败”到“一行工具调用 5 秒成功”,这就是补齐工具的价值。


三、具体新增了哪些工具?

工具从 7 个扩展到 11 个,新增四个:

工具作用
write_csv写入 CSV 文件,比用 run_shell 拼接 Python 命令可靠得多
create_excel创建 .xlsx 文件;支持 from_workspace_files=true 一键导出工作区文件列表
run_script运行工作区内 .ts/.js/.py 脚本(自动识别用 tsx/node/python)
install_deps在白名单内安装 pip/npm 依赖,可选 verify(验证是否安装成功)

3.1 create_excel —— 今天的主角

这个工具最核心的参数是 from_workspace_files

{
  path: string;                    // 输出路径,如 exports/workspace_files.xlsx
  from_workspace_files?: boolean;  // 是否自动扫描工作区文件
  data?: any[][];                  // 或者手动传入二维数组
}

from_workspace_files=true 时,工具会自动:

  1. 递归扫描 workspaceRoot 下所有文件(排除 node_modules.git 等)。
  2. 生成带序号和路径的表格。
  3. 写入 .xlsx 文件。

不再需要模型去理解 os.walk() 怎么写、openpyxl 怎么用。

3.2 write_csv——轻量版导出

如果用户只是想快速导出结构化数据,CSV 格式更轻量。write_csv 接受二维数组或对象数组,自动转成 CSV 格式写入。

3.3 run_script——编码闭环的关键

现在 Agent 的编码工作流变成了:

write_file → 写代码
edit_file → 改代码(局部)
run_script → 运行测试
install_deps → 缺依赖时补装

以前 write_file 写完代码后,Agent 只能用 run_shell 去执行,但 run_shell 不知道文件类型,路径拼接也容易出错。现在 run_script 会根据文件扩展名自动选择执行器:

  • .tstsx
  • .jsnode
  • .pypython

3.4 install_deps——白名单 + verify

这个工具最核心的是 白名单机制

// npm 白名单
const NPM_ALLOWLIST = ['xlsx', 'csv-parse', 'csv-stringify', 'dotenv', 'tsx', 'typescript'];

// pip 白名单
const PIP_ALLOWLIST = ['openpyxl', 'pandas', 'numpy', 'xlsxwriter', 'python-dotenv', 'requests', 'openai'];

不在白名单里的包,工具会拒绝安装。同时支持 verify 参数——安装后尝试 import 验证是否真的可用。

这个设计的原因是:我 不想让 Agent 随便装任何包,万一它要装一个恶意包或者不兼容的版本,整个项目就崩溃了。白名单是安全底线。


四、这一切发生在 REPL 里——交互式 REPL 正式上线

4.1 之前的问题:每次都是“一次性对话”

之前的 CLI 命令是:

npm run dev -- "问题"

每次运行都是一次性的:启动 → 执行 → 退出。如果你想连续问三个问题,就得运行三次命令,而且每次都是“失忆”状态(除非你手动加上 --continue)。

4.2 现在:真正的交互式对话

npm run repl

进入 REPL 后:

Agent REPL 已启动
   输入 /help 查看命令

> 

然后你可以像聊天一样连续输入,Agent 会保持上下文:

> 我叫小明,正在学 agent harness
你好,小明!欢迎学习 agent harness...

> 我叫什么
你叫小明。

> 我现在这个工作区你可以看见吗
可以看见。当前工作区是xxxxx

> 你可以建一个Excel然后把工作区文件名都写入到里面吗
Excel 已生成!文件在 exports/workspace_files.xlsx,共 122 个文件...

这其实就是在同一个 Node.js 进程内,每一轮自动维护会话。第一轮默认开新会话,之后的每一轮自动 continue

4.3 REPL 内命令

命令作用
/help显示帮助
/exit退出
/new下一轮开启新 session(遗忘之前的内容)
/forget清除上次 session
/tool强制 tool 模式
/direct强制 direct 模式
/mode恢复自动判断(默认)

举个例子,如果你想暂时让 Agent 只做纯聊天,可以:

> /direct
> 你好,随便聊两句

想切回工具模式:

> /tool
> 帮我看一下这个目录

4.4 Session 文件长什么样?

以一次成功的对话为例,Session 文件记录如下(sess_20260702100806_922cik.json):

{
  "sessionId": "sess_20260702100806_922cik",
  "createdAt": "2026-07-02T10:08:06.148Z",
  "workspaceRoot": "xxxxxx",
  "messages": [
    { "role": "user", "content": "我叫小明,正在学 agent harness", "runId": "..." },
    { "role": "assistant", "content": "你好,小明!欢迎学习 agent harness...", "runId": "..." },
    { "role": "user", "content": "我叫什么", "runId": "..." },
    { "role": "assistant", "content": "你叫小明。", "runId": "..." },
    { "role": "user", "content": "我现在这个工作区你可以看见吗", "runId": "..." },
    { "role": "assistant", "content": "可以看见。当前工作区是...", "runId": "..." },
    { "role": "user", "content": "你可以建一个Excel然后把工作区文件名都写入到里面吗", "runId": "..." },
    { "role": "assistant", "content": "Excel 已生成!...", "runId": "..." }
  ]
}

只存文本对话,不存工具调用过程。每次 Run 的完整 Trace 依然在 runs/ 目录下,两者互不干扰。Session 负责记录“说了什么”,Trace 负责记录“执行过程”。


五、顺便把 Eval 强化了一下:Tool Call Checker

之前 Eval 只能检查最终答案是否包含关键词,但没法验证 agent 有没有正确调用工具

比如一个任务要求“用 list_files 列出根目录”,模型可能直接回答了——没调工具,但回答看起来像那么回事。这就没法在 Eval 里发现。

现在我在 src/eval/toolCallChecker.ts 里增加了一层校验,任务可以声明:

expectToolCalls: {
  minToolCalls?: number;           // 至少调 N 次工具
  maxToolCalls?: number;           // 最多调 N 次
  mustIncludeTools?: string[];     // 必须包含某些工具
  mustNotIncludeTools?: string[];  // 不能包含某些工具
}

Eval 输出会区分 textCheck(文本匹配)和 toolCheck(工具调用校验),并写入 runs/eval_*.json 报告。

这样一来,我就可以批量跑测试,自动发现“任务完成了但工具没用对”这种 subtle bug。


六、总结

6.1 工具缺了就是缺了,模型绕不过去

第一次做 Excel 失败的时候,我一度在想“是不是模型理解能力不行?”后来看了 trace 才发现,模型在 8 轮里每次都调用了 run_shell,每次都卡在 Windows 引号转义上。它不是不想做,而是工具集里没有合适的工具

这让我想起一个道理:Agent 的智能上限 = 模型能力 × 工具完备度。模型再强,工具缺了就是缺了,绕道只会增加失败概率。

6.2 REPL 是发现工具缺口的最佳方式之一

如果我只用 npm run dev -- "做Excel",跑一次,失败就结束了,我可能只会想“这次任务没成功”,而不会深入去排查原因。

但在 REPL 里,我可以连续追问、重试、观察 Agent 的行为模式,快速定位瓶颈。REPL 把“测试”变成了“对话”,这是开发 Agent 时非常重要的调试方式。

6.3 数据导出类工具应该是“直给”的

create_excelwrite_csv 的逻辑很简单,但它们解决了 Agent 的一大痛点:把结构化数据从工作区搬到用户手里。之前 Agent 只能回答“文件列表是什么”,现在它可以“把文件列表生成 Excel 发给你”。前者是“说”,后者是“交付”。

6.4 白名单是安全的底线

install_deps 的白名单看起来限制很大,但我认为这是必要的。Agent 理论上可以执行任意 shell 命令,如果它某天决定 npm install some-malicious-package,后果不堪设想。白名单 + run_shell 黑名单 + pathSafety,三层安全机制虽然麻烦,但值得。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忘路之远近i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值