Flowness / 多 Agent 系统真实问题 · 首期

十四处引文核验通过,为什么“谁导致了交付失败”仍然判反了

一次 Flowness 历史调查中,十四处证据抽验都能对上,核心归因却被接收方记录推翻:子会话写完了报告,主会话并没有收到。本文沿原分析与独立复核,解释启动模式、工具权限与派发说明怎样错配,以及为什么引文准确、计时准确,都不能替尚未核实的因果关系担保。

我们在复盘 Flowness 的一次多 Agent 协作时,遇到了一份很容易让人信任的调查报告。它读了完整的执行记录,给重要判断标了原文坐标,还查过角色的工具权限。后来的独立复核抽验了十四处,记录都能对上。但当复核者打开报告接收方的会话,一条核心解释反了过来:原分析认为顾问和催报要求错了,实际运行却表明,派发说明才写错了交付方式。

这个案例使我们开始认真区分两件经常被一起叫作“有证据”的事:一句话是否真的被说过,以及它是否足以解释事情为什么发生。前一件事查得很细,不会自动让后一件事成立。

Flowness 是我们自己开发的 Harness 项目,用来组织多个 AI Agent 持续协作,管理任务、上下文、工具调用与工作记录。多个 Agent 分工时,一个会话负责研究,另一个会话接收结果、决定下一步。研究者把报告写好,只完成了其中一段;报告还得沿着实际可用的通道送到需要它的人那里。这个交付关系,恰好是原分析没有打开的部分。

只看发送方,解释为何显得合理

2026 年 7 月 3 日,我们正在调查系统里的锁。主会话派出一个只读研究子会话,要求它梳理哪些环节有锁、上一轮修复做了什么、还有哪些缺口。派发信约定了报告结构,并明确告诉它:最终文本就是返回值。角色允许读文件、搜索和执行命令,没有启用发送消息的工具。

研究进行得相当顺利。子会话查了代码、历史任务和系统状态,也咨询了顾问。顾问在收尾建议中要求它使用 SendMessage,主动把报告发给负责人。子会话先写出了完整的最终报告,随后又收到负责人催报:已经看到它空闲,但还没收到报告,请发送正文。

它尝试照做,工具返回“当前上下文没有启用 SendMessage”。于是,它再次把报告写在最终文本中,解释自己无法使用发送工具。站在这个子会话里看,事实很容易组成一条顺畅的故事:它已经按派发信完成交付,顾问和催报却要求一个它根本没有的工具,迫使它重复整理相近内容。

原分析就是这样判断的。它并未编造权限限制,也没有伪造顾问的要求;角色定义和工具报错都支持“这个子会话无法发送消息”。它还量到催报消息在某个相邻事件之后176毫秒到达,据此认为催报是脚本或模板自动生成的。权限错误、快速催报、重复输出,几项事实似乎互相印证,最后被写成高置信度归因:顾问没有理解研究角色的特殊权限,催报又重复了同一个错误。

但在报告开头,作者也诚实地登记了一项缺口:没有读主会话。它不知道报告究竟有没有送达,却已经把“报告本来已经交出,因此催报没有必要”当成了解释的前提。没有读的对象被留在一份清单里,没有读造成的影响却没有进入结论。

接收方记录改变了对失败的解释

独立复核先抽验了十四处记录,再去补查原分析没有覆盖的接收方。两项工作得到的结果并不矛盾:抽到的记录相符,原归因仍然可以错误。那十四处核验包括案文的不同部分,说明被抽验的证据基本如实记录;它们不是十四次因果实验,也不是报告整体正确率。复核还在别处发现了坐标使用问题,因而更不能把这一结果扩展为“全篇引用都已验过”。

真正改变解释的,是主会话中的启动回执。那次启动采用异步消息模式,工具返回的是“子会话已启动、将通过邮箱接收指令”的确认,随后主会话继续运行。子会话末尾写出的文本,并没有作为调用结果自动回到主会话。派发信所说的“最终文本就是返回值”,描述了另一种返回关系,却被用在这次异步任务上。

接着,复核看到了催报前发生的事。主会话收到的是空闲通知,没有收到研究报告正文。负责人说“还没收到报告”,描述的是它所处一侧的实际状态。原分析只在发送方看见报告已经存在,就把接收方仍然缺少报告理解成了错误催促。

176毫秒的解释也随之改变。接收方记录显示,主会话在06:44:05收到空闲通知,到06:44:21才发出催报,间隔约16秒。原分析测到的176毫秒,是后续消息投递环节的时间差。这个数字可以算得完全正确,但它回答的是投递时序,不能据此认定催报内容必定来自脚本。若要判断是谁生成了消息,就需要查生成端,不能拿接收端相邻两条记录的距离替代。

最后,复核看到人于06:51左右把约五千三百字符的报告手工粘进了主会话。原分析认为没有新增信息的那份文本,通过这次搬运才真正抵达消费者。内容在子会话里相近,并不意味着它对等待中的主会话没有价值。对整个任务来说,可见的损失包括约七分钟的等待和一次人工转交;只计算研究者多写了多久,会漏掉交付未完成造成的成本。

这次反转不能被缩写为“顾问全对,派发信全错”。顾问要求主动发送,符合这次异步任务的交付需要;但研究角色确实没有发送权限,单独给它这条建议仍然不能完成交付。复核没有消除这个权限事实。更准确的解释是:实际启动方式要求主动回传,角色工具却不能完成回传,派发说明又让执行者相信写出最终文本即可。三者组合在一起,没有形成可执行的交付路径。

发送工具缺失这一事实没有被推翻。复核改变的是它与交付失败的关系:最终文本未自动回流,催报反映了接收方确实没有报告。十四处抽验说明被抽到的记录相符,不能代替对这条交付关系的核验。

同团队的另一个研究子会话也遇到了类似回传问题,并依靠人手转述完成补救。这让我们有理由继续追查启动方式、角色权限与派发文本之间的相容性。不过,两个现场仍然只是这个团队里的两个现场;它们足以暴露具体错配,尚不能告诉我们它在所有任务中有多常见。

因此,这个问题包含两层。运行层的问题是报告无法按要求送回去,调查层的问题是我们差一点修错地方。如果沿用原归因,后续很可能要求顾问停止建议主动发送,再让子会话放心地输出最终文本。这样可以减少一次失败的工具调用,却没有让等待中的主会话收到报告。错误归因的代价,会沿着修复决策继续向后传递。

数字算对了,也要确认它测量的对象

我们在另一份共识机制调查中也看到过相近的推理跳跃。原分析认为,一项修复验证通过后,等了约11小时才进入主线,等待期间还在继续产生新的错误派发。尾部复核把时间统一到同一时区,发现从修复提交到合并约为1.5小时,从首次独立复核到合并约为50分钟。原来的11小时,把UTC时间和本地时间相减了。

同一复核又检查了“还在发生”的依据。探针数出的两个错误实例,正是最初被诊断的那两例,并没有给出等待期间新增的实例。这里有两个必须分开的判断:修复尚未合并了一段时间,以及这段时间内出现了新的受害任务。确认前者,不能自动确认后者。这个案例没有推翻“部署延迟可能造成损失”的机制假设,但它撤掉了用这段经历证明持续损失的依据。

两份调查的共同困难,是一句自然语言结论往往包着几个不同命题。比如“顾问要求研究者使用没有授权的工具,因此造成了无意义的重复交付”,里面至少有工具权限、顾问原话、此前是否已交付、重复输出是否无价值,以及各项之间的因果关系。前两项可以通过逐字核对确认,后面几项需要接收方记录和实际运行方式。若整句话只标一个“高置信”,被核实的部分就很容易替未经核实的部分担保。

这也解释了为什么增加引用或再请一个模型核对,仍可能没有碰到问题。只要检查者一直在作者挑好的材料里查“这句话是否存在”,它就在反复验证同一种文本事实。本案中的独立复核之所以有效,是因为它允许补读作者没有选择、却决定结论是否成立的对象。独立性不仅来自换了一个模型,也来自检查者能改变调查范围。

让证据缺口真正改变调查动作

我们从这里得到的一个具体办法,是让缺失证据直接影响依赖它的判断。如果没有打开报告消费者,仍然可以确认“发送工具不可用”,但应该把“催报没有必要”留作待核实解释。下一步也应随之改变:先检查是否收到报告、当前用的是什么返回方式,再决定归因。单纯在末尾加一句“部分材料未读”,不会替我们完成这个调整。

原研究据此提出过一条自检建议:将“明确没看的对象”与报告中被判定为“说错了、没做、发出了错误指令”的对象交叉检查,命中后降低相关结论的置信度。它有助于发现本案这种缺口,但还不是自动判断因果的通用办法。有时材料已经读过,真正的问题仍然是用了错误时区、错误测量区间,或者把旧实例计数当成新损害。检查还需要追问:这份证据究竟验证了结论里的哪一部分?

接下来值得验证的,是这种检查能否改变调查行为。可以从历史报告里取出带有强归因的完整句子,先写清每个子判断所需的证据,再让复核者逐一检查支持关系。我们尤其关心:它是否会促使研究者补读决定方向的材料,是否能减少错误归因,又会增加多少调查成本。现有材料展示了归因被纠正的过程,并没有给出这套方法的整体检出率。

运行系统也有相应的验证方向:创建子任务时,让实际交付方式、角色可用工具和派发说明能够对得上;任务结束后,从接收方确认产物确已送达。本案里真实发生的补救是人工搬运,后续设计是否有效,需要在实际协作中观察报告能否到达、无人补救时如何暴露失败。写出了相容性检查的建议,还不能替代这一步。

我们继续记录这些问题,是因为 Flowness 不仅要组织 Agent 工作,也会依赖 Agent 调查自己的运行情况。系统越能产出带引文、带数字、带置信度的报告,越需要分清这些标记实际证明了什么。一份报告可以对每条已读记录都很认真,同时漏掉决定解释方向的关系;修复这个缺口,需要让核验从文字本身继续走到工作如何发生。