Chrome 浏览器多实例隔离(三):会话状态同步、锁机制与迁移

多智能体场景下状态同步的三种模式:乐观锁、悲观锁、无共享。以及当智能体需要迁移到另一个浏览器实例时,如何保留会话状态。

亿牛云技术团队2026年4月12日4 分钟阅读

多智能体状态管理的核心矛盾

单智能体场景中,状态管理很简单:一个智能体操作一个页面,状态就在那个页面上。操作成功就继续,失败就重试。

多智能体场景中,问题变得复杂:

  1. 共享状态:两个智能体操作同一个目标网站时,谁更新了订单状态?谁修改了配置?
  2. 状态视图不一致:智能体 A 记录"已提交表单",智能体 B 在同一时刻查询仍是"未提交"。
  3. 会话移动:智能体 A 的会话需要迁到另一个节点,但 Cookie 和 localStorage 绑定在原来的 Chrome Profile 上。

问题 1:共享状态同步

场景

两个智能体管理同一个电商平台的多个店铺。智能体 A 更新了店铺 1 的产品库存,智能体 B 不知道,从缓存中读取了旧的库存数据,覆盖了 A 的更新。

方案:三种同步策略

乐观锁(Optimistic Locking)

假设冲突很少发生,操作时记录版本号,提交时检查版本号是否变化:

# 乐观锁:更新时检查版本号
def update_inventory(store_id, new_count, expected_version):
    # SQL: UPDATE inventory SET count = new_count, version = version + 1
    #       WHERE store_id = ? AND version = expected_version
    # 如果 affected_rows == 0,说明版本冲突,需要重试
    result = db.execute(
        "UPDATE inventory SET count = ?, version = version + 1 \
         WHERE store_id = ? AND version = ?",
        [new_count, store_id, expected_version]
    )
    if result.affected_rows == 0:
        raise ConflictError("Inventory was updated by another agent")

适合冲突概率低的场景(读取多、写入少)。

悲观锁(Pessimistic Locking)

操作前先锁定资源,操作完成后再释放:

# 悲观锁:操作前锁定资源
def lock_and_update(store_id):
    lock_key = f"lock:inventory:{store_id}"
    acquired = redis.setnx(lock_key, "locked", expire=30)  # 30 秒自动过期
    if not acquired:
        raise ResourceBusyError(f"Store {store_id} is being updated by another agent")
    try:
        # 执行更新操作
        update_inventory(store_id)
    finally:
        redis.delete(lock_key)

适合写入频率高的场景。

无共享(Shared Nothing)

每个智能体只操作自己分配的资源,不交叉:

# 资源分区:每个智能体只操作自己的店铺
AGENT_A_STORES = ["store_1", "store_2"]
AGENT_B_STORES = ["store_3", "store_4"]

任何同步操作都不需要,因为没有共享状态。这是理论上最简单、实际中最容易被忽略的方案。

问题 2:会话迁移

场景

一个智能体在浏览器实例 A 上登录了一个目标网站,跑了 20 分钟操作,积累了 Cookie、localStorage 和 IndexedDB。现在实例 A 需要重启(内存泄漏、版本更新、节点故障)。智能体的会话需要迁移到实例 B,不丢失登录态。

方案一:导出/导入存储状态

import json
 
def export_session_state(cdp_ws_url):
    """从 CDP 导出会话状态"""
    cookies = page.evaluate("document.cookie")
    local_storage = page.evaluate("JSON.stringify(window.localStorage)")
 
    return {
        "cookies": cookies,
        "localStorage": local_storage,
        "sessionStorage": page.evaluate("JSON.stringify(window.sessionStorage)"),
    }
 
def import_session_state(cdp_ws_url, state):
    """导入会话状态到新的浏览器实例"""
    # 设置 Cookie
    for cookie in state["cookies"]:
        page.set_cookie(cookie)
    # 恢复 localStorage
    for key, value in json.loads(state["localStorage"]).items():
        page.evaluate(f"window.localStorage.setItem('{key}', '{value}')")

这种方式的问题:导出的 Cookie 中的 HttpOnly 标志无法通过 JavaScript 读取,只能通过 CDP 的 Network.getCookies 获取。

方案二:CDP Network.getCookies + Network.setCookies

# 导出
cookie_result = cdp.send("Network.getCookies")
state["cookies"] = cookie_result["cookies"]
 
# 导入到新会话
cdp.send("Network.setCookies", {"cookies": state["cookies"]})

这种方式可以导出 HttpOnly Cookie,但需要 CDP 级别的访问权限。

方案三:复用持久化 Profile

最可靠的方式:把整个 Chrome Profile 目录从一台机器复制到另一台机器:

# 源机器
tar czf profile_backup.tar.gz -C /data/profiles/agent_a .
 
# 目标机器
tar xzf profile_backup.tar.gz -C /data/profiles/agent_a/

然后在新机器上使用相同的 Profile 路径启动 Chrome。所有会话状态保持完整。

这种方式的问题:Profile 目录可能包含大量缓存数据(数百 MB 到 GB),跨网络复制需要时间。更实际的做法是只复制关键的 Storage 子目录:

# 只复制需要的存储数据
tar czf session_state.tar.gz \
  -C /data/profiles/agent_a/Default \
  Cookies Cookies-journal Local\ Storage IndexedDB

问题 3:操作编排

在多智能体场景中,另一个常见问题是操作时序。智能体 A 和 B 同时对同一个页面发出不同的操作指令,导致页面状态不可预测。

编排策略

最简单的解决方案是串行化:任何针对同一目标的操作都通过中心化队列调度:

# 串行化操作队列
import asyncio
import aioredis
 
class OperationQueue:
    def __init__(self, redis):
        self.redis = redis
 
    async def enqueue(self, target_key, operation):
        """将操作加入队列"""
        await self.redis.rpush(f"op_queue:{target_key}", operation)
 
    async def dequeue(self, target_key):
        """从队列取出下一个操作"""
        return await self.redis.blpop(f"op_queue:{target_key}", timeout=5)
 
# 使用
queue = OperationQueue(redis)
# 智能体 A 的操作
await queue.enqueue("store_1", {"action": "update_price", "value": 99.99})
# 智能体 B 的操作
await queue.enqueue("store_1", {"action": "update_stock", "value": 50})
# 执行器按顺序执行
while op := await queue.dequeue("store_1"):
    execute_operation(op)

总结

多智能体场景的状态管理比单智能体复杂至少一个数量级,因为你需要处理:

  1. 共享状态同步——乐观锁、悲观锁或无共享
  2. 会话迁移——CDP Cookie 导出/导入或 Profile 目录复制
  3. 操作编排——串行化队列保证操作顺序

无论选择哪种方案,有一条原则值得记住:两个智能体不应该同时操作同一个浏览器的同一个页面。 要么隔离资源(无共享),要么串行化操作(队列),不要让两个大脑同时控制同一双手。

需要企业代理方案?

我们可根据目标站点、并发规模与稳定性目标提供定制方案。