让 Agent 操作真实业务系统,最难的一步,是知道它究竟有没有做成。
序行 Agent 是我独立开发的私人闭源商业项目:一个面向跨平台业务协作的 Windows 智能体工作台。用户用自然语言提出任务,Agent 在实际业务页面中查询、整理资料、导出文件,并准备跨系统客户与订单录入。
我负责从产品交互、Agent 运行时与工具桥接,到 Rust 业务执行器、浏览器集成和桌面打包的完整开发。这篇文章重点记录其中的 Agent 工程:如何让模型选对工具、依据真实页面行动,在长任务中保留事实,并把涉及业务写入的动作交给可核验的执行流程。
独立开发 · 私人商业项目 · Agent Runtime · Tool Calling · Rust · CDP
我想交付的是一条完整业务链路
一句“把这个平台的客户录入另一个系统”,落到执行层,需要先确认来源范围、读取详情、检查分页,再核对目标租户、查重、匹配字段、填写表单,最后进入保存复核。每一步都会改变下一步的条件。
这也是我按商业产品来开发它的原因:对话之外,还要有账号管理、可见的浏览器、任务进度、人工接管、保存复核、失败恢复和可分发的桌面程序。用户需要看见当前发生了什么,也需要在任务中断后继续处理已有工作。
技术上,我使用 DeepSeek Harness 承载对话与模型执行,在其上接入业务技能、工具桥和运行时约束;Rust 负责本地业务状态、浏览器控制与提交核验,Tauri 和 React 承载桌面交互。Agent 的核心工作沿着下面的循环展开:
用户目标 → 加载技能 → 观察页面 → 选择工具 → 执行动作 → 回读证据 → 继续、等待复核或结束
模型决定下一步的意图,工具决定能执行什么,业务回执决定任务到了哪一步。这个边界贯穿了整个实现。
01 · 工具越多,越需要控制何时开放
客户查询、订单读取、表单填写、文件导出、保存预览都有各自的前置条件。把所有工具一次交给模型,会让它同时面对大量相似入口,还可能在尚未获取来源资料时就尝试后续动作。
我的处理方式是按技能逐步开放能力。初始阶段只暴露技能发现、技能加载、历史检索与任务计划;加载相应技能后,再加入其业务工具。纯知识检索和查询结果交付阶段,也各自使用更小的工具集合。
下面是运行时中控制可见工具的实现,节选自 runtime/tools.mjs,仅调整排版:
1 | const visible = new Set(phaseTools || [ |
工具可见性之外,部署侧还有一层执行白名单:
1 | const allowed = new Set(phaseTools || definitions.map(([name]) => name)); |
这层约束让上游框架新增工具时,业务工作台仍然只执行自己明确接入的能力。提示词负责解释规则,运行时负责执行规则。
我也把工具接口设计成业务契约。例如,browser_action 必须接收最近一次页面观察返回的元素 ID、token 和允许动作;prepare_ainajia_save 只生成保存复核卡;present_query_results 接收查询组编号与列名,表格内容由执行器从已读取的原始字段生成。这样,模型不必再次手写一遍订单数字,也就减少了结果重述时改错字段的机会。
02 · 浏览器 Agent 的并行调用,有时会破坏因果关系
浏览器页面是有状态的。点击下拉框以后,选项才会出现;选中客户以后,关联字段才会变化。如果模型在一次响应里连续发出多个动作,后续操作可能仍然使用前一个页面的元素和 token。
因此,业务运行时会对相关模型流进行串行约束:一轮保留第一个工具调用,等待其结果,再让模型基于新状态作下一次决策。
这段流处理来自 runtime/tools.mjs:
1 | export async function* oneToolAtATime(stream) { |
这里容易忽略的是 replayState。被丢弃的调用仍可能藏在模型提供方的不透明回放状态中,所以发生裁剪时,也要移除这部分状态,让后续回放依据规范化的消息历史。只过滤界面上看到的调用块,还不够。
这项取舍会增加模型往返次数,但换来了更清楚的执行顺序。对于依赖前一步页面结果的操作,我更看重每次动作都有可靠依据。
03 · “我将继续处理”不能成为任务的结束条件
Agent 有一种很隐蔽的失败:回答流畅地描述下一步,实际执行却停在那里。另一种情况是页面只读了一部分,最终答复已经像一份完整结果。
我在 runtime/runner.mjs 中加入执行后的核验。查询任务需要实际页面记录;批次是否还有待办,要读取批次回执;表单准备结束前,执行器还会重新观察页面,再逐项对照本轮要求。打开下拉框、输入搜索词和真正选中字段,也必须分开判断。
对于仍以“下一步计划”结束的特定任务,运行时会先重新观察页面,再进行有上限的续接;最多续接两次,仍未取得结果就显式留下未完成状态。相关判断结合了结构化进度与受限的文本识别,所以它是一套针对已接入场景的纠偏机制,不能保证任意模型、任意网页都自动完成。
另一个细节是重复失败动作的预算:页面重新观察会产生新 token,但这不应让同一个失败动作重新获得无限次尝试机会。观察更新了证据,不能抹掉失败历史。
我希望最终交付的是可追溯的结果:查询对应已读页,导出对应真实文件,写入对应保存与回读回执。模型的总结只是这些结果的表达层。
04 · 长任务的上下文,要同时保存事实和未完成事项
跨平台任务会不断积累页面快照、工具结果和字段信息。上下文变长以后,如果直接总结整段聊天,很容易把“准备填写”压缩成“已经填写”,或者丢掉尚未读取的分页。
我的实现把信息分成三个用途:
| 信息 | 保存方式 | 恢复时的用途 |
|---|---|---|
| 用户原始目标与当前页面规则 | 独立的上下文区段,按原文注入 | 保留范围、数量、日期与操作限制 |
| 已观察到的查询结果 | 查询组、已读页与原始字段记录 | 通过 read_query_evidence 按组和页回读 |
| 任务来源、批次与业务回执 | Rust 侧持久化任务状态 | 判断已经执行什么、还能继续什么 |
对话摘要则承担交接作用。我扩展了 Harness 的压缩引擎,将摘要固定为四部分:已执行、已核实结果、未完成、下一步。摘要过程关闭工具,并检查模型流是否正常结束、输出是否包含有效文字;被截断或为空的摘要不能作为正常结果接受。
这样做并不意味着摘要不会出错。因此,数字明细仍然可以回读,提交前仍然需要实时核验,旧摘要里的元素编号和 token 也不能直接拿来操作。
同时,网页内容、历史消息和查询结果都按数据处理,不能因为其中出现了“忽略规则”之类的文字,就扩大工具权限。保留上下文与授予操作权限,是两个独立的问题。
05 · 写入业务系统,需要把“已创建”和“已核验”分开
商业场景里,最麻烦的状态往往是:请求已经发送,客户端却没能确认结果。直接重试,可能生成两条客户记录或订单;直接宣布成功,又可能交付错误数据。
我把保存做成分阶段流程。Agent 先准备表单,宿主拦截待发送的保存请求并生成复核卡;用户确认后,执行器重新读取表单,检查是否仍与预览一致,再核验请求、查重并放行对应保存。
以下是 src-tauri/src/submission.rs 中确认阶段的两道检查,节选并调整排版:
1 | let current = validate_form(&page)?; |
即使平台已经返回新增记录 ID,执行器也会先标记 created: true, verified: false,再按记录 ID 回读并比较字段。比较通过,才进入已核验状态;结果不明确则保留待核验状态,阻止自动重做。这是一套防止重复写入的本地控制,并不等于外部平台提供了端到端的 exactly-once 保证。
字段核验也不能只比较文本。订单涉及数量、单位、单价、税率和币种,我使用十进制定点表示与整数计算来处理相关校验。来源信息被遮蔽、字段缺失或精度无法满足目标要求时,执行器会指出缺项;同一产品出现在多行,也按来源项次保留,不能擅自合并。
这些约束让 Agent 的操作结果可以进入业务复核流程,也让错误有明确的停靠位置。
06 · 一次草稿恢复问题,让我重新检查了任务边界
开发记录里有一个具体问题:用户要求“继续当前客户草稿”,指令已经进入业务执行器,但旧版本没有恢复原客户批次,执行过程因此错误地绕回订单页面。
单靠向模型追加一句“继续刚才的任务”,无法修复这个问题。缺失的是执行器中的来源状态,而不是聊天里的任务描述。
在 2.3.17 中,我将恢复路径改为按草稿类型与来源身份匹配:客户草稿核对客户来源,订单草稿核对订单来源,同时恢复原批次并保留本次补充条件。已完成任务、其他来源的草稿和不构成续办指令的疑问句,不能被误当作当前待办;过期保存回执也不会随任务恢复复用。
这次修正让我更明确了一条原则:恢复 Agent 任务,需要恢复可验证的业务状态,再重新观察当前环境。 聊天记录可以帮助理解意图,却不能代替这两步。
从 Agent 运行时到桌面产品交付
围绕这套执行内核,我完成了桌面端的产品整合:对话区与嵌入式浏览器协同显示,任务进度与复核卡呈现执行状态,本地凭据通过 Windows 凭据管理器保存,便携构建负责携带所需运行时与浏览器资源。打包时还会核对版本和资源哈希,减少源码与交付包不一致的问题。
对我而言,全栈开发在这里意味着贯通整条链路:模型产生工具调用,运行时约束执行,Rust 保存业务事实,界面让用户理解状态,桌面包把这些能力交付到实际使用环境。Agent 技术最终要体现在这条链路能否被观察、核验和继续处理。
当前版本为 2.3.17 测试版。 已有真实平台登录、来源读取、客户查重与部分表单填写的验证记录,也已生成 Windows 便携构建;工具约束、上下文处理、保存复核与任务恢复均有代码实现及相关局部检查。完整的跨平台保存、客户批次与订单流程仍在进行实机验收,尚不能将这些局部结果视为完整生产验收。
项目为私人闭源商业项目,暂不提供公开源码与下载。本文特别声明:展示的代码节选用于解释工程实现,不代表对项目完整源码授予开源许可;代码节选不适用页面默认的 CC BY 4.0 声明。