Agent 终于会“看”表格了——read_csv 和 read_excel 的故事

以前只能“写”,现在终于能“读”了


一、一个让我很无语的场景

前几天我在 REPL 里测试数据分析能力。

我手头有一个 CSV 文件,大概 2000 多行,里面有“ASR 识别文本”和“是否是 POI”两列。我想让 Agent 帮我看看这个文件长什么样。

我说:“读取一下这个 CSV,看看它有多少行,有哪些列。”

Agent 说好的,然后调用了 read_file

read_file 返回了什么呢?一大坨原始的 CSV 文本。
2000 多行的内容,密密麻麻全是逗号和引号。模型面对这堆东西,完全没办法"理解"——它只能看到一堆文本,不知道哪行是表头、哪列是什么含义、有多少行、有多少空值。
结果 Agent 含糊地说:“这个文件看起来有一些数据……”
就这?

我当时的内心 OS:你倒是跟我说说有几行几列啊!

后来我才意识到,这不是模型的问题——是我的工具设计有问题。


二、先解释一个概念:什么是“数据预览”?

概念解释:数据预览(Dataset Preview)不是把整个文件的内容都倒出来,而是先看一眼它的“骨架”

  • 这个表格有几行几列?
  • 列名叫什么?
  • 每列是什么类型的数据(数字、文字、日期)?
  • 有没有空值?
  • 前几行长什么样?

就像你拿到一本厚厚的书,不会从第一页开始读——你会先看目录、看摘要、看第一章的前几段,判断这本书值不值得读。

数据预览就是这个道理:先看一眼“骨架”,再决定怎么用。

如果模型能先拿到这些信息,它就能做出更好的判断:

  • “哦,这个表有 2473 行,两列,ASR识别文本 有 12 个空值”——那它就知道数据质量还可以,可以继续分析。
  • “哦,这个表有 10 万行”——那它就知道不能用 read_file 全量读取,得用采样或聚合方式处理。—

三、为什么之前没有这个能力?

回顾一下我当时手上的工具:

写侧(已经有)

工具 能力
write_csv 写入 CSV
create_excel 创建 Excel

读侧(之前没有)

工具 能力
读取 CSV / Excel 的结构化信息

我只能“写”表格,不能“读”表格。

这就很尴尬了——Agent 可以生成一个 Excel 给你,但它打不开你发给它的 Excel。

就像一个作家只会写书、不会读书。


四、解决方案:read_csvread_excel

今天新增了两个工具:

工具 作用
read_csv 读取 CSV 文件,返回结构化预览
read_excel 读取 Excel 文件,返回结构化预览

4.1 具体返回什么?

当 Agent 调用 read_csvread_excel 时,返回的不是原始文本,而是一个结构化预览

{
  "path": "data/sample.csv",
  "format": "csv",
  "rows": 2473,
  "columns": ["ASR识别文本", "是否是POI"],
  "dtypes": {
    "ASR识别文本": "string",
    "是否是POI": "string"
  },
  "nullCounts": {
    "ASR识别文本": 12,
    "是否是POI": 0
  },
  "sample": [
    { "ASR识别文本": "今天天气不错", "是否是POI": "" },
    { "ASR识别文本": "去天安门广场", "是否是POI": "是" }
  ],
  "sheets": ["Sheet1"],
  "sheet": "Sheet1",
  "scannedRows": 2473,
  "bytes": 128400
}

模型拿到了这些信息,就可以很准确地告诉用户:

  • “这个 CSV 有 2473 行、2 列。”
  • “列名是 ASR识别文本是否是POI。”
  • ASR识别文本 列有 12 个空值,需要注意。”
  • “前两行数据是……(展示样例)”

4.2 两个关键参数

为了让模型能灵活控制预览的“深度”,我添加了两个参数:

参数 说明 默认
sample_size 返回多少行样例 10(最多 50)
scan_rows 扫描多少行做统计 5000

也就是说,即使一个文件有 100 万行,read_csv 也不会全量加载——它只扫描前 5000 行来推断列类型和空值统计,然后返回 10 行样例。

控制 token 消耗,避免撑爆上下文。

4.3 环境变量调优

READ_DATA_MAX_BYTES=15000000   # 最大读取大小,15MB
READ_DATA_SAMPLE_SIZE=10       # 默认样例行数
READ_DATA_SCAN_ROWS=5000       # 统计扫描行数

五、对比:以前 vs. 现在

场景:用户说“帮我看看这个 CSV”

之前(用 read_file

Agent: 调用了 read_file("data.csv")
read_file: 返回 2000 行原始 CSV 文本(全是逗号和引号)
Agent: “这个文件有一些数据……”(含糊其辞)
用户: ???

现在(用 read_csv

Agent: 调用了 read_csv("data.csv")
read_csv: 返回结构化预览(行数、列名、类型、空值、样例)
Agent: “这个 CSV 有 2473 行、2 列。列名是 `ASR识别文本` 和 `是否是POI`。`ASR识别文本` 有 12 个空值,建议先处理空值再分析。”
用户: 清晰、有用!

同一个文件,不同的工具,天壤之别。


六、为什么这样设计?—— 三点思考

6.1 专用工具 > 通用工具

read_file 是“万能”的——什么文件都能读,但什么都不精。

read_csvread_excel 是“专用”的——它们只做一件事,但做得好。

这让我想起一个设计原则:通用工具解决“能不能做”的问题,专用工具解决“做得好不好”的问题。

给 Agent 一堆万能工具,它能做很多事,但每件事都做得一般。给它一堆专用工具,它才能在特定领域做得专业。

6.2 预览 > 全量加载

以前我总觉得“要分析数据,就得把全部数据给模型”。

但这是错的——模型不是数据库,它的优势是“理解”和“推理”,不是“存储”和“检索”。

正确的做法是:先预览骨架,再按需读取细节。

就像数据分析师拿到一个数据集,不会先看全部数据——他会先 head() 看看前几行,info() 看看列类型,describe() 看看统计分布。

read_csvread_excel 做的就是这个事。

6.3 读侧补全写侧,形成闭环

  • 写侧:write_csv → 创建 CSV
  • 写侧:create_excel → 创建 Excel
  • 读侧:read_csv → 读取 CSV
  • 读侧:read_excel → 读取 Excel

现在 Agent 终于有了完整的数据操作闭环:能写、能读、能分析。


七、和之前的功能怎么配合?

功能 作用 和 read_csv/excel 的关系
write_csv / create_excel 写数据 读侧和写侧对称,形成闭环
TaskStateTracker 检查子任务完成度 read_csv 可以作为“验证”子任务的完成条件
ContextCompressor 压缩上下文 read_csv 返回的是预览(小体积),天然友好
IntentPlanner 拆子任务 数据分析场景下,会优先推荐 read_csv / read_excel

举个例子:用户说“分析这个 Excel 的数据质量”。

IntentPlanner 拆出子任务:

  1. 读取 Excel 预览 → read_excel
  2. 检查空值占比 → 从 nullCounts 中读取
  3. 总结数据质量 → 模型推理

TaskStateTracker 确保每一步都完成,才会允许 final_answer。环环相扣。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐