---
title: "Long-running Agent 并不一直运行"
author: "Shawn Jiang"
lang: zh
date: 2026-08-28
canonical: https://tplog.dev/zh/essay/long-running-agent-is-not-always-running/
translations:
  en: https://tplog.dev/en/essay/long-running-agent-is-not-always-running/
  ja: https://tplog.dev/ja/essay/long-running-agent-is-not-always-running/
---

# Long-running Agent 并不一直运行

我们通常把“长期运行”理解为一个进程长期存活。数据库、消息队列和 Web 服务启动以后，会持续占用内存、监听请求，直到进程退出。按照这种直觉，一个 Long-running Agent 似乎也应该是某个一直在线、始终思考的模型实例。

Agent 采用另一种连续方式。它可能几小时没有执行任何推理，所在的进程可能已经退出，机器可能已经释放，下一次启动时甚至可能换了一种模型。只要此前的目标、历史和判断能够恢复，我们仍会把它视为“同一个 Agent”。Long-running Agent 的“长期”来自状态可以被反复重建。

## Agent 大部分时间并没有在运行

传统的 Long-running Service 通常遵循这样的生命周期：

```text
进程启动 → 持续运行 → 进程退出
```

它的连续性和进程存活高度相关。进程一旦退出，内存中的状态也会随之消失。为了让服务继续工作，我们需要重启进程、恢复数据，并重新建立连接。

Long-running Agent 更接近另一种模式：

```text
被唤醒
  → 加载持久状态
  → 组装当前上下文
  → 执行一次 Turn
  → 保存结果
  → 进入休眠
```

在两次唤醒之间，它可能没有模型实例，没有运行中的推理任务，也不消耗推理资源。比如，一个 Agent 周一收到任务，分析了代码并记录下一步计划；周三因为新的事件再次被唤醒，继续修改同一个项目。中间两天没有发生任何计算，但周三恢复出的 Agent 知道自己在做什么、已经做过什么、接下来应该做什么。这两次运行因此构成了一个连续过程。

## 分清 Agent、Conversation、Session 和 Turn

讨论 Long-running Agent 时，四个概念经常被混在一起：

- **Agent**：一个可以长期延续的身份及其行为约束。
- **Conversation**：与这个 Agent 相关的持久对话历史。
- **Session**：用户或系统与 Agent 的一次活动连接。
- **Turn**：模型被实际调用并完成一次推理的过程。

一次 Session 可以包含多个 Turn，一个 Conversation 可以跨越多个 Session，一个 Agent 又可以参与多个 Conversation。浏览器关闭后，Session 可能结束，Conversation 仍然存在；运行模型的容器被释放后，本次 Turn 已经结束，Agent 保存的目标和记忆仍可在下一次连接时加载。换机器、换运行时，甚至换模型，都不一定会破坏连续性。关键在于恢复出的身份、历史、目标和行为能否保持足够的一致性。

这里的“身份”并不神秘。它通常由一组可持久化的材料构成：系统指令、用户关系、任务状态、长期记忆、对话历史，以及运行环境中的资源和权限。

这些材料会在一次具体运行中重新组合，由此产生的行为结果构成了当下的 Agent。

## Harness 是 Agent 的操作系统

一次模型调用只能看到传入上下文窗口的内容。调用结束后，模型不会自动保存这次经历，也不会在下一次调用时自然地“想起”上一次发生了什么。保存和恢复工作由模型外部的 Harness 完成。Harness 可以理解为承载 Agent 的运行系统：它管理对话、记忆、工具、环境和模型调用，并在每个 Turn 开始之前决定哪些信息应该进入上下文。

这个关系很像程序与操作系统：

| Agent 系统 | 操作系统 |
|---|---|
| Context window | 物理内存 |
| Conversation history | 磁盘 |
| Compaction | 压缩与换页 |
| Recall | 按需重新调入 |
| Long-term memory | 持久化结构化存储 |
| Harness | 操作系统 |

Conversation 可以持续增长，但模型的 Context window 始终有限。这就像磁盘可以存储大量数据，而一个进程只能把当前需要的工作集放进有限内存。

当对话超过上下文限制时，Harness 必须压缩旧内容、保留近期消息，并根据当前任务检索相关历史。它实际上在做一件类似内存管理的工作：决定什么留在当前工作集里，什么暂时移出，以及什么时候重新调入。

Harness 每次调用模型之前，都要据此重新构造 Agent 当下的“意识”。

没有 Harness，模型只是一次性的计算函数。输入一段上下文，生成一段输出，然后结束。正是 Harness 把许多彼此分离的模型调用组织成了一个看似连续的行动主体。

## 连续性是一种编译结果

每次 Agent 被唤醒时，Harness 都会从多个来源收集材料：

```text
system prompt
+ agent memory
+ conversation summary
+ recent messages
+ recalled history
+ current environment
+ available tools
= 本次模型实际看到的 Agent
```

这个过程更像编译，而不是回放。Harness 通常不会把 Agent 的全部历史原封不动地交给模型。它需要根据有限的上下文预算，对历史进行选择、压缩和重组，生成当前 Turn 可以使用的工作集。系统保存的是源材料，模型看到的是编译结果。

一段历史可以完整保存在数据库里，但如果它没有被放进当前上下文，模型此刻就不知道它。一次关键决策可能仍然存在于三个月前的消息中，但如果摘要没有保留、检索也没有命中，它对当前 Agent 就等于暂时不存在。

历史保存在数据库里，只说明材料仍可取回。Agent 的连续性取决于 Harness 能否针对当前任务，把正确的历史重新编译进有限上下文。

## 对话可以增长，记忆仍然受限

许多 Agent 产品允许对话持续很长时间，甚至给人一种上下文可以无限增长的感觉。Harness 可以隐藏底层限制，却无法消除它。只要模型的上下文有限，系统就必须做选择；选择意味着信息损失，也会带来几类典型问题。

### 1. 压缩损失

对话过长时，Harness 会把旧消息压缩成摘要。

摘要通常能够保留结论，却未必能够保留形成结论的完整过程。具体措辞、证据来源、被否决的方案，以及某个判断成立的条件，都可能在压缩中消失。

例如，原始讨论的结论可能是：

> 当前阶段选择方案 A，因为部署环境暂时不支持方案 B；如果环境升级，需要重新评估。

经过多轮压缩后，它可能只剩下：

> 项目决定使用方案 A。

结论看似保留了，适用条件却丢失了。Agent 仍然“记得决定”，却不再记得为什么做出决定，也不知道什么时候应该推翻它。

### 2. Recall 失败

有些信息不会一直放在上下文中，而是在需要时通过检索重新加载。

问题在于，检索取决于当前查询。如果用户使用了不同的说法，或者 Agent 没有意识到某段旧历史与当前问题有关，相关信息即使存在，也可能无法命中。

信息仍然存在，系统却不知道此刻应该去找它，这是一种检索层面的遗忘。

### 3. 身份漂移

如果重要经验只存在于对话历史中，却没有进入稳定的长期记忆，Agent 每次恢复出来的状态就可能略有不同。

一次摘要强调用户偏好，另一次摘要强调当前任务；一次检索找到了过去的约定，另一次没有。随着时间推移，Agent 的行为边界、表达习惯和判断依据都可能缓慢漂移。

这种变化不会在某次恢复后突然发生。Agent 会在许多次不完整恢复中，逐渐失去原来的部分特征。保存全部历史相对容易，困难的是在下一次行动发生时恢复最重要的内容。

## 用可恢复性衡量 Long-running Agent

如果接受这个视角，设计 Long-running Agent 的重点也会随之改变。

设计重点需要从进程 uptime 转向状态恢复：检查关键约束能否在需要时重新进入上下文，并区分近期工作集、对话档案、长期经验和当前环境。

一个 Agent 是否具有连续性，可以用几个更实际的问题判断：

- 它醒来后是否知道自己正在完成什么目标？
- 它是否知道此前已经采取了哪些行动？
- 它能否恢复关键决定及其成立条件？
- 它是否记得与用户之间长期有效的约定？
- 当环境发生变化时，它能否区分旧事实与当前事实？
- 即使更换模型或运行时，它的核心行为是否仍然稳定？

评估时，可以故意结束进程，让任务休眠一段时间，甚至更换运行时或模型，再重新唤醒 Agent。检查它能否恢复目标、已完成的行动、关键决定的适用条件和长期用户约定，也要检查它能否识别环境已经变化。只有数据库里保存着对话，而恢复后的 Agent 无法使用这些材料，连续性就没有建立起来。
