文章把探索性数据分析拆成四个 Agent,并为每个阶段采用先规划、后执行的结构;模型负责判断,计算、代码执行和文件写入则交给确定性程序。固定流程边界既保留了模型灵活性,也改善了一次性调用难以检查的问题。
让 LLM 一次性分析电子表格,你得到的只会是一个不透明的结论——看不到推理过程,也无法核查其中的数字。Agentic EDA 采用的则是一套半自主的 Agentic 工作流:四个 LLM Agent,每个 Agent 都拆成“先规划、再执行”的两步;所有需要判断的工作交给模型,所有计算、代码执行和文件写入则交给普通代码完成。这里的自主权是有意分配的,而不是一味追求最大化——pipeline 的步骤预先固定,只有每一步内部具体做什么由模型决定。本文会介绍“Agentic”在这里究竟带来了什么,为什么半自主方案优于单次调用和全自主 Agent,以及在构建可实际运行的版本时暴露出的真实问题。
探索性数据分析(Exploratory Data Analysis,EDA)是面对任何新数据集时进行的第一轮分析——分析数据特征、发现规律、绘制重要图表,并整理分析结果。从最简单且实用的定义来看,Agentic AI 工作流是指基于 LLM 的应用通过执行多个步骤来完成任务,而不是一次调用就直接给出答案——Anthropic 自己关于 Agent 设计的文章也明确采用了这一表述。这里要讨论的核心,正是这种区别。
并不是所有多步骤系统都会赋予模型同等程度的控制权。大致可以分为三个层级:
自主程度较低——每一步都预先确定,工具调用被硬编码,模型只负责在固定位置生成文本。
半自主——模型会在明确的检查点做出真正的决策,但可执行的步骤和可使用的工具都已预先固定。
高度自主——模型自行决定步骤,并且可以随时选用甚至临时创造工具。
Agentic EDA 有意选择了中间这一层。它的四个阶段,以及每个分析阶段内部的两轮调用,都是固定的——什么时候调用模型、向模型提供什么内容,都不由模型决定。但在这些固定位置内部,交给模型的确实是真正需要判断的工作:某个变量适合使用哪种图表、某项相关性是否值得绘图、某张图为什么重要。
Agentic 应用还可以根据两个维度来划分:步骤能否提前确定,以及涉及多少种模态。步骤清晰、逐步执行且只处理文本的流程属于简单的一端;需要临场解决未知步骤,同时横跨多种模态的流程则属于困难的一端。EDA 位于两者之间:四阶段的整体结构固定,而且大部分工作都基于文本;但面对任意数据集时,应该绘制哪些图表无法提前确定,并且最终的报告阶段是真正的多模态任务——模型必须查看渲染完成的图表,而不能只阅读对图表的文字描述。
大多数人最先采用的默认方案,其实根本不算 Agentic——它只是调用一次 LLM:“这是我的数据,请给我写一份带图表的报告。”这种方案很快就会暴露问题:模型可能给出一个自己从未真正计算过的相关性强度;可能用一段图表脚本生成十几张图,结果要么直接失败,要么悄无声息地把其中一张画错;你也没有机会追问它为什么选择散点图而不是柱状图,因为这个选择从未作为一个独立、可见的决策存在过。
把“执行 EDA”变成一套工作流,意味着将它拆解为人类分析师原本就会遵循的步骤。
同样的四个步骤支撑着三种入口——脚本、notebook 和流式 Web 应用——任何入口都没有单独复制一份业务逻辑。
当输出结果需要接受独立核查,而且单次运行成本高到“事后才发现结果错误”会造成明显损失时,这套方案值得采用。对于只想快速浏览规模较小、结构简单的数据,它就显得有些大材小用:一个有充分数据依据的 prompt 确实已经足够,连续进行四次调用只会增加等待时间。
只把真正需要判断的决策交给模型,并且为每一种决策提供恰好一个预定义工具——绝不让模型负责算术、文件访问或 pipeline 自身的控制流。这正是系统能够保持半自主,而不滑向两个极端的原因:它既不是一次黑盒调用,也不是一个自行选择工具和步骤的 Agent。四个阶段及其内部轮次构成固定脚手架;但在每个阶段内部,模型仍然必须进行真正的推理。
按这种方式拆分工作后,会形成两个明确的参与方。模型只和文字打交道——它接收数据摘要和需要严格填写的格式,通过推理得出决策;它永远不会接触文件,也不会决定自己的输出要写到哪里。普通代码则负责一切会产生实际后果的工作——计算真实统计数据、运行模型生成的内容以检查其是否有效、收集生成的图表,并且每次都以相同方式排版报告。
正是在这里,Agentic 工作流相对于一个超大型 prompt 的常见优势才真正显现出来:
更好的结果——模型从不负责生成数字,因此无法悄悄捏造数字;它只能同意或质疑由普通代码预先计算出的事实。
模块化——每个阶段都是独立的 Agent,可以分别选择模型;如果要增加第五个阶段,例如分析分类变量之间的关系——这项能力目前明确不在范围内——只需编写一个新 Agent,无须修改其他四个 Agent。
可通过并行提升速度——列级分析阶段与关系级分析阶段不共享状态,并且读取的是同一份清洗后文件;它们已经彼此独立,完全可以并行运行,只不过当前 pipeline 仍然按顺序执行它们。
下面这个例子可以具体说明其中的取舍。假如让模型在没有任何脚手架的情况下“寻找有趣的关系”,它可能会煞有介事地报告:Order ID 与 Month 几乎完全相关——这个结果是真的,却毫无意义,因为订单 ID 只是随着行数递增的编号,而日期列也在同步稳定增长。
在这套 pipeline 中,模型根本没有机会掉进这个陷阱:系统会先计算每一对数值变量之间的真实相关性,再把它作为事实交给模型;同时明确要求模型分析某种关系为什么可能存在,而不只是观察它看起来有多强。在真实销售数据上,模型确实这么做了——它先指出 Order ID 与 Month 的关系超过了“值得研究”的阈值,随后又通过文字解释:这个特例只是行顺序造成的巧合,并不是真实规律。
这张最终被否决的图,正是这套设计物有所值的最有力证明:在大约三十多对候选列组合中,模型只建议为其中八对绘图——而每一对被否决的关系背后,都有一条书面理由。一次“直接给我写报告”的调用不存在与之对应的环节;某项关系要么出现在图表中,要么悄无声息地消失。
github: sreeharsha-rav/agentic-ai — agentic_eda/ 中包含四个 Agent、一个负责端到端运行它们的脚本、一个 notebook,以及一个能够实时流式展示每一步的小型 Web 应用。
flowchart TD
A[Raw spreadsheet] --> B[Clean the data]
B --> C[Look at each column]
B --> D[Look at relationships]
C --> E[Write the report]
D --> E
E --> F[Finished report]
简要来说,整个系统由以下部分组成:
一个清洗 Agent 分析原始数据的特征,决定如何修复数据,编写清洗代码,然后返回清洗后的文件。
另外两个 Agent 针对清洗后的文件独立运行——一个逐列分析,另一个分析列与列之间的关系。
这两个 Agent 都分两轮完成工作,而且两轮过程都清晰可见:首先决定哪些内容值得展示以及原因,然后编写图表代码——第二轮会继续使用它自己在上一轮中的推理,而不是从零开始。
模型生成的任何代码都必须先在沙箱化子进程中运行,验证通过后才会被信任;如果执行失败,错误会直接反馈给同一个模型,并限制在较少的修复次数内。
最后一个 Agent 汇总此前的所有决策和图表,并撰写叙述性内容;普通代码负责排版最终文档。
plan = ask_model(
instructions="Decide which relationships are worth charting, and why.",
context=data_summary_and_real_correlations,
)
chart_code = ask_model(
instructions="Now write the chart code for exactly what you planned.",
context=plan, # the model's own reasoning, carried forward
)
一套共享的安全阀。模型编写的每一段代码——无论是清洗代码还是图表代码——在获得信任前,都必须经过同一小段普通代码的验证:先写入临时文件,再放到带有时间限制的独立进程中运行;只有代码确实生成了预期输出,才会被视为执行成功。
一个共享的开关。每个 Agent 都可以选择通过同一个可选 callback 汇报自己的进度。脚本和 notebook 不使用它,Web 应用则会使用——正因为有这项设计,实时界面才能在完全不修改 Agent 自身推理方式的情况下构建出来。
两次运行窃取了彼此的图表。收集已生成图表的代码采用了一种简单方式——在文件夹中查找新出现的图片文件。如果同时启动两次分析,并让它们指向同一个文件夹,每次运行都会把另一次运行生成的图表也收集进来。解决方案是:从一开始就为每次运行提供独立的私有文件夹,而不是事后再设法理清文件归属。
一次缓慢的模型调用冻结了整个 Web 应用。模型调用和代码检查步骤都不快——有些会持续数分钟。如果直接在 Web server 内部执行其中任何一项工作,整个 server 都会一直被占用,在任务完成之前无法响应其他人。解决方案是:将每次运行交给独立的后台 worker,让 server 保持空闲。这样还有一个很好的副作用:工作不再依附于当前请求,因此即使用户关闭浏览器标签页,也不会取消一个已经执行数分钟、并且正在产生真实费用的任务。
长时间沉默看起来像是程序崩溃。某个分析步骤可能连续几分钟不产生任何可见输出——在屏幕上,这与卡死没有区别。解决方案是:让每个 Agent 连中间的细小状态也一并汇报,例如“正在规划”“正在编写代码”“正在重试”;此外每十秒发送一次心跳。这样,安静的一段时间会被理解为“仍在工作”,而不是“它还在吗?”
模型生成的图表代码有时根本无法运行。模型偶尔会编写引用某个列的代码,但该列已经在清洗后不复存在;它也可能使用已经过时的图表 API。失败表现与任何损坏的程序一样——错误消息会附带程序输出和 traceback。系统不会因此终止运行,而是直接将错误消息作为后续反馈交给同一个模型:“这是刚才的问题,请修复它。”重试次数最多为两次,避免一个确实有问题的方案无限重试。在界面上,这会被如实标记为重试,而不是暗中隐藏起来——这是一次运行中最有意思的事情,而不是什么需要遮掩的难堪。
这个项目的关键并不在于把工作分散给四个 Agent,而在于有意识地决定每一步究竟需要多少自主权。每一个需要判断的决策都夹在两个普通代码步骤之间:前一步向模型提供它无须猜测的事实,后一步检查模型生成的内容是否真的站得住脚。
这个经验并不局限于电子表格:自主程度是一个需要按步骤调节的旋钮,而不是一个应该默认调到最大值的选项。一套工作流不需要高度自主也能称为 Agentic——它只需要让模型在真正依赖判断的步骤做出真实决策,而不让模型负责其他事情。提前决定哪些步骤可以安全移交、哪些步骤不能,把这套脚手架固定在代码中,然后只允许模型在脚手架内部自由推理。
如果你正在考虑是否要在自己的项目中采用这种设计,还有一点值得注意:实践中,固定步骤、让模型在步骤内部做决策,通常就是最理想的平衡点;远在“让 Agent 自己规划 pipeline”所增加的风险变得合理之前,这种方案就已经足够有效。把完全自主留给那个真正需要它的步骤。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。