
先说结论这套组合我跑了大半年每天省下的人工操作时间大约三个小时审单环节的错误率从人工时期的 2% 降到 0.5% 以内。老系统还是那套没人敢动代码的老系统但我给它外面套了一个“数字员工”让它自己审单、自己爬数据、自己写报告。这篇文章就是记录这个项目从零到上线的完整过程包括选型逻辑、部署步骤、核心代码、调度配置以及我在实际落地过程中踩过的坑。如果你手头也有一套历史遗留系统每天靠人工盯着屏幕做重复操作这篇文章应该能给你一条可以直接照抄的路线。把 OpenClaw 和 Java 放在一起最初并不是我拍脑袋想出来的组合。老系统的技术栈是 Java系统的核心业务逻辑也绕不开 Java 的生态而 OpenClaw 负责的是“大脑”层面的任务编排它能理解自然语言指令、拆分任务、调度工具但真正去操作老系统页面、生成复杂报告、处理回传数据这类脏活累活还是要有代码层面的承载。两者各管一段才形成了一套完整的自动化链路。1. 项目整体设计为什么是“OpenClaw Java”这对组合1.1 老系统的真实痛点是什么我之前面对的系统是一套运行了十来年的订单管理系统前端还是 JSP 页面后端是 Spring MVC 加 Hibernate数据库表结构混乱到没人敢动。系统没有开放任何对外 API所有操作都必须在浏览器里完成查询订单要登录、点菜单、输条件、翻页导出数据还要依赖系统自带的“导出 Excel”按钮而这个按钮经常超时。每天的业务流程大概是这样的上午人工打开订单查询页面筛选出待审核订单逐条核对金额、客户信用额度、库存状态然后人工改状态下午去几个行业网站和竞品网站手动复制价格数据粘到 Excel 里傍晚再把一天审核结果和价格数据汇总成日报发给相关同事。整个过程至少耗费三个小时碰上数据异常的情况还要加班。这里最关键的问题不是某一步有多复杂而是每一步都极其依赖人工介入且操作路径是固定的。这意味着它完全具备被自动化替代的条件也意味着任何一个自动化方案都必须先解决“如何操作一个没有 API 的老系统”这个问题。1.2 为什么不用纯 RPA也不用纯 Java 硬写最早我考虑过两条路用 UiPath 这类 RPA 工具或者用 Java 写一个定时任务脚本。后来都放弃了。纯 RPA 的问题在于它的录制回放机制对页面结构变化极度敏感老系统页面只要有一个按钮的 id 变了整个流程就要重新录制。而且价格数据的抓取涉及不同网站的反爬策略RPA 处理这类动态逻辑的能力很弱。更现实的问题是RPA 工具的授权费用不低为一个内部辅助功能开一笔不小的预算领导层不一定能接受。纯 Java 硬写的方案我也试想过。用 Selenium 模拟浏览器操作订单页面用 HttpClient 爬数据用 POI 生成报告技术上完全走得通。但这条路有个更大的问题所有的任务编排逻辑全部固化在代码里哪天想改一下审核规则、换一个数据源、调整报告发送时间都要改代码、重新打包、重新部署迭代成本非常高。而 OpenClaw 这类 Agent 编排平台可以把任务流程和代码逻辑解耦流程调整通过配置和自然语言指令就能完成Java 只负责提供稳定的原子能力。1.3 整体架构的核心思路这套方案的架构可以分为三层层级承担角色技术选型核心职责编排层大脑OpenClaw理解任务目标拆分步骤调用工具处理异常分支执行层手脚Java 程序操作老系统页面、抓取数据、生成报告、调用内网接口触发层闹钟定时调度按业务时间表触发流程监控执行状态补跑失败任务OpenClaw 在整个体系里扮演的是“数字员工”的 Manager它接收“审核今天的订单”这类自然语言指令然后把任务拆解成“登录系统-查询待审列表-逐条执行审核逻辑-汇总结果”等子任务再按顺序调用 Java 侧暴露出来的工具接口。Java 侧被做成了一个个独立的命令模块每个模块只做一件事查询订单、审核订单、抓取价格、生成报告。模块之间不直接互相依赖只和 OpenClaw 的任务编排层通信。这种设计的好处非常明显业务规则变了只需要在编排层调整指令和参数Java 代码一行不用改Java 侧出了 bug也只影响单点能力不会让整条流程雪崩。项目上线后期我调整过三次审核规则全部都是在 OpenClaw 的配置层完成的没有重新部署过一次 Java 服务。2. 环境准备OpenClaw 部署与 Java 工具箱2.1 OpenClaw 在 Ubuntu 上的安装过程OpenClaw 的部署方式我选择的是 Linux 服务器本地部署具体环境是 Ubuntu 22.04。网上关于 Windows 上跑的方案也不是不行但生产环境放在 Linux 上更稳尤其适合无人值守的定时任务。安装过程并不复杂核心步骤是拉取安装脚本、配置环境变量、初始化运行目录# 更新系统基础依赖 sudo apt update sudo apt install -y curl git build-essential # 拉取 OpenClaw 安装脚本并执行 curl -fsSL https://openclaw.example.com/install.sh | bash # 初始化配置目录 openclaw init --config-dir ~/.openclaw # 启动控制台服务 openclaw serve --port 8730注意不同版本的 OpenClaw 安装命令略有差异具体以官方仓库对应版本的 README 为准。我第一次装的时候因为跳过 init 直接启动导致后面所有插件都加载失败这个步骤千万不要省。安装完成后OpenClaw 会生成一个配置文件目录里面至少包含config.yaml、agents/、tools/三个部分。config.yaml负责定义大模型接入参数、Agent 全局行为agents/目录里可以定义多个不同的数字员工角色tools/目录用来放自定义工具的注册信息。部署 OpenClaw 之后需要先把大模型接口配好。这里要注意的是生产环境中我建议把模型上下文长度设大一些因为审单任务需要读入整页订单列表再做判断上下文窗口太小会导致信息截断Agent 就会做出错误决策。2.2 Java 侧的基础构件准备Java 侧我用的是 JDK 17没有用更高版本主要是考虑和老系统环境一致部署维护都省心。需要引入的核心依赖有这么几类HttpClientJDK 自带的java.net.http.HttpClient就够用抓取数据、调用接口都靠它不需要额外引第三方库Selenium Java 绑定用来驱动浏览器操作老系统页面配合 WebDriverManager 自动管理浏览器驱动版本Apache POI生成 Word 报告和 Excel 表格注意 POI 同时需要引入poi-ooxml和poi-ooxml-full两个包否则处理复杂样式时会报方法找不到FastJSON 或 JacksonJSON 序列化反序列化用于和 OpenClaw 之间传递任务参数和结果Quartz本地定时调度框架虽然主调度在 OpenClaw 层但 Java 侧也要保留定时兜底一个完整的 Maven 依赖片段参考如下dependencies dependency groupIdorg.seleniumhq.selenium/groupId artifactIdselenium-java/artifactId version4.15.0/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.5/version /dependency dependency groupIdorg.quartz-scheduler/groupId artifactIdquartz/artifactId version2.3.2/version /dependency dependency groupIdcom.alibaba/groupId artifactIdfastjson/artifactId version2.0.43/version /dependency /dependencies2.3 第一个连通性测试让 OpenClaw 调起一个 Java 方法环境准备完成后我做的第一件事不是写业务逻辑而是打通 OpenClaw 和 Java 之间的通信链路。方法是在 OpenClaw 的自定义工具里注册一个 HTTP 接口Java 侧用 Spring Boot 暴露一个测试端点RestController RequestMapping(/internal/tools) public class ToolController { GetMapping(/ping) public MapString, String ping() { return Map.of(status, ok, service, legacy-system-digital-worker); } }然后在 OpenClaw 的tools/目录下注册这个工具name: java_ping description: 测试 Java 集成服务是否在线 endpoint: http://127.0.0.1:8080/internal/tools/ping method: GET response_format: json在 OpenClaw 控制台里输入“调用 java_ping 检查服务状态”如果日志里出现了statusok的返回说明链路已经通了。这一步非常重要因为它同时验证了工具的注册格式、网络连通性、返回解析三个环节后续所有业务工具都可以照这个模板逐个添加。3. 三大核心功能的实现逻辑3.1 自动审单规则引擎加页面操作自动审单是所有任务里最敏感的一个因为它直接关联到业务数据准确性错了要担责任。我设计的第一版审单规则只有三条硬性指标订单金额大于等于 10000 元时需要主管复核客户当前信用额度减去本单金额之后小于 0 的标记为风险订单库存状态为“不可用”的直接驳回。但这里有一个藏在细节里的难点老系统的订单数据只能通过页面看到没有任何接口可以批量拉取。所以审单流程的第一步必须是模拟人工操作——用 Selenium 驱动浏览器打开订单查询页面、输入查询条件、点击查询、读取表格数据。读取出来的订单数据不会直接进入审单判断而是先经过一层清洗和结构化。页面表格里“订单金额”往往是¥12,345.00这种带格式的字符串需要先拆掉货币符号和千分位符再转成 BigDecimal 做比较“信用额度”在页面上显示的是总额度已用额度要进入客户详情页才能看到所以还要做一次页面跳转抓取。经过清洗的数据会传给一个纯 Java 的规则决策模块。这个模块不碰任何外部依赖只接收订单实体类返回审核结论方便单元测试覆盖public class OrderAuditRuleEngine { public AuditResult audit(Order order, CustomerCredit credit, StockStatus stock) { if (order.getAmount().compareTo(new BigDecimal(10000)) 0) { return AuditResult.needsSupervisor(订单金额超过一万需要主管复核); } BigDecimal creditAfterDeduct credit.getTotalCredit().subtract(order.getAmount()); if (creditAfterDeduct.compareTo(BigDecimal.ZERO) 0) { return AuditResult.risk(客户信用额度不足); } if (stock.getStatus() ! StockStatus.AVAILABLE) { return AuditResult.reject(库存状态不可用); } return AuditResult.approve(); } }审核结论会写回数据库同时通过 Selenium 在页面上做出对应的状态更新操作。这一步我把“读取数据”和“写回状态”拆成了两个独立的 Selenium 会话读取用只读权限的账号登录写回用有审核权限的账号登录。这样即使写回步骤出错也不会影响读取步骤的正常运行同时权限上也更安全。3.2 爬数据网页抓取的三种姿势“爬数据”是这次项目里另一个核心关键词实际业务场景是从行业网站和两家竞品网站上抓取每日价格信息。不同网站的技术栈不同反爬策略也不同我用三种方式分别应对第一种是普通静态页面直接把 HttpClient 发出去拿 HTML然后用 Jsoup 解析。这类网站最简单只需注意请求头里的 User-Agent 不要太可疑频率控制在每秒一次以内就行。第二种是接口型数据。很多看起来是网页的网站实际上数据是通过异步请求从 JSON 接口返回的。这种情况直接用浏览器的开发者工具找到接口地址模拟请求参数就能拿到结构化数据比解析 HTML 高效得多。第三种是重反爬站点这个是最费工夫的。我遇到的难点主要是请求头校验和 IP 频率限制。应对方式是用 Selenium 模拟真实浏览器访问配合合理的执行时间窗口和代理池。这里有两条血泪经验一是千万不要用默认的 Selenium 指纹去高频访问否则很快会被识别二是每个数据源的采集频率必须单独设置不能一概而论。抓到的数据统一清洗后写入本地数据库。价格字段带有单位、币种、有效时间清洗逻辑要做到能处理“1,299元/吨”“$189.5”“联系客服询价”这类五花八门的值。对于无法自动解析的格式我宁可直接标记为“解析失败”也不会给一个猜测的值因为报告里的数据错了比没有数据更尴尬。3.3 发报告文档生成与多渠道投递报告功能是整个流程的出口也是同事感知最强的一个模块。最初的需求只是每天发一份日报邮件后来陆续加了周报、异常预警两个类型。我统一用 Apache POI 的 XWPFDocument 来生成 Word 报告因为 Word 格式可以被大家直接编辑领导也习惯在 Word 上批注。报告模板的思路是先在 Word 里手工制作一份模板把需要动态填充的位置用${orderCount}、${totalAmount}这类占位符标出来Java 侧读取模板后遍历占位符并替换为计算结果。这个方案比完全代码化生成文档要稳得多样式部分在 Word 里调代码只负责填数。生成报告的核心代码大致是这样的public void fillReportTemplate(String templatePath, String outputPath, ReportData data) throws Exception { try (FileInputStream fis new FileInputStream(templatePath); XWPFDocument doc new XWPFDocument(fis)) { for (XWPFParagraph paragraph : doc.getParagraphs()) { for (XWPFRun run : paragraph.getRuns()) { String text run.getText(0); if (text ! null text.contains(${)) { text text.replace(${orderCount}, String.valueOf(data.getOrderCount())) .replace(${totalAmount}, data.getTotalAmount().toPlainString()) .replace(${abnormalCount}, String.valueOf(data.getAbnormalCount())); run.setText(text, 0); } } } try (FileOutputStream fos new FileOutputStream(outputPath)) { doc.write(fos); } } }注意POI 处理 Word 模板的占位符替换有个大坑——占位符可能被拆进多个 Run 对象里导致run.getText(0)拿到的不是完整占位符。遇到这种情况需要合并段落里的多个 Run 再做替换否则报告里会残留${orderCount这样的残缺字段。报告投递走的是邮件和企业微信机器人双通道。邮件发送用 JavaMail企业微信机器人就是发一个 Webhook POST。两个通道都发送成功后才认为这一轮报告任务完成。任一个通道失败自动触发重试重试三次仍失败就转人工告警避免出现“报告发了但没人看到”的情况。4. 实操过程从任务编排到稳定运行4.1 用 OpenClaw 编排一条完整业务链当 Java 侧的工具模块全部就绪后剩下最重要的工作就是在 OpenClaw 里编排任务流程。我把它设计成三个独立的 Agent审单员、数据采集员、报告助理。每个 Agent 负责一个环节互相之间通过任务队列传递结果。审单员 Agent 的指令模板大致是name: order-auditor description: 自动审核待处理订单 steps: - tool: java_pull_pending_orders params: limit: 100 - tool: java_execute_audit_rule params: rule_version: v2 - tool: java_write_audit_result params: dry_run: false - tool: java_notify_abnormal params: level: supervisor_requiredOpenClaw 不需要死板地按顺序执行每一步它可以理解步骤之间的依赖关系在某个工具返回异常时自主决定是重试还是跳过还是中止整个流程。例如执行审单规则时如果发现传入的订单数据为空Agent 会主动回到第一步重新拉取而不是直接让流程失败。实际操作下来OpenClaw 编排层最值得调校的是“工具调用超时时间”和“最大重试次数”。老系统页面响应经常要几十秒如果超时设得太短Agent 会误判工具调用失败设得太长真正的死循环又不能及时暴露。我最终调成单次超时 90 秒重试 2 次整体表现还算稳定。4.2 Java 服务端的关键实现细节Java 服务端的整体结构我设计成独立的 Spring Boot 应用提供若干 REST 接口给 OpenClaw 调用。每个接口都是无状态的入参和出参都是 JSON不保存中间状态这样即使 OpenClaw 重启任务重新发起时也不会产生脏数据。订单查询接口是这个体系里最核心的一个。它内部使用 Selenium 操作浏览器但对外暴露的是干净的业务接口PostMapping(/orders/pending) public ApiResultListPendingOrder pullPendingOrders(RequestBody PullRequest req) { ListPendingOrder orders legacyOrderPage.search(req.getStatus(), req.getLimit()); ListPendingOrder cleaned orders.stream().map(OrderDataCleaner::clean).toList(); return ApiResult.success(cleaned); }这里有个细节接口返回之前订单数据必须完成清洗和结构化不能把页面上的原始字符串直接抛给 OpenClaw。因为大模型处理不规范数据时容易产生幻觉把“1,200.00”理解成一万二这种低级错误在真实场景中是真实发生过的。爬数据服务和报告生成服务相对独立没有特别的联动关系。我把它们用 Quartz 做了本地兜底调度即使 OpenClaw 编排层整体宕机Java 侧也能按备用计划把核心数据抓取和报告发送完成。这个兜底机制看起来简单但有一次 OpenClaw 服务因为磁盘空间不足挂了整个上午只有 Java 兜底任务在跑最终报告还是按时发出去了。4.3 定时触发、重试与补偿机制整个数字化员工体系的时间表是这样的时间任务执行方式失败策略09:00自动审单第一批OpenClaw 编排重试 2 次失败转人工队列10:30爬取行业价格数据OpenClaw 编排单源重试不阻塞其他源11:30生成并发送日报Java 兜底 OpenClaw 编排双通道告警重试 3 次15:00自动审单第二批OpenClaw 编排重试 2 次失败转人工队列17:30生成并发送日终汇总Java 兜底 OpenClaw 编排双通道告警重试 3 次失败补偿是这套系统里最容易被忽视但最关键的环节。我总结出来的原则是重试一定要区分失败类型。网络超时可以放心重试接口返回业务异常时不能盲目重试必须先把异常信息记录下来再决定下一步。页面元素找不到这类 Selenium 异常往往重试没有意义直接跳到人工处理更明智。OpenClaw 的业务日志我也接到了统一的日志平台关键节点都打上了结构化日志。执行完一个完整任务流之后我能在日志平台里直观地看到某个订单在几点几分被拉取、几秒后完成审核、结论是什么、报告投递到哪个群。这套可观测性设计让后续排查问题省了大量时间。5. 常见问题与排查技巧实录5.1 老系统登录态过期导致的连锁失败老系统的登录态有效期只有 4 小时这是项目上线后遇到的第一个重大故障。表现是中午 11 点前跑得好好的任务到了下午第一次执行就直接在登录页打转Selenium 找不到任何订单数据OpenClaw 反复重试都没有用。排查之后发现问题出在“无头浏览器”模式下登录态的刷新机制和正常浏览器不一样。解决思路不是去改老系统的认证逻辑而是做两层保障第一Java 侧建立一个登录态管理器在每次执行操作前检查当前会话是否有效第二在 OpenClaw 编排层增加一个前置检查步骤发现登录失效时先执行“重新登录”工具再继续后续任务。5.2 爬数据时遇到反爬和限流爬数据遇上反爬几乎是必然的。我实际遭遇过的情况包括请求频率过快被临时封 IP、缺少特定请求头被返回验证码页面、Cookie 校验失败被重定向到首页。我的应对思路不是硬刚而是“慢、换、绕”每个数据源的采集任务单独控制频率绝不用统一的延时参数请求头完全模仿真实浏览器的完整指纹如果某源持续返回异常就在当轮任务里标记为失败并跳过不阻塞其他数据源的采集和报告生成。三个数据源偶尔会有一个失败但全挂的情况半年里没有出现过。5.3 OpenClaw 任务挂起和超时OpenClaw 的 Agent 在调用工具时偶尔会出现任务挂起工具已经正常返回结果了但 Agent 迟迟不进入下一步日志里没有报错流程就是不动。这个问题排查了很久最终发现是Agent上下文中的“中间思考过程”过长占满了上下文窗口。解决办法有两个方向一是给每个工具调用设置明确的超时时间二是把一次性推送多个订单改为分批推送避免单轮交互的数据量过大。分批复核还有一个额外收益即使某一批判断出错Agent 也能在下一批基于前一批结果自我纠正不会全盘皆输。5.4 生成的报告格式错乱POI 处理 Word 模板时最常见的坑是中文乱码和样式丢失。中文乱码通常是字体兼容问题POI 默认字体在 Linux 环境下没有对应的中文字体文件安装fonts-noto-cjk就能解决这个问题网上说法很多我的实测经验就是装字体包最直接有效。样式丢失的问题则通常和模板本身有关。不要在 Word 模板里使用过于复杂的嵌套表格和文本框POI 对这类复杂结构的还原能力有限。把模板做得简单干净、多用段落和简单表格生成的报告基本能保持 95% 以上的原始样式。5.5 问题排查速查表现象可能原因排查步骤解决方向Selenium 找不到页面元素老系统页面改版或元素属性变化查看失败截图和页面 DOM改用更稳定的 XPath 定位审单结果和人工复核不一致清洗逻辑处理异常值有误比对原始数据和清洗后数据补充异常值解析规则爬取数据部分缺失目标网站改版或接口字段变化检查单源采集失败日志标记失败源配置备用数据源报告发送成功但没人收到企业微信机器人被移出群或地址失效检查 Webhook 调用返回码更新 Webhook 地址OpenClaw 任务执行很慢上下文过长或模型推理次数过多查看每次工具调用的耗时记录分批下发任务精简提示词老系统操作偶发卡死浏览器进程残留导致资源耗尽检查服务器进程列表定时重启浏览器实例最后再分享一点个人体会。做这种“给老系统加数字员工”的项目真正考验人的往往不是技术能力而是对业务边界的理解。自动化能把 80% 的重复工作扛下来但剩下 20% 的异常情况仍然需要人来兜底。我在设计整套流程时特意保留了“人工复核”的入口审单结论异常转人工、爬取失败转人工、报告发送失败转人工。这不是技术上的退缩而是对业务负责任的态度。自动化是帮人省时间不是替人担责任。这套系统上线以来我每天要看的异常工单不超过十条相比之前三个小时的重复劳动已经是从根本上改写了工作方式。