接到一个需求:要用 UiPath 把散在不同办公点的十几台设备全部做成无人值守,每天固定时间自动跑数据采集,同时所有执行参数都从 json 文件读取。这类项目做多了以后,我最大的感受是:真正难的不是写几个自动化流程,而是“让流程在没有人工干预的情况下可靠地在远端设备上跑起来”,以及“让参数改动不动代码”。
这篇文章我不会讲 UiPath 的入门安装,也不会讲怎么录制一个网页点击,而是聚焦两个实战关键点:一是多台远程设备的无人控制怎么设计、怎么配置、怎么避免翻车;二是 json 文件在无人值守流程中的读取方式,尤其是远程机器上那种“路径不确定、权限受限、一行写错直接崩”的情况。如果你是正在做 RPA 推广、准备把流程从本地搬到企业环境、或者刚接手无人值守项目的同学,这篇内容基本可以当一份踩坑笔记来用。
1. 需求分析与总体架构:先搞明白“无人控制”到底在解决什么
1.1 复盘一个真实的无人值守场景
先说我实际做过的案例。业务方有 4 台设备分布在 3 个办公点,每台设备需要登录不同的业务系统,把当天的单据下载下来,再按规则重命名后归档。以前是每天晚上让值班同事手动操作,操作一次大概 15 分钟,还要填 Excel 记录哪台做完了、哪台失败了。
这种需求的本质是什么?是“定时 + 远程 + 无人化 + 按设备差异化执行”。业务系统不可能为每台电脑单独开发接口,所以最合适的自动化载体就是 UiPath。但这里有几个坎:
第一,设备离你很远,你不可能每天跑过去看屏幕;第二,流程执行时需要 Windows 账号和系统权限,不能只靠有人登录后手动点“运行”;第三,每台设备的业务参数不一样,比如单据类型、下载目录、账号密码,这些如果硬编码在流程里,改一次发一次包,维护成本高到离谱。
所以在做架构之前,必须先把“远程设备控制”的思路厘清:不是每台设备人都去盯,而是通过一个中央控制端把任务下发给各设备的 Robot,Robot 执行完把结果回报回来。中央控制端负责调度、队列、日志、重试,设备上的 Robot 只负责执行。
1.2 技术选型:为什么不用系统计划任务自己写脚本
有人会问,既然这些设备上都已经装了 Windows,为什么不用“任务计划程序”定时启动一个脚本,还要花钱买 UiPath Orchestrator?这个问题我面试时经常被考,实战中的答案很直接:
- 业务系统是网页或客户端软件时,纯脚本很难稳定操控界面,而 UiPath 擅长 UI 自动化;
- 多台设备需要统一监控执行状态,光靠脚本返回码没法表达“登录失败”“文件没生成”“网络超时”这些业务异常;
- 失败重试、并发控制、优先级队列,自己从零写一套很痛苦;
- 流程更新后,需要让十几台设备同时拿到新版本,计划任务做不到一键发布。
简单对比一下:
| 对比项 | Windows 计划任务 + 脚本 | UiPath Orchestrator + Robot |
|---|---|---|
| 界面自动化能力 | 弱,基本靠脚本模拟 | 强,支持 Web 和桌面应用 |
| 集中下发流程 | 需要自己维护脚本包 | 发布后自动分发到已注册设备 |
| 失败重试和队列 | 配置繁琐 | 原生支持队列和重试策略 |
| 日志与告警 | 依赖自建日志系统 | 每个作业都有日志和状态 |
| 参数动态化 | 改配置还要处理不同机器同步 | 可在队列项里传递 json 字符串 |
并不是说计划任务一无是处,而是涉及“大量设备 + UI 操作 + 标准化管理”时,UiPath 这套体系的优势更明显。
1.3 核心数据流:json 文件在无人控制里处于什么位置
我见过很多刚接触无人值守的同事,一上来就在编排页面上拼命拖各种 Activity,却忽略了数据设计。实际上,一套稳定的无人值守方案,数据流大概是这样:
总控侧预先定义好一份或一批 json 任务参数,例如本次要处理哪些设备、每个设备的业务编码和归档路径;流程触发时,Orchestrator 把“任务信封”通过队列项发给对应机器人;远端设备的 Robot 启动后,先读取本机上的 json 配置(也有可能是从队列项的 SpecificContent 里取出一段 json 字符串),解析出本次要做什么、参数是什么,再执行业务操作,最后写日志。
这里最容易被忽略的就是 json 解析环节。它很基础,但放到无人值守场景里会放大很多问题:路径不是用户桌面、编码不是 UTF-8、字段可能为空、数组深度两层以上、整个 json 文件被业务方随手填错一个逗号……任何一个问题都会让流程在半夜静默失败。所以后面我会把 json 读取单独拿出来详细讲,因为它真的值得。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 多台远程设备无人值守架构搭建:Orchestrator 与 Robot 的正确连接方式
2.1 先理解 UiPath 无人值守的运行模式
在真正做多设备调度之前,你需要对 UiPath 的几种 Robot 类型有概念。有人值守 Robot(Attended)通常装在用户桌面,由用户自己触发,适合辅助人工处理前台任务;无人值守 Robot(Unattended)则运行在独立机器上,由 Orchestrator 远程触发,不需要有人守在屏幕前。
无人值守 Robot 的工作方式,简单理解是:Robot 在电脑后台创建一个独立的 Windows 会话,用你预先授权的账号登录并执行流程。所以核心前提有两个:一是这台电脑必须能和 Orchestrator 正常通信并完成注册;二是执行流程所用的 Windows 账号必须具备登录和运行权限。
这就解释了为什么很多人把流程发布到 Orchestrator 后,点“运行作业”却发现设备一直在“挂起”。可能不是流程代码的问题,而是 Robot 没有注册成功、账号密码过期、或者机器之间的网络端口不通。
2.2 环境、机器、机器人三者的关系别搞混
进入 Orchestrator 后,你会看到 Machines、Environments、Robots 这几个概念。很多新手就栽在这里——把一台机器当作一个机器人,或者不知道环境有什么用。
简单画一下逻辑:
- Machine:代表一台物理设备或虚拟机,安装 UiPath Robot 后会生成一个机器身份;
- Robot:是这台机器上的一个执行账户,一台机器可以对应多个 Robot 账户;
- Environment:是一个执行单元集合,你可以把多个 Robot 放进去,发布流程时选择目标 Environment,启动作业就能同时发给这个环境里的所有 Robot。
当你说“给 3 台设备各下发一个任务”时,正确做法不是建 3 个 Environment,而是建 1 个包含 3 台机器/多个 Robot 的环境,然后通过队列来按目标设备分配任务。如果对环境不熟,先不要把几十台设备全部塞进一个环境里,建议按“流程类型”或“设备职责”拆分,这样排查问题会容易很多。
2.3 多设备注册的完整步骤
说下基本流程,不同 2020 版本之后界面会略有变化,但逻辑相通。
第一步,在每台目标电脑上安装 UiPath Robot(或者 Studio 组件中包含的 Robot),安装完成后打开 UiPath Robot 客户端,选择连接到 Orchestrator,输入 Orchestrator 地址和管理员分配的机器密钥。
第二步,在 Orchestrator 中创建 Machine,选择机器类型为 Standard Robot(或 Modern 环境中的机器组方式),系统会生成 machine key,把这串 key 填到 Robot 客户端的注册项里。注册成功后会看到这台机器在线。
第三步,为这台机器配置运行无人值守作业的 Windows 账号。通常建议用专门的自动化服务账号,而不是员工个人账号,避免员工改密码导致机器人无法登录。账号密码需要维护在 Orchestrator 的 Credential Asset 或 Windows 凭据里。
第四步,将包含这台 Robot 的环境创建好,并添加对应的 Robot 条目。执行账户设置为刚才那个自动化账号,并勾选 Unattended。
完成这些后,你不是万事大吉,而是要做一个验证:从 Orchestrator 手动启动一次测试作业,确认远端设备屏幕上真的会出现一个独立桌面会话并执行流程。如果只看到“作业运行中”但远端没有反应,先不要怀疑流程,大概率是账号或注册问题。
这个注册过程我建议整理成一份 checklist,因为每加一台新设备都会用到,记在脑子里总有一天会漏步。
3. json 文件读取的实战细节:三种方式与路径避坑
3.1 为什么无人值守场景坚持用 json 当配置载体
在做无人值守流程参数化时,不外乎几种选择:Excel、ini、数据库、json。我个人的倾向很明确:能用 json 就用 json。
原因是多方面的。Excel 文件读取需要安装 Office 或安装相应依赖,在远端无人值守机器上很容易因为“未安装 Excel”或“Excel 弹窗卡住”导致流程异常;ini 表达能力有限,遇到数组结构只能靠分隔符硬拆;数据库虽然强大,但对一个小型任务来讲太“重”了,不是每台设备都有数据库客户端权限。
json 本身就是结构化文本,一个文件可以表达字符串、数字、布尔值、数组、嵌套对象,几乎任何业务参数都能装下。而且 UiPath 对 json 有天然支持,读文本再反序列化就能用,不需要额外装太多东西。对远程设备的自动化和运维来说,json 文件还方便人工打开检查和修改。
3.2 从零开始的读取入口:文件路径设计是第一个坑
json 文件读取的第一步不是解析,而是“找到文件”。在无人值守场景里,Robot 运行所在的 Windows 会话可能和人工登录时的用户目录完全不同,如果你在流程里写死 C:\Users\zhangsan\Desktop\config.json,大概率报文件不存在,或者权限不足。
我建议把设备级配置统一放到公共目录,比如:
text复制C:\ProgramData\RPA\Config\device.json
ProgramData 目录对所有用户共享,一般不会因为执行账号切换而读不到。在 UiPath 表达式中可以用:
text复制Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\RPA\Config\device.json"
这样即使不同机器上系统盘位置不同,也能更稳妥地拼出路径。
其次要注意编码。很多业务同事用记事本另存为 json 时,默认可能是 ANSI 编码文件,里面如果包含中文,读取后就是乱码,后面无论怎么解析都会有问题。规范做法是统一要求 utf-8 编码,并在上游有一道校验。
3.3 实战第一步:用反序列化 JSON Activity 读取简单配置
假设 device.json 内容是:
json复制{
"deviceId": "DEVICE-03",
"department": "销售一部",
"sourceFolder": "D:\\AutoTask\\Source",
"archiveFolder": "D:\\AutoTask\\Archive",
"retryCount": 3
}
在 UiPath 中的标准做法是:
- 使用 Read Text File 活动,File Path 填上述公共目录路径,输出字符串变量
configText; - 拖入 Deserialize JSON 活动,把
configText转为JObject类型的变量config; - 使用 Assign 活动取值:
config("deviceId").ToString(); - 对于数字类型如
retryCount,需要用CInt(config("retryCount").ToString())或转成 int。
这里有个很隐蔽的问题:Deserialize JSON 的输出类型如果没指定,变量可能变成字符串或者 JValue,后续取值会报“无法从 X 转换为 JToken”。解决方法是直接在变量类型里明确填 Newtonsoft.Json.Linq.JObject,如果找不到该类型,先在 Manage Packages 里安装或引用 Newtonsoft.Json。
3.4 多级嵌套 json 的解析:用 SelectToken 是最省事的方式
如果 json 结构比较简单,用索引器一层层取没问题。但无人值守项目里经常会遇到嵌套数组:
json复制{
"taskId": "TASK-20250312-001",
"targetDevice": "DEVICE-03",
"tasks": [
{
"name": "download_report",
"type": "web",
"enabled": true,
"params": {
"startDate": "2025-02-01",
"endDate": "2025-02-28",
"exportFormat": "xlsx"
}
},
{
"name": "archive_cleanup",
"type": "filesystem",
"enabled": false,
"params": {
"keepDays": 30
}
}
]
}
如果你要取出第一组任务里 params 的 startDate,我不建议 config("tasks")(0)("params")("startDate").ToString(),因为这种写法在 UiPath 的 VB 表达式里偶尔会因为重载不匹配翻车。更稳的是用 JSONPath:
text复制config.SelectToken("$.tasks[?(@.enabled==true)].params.startDate").ToString()
SelectToken 是 JObject 自带的方法,UiPath 的表达式面板里可以直接调用,读取路径非常灵活。它还能做条件过滤,例如筛选 enabled 为 true 的 task,非常契合“同一个 json,不同设备执行不同子任务”的场景。
如果你的项目启用了 C# 模式,用 Invoke Code 也行,核心代码大致是:
csharp复制var config = Newtonsoft.Json.Linq.JObject.Parse(File.ReadAllText(configPath));
string startDate = (string)config.SelectToken("$.tasks[0].params.startDate");
但注意 Invoke Code 在无人值守流程里不是不能用,而是你要确保引用了 System.IO 和 Newtonsoft.Json,并且异常处理足够完善,不能让一段 Helper 代码把整个流程带崩。
3.5 更结构化的做法:用 DTO 类反序列化,避免魔法字符串
如果 json 结构非常固定,项目里用到的字段很多,我强烈建议用 DTO 类来做强类型反序列化。在 UiPath 中可以在项目中添加一个类,或者用 Invoke Code 内嵌类定义。比如:
csharp复制public class DeviceTask {
public string TaskId { get; set; }
public string TargetDevice { get; set; }
public List<TaskItem> Tasks { get; set; }
}
public class TaskItem {
public string Name { get; set; }
public string Type { get; set; }
public bool Enabled { get; set; }
public Dictionary<string, object> Params { get; set; }
}
然后调用:
csharp复制var taskConfig = JsonConvert.DeserializeObject<DeviceTask>(jsonText);
这样所有字段都变成了对象的属性,IDE 能自动提示,写错字段名会在编译期暴露出来,而不是等运行到那一步才发现取到 null。缺点是前期建模需要多一点时间,而且一旦 json 结构乱七八糟,反序列化会直接抛异常。所以我通常这样分工:结构固定的设备配置用 DTO,结构不太确定的业务任务用 JObject + SelectToken 动态读。
3.6 读取结果为空时的静默失败问题
无人值守最怕的不是流程报错,而是流程“看似成功”,但实际上因为某个字段没读到,导致后面处理了错误数据。比如 json 里 tasks 数组为空,流程没有做判断,直接继续跑,最后等于空转了一个多小时。
我现在的习惯是每读一个关键字段,马上加校验:如果是空字符串或者空集合,正常结束流程,而不是继续往主逻辑走。这个习惯在有人值守时只是烦一点,在无人值守时能救命。
4. 多设备任务分发与状态监控:从手动点作业到队列驱动
4.1 手动启动作业只适合验证,不适合长期无人值守
最早做测试时,我都是在 Orchestrator 上手动选中 Robot,点“Start Job”,然后盯着看日志。这最多算远程启动,距离“无人值守”还差得远。
真正的无人值守需要一个“事件源”。UiPath Orchestrator 里有两类常用触发方式:时间触发器和队列触发器。时间触发器适合“每天固定时间所有设备执行固定批次”,队列触发器更适合“任务不是预先固定,而是运行时动态产生”。
我在实际项目里偏向用时间触发器做“看门狗流程”,让某个机器人定时去扫描共享任务目录,发现新的 json 任务文件后,再向所有执行机器人批量推送任务。这样既满足定时执行,又让任务内容可以根据 json 动态变化。
4.2 用 Queue 的 SpecificContent 传 json,而不是堆一堆自定义字段
有人会问,如果我需要给 10 台设备分别传 20 个参数,是不是在队列项里建 20 列?千万不要。队列项每一列都有长度和类型限制,后期维护就是灾难。
UiPath Queue 的 SpecificContent 本身是一个字典结构,最佳实践是只放一个大字段:payload,值就是一段 json 字符串。例如:
json复制{
"deviceId": "DEVICE-03",
"taskFile": "task_20250312.json",
"priority": "high"
}
然后在执行流程里拿到队列项后,取出 payload,用第 3 章提到的 Deserialize JSON 或 JObject 解析,后面的逻辑全部基于这个动态对象展开。好处是以后任务参数变化,你只需要改 json 结构,不需要改 Orchestrator 队列字段,不用重新发一次自动化包。
这里要特别注意:启动 Unattended Robot 获取队列项时,一个 Robot 默认只能占用一个队列项,如果一个“任务”被拆成多个队列项,要让机器人在确认处理完成后再获取下一个,不能一上来就并发取一堆 Queue Item,那会造成同一台设备同时跑多个流程实例,锁定冲突很严重。
4.3 失败重试要与业务异常区分开
Orchestrator 的队列本身具备重试机制。你可以设置最大重试次数和重试间隔。但有个经验:不要把“重试次数”堆得太高,尤其是无人值守流程里如果是因为界面环境异常,比如系统升级、弹窗变化、网络断连,你重试再多次也一样失败。
我的处理方法是区分两类失败:
- 技术性失败:网络抖动、文件被占用、Robot 临时离线。这类可以重试 2 到 3 次;
- 业务性失败:登录账号无权限、查询无数据、json 字段缺失。这类直接终止流程,并把详细原因写进队列项的 Error 信息里。
判断方式通常是在流程里用 Try Catch 捕获异常类型,或者根据业务返回值设置一个“状态码”。如果所有失败都交给 Orchestrator 重试,不仅浪费机器资源,还会因为重试后产生重复单据,反而把脏数据放大。
4.4 一台机器上到底能跑几个无人值守流程
关于并发,很多新手常犯的错误是以为每个 Robot 可以无限制并发。实际上一个 Windows 账号在同一时刻通常只能跑一个无人值守会话。如果你想在一台高配物理机上并行跑多个流程,有两种做法:一是注册多个 Robot 条目,每个 Robot 用不同 Windows 账号;二是把机器设置成支持多会话的虚拟机或 RDS 环境,但需要额外的授权和系统配置。
我通常不贪这个并发,宁可把任务分批错开。无人值守项目讲究的是稳定,而不是峰值跑满。三台设备分别在不同时段跑,日志也更容易对应,出了问题不用在一堆并发 Job 里翻来翻去找是哪一个实例导致的。
5. 无人值守运行中的常见问题与排查实录
5.1 高频问题速查:先从现象判断方向
把我在多台设备无人值守和 json 读取过程中遇到的高频问题整理成了一个速查表:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 作业一直“等待”或“挂起” | Robot 未成功注册,或机器离线 | 检查 Robot 客户端与服务端连接状态 |
| 作业显示成功但什么都没做 | json 里字段为空或逻辑分支走了空路径 | 在流程入口增加参数校验和日志 |
| 读取 json 报文件找不到 | 路径写死,执行账户无权限 | 改用 CommonApplicationData 公共目录 |
| 中文内容乱码 | 文件编码不是 UTF-8 | 统一用 UTF-8 保存并做编码检测 |
| Deserialize JSON 报类型转换错误 | 输出变量类型不明确 | 把变量显式设为 JObject |
| SelectToken 结果为空 | JSONPath 写错或节点不存在 | 先打印 json 文本再核对路径 |
| 无人值守会话偶尔登录失败 | Windows 账号密码过期或凭证被改 | 使用自动化专用账号并定期改密 |
| 多台设备同时跑某段流程时互相干扰 | 访问了共享文件未做文件锁 | 各设备使用独立临时目录后再转发 |
这张表不能解决所有问题,但能帮你快速定位方向,避免一上来就在流程代码里翻半天。
5.2 三个让我印象很深的坑
第一个坑,是流程因为“桌面会话没起来”而失败。一开始部署无人值守时,我把所有精力放在流程代码上,结果第一晚三台设备全军覆没。后来才发现是执行账号没有“自动登录”和“允许服务与桌面交互”相关权限。UiPath 的无人值守 Robot 需要在后台拉起一个桌面式会话,系统安全策略如果限制太死,作业就只能在日志里留下一个模糊错误。这个问题最恶心的点在于它只在半夜出现,白天测试时却一切正常,因为白天我手动登录着账号。
第二个坑,是 json 文件里多了一个看不见的 BOM 头。我用 UiPath 的 Deserialize JSON 去解析一个由某业务系统导出的 json 文件,现场人员说“记事本看着没问题”,但一解析就报错。后来用十六进制工具查看,才发现文件开头多了几个字节。这些看起来“毫无影响”的细节,在无人值守批量跑的时候会被放大成所有机器集体失败。
第三个坑,是日志和任务参数没有保存原始 json。无人值守流程运行到一半挂掉了,你去看日志时如果只记录“任务失败”而不记录“当时读到的 json 内容是什么”,排查基本只能靠猜。我现在要求所有关键流程必须把队列项 payload 或配置文件原文输出到日志,用 Debug 级别打印一遍,注意敏感信息脱敏,这样重放问题时能直接知道是任务数据异常还是代码逻辑异常。
5.3 日志设计要有“现场还原”能力
说到日志,我习惯在每个重要节点写结构化日志。比如:
text复制[DEVICE-03] 读取配置成功,taskId=TASK-20250312-001
[DEVICE-03] 检测到 enabled 任务数为 2,开始执行 download_report
[DEVICE-03] download_report 下载完成,文件大小 102400 字节
不要只写“成功”或“失败”,要带上设备标识、任务标识和关键业务量。很多无人值守事故其实不是代码写错,而是没法从日志里判断它当时到底跑到哪一步。你要把日志当成事故现场的监控录像来设计,而不是应付式地打几个点。
6. 上线前预检与我的实践心得
6.1 每一台远程设备上线前必查的几件事
无人值守项目最忌讳“在一台设备上跑通了就以为全部设备都跑通”。每台机器的系统补丁、Office 版本、默认浏览器、网络位置都不一样。我现在上线前必做五件事:
- 检查 Robot 在线状态,确保 Orchestrator 能正常列出该设备;
- 用自动化账号手动执行一次流程,确认 Windows 会话可以拉起;
- 检查公共配置目录是否存在,json 文件编码是否统一为 UTF-8;
- 验证该设备的网络共享访问权限,确保跨设备文件读写正常;
- 设置好告警通知,当作业失败或长时间无输出时能发邮件或企业微信通知。
最后一个经常被忽略。有人以为无人值守就是配置好之后完全不用管,但实际上流程会因为各种外部因素失败,完善的告警机制才是“无人”两个字的底气。
6.2 几个让我节省大量时间的小技巧
最后一个技巧,是关于 json 调试的。在 UiPath 里调试 json 解析时,不要总是直接跑到远端设备上去试。先在本地用同一个 json 内容把解析脚本调通,然后再发布。远端设备的问题更多出现在路径、权限和编码,而这些问题已经不属于业务流程逻辑,应该在定位清楚后再一次性上去处理。
另一个技巧是把“当前运行环境信息”也输出到日志。流程启动时先打印一下当前机器名、执行账号、工作目录、配置文件路径。这些小信息在你同时维护几十台设备时特别有用,否则你收到一封报警邮件,连是哪台设备、用的哪个账号都分不清。
还有,发布流程时最好带上版本号,并且配好回滚机制。无人值守环境一旦出现批量失败,第一件事不是马上改代码,而是先暂停对应 Job、把流程版本退回上一个稳定版,避免问题进一步扩大。我见过有人凌晨两点还在远程改流程,改完重新发布,结果因为编码问题比上一个版本还糟。稳,永远比快重要。
如果你准备把自己的 UiPath 流程从单机搬成多设备的无人值守,建议先不要贪多,先拿两台设备把从头到尾的链路摸熟,包括环境注册、无人值守账户、json 配置、队列触发、失败重试,再逐步加上第三台、第四台。这套链路一旦通了,后面每加一台设备就只是重复执行而已。
