常驻助手与自动化
一次性对话结束就结束;长期在线的助手结束时,事情可能正做到一半。
- 一次性对话与长期在线的助手,多出哪些必须处理的问题;
- 状态保存、任务排队、失败重试、费用控制这四件事怎么做;
- 定时任务与自我唤醒;为什么不能依赖某个一直开着的窗口;
- 记忆该分层,以及长任务可恢复的三个前提。
一、从一次问答到长期在线
用一次对话解决问题,状态很简单:你问,它答,关闭窗口,一切结束。出错了重新问一遍就行。
当助手开始长期为你工作时,这个模型就不够用了。它可能同时在处理三件事,其中一件在等外部系统回应, 另一件刚失败需要重试,第三件需要你确认才能继续。你关掉网页,它们不该全部消失。
这些要求彼此交叉,不能靠每个按钮各写一份状态来解决。把它们归成四类:
| 问题 | 具体表现 |
|---|---|
| 状态 | 关掉窗口后任务是否继续?重启之后怎样恢复?两个设备看到的是不是同一个进度? |
| 排队 | 同时来了五个任务,按什么顺序做?某个任务卡住了,其他任务是否要等? |
| 重试 | 失败几次算失败?哪些错误值得重试?重试会不会重复执行已经完成的部分? |
| 费用 | 一次任务花多少?到达上限时应该停下、降级,还是向你请示? |
二、状态:任务有阶段,不只是一句话
一个看起来简单的需求,背后已经有一组状态需要说清楚。以「把本周项目记录整理成周报」为例:
- 它要找到资料、核对内容、生成可继续编辑的文件。
- 只在聊天框里输出一段建议,不算完成。
- 生成了文件也不够:能不能打开、是否包含要求的内容、数字与来源有没有核对、哪些地方还缺资料,都属于交付的一部分。
执行过程中你还会改变主意,而每一种改变都对应一组状态迁移:
| 你说的话 | 正确的行为 |
|---|---|
| 「补充这个文件」 | 把新材料加入当前上下文,不重启任务 |
| 「暂停一下」 | 保留进度,停止发起新步骤,已经完成的修改不回退 |
| 「不要修改原件」 | 切换为只读模式,后续写入改为副本 |
| 「先把已经完成的部分给我」 | 导出当前中间产物,任务继续在后台进行 |
| 「取消」 | 停止任务,已经产生的外部动作不会自动撤销,需要单独处理 |
「停止」这一个按钮背后,就有一组需要讲清楚的状态迁移。这也是为什么长期在线的助手不能靠「每个功能各写一套状态」来实现。
三、排队与并发:哪些能并行,哪些必须排队
同时来多个任务时,先要分类:
- 可以并行的:彼此不写同一份资源、不依赖对方结果的任务。例如同时整理三份不同来源的资料。
- 必须排队的:会写同一份资源、或有先后依赖的任务。例如两个任务都要修改同一份记忆文件。
- 需要合并的:同一件事被重复请求。例如你连点三次「生成周报」,应该合并成一个任务,不必排队三次。
排队机制本身也要能表达优先级与插队规则。紧急的任务应该能插到前面,但插队不能让已经执行一半的任务停在半途而废的状态。
flowchart LR A["任务失败"] --> B["判断失败类型"] B -->|"暂时性"| C["有限次重试
带幂等标识"] B -->|"确定性"| D["不重试
报告给用户"] B -->|"结果未知"| E["先查实际状态"] E -->|"已完成"| F["直接继续下一步"] E -->|"未完成"| C C --> G["达到预算上限"] G --> H["停止并交回
已完成部分与原因"]
四、重试:区分三种失败
第 15 章讲过失败分三类,这一章把它落到长期运行的场景里。重试策略应该按类型区分:
| 失败类型 | 处理 | 要注意 |
|---|---|---|
| 暂时性(超时、限流) | 有限次重试,间隔递增 | 要带上幂等标识,避免重复执行 |
| 确定性(权限、输入错误) | 不重试,报告给用户 | 原样重试只会浪费时间与费用 |
| 结果未知 | 先查实际状态,再决定 | 这是最容易重复执行的一类 |
还要设一个总预算:连续失败达到次数上限就停止,并把失败原因与已完成的部分一起交给你。 无限重试是最坏的设计,它会在你不知情的情况下持续消耗费用。
五、费用:预算不是界面上的一个数字
长期在线的助手会持续消耗资源。预算要管住三件事:
- 计量:模型请求、工具调用、执行时间都要计量,不能只算其中一项。
- 预警:接近上限时提前告知,让你有机会调整。
- 到达上限后的明确行为:是停下、降级到更省的模式,还是向你请示?必须事先定义,不能到时候各模块自行其事。
权限也是同理。读取一个目录、修改其中的文件、把内容发给外部服务,是不同的动作,需要不同的授权。 仅仅在提示词里写「请注意权限」,不等于实现了一条可靠的边界。
六、定时任务与自我唤醒
常驻之后很自然的需求是「每天早八点整理一次昨日总结」「每小时检查一次有没有新通知」。这类需求靠两样东西实现:
- 定时器:到点触发一个任务。
- 自我唤醒:任务主动安排自己在未来某个时间被重新调度,例如「十分钟后再看一次这个文件有没有更新」。
两者都要考虑重复触发与错过触发。错过触发怎么补:一次补完、只补最近一次,还是直接跳过?不同业务要求不同,必须明确。 重复触发怎么办:用幂等标识与去重表。
七、不要依赖某个一直开着的窗口
早期做自动化最常见的设计错误,是把整个系统挂在一个始终打开的窗口上:必须有人一直开着某个页面, 定时任务才会跑。这个设计有三个致命问题:
- 电脑休眠、网络切换、误关窗口,任务就停了,而你并不知道。
- 你无法同时用这台电脑做别的事,或者只能在特定设备上工作。
- 任务没有独立的生命周期,谈不上「恢复」。
正确做法是把执行放在服务端或一个独立的后台进程里,界面只是查看与操作的入口。 这叫瘦客户端、胖服务端:界面轻,逻辑重,状态存在能被多家设备访问的地方。
八、记忆:什么记成信息,什么记成命令
长期助手需要记忆,但记忆不是把所有对话都存下来。至少分三层:
| 层 | 存什么 | 例子 |
|---|---|---|
| 事实层 | 稳定的事实与偏好 | 「我的课表在每周三下午没有课」 |
| 规则层 | 你纠正后形成的做法 | 「整理通知时,不要把报名截止时间当成活动开始时间」 |
| 过程层 | 当前任务的进度与中间结果 | 「周报已生成初稿,等待核对数字」 |
三层混在一起会出问题。事实与规则会被误当成当前的指令;过程记录会被误当成长期知识。 一个实用的原则是:记忆是信息,不是命令。从资料里读到的一句话,不能自动变成对助手的授权。
九、长任务可恢复的三个前提
「接着做」这件事要成立,需要三样东西同时具备:
- 知道做到哪了:进度是可查询的,不是靠回忆。这通常意味着每一步都有记录。
- 知道有没有副作用:哪些动作已经对外界发生了,重做会出问题。这需要幂等标识与执行记录。
- 知道外部状态有没有变:恢复时外部文件、数据可能已经被人改过,需要实际检查,不能凭假设。
三样缺一样,恢复就可能变成二次破坏。这也是为什么很多「支持恢复」的功能实际上只在理想情况下有效: 它只做了第一条。
十、一台普通的电脑也能开始
这套东西听起来需要服务器,其实起步阶段用一台常开的旧笔记本或一台小主机就够。 关键是三件事:任务不在你的工作窗口里跑、状态存在能被多处访问的地方、失败能被你看见。
如果连一台常开的机器都没有,可以先做「半自动」:任务由你手动触发,但状态与记录完整保存, 下次触发时能接着上次的进度。这已经能解决大部分「重复劳动」的问题,也比一上来就搭一套复杂的调度系统更容易成功。
① 触发方式(定时、手动、事件);
② 状态存在哪里(文件、数据库、还是某个服务);
③ 哪一步可能重复执行,用什么标识去重;
④ 失败时你怎么知道,怎么接着做。
四行写完,你已经把「自动化」从想法变成可实施的设计。