Post

从自研 Harness 到 Pi:商品图片产品应该掌握哪些 Agent 能力

从自研 Harness 迁向 Pi 的实践出发,讨论商品图片产品应该保留哪些业务能力,以及运行时维护成本和架构边界如何取舍。

项目 阅读 17 点赞 0 评论 0

从自研 Harness 到 Pi:商品图片产品应该掌握哪些 Agent 能力

一次运行时迁移背后的维护成本与业务边界

自己写 Agent 运行时,最吸引人的地方是几乎什么都能改。模型循环不合适,可以改;工具反馈不够,可以加;对话里想显示新的执行步骤,也能直接接进去。

但当你做的是一个商品图片产品,下一周最重要的问题可能是:为什么用户只想精简第二张图的文案,系统却改动了整套方案?你当然可以继续优化运行时,却仍然需要回到商品资料、对象关系和实际写入,才能修好这次工作。

ProductFlow 曾使用自研 Go harness,后来将通用模型循环迁到 Node.js 中嵌入的 Pi SDK。迁移改变了我们需要长期维护哪些能力,也留下了一些不能交给 SDK 决定的事。

这篇文章讨论这次选择的理由与代价:什么时候自研值得,SDK 与 RPC 各自隔离什么,以及为什么一个能够保存会话的 Agent,仍然需要自己的业务恢复设计。

1 从一条修改要求看运行时的责任

1.1 用户交给系统的是一项有边界的工作

ProductFlow 希望帮助商家得到符合预期、能够实际使用的商品素材图。用户会上传照片、补充参数、安排主图与卖点图,也会对已经完成的内容继续修改。Agent 的价值需要落实在这些工作中。

考虑下面这条要求:

第二张卖点图的文案太多,保留容量信息,把其它文字精简。先让我看方案,不要重新生图。

这是贯穿本文的设计场景,不是一次已记录的用户实验。正确执行至少依赖四个判断:找到第二张图及其方案;分清容量事实与可以改写的宣传表达;判断方案是否被其它图片共享;生成待审阅建议并在生图之前停下。

一次模型循环可以完成读取、推理和工具调用,但这些步骤的业务含义来自产品。工具即使返回成功,也可能只生成了候选;用户还没有采用,正式方案就不应发生相同变化。模型最后说“已完成”,同样不能证明图片没有被意外重做。

因此,我们需要选定的不只是一个可以调用模型的库,还包括一套明确分工:谁帮助模型选择行动,谁决定行动是否允许,谁记录行动实际造成的结果。

1.2 自研带来的收益与成本

早期自研 Go harness 给了我们直接修改模型循环、工具执行和事件投影的能力。在产品仍在探索阶段时,这种控制很有用:交互缺了什么,可以在相应层次增加机制,无需等待上游支持。

但通用执行机制会持续产生独立于商品业务的工作。模型协议变化、上下文压缩、流式事件、工具结果接续,都需要维护和验证。与此同时,产品还在改变方案结构、参考图用途和用户确认方式。

两类工作都重要,却没有天然相同的投入优先级。商家遇到“改错了图片”,并不会因为底层模型循环完全自研而减少返工;工程师把大量时间用于模型接入,也不必然改善商品保真。

我们开始用一个具体问题审视自研范围:下一项最有价值的产品改进,需要改变通用执行机制,还是需要改变模型能看见什么、能调用什么,以及业务如何处理调用?

如果高频问题集中在后者,继续掌握全部模型循环就需要额外理由。控制力只有在经常被正确使用时,才抵得过维护成本。

2 选型的判断单位:未来会发生的变化

2.1 将能力比较改成变化比较

单独比较功能清单容易得到过宽的结论。自研可以实现很多能力,成熟运行时也往往提供很多入口;真正限制产品演进的,是一个需求会迫使多少职责同时变化。

可能出现的需求应当主要改变的部分用来检查边界的反例
精简文案时必须保留已确认容量领域指令、观察和候选校验为新增商品规则重写模型循环
支持新的模型事件格式模型适配和事件归一浏览器开始解析上游原始事件
对某类写入增加确认条件业务命令与确认流程只有对话入口遵守,手工入口绕过
上下文变长,需要压缩历史会话机制与上下文组织把商品事实只保存在压缩摘要里
中断后判断方案是否已经提交业务事务与执行记录根据模型是否收到回复决定重做

这张表没有要求一项需求只能修改一个模块。跨层改动可能确有必要,但各层应当围绕同一条因果链工作。新增一个容量字段,与修正流式协议,不应长期要求同一组业务规则在多处重写。

2.2 三条路径的成本结构

迁移时可以继续扩展自研 harness,也可以复用 Pi。复用以后,还要在 SDK 嵌入与独立进程协议之间选择。

路径主要收益留给产品团队的成本更值得采用的条件
继续自研 harness直接改变执行循环与调度语义模型适配、会话机制、执行协议及持续回归产品差异长期依赖通用运行机制的特殊行为
在服务内嵌入 SDK工具、会话和事件可以直接装配上游升级、适配代码及服务内资源管理需要成熟循环,同时需要深度定制业务入口
通过 RPC 接入执行进程独立管理进程生命周期与部分故障进程拉起、通信、断连、事件对齐和资源管理已有明确的进程隔离或独立部署需求

这里没有为某条路径给出总分。我们没有完成同条件性能、开发工时或稳定性实验,无法将迁移写成一个量化胜出的评测结果。表中是责任和成本的分析,最终选择也受当时团队规模与已有实现影响。

Pi 的官方 SDK 提供会话、工具扩展、资源装配和事件订阅等入口,使它能够被嵌入现有应用。这些入口与 ProductFlow 当时希望复用的部分相符。商品规则是否正确,还需要由产品自己的调用和验证证明。

2.3 SDK 与 RPC 改变的是哪一层隔离

ProductFlow 的 Agent 原本已经是独立服务。选择 SDK,意味着将 Pi 放进这个服务的进程;选择 RPC,则可能在服务内部再管理一个执行进程。

独立进程可以让某些崩溃和资源终止更容易单独处理,但必须明确进程粒度:每个服务一个子进程,与每个会话一个子进程,故障影响范围不同。它也不会自然提供文件访问隔离或业务授权,更不会使跨服务写入变成原子事务。

在没有相应故障证据时,我们选择 SDK 作为默认集成方式,将额外进程隔离保留为可重新评估的方向。以后若出现会话资源相互干扰,或确需运行不受信任的扩展,就应依据故障与权限需求重新设计隔离。单纯增加一次 RPC 调用,不足以完成这些目标。

3 迁移后的架构:领域壳连接模型与业务

3.1 已经发生的变化

2026 年 8 月,我们决定采用 Pi,随后替换了主线的 Go Agent 服务,将自研 harness 保留在实验方向。业务后端迁到 Go 则发生在随后阶段。

因此,当前的 Go 与 Node 分工来自两次不同调整。语言选择影响部署与维护,但理解架构需要看数据和决定权的归属。

商家目标:修改指定图片的方案领域壳:指令、Skill、上下文与工具Pi:模型交互与会话推进ProductFlow 工具请求Go:对象、版本、确认与业务提交PostgreSQL:业务状态与持久事件工作台:实际方案、候选与任务进展工具反馈

图 1. 模型可以提出行动,业务后端决定行动能否成立。 图中按职责组织,省略具体传输和进程管理。

领域壳包含模型完成商品任务所需的指令、Skill、上下文和工具。它需要告诉模型“第二张卖点图”对应什么对象,也需要暴露共享关系和当前文稿。Go 后端则检查请求是否符合业务规则,并保存真实结果。

3.2 显式工具装配意味着什么

当前 Pi 适配关闭内置工具,并注册 ProductFlow 的自定义工具;资源加载关闭默认扩展、Skill、提示词模板和上下文文件的自动发现,通过受控装配提供产品需要的内容。

这样做使一次执行的能力来源可以被检查。商品工作台没有理由因为采用编码代理 SDK,就自动获得工作目录中全部文件与进程操作能力。

不过,注册名单只能约束可请求的能力。某次调用是否可以修改这款商品、是否基于正确版本、是否已经得到确认,仍然需要业务请求逐次判断。

Skill 可以教模型何时提问、怎样选择对象,但它不能替代权限检查。模型忘记一段指令时,业务边界仍应成立;用户明确允许的操作,也不应因为两份提示词对规则解释不同而得到不一致结果。

3.3 工具粒度决定模型承担多少隐含推理

在示例任务中,如果模型只能读取一份巨大的图 JSON,再提交任意 JSON 修改,它必须自行理解共享关系、候选语义和版本规则。工具接口很通用,但业务复杂度被转移到了每一次推理中。

如果工具能清楚提供目标图片、对应方案、共享使用者和可采用章节,模型就更容易提出符合用户范围的建议。粒度过细也有代价:一次普通工作可能需要许多调用,更多往返又会增加时间、费用和中断点。

我们因此按用户实际决定的对象组织工具:读取足以判断作用范围的资料,用业务命令提交修改,让候选与正式写入具有可区分结果。是否需要进一步调整,要观察模型在哪一步反复误解或多次往返。

采用 Pi 并不会自动完成这项设计。它提供扩展位置,产品团队仍需要决定扩展应当表达什么。

4 三份进展:模型记忆、用户所见与业务事实

4.1 一次成功调用也可能留下不一致的观察

设想 Agent 已经提交候选,数据库完成事务,但工具响应返回前连接中断。模型没有收到成功结果,浏览器也可能尚未显示候选。

这时,“模型不知道”“用户还没看见”和“业务没有完成”是三种不同情况。把它们合并成一个失败状态,会让恢复过程错误地重复操作。

需要恢复的内容对应证据证据不能回答的问题
模型下一步如何继续Pi 会话消息与运行上下文某笔业务事务是否提交
浏览器漏了哪些进展已提交事件与读取游标尚未持久化的上游输出是什么
候选或方案实际变化了什么业务对象、操作与副作用记录模型是否已经理解了结果
哪个执行者还能写入当前执行身份与有效 lease外部请求是否已停止运行

Pi session 能保存模型交互所需材料,但商品状态需要回到业务后端核对。当前架构将业务终态、journal 与 effect 等职责保留在 Go;Node 承担 Pi 会话和产品协议适配。

4.2 浏览器为什么只读一份持久事件

拆成 Go 与 Node 两个服务以后,最直接的流式方案是让浏览器订阅 Node,把 Pi 产生的文字和工具事件立刻显示出来。问题出在重连:在线时看的是一条即时流,刷新后读的是另一份数据库投影,两条路径很容易对“已经发生到哪一步”给出不同答案。

当前浏览器统一从 Go 读取 PostgreSQL 中的事件日志,活动执行和已结束执行使用同一份序列。Node 先保存自己的待提交日志,再将事件批量交给 Go;Go 接受并提交以后返回回执,Node 核对执行身份、序号和事件类型,再推进本地确认位置。

这个回执很有用。如果数据库已经提交,但响应在返回途中丢失,Node 手里仍保留未确认事件,可以按同一身份继续核对。若只在收到 HTTP 200 时就笼统地删除一批本地记录,错位的回执也可能把尚未提交的事件一起吞掉。

浏览器记住已经读到的游标,断线以后继续从持久日志补齐。通知只负责提醒“有新内容”,不承担保存内容的责任。一次通知没收到,下一次回读仍能找到事件;反过来,尚未写进日志的输出,也不会靠重新连接自动出现。

我们为此增加了一段事件写入和投影代码,换来的是在线显示与刷新恢复能够解释同一段进展。这里无需保存第二套模型循环,Pi 会话仍然负责模型上下文;业务日志负责让产品找回已经确认的工作。

4.3 恢复需要找回事实,也需要拒绝旧执行者

lease 是有期限的执行权。它过期时,旧进程未必已经死亡:网络恢复后,旧执行者仍可能继续返回结果。因而恢复不仅要决定是否允许新的执行机会,还要保证旧执行者不能按过期身份继续修改当前状态。

对于已经提交的业务写入,应按操作身份查证结果,并让后续交互接上这个事实。对于外部操作已经开始、结果却缺乏证据的情况,应保留结果不明,不能仅凭缺少工具回复重新调用。

数据库Go 业务服务Pi 执行数据库Go 业务服务Pi 执行响应返回前中断候选已经存在后续交互应使用核对结果提交候选请求校验并提交候选恢复时核对操作与执行身份返回已提交事实

图 2. 部分完成场景中的恢复依据。 这是机制推演;具体续接方式还受工具与运行状态合同约束,不能推定所有中断都能透明续跑。

SDK 与 RPC 都会遇到这类窗口。增加进程边界会改变故障表现,却无法让模型得知结果与业务事务提交同时发生。

4.4 用户等待输入,也是一种正常状态

商品资料不完整时,Agent 需要停下来询问。用户可能几小时后再回答,这种等待与 worker 失联具有不同含义。恢复扫描若只依据时间判断,可能把正常等待误判为执行异常。

运行状态需要表达为什么没有继续。当前架构对等待输入、等待确认和执行中断分别处理。设计新状态时,应从实际续接动作推导:回答要注入哪里,确认批准了哪项内容,恢复后如何避免再问一次已经回答的问题。

这种交互语义属于产品。通用 session 提供接续能力,具体哪一份答案能够推进哪项任务,仍需业务身份关联。

5 适配层为什么仍然会增长

5.1 必要的翻译工作

复用模型循环以后,ProductFlow 仍有进程管理、单轮编排、事件发布、工具步骤投影和上下文装配。当前代码已经将这些职责拆分到相应模块。

这部分代码增长有合理来源。上游产生工具事件,工作台需要将它表达为“读取了商品资料”或“生成了待审阅方案”;模型看到的是消息序列,用户看到的是一项持续进行的商品任务。两者之间需要翻译。

检查适配层时,应追问它是否在翻译已有事实,还是开始创造第二套决定。若 Node 自己认定方案已采用,而 Go 记录仍是候选,就出现了两个独立决定结果的位置。即使文件很短,也会产生恢复与解释问题。

5.2 避免为了可替换而复制整个运行时

采用上游依赖会带来锁定成本:会话格式、事件类型和工具生命周期都会影响适配。为此可以将依赖控制在清晰模块中,冻结版本,并为关键交互保留合同测试。

但如果为了“以后随时换框架”而在 Pi 外面重建一套同样完整的通用抽象,就可能重新承担刚刚交出去的维护责任。抽象应服务已经出现的变化,尤其是业务协议需要稳定、上游协议确实易变的位置。

未来若要更换运行时,最有价值的资产应是明确的业务工具、可重放的任务证据和稳定的验收条件。它们能够说明新运行时是否完成同一项商品工作,远比一层名义通用的接口更有说服力。

6 怎样验证这次选型确实有用

6.1 将验证放在一项完整工作上

对“精简第二张卖点图,先看方案”这项任务,正常路径要检查目标、保留信息、候选和未触发生图。除此之外,还需要主动制造以下条件:

条件应观察的业务结果不能作为替代证据的现象
第二张图使用共享方案修改范围符合用户要求模型终答说“只改了第二张”
缺少容量依据提问或保留缺口,不补造参数对话能够正常结束
工具提交后响应中断已提交结果可核对,避免无依据重做session 文件成功重新打开
用户等待期间进程重启答案仍对应原问题与任务重新创建一个看似相同的会话
执行权已被替换,旧结果迟到旧身份不能推进新状态新执行者最终成功
浏览器重连从持久事实重建一致进展连接建立且返回 HTTP 成功

这些场景检查的是不同性质:对象范围、交互接续、旧写入拒绝和用户所见的一致性。会话能够重开,只能支持其中很小一部分。类似迁移应当分别报告这些结果,避免把正常路径成功推广为所有中断都可恢复。

6.2 维护收益需要另一类证据

代码能运行只能说明集成成立。若要证明复用降低了成本,还应在连续需求中记录:通用适配耗时、领域改动耗时、升级回归成本、故障定位时间,以及最终任务的成功率与费用。

这些指标需要稳定口径。迁移同时改变模型、提示词与业务工具时,无法把全部收益归因于 SDK;工程师熟悉度提高,也会影响开发时间。

当前能够支持的结论更窄:迁移将通用循环交给 Pi,同时保留了产品所需的业务决定位置。它提供了更明确的维护分工,收益大小仍需后续工作证明。

7 后续投入的判断

ProductFlow 的长期能力需要积累在商品事实、图种表达、可控修改和真实交付上。领域壳可以成为持续改进的对象:观察缺了什么,工具暴露得是否合适,模型在哪类任务中反复误解,这些都能形成可验证的修改。

当问题来自执行机制本身时,仍然应修改适配或上游,必要时重新审视自研范围。采用成熟运行时并不要求永久接受其全部限制。

我们会用下一次实际需求检验这份分工:新增商品约束时,业务规则能否在正确位置生效;出现中断时,是否能找回已完成的工作;升级模型时,是否能在固定任务上判断收益与退化。选型的价值最终体现在这些持续发生的工作里。

延伸阅读

文中的商品修改场景用于解释设计。ProductFlow 的迁移经验尚未经过同条件成本实验,本文不据此给出运行时的通用优劣排名。

继续阅读

继续阅读

全部归档

评论