把 Coding Agent 接入真实商业软件:Foundax 的架构实践
结合真实业务场景,探讨如何让 Coding Agent 参与业务系统工作。Agent 负责业务理解、探索推理与通过代码组织任务,宿主平台则负责数据授权、持久化状态管理、真实业务写入与人工协作编辑,兼顾灵活性与系统边界。
工具调用适合边界清晰的单一步骤,但在面对复杂电商工作流时,多工具组合会带来显著的上下文与推理开销。本文回顾从工具调用到代码执行的架构演进,分享沙箱隔离、授权视图、不可变快照、版本水位与受控写入的工程考量。

在 Foundax,一个商家可能会这样提出需求:
帮我准备周末活动,挑出库存充足的商品,补齐缺失的中英文介绍,再配上合适的优惠。先让我看看改动。
这句话里没有表名、接口,也没有规定执行顺序;商家表达的是一件完整的经营任务。但对 Foundax 而言,接下来的工作远不止生成几段文案。系统需要定位商品与规格、计算可售库存、检查现有内容、理解促销规则,最终将修改安全地送入真实业务系统。
不仅如此,任务执行期间随时可能有其他商家在后台调价,新订单可能占用库存,另一位运营也可能在修改同一份商品介绍。
为了应对这些复杂性,我们经历了“封装工具—整理业务能力—调整 Agent 执行循环”的演进。随着任务组合愈发复杂,我们逐步引入了带有 Shell 和文件系统的隔离代码执行环境(Sandbox,下文简称“沙箱”),让模型通过编写并运行程序来完成任务。 这在减少逐轮协调开销的同时,也带来了数据准备、执行隔离与状态同步等新的工程挑战。本文将复盘这一架构演进的工程思考。
Foundax 的底层连接着商品、库存、内容、促销和交易等多个业务域。商家提出的任务,往往需要跨域协同。将开头的需求具体化,实际上是:只选可售库存超过 20 件的商品,排除已参加优惠的商品,仅补充缺失的英文介绍,最后生成周末折扣方案。
如果将这些能力封装为供大模型调用的工具,首先面临的就是“工具粒度”的抉择。
一种做法是按完整任务封装,例如提供一个“准备周末活动”工具。它将选品、内容和优惠打包处理,模型调用非常便捷。但下一位商家可能只想补介绍而不改优惠,或者要求保留现有折扣仅挑出库存充足的商品。为了支持无尽的变化,你要么不断为大工具增加参数与分支,要么持续开发新工具。
另一种做法是按数据接口拆分:查询商品、查询规格、读取库存、读取翻译、查询促销、更新介绍、创建优惠。这样组合更自由,但原本藏在大工具内部的协调工作就被推给了模型。它需要把商品 ID 带入规格查询,将规格与库存关联,再用商品 ID 检查翻译和促销,不仅要记住符合条件的对象, 最后还要分别准备修改动作。
当然也可以取中间粒度,例如将商品和规格合并读取,将优惠准备做成一个完整命令。这能减少一部分调用,却依然需要架构师去决策:哪些关系提前封装?哪些关系留给模型动态组合?
| 封装方式 | 在这个任务中省下什么 | 又需要处理什么 |
|---|---|---|
| 一个“准备活动”工具 | 模型不用协调内部步骤 | 商家只需要其中一部分时,需要额外参数或其他入口 |
| 商品、库存、翻译、促销分别提供工具 | 可以按需组合 | 模型反复选择工具、传递 ID、关联结果和维护中间状态 |
| 按业务对象与完整命令组织工具 | 常见组合更直接 | 跨对象任务仍需动态组合,能力目录也要持续维护 |
商家的需求永远不会严丝合缝地沿着系统的接口边界展开。对于边界明确的查询和操作,传统工具非常合适;但当需求跨度大、组合难预测时,如何让执行过程依然顺畅且经济,是 Foundax 需要进一步解决的问题。
随着业务能力扩充,工具目录本身演变成了一个需要设计的系统。全部塞入上下文会让模型阅读大量描述;按需检索,则要先精准命中候选工具,必要时还需多轮召回。目录、命名、召回机制的开发与维护成本高昂。
更核心的问题在于,即便工具找对了,任务里的数据关联依然是巨大的负担。模型看完库存结果后,可能发现还缺一个促销条件,于是再次调用工具、读取结果、调整筛选逻辑。商家若补充一句“只改英文”,前面的中间结果又得重新校验。
智能体执行循环(Agent Loop)允许模型根据中间结果继续行动,但每一次往返,都意味着模型需要重新处理已有上下文、解释返回值并决策下一步。尽管批量接口和并行调用能降低一定开销,但依赖前序结果的组合协调成本依然存在。对用户而言,这体现为漫长的等待;对系统而言, 则是推理算力、Token 和运行资源的剧烈消耗。
当任务跨越大量工具时,检索工具、理解参数、结果关联、执行反馈与失败重试,都在消耗同一份预算。如果中途涉及参数修正,极易在任务完成前就耗尽 Token、执行轮数或响应时间限额。
因此,单个工具运行正确,并不足以保证组合任务具有可接受的成本与成功率。我们必须将那些原本可以由程序连续完成的确定性步骤,从昂贵的多轮模型推理中剥离出来。
设计 Agent 时,我们很容易陷入现有后台界面的惯性:先到商品页改介绍,再到库存页调数量,最后去营销页建活动。我们最初也按这些入口组织工具,让模型学习每个入口怎么填、返回什么、下一步去哪。
这种映射固然直观,也便于复用现有接口。但这并非工程师处理复杂任务的思维方式——工程师往往先看数据关系:商品包含哪些规格?库存如何计算?介绍按何种语言存储?哪些商品已绑定促销?
关系明确后,筛选与组合就能化作一段干净的代码。例如:可售库存等于现有数量减去预留数量;库存记录通过规格关联商品;商品介绍通过商品和语言定位;促销关系用于排除已优惠的对象。这些条件完全可以放在同一次计算中,直接输出需要补充的内容与优惠建议。
这促成了一个朴素的架构转变:能否让 Agent 也先阅读表结构、字段说明和关联关系,然后用代码来编排业务任务?
对于擅长阅读代码和编写程序的模型而言,这是一种极其熟悉的工作方式。它可以把一次任务的计算逻辑写下来、运行、检查,并按需修改,而不再把每个中间步骤都铺开成一次业务工具的选择。模型依然在理解业务含义,改变的只是理解和操作的载体:用结构化的业务契约和可执行的程序, 去承接那些难以预先穷举的需求组合。
明确了这一转变后,我们将授权范围内的源码、业务说明与数据快照挂载到沙箱中。模型通过 Shell 检索文件、读取 Schema 并运行脚本,按任务需要探索业务。
这里的“数据表”是面向任务的业务视图。除了字段类型,还必须向模型澄清:一行代表什么、主键与外键是什么、金额和数量的单位,以及数据覆盖哪些商品与语言。例如,价格和库存属于规格,多语言介绍属于“商品×语言”,这些关系应直接写进契约。
回到周末活动的例子,Agent 此时可以读取视图,编写一段脚本完成库存筛选、促销排除和内容缺失检查,最终输出具体的建议。当商家调整库存门槛时,只需修改脚本重新运行即可。中间集合与计算结果保存在会话文件中,免去了模型在多轮文字交互中反复搬运数据的负担。
需要强调的是,这并不意味着一项任务只需一次模型调用,也绝非将工具调用(Tool Calling)与代码执行对立。模型仍会探索、修正和验证,它依然需要通过工具读取文件、运行命令、提交产物。Tool Calling、Agent Loop 与沙箱执行共同构成了完整的任务链路。
在这个链路中,职责变得更加清晰:模型负责组织计算逻辑、解释结果与处理非确定性判断;而确定性的关联与批量计算则集中交由脚本在沙箱中执行。

模型拥有了决定如何组织任务的自由,而底线的防守——哪些修改真正成立——依然由业务服务牢牢把控。
允许模型编写程序后,执行边界就成了架构设计的核心。脚本需要文件与计算能力,但绝不能获取生产数据库的直连账号,更不能继承应用进程的完整权限。
我们将生成代码的执行环境与可信宿主严格分离。宿主负责校验用户身份、准备允许读取的数据、下发命令并取回产物;沙箱仅负责纯粹的脚本运行。文件访问、网络请求与计算资源由沙箱限制,而核心的业务写权限依然由宿主后端掌控。
例如,脚本根据库存计算出一份修正建议并写入输出目录。这份文件随后会被移交给后端进行业务校验,绝不能仅仅因为“代码在沙箱里跑通了”,就直接篡改真实的生产库存。代码执行权限、数据读取权限和业务写入权限,这三者必须独立成立。
此外,执行隔离必须兼顾交互延迟。我们曾对内部执行器的会话准备与首次命令执行进行过专项优化,基准记录如下:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 会话启动耗时 | 约 2.1 秒 | 约 1.0–1.4 秒 |
| 首个工具链命令耗时 | 约 0.87 秒 | 约 0.10–0.15 秒 |
必须指出,这组测量数据仅对应执行器自身的准备与命令调度开销。模型推理、数据导出和业务最终落库的耗时需另外统计,整项任务的延迟与 Token 成本仍需结合完整链路评估,不能将其等同于端到端任务耗时或 Token 节省。将程序执行置于沙箱内只是架构起点; 唯有持续压降会话启动、重复准备和命令调度的开销,代码执行机制才真正具备连续交互的可用性。
沙箱执行带来了探索与计算的灵活性,但“准备环境”本身代价不菲。如果每个会话都全量复制一份源码,反复导出同一批业务数据,不仅浪费存储,还会拖垮启动速度并增加数据库查询压力。但若为了缓存而长期不更新数据,又会严重影响后续的业务判断。
因此,源码、业务数据和任务文件必须分层管理。它们的变更原因不同,共享范围也截然不同。公开参考设计将文件系统划分为四类逻辑挂载点(Mount Points):
| 内容 | 为什么单独管理 |
|---|---|
| /repo 源码与业务说明 | 跟随构建和访问策略变化,同一可见范围可以共享只读产物 |
| /data 授权业务数据 | 跟随商品、库存、规则和权限变化,需要判断何时刷新 |
| /work 任务脚本与中间结果 | 属于当前会话,刷新数据时仍应保留有用工作 |
| /outputs 修改建议与交付物 | 交给平台检查、展示和执行 |
源码以不可变快照(Immutable Snapshot)形式沉淀为版本确定的只读产物,在允许读取同一内容的会话间复用。而业务数据必须严格绑定租户、资源范围与权限进行准备,绝不能因为两个人在执行相似任务,就让他们共享同一份数据。
数据刷新的难点在于识别依赖。例如库存变更会影响商品可售状态,但通常不需要触发营销资料的重新准备。按业务域声明依赖,并记录数据源的版本水位(Version Watermark),系统便能精准识别需要重建的快照范围。至于已写好的计算脚本,则安全存放在会话目录中, 待新数据挂载后继续运行。
这里还隐藏着一个极易被低估的一致性陷阱:导出的数据与记录的版本,必须属于同一个状态截面。如果先读取旧数据,期间生产库发生业务写入,随后又打上了最新的版本号,缓存就会将脏数据误认为最新。依赖版本如何可靠更新、数据与版本如何强一致读取,是快照方案需要单独解决的工程问题。
我们在单次命令执行期间固定数据版本,在命令切换时才挂载完整的新版本数据,以此保障单次计算输入的稳定性。但这依然不能替代最终落库时的当前状态校验。
假设 Agent 已根据库存成功生成了优惠方案。在用户审阅并点击“确认”之前,线上的新订单预留了一部分商品。此时,原方案可能已经打破了库存约束条件。
因此,计算完成、用户确认和业务写入必须是解耦的三个阶段。 平台需要持久化具体的修改建议,向用户清晰展示被修改的对象与字段;而在最终执行时,后端必须重新校验权限、字段合法性以及实时的业务状态。一旦发生冲突,需要拒绝执行并重新读取、调整方案。
一次横跨多个业务域的任务,还会遭遇事务与重试的挑战。商品介绍更新成功,但促销创建失败了,该如何呈现结果?系统自动重试是否会导致重复创建?哪些修改可以放在同一事务里?这些问题需要由具体的业务服务和任务协议来兜底解答。仅仅把建议打包进同一份输出文件, 并不会自动获得跨系统的原子执行能力。
执行结束后,必须重新读取真实状态。模型回答的“已经完成”,不能代替后端的最终确认。简而言之,沙箱为模型提供了广阔的组合与计算空间,而平台则负责将这些计算结果,重新收束进严谨、可校验的业务操作中。
通用 Agent 的执行架构目前已有较成熟的基础积累。例如,OpenHands Software Agent SDK 提供了模型、工具、会话管理和远程执行等底层能力。 业务团队完全可以在此基础上搭建自己的智能产品。
但更贴近 Foundax 现实的考问是:当用户的诉求跨越多个垂直业务域,且组合方式难以预测时,如何让 Agent 在明确的权限、状态和资源边界内,将任务交付得更顺畅?如何规避重复查库与不必要的模型往返?数据变动后工作流如何接续?用户确认的建议又如何稳妥地着陆于原有业务服务?
“基于沙箱的代码执行”是我们沿着这些问题探索出的一条路径。它将动态探索与逻辑组合交还给模型最熟悉的编程环境,同时也倒逼平台认真承担起快照流转、沙箱隔离、状态续跑和受控写入的工程责任。维护这套机制本身不乏成本, 团队需要持续观测任务质量、准备耗时、数据新鲜度与 Token 支出,绝不能单纯因为“跑通了一段代码”就断言该方案更具经济性。
我们将这部分实践沉淀在了 Agent Business Workspace 中,公开了数据契约、快照生命周期和受控写入的工程设计。 仓库内还提供了一个可运行的库存盘点示例,基于合成数据演示了从生成修改建议,到业务服务校验作用域、字段、库存约束与记录版本的全流程。沙箱挂载、快照缓存与自动刷新机制在设计文档中有详细展开,本地示例则聚焦于读取、计算与修改的校验回路。
这些挑战广泛蛰伏于各类企业级商业系统中。电商要关联商品与库存,CRM 要串接客户与互动,ERP 要匹配物料与采购;无一例外,都需要直面复杂的鉴权、流变的数据与严苛的真实写入。
我们期待与更多正在业务中落地 Agent 的团队继续交流:如何在赋予大模型足够组合空间的同时,将其任务质量、响应时间和资源开支控制在合理范围内。欢迎通过仓库的 Issues、Discussions, 或邮件[联系 Foundax 团队](mailto:contact@novariseintl.com)(contact@novariseintl.com),探讨真实的业务场景与工程经验。