写技术教程最怕两件事:一是概念讲得云里雾里,二是代码拿回去跑不通。之前在业务里接触 Agent 开发时,一直觉得上下文管理是个“玄学”——明明对话记录都在,模型却总是“失忆”;明明上下文窗口还有余量,运行效率却直线下降。后来看到 OpenViking 这类把 Agent 上下文设计成文件系统的思路,才意识到问题的本质:我们一直在用“对话流”的方式管理上下文,但真正需要的其实是“文件系统”式的组织能力。

这篇文章就围绕 OpenViking 的核心思想展开,讲清楚为什么 Agent 上下文需要文件系统抽象、它的目录结构怎么设计、读写语义怎么定义,以及怎样在真实工程中落地上层。文章会给出可运行的示例代码,也会把常见的坑整理成排查清单,适合正在做 Agent 开发、研究 AI 编程助手上下文管理、以及想了解上下文工程(Context Engineering)的开发者阅读。

1. 背景与核心概念

1.1 Agent 上下文到底是什么

我们先从最基础的问题说起:Agent 上下文到底是什么?

从大模型应用的角度看,Agent 上下文是模型在某一轮推理过程中可以访问的全部信息。它不只是用户和助手之间的聊天记录,还包括:

  • 系统提示词(System Prompt)中定义的规则、人格、工作流。
  • 用户在当前会话中提供的输入。
  • Agent 调用工具后返回的结果。
  • 模型自己生成的中间推理过程(Chain of Thought)。
  • 从外部存储、数据库、知识库中检索到的片段。
  • 代码仓库中的文件内容、执行环境的状态、前端页面结构等环境信息。

也就是说,上下文是 Agent 的“工作台”,是它思考和行动的依据。

在传统软件工程里,程序的状态可以通过变量、数据库、文件持久化来保存。但大模型 Agent 不一样,它每一次推理都要把相关信息塞进上下文窗口。窗口越大,能塞的东西越多,但同时也会带来两个问题:

  • 成本增加:Token 越多,API 调用费用越高。
  • 效果下降:上下文过长时,模型对中间信息的关注度会衰减,出现“迷失在中间”(Lost in the Middle)现象。

所以 Agent 上下文管理,本质上是在做“如何在有限窗口内,保留最有价值的信息”这件事。

1.2 上下文管理的经典痛点

做过 Agent 开发的读者,应该都经历过下面这些场景:

场景一:新开会话丢失记忆

用户在使用一个 AI 编程助手时,刚写完一段核心逻辑,然后新开了一个会话,结果助手完全不记得之前的代码了。原因很简单——会话结束了,上下文没有持久化。这是当前很多 Agent 工具的通病。

场景二:上下文过大,自动总结也没用

对话进行了几十轮,程序不断调用自动总结功能,把前面的内容压缩成摘要。但总结本身也占 Token,而且总结过程中可能丢失细节。最常见的结果是:

Context length exceeded. Please start a new conversation.

有些框架里会报错,比如类似 “The agent execution provider did not respond in time”,本质都是上下文或者执行链路超过了处理边界。

场景三:上下文压缩导致信息失真

自动总结确实能压缩篇幅,但它不是无损的。比如前面有用户明确指定的一个技术选型:“不要用 Spring Security,用自研过滤器”,压缩后可能就变成了“用户对权限方案有要求”,细节全没了。Agent 后续的行为就会偏离用户预期。

场景四:多个上下文之间难以协同

一个大型 Agent 项目可能包含多个子 Agent:负责代码生成的、负责测试的、负责文档的。每个子 Agent 都有自己的上下文,但它们之间如何共享信息?目前大多数方案的答案是:没有方案,各自为战。

这些痛点的共同根源是:上下文被当作“一条流”,而不是“一组有结构的数据”。

1.3 为什么选择“文件系统”作为抽象

文件系统(File System)是操作系统中用于管理持久化数据的机制。Linux、Windows、macOS 都有文件系统,它们负责把数据组织成文件和目录,并提供统一的访问接口。

如果把 Agent 上下文比作数据,那么我们很自然地会想到一个问题:能不能用文件系统的方式来管理上下文?

OpenViking 的核心思路正是如此。它把 Agent 的上下文建模成一个虚拟文件系统,上下文中的每一项信息都可以映射为一个文件或一个目录,并通过路径(Path)来寻址。

这种设计带来的价值非常明显:

  1. 统一寻址 所有上下文条目都有固定路径,例如 /memory/user_preference.md /workspace/code/main.py 。Agent 可以通过路径精准读写,不需要在冗长的对话流里搜索。

  2. 支持增量更新 文件系统支持按需读取。Agent 不需要把整个上下文读进来,只需要读取当前任务对应的目录或文件。这天然解决了上下文过大的问题。

  3. 天然持久化 文件系统本身就是持久化存储。Agent 的任务执行完,上下文可以落盘(Sync),下次启动时自动挂载(Mount),会话记忆不会因为窗口关闭而消失。

  4. 灵活的信息组织 目录天然支持层级分类,可以把工作区、用户偏好、工具缓存、对话历史分开管理。不同的 Agent 还可以通过目录权限隔离,避免互相污染。

  5. 兼容现有工具链 文件系统的概念已经存在几十年,开发者熟悉 ls cat grep 这些操作。如果 Agent 的上下文可以通过类文件系统接口操作,那么自定义工具、脚本、CI/CD 流程都可以复用现有经验。

这里需要区分一个容易混淆的概念:OpenViking 不等于“把上下文保存成磁盘上的文件”。它做的是一种抽象层,底层可能确实使用磁盘文件,也可能使用数据库、内存或对象存储,但对上层暴露的是文件系统的语义。就像 Linux 的 VFS(Virtual File System)一样,它屏蔽了不同底层存储的差异,对外提供统一的文件操作接口。

2. 环境准备与版本说明

2.1 运行环境

本文的示例代码基于 Python 3.9+ 编写,不依赖第三方框架,核心逻辑使用标准库即可运行。操作系统方面,Windows、macOS、Linux 都可以,但如果你希望在本地体验“挂载目录、查看文件数”这类操作,建议使用 Linux 或 macOS,体验更贴近文件系统语义。

依赖项:

  • Python 3.9 或更高版本。
  • 操作系统自带文件系统权限(用于演示本地目录读写)。
  • 如果希望接入大模型 API,需要准备对应的 API Key;但本文的核心示例不强制调用模型,只演示上下文文件系统的构建与检索。

版本说明:OpenViking 目前还处于快速迭代阶段,不同版本的 API 可能有差异。本文的代码以“思路 + 可运行示例”的方式呈现,重点帮助你理解设计模式。真正接入具体项目时,需要根据你使用的版本调整包名和调用方式。

2.2 需要掌握的基本概念

在开始写代码之前,先约定几个文件系统领域的术语,方便后文阅读。

术语 含义 在上下文文件系统中的对应
Mount(挂载) 将一个文件系统连接到某个目录下 加载 Agent 历史上下文到当前会话
Path(路径) 文件或目录在文件系统中的位置 上下文条目的唯一地址
Directory(目录) 用于组织文件的容器 上下文的分类,如工作区、记忆
File(文件) 存储具体数据的单元 一条具体上下文,如一段对话
Read(读) 从文件获取数据 将某部分上下文放入提示词
Write(写) 将数据保存到文件 新增或更新上下文
Sync(同步) 将内存中的变更持久化 将上下文快照写回存储
Snapshot(快照) 某一时刻的完整状态 上下文版本备份

理解这些概念后,我们进入核心原理拆解环节。

3. 核心原理拆解:上下文文件系统的设计思路

3.1 挂载点:上下文从哪里来

在 Linux 中, mount 命令将一个块设备或远程文件系统挂载到某个目录。在 OpenViking 的设计中,挂载点就是 Agent 上下文的“根目录”。

假设我们定义根目录为 /ctx ,那么 Agent 的上下文都从这个根目录开始寻址。第一次启动时,根目录可能为空;如果是恢复上一次会话,Agent 会从持久化存储中读取快照并恢复整个目录结构。

示例流程:

  1. Agent 启动。
  2. 检查是否有历史快照。
  3. 如果没有,创建空目录树;如果有,从快照恢复。
  4. 将工作目录挂载到 /ctx/workspace
  5. 加载用户偏好到 /ctx/memory/user_profile.md
  6. 加载系统提示词到 /ctx/system/prompt.md

这个过程非常像 IDE 打开一个项目:先恢复上次的工作区状态,再加载各类配置文件。

3.2 目录结构:上下文的分层组织

一个合理的上下文文件系统应该按“关注点”划分目录,而不是按时间线平铺。下面是一棵推荐的目录树:

/ctx
├── system/                  # 系统级上下文,Agent 运行规则
│   ├── prompt.md            # 主系统提示词
│   ├── tools.md             # 工具描述说明
│   └── skills/              # 技能定义
│       ├── coding.md
│       └── testing.md
├── memory/                  # 长期记忆,跨会话保留
│   ├── user_profile.md      # 用户偏好与画像
│   ├── decisions.md         # 项目中做过的关键决策
│   └── facts/               # 领域事实
├── workspace/               # 当前任务工作区,短期上下文
│   ├── code/                # 源代码文件
│   ├── data/                # 数据文件
│   └── output/              # 输出结果
├── session/                 # 当前会话相关的中间状态
│   ├── plan.md              # 当前执行计划
│   ├── progress.md          # 已完成/待办状态
│   └── cache/               # 临时缓存
└── logs/                    # 运行日志与审计
    ├── actions.log
    └── tokens.log

这个设计有几点考虑:

  • system 和 memory 属于高频引用内容,可以常驻上下文窗口或按需加载。
  • workspace 属于任务相关内容,任务结束后可以归档或清理。
  • session 是易变状态,每次会话都会重写。
  • logs 用于审计和调试,不参与模型推理。

通过这种分层,Agent 可以快速确定“当前任务需要加载哪些目录”,而不是把所有内容一股脑塞进提示词。

3.3 读写语义:open / read / write / close

文件系统最核心的操作是打开、读取、写入、关闭。OpenViking 把这种语义搬到了上下文管理中。

下面是一个伪代码级的设计示例:

# 文件路径:context_fs.py
class ContextFile:
    def __init__(self, path: str, content: str = ""):
        self.path = path
        self.content = content
        self.is_open = False

    def open(self):
        self.is_open = True
        return self

    def read(self) -> str:
        if not self.is_open:
            raise RuntimeError("File is not open")
        return self.content

    def write(self, data: str):
        if not self.is_open:
            raise RuntimeError("File is not open")
        self.content = data

    def append(self, data: str):
        if not self.is_open:
            raise RuntimeError("File is not open")
        self.content += data

    def close(self):
        self.is_open = False

在这个设计中, open 类似于把上下文文件“加载到内存”, read 返回内容用于拼进 Prompt, write 更新内容, close 释放句柄。如果 Agent 只读取了几个文件,那么这几个文件的内容才是本轮推理的上下文。

这与传统的对话式上下文管理最大的区别在于:传统方式通常会一次性把所有历史消息发送给模型,而文件系统方式允许按路径精确选择要发送的内容。

3.4 同步与落盘:sync 的意义

文件系统里有一个重要概念叫“脏页”,即内存中被修改但尚未写入磁盘的数据页。 sync 命令的作用就是把脏页写入磁盘,防止断电丢数据。

上下文文件系统同样需要同步机制。Agent 在一轮推理中可能会写入很多临时信息,这些信息最初保存在内存中,风险是:

  • 进程崩溃后,所有临时上下文丢失。
  • 模型上下文窗口清理后,未持久化的数据无法找回。
  • 多个 Agent 之间无法共享内存中的数据。

引入 sync 操作后,Agent 可以在关键节点主动持久化。比如:

  • 每完成一个子任务,执行一次 sync
  • 在调用外部工具前 sync ,防止工具运行失败导致状态丢失。
  • 在会话结束时强制 sync ,保存完整快照。

类比 Linux VFS,OpenViking 的设计可以理解成:上层提供统一的 sync 抽象,底层可能调用本地磁盘、远程存储或数据库的写入接口。这一层抽象让上下文管理变得可插拔。

3.5 快照与版本回滚

文件系统的另一个优势是支持快照。快照是文件系统在某个时间点的完整副本,可以用来回滚。

在 Agent 项目中,快照的价值体现在:

  • 如果 Agent 执行了一系列错误操作(比如改坏了代码),可以回退到执行前的上下文状态。
  • 如果自动压缩上下文时丢失了细节,可以回到压缩前的快照重新压缩。
  • 如果调试时发现某个决策有误,可以查看当时上下文快照来定位原因。

快照不需要做完所有变更,建议采用“关键节点 + 定期”的策略。

4. 完整实战案例:用文件系统管理 Agent 上下文

这一节我们实现一个简化版的上下文文件系统。它包含以下能力:

  • 创建目录结构和基础文件。
  • 按路径读取上下文。
  • 增量写入上下文。
  • 将内存状态同步到磁盘。
  • 创建快照。
  • 根据关键词检索上下文。

代码可以独立运行,不需要连接任何大模型 API。

4.1 定义上下文文件系统类

# 文件路径:context_vfs.py
import os
import json
import shutil
from datetime import datetime
from pathlib import Path


class ContextFile:
    """上下文文件,对应文件系统中的一个文件"""

    def __init__(self, path: str, content: str = ""):
        self.path = path
        self.content = content
        self.is_open = False

    def open(self):
        self.is_open = True
        return self

    def read(self) -> str:
        if not self.is_open:
            raise RuntimeError(f"File {self.path} is not open")
        return self.content

    def write(self, data: str):
        if not self.is_open:
            raise RuntimeError(f"File {self.path} is not open")
        self.content = data

    def append(self, data: str):
        if not self.is_open:
            raise RuntimeError(f"File {self.path} is not open")
        self.content += data

    def close(self):
        self.is_open = False

    def to_dict(self):
        return {"path": self.path, "content": self.content}


class ContextVFS:
    """简化版上下文文件系统"""

    def __init__(self, root: str = "/ctx"):
        self.root = root
        self.files = {}
        self.dirty = set()
        self._init_default_tree()

    def _init_default_tree(self):
        """初始化默认目录结构"""
        default_files = {
            "/ctx/system/prompt.md": "你是专业的技术开发助手。",
            "/ctx/system/tools.md": "可调用工具列表:代码搜索、文件读写、命令执行。",
            "/ctx/memory/user_profile.md": "用户偏好:Python 技术栈,注重代码可读性。",
            "/ctx/memory/decisions.md": "关键决策记录:使用 SQLite 存储元数据。",
            "/ctx/workspace/code/main.py": "# 主程序入口\n",
            "/ctx/session/plan.md": "当前计划:实现上下文文件系统。",
            "/ctx/session/progress.md": "已完成:目录设计。\n待办:编码实现。",
        }
        for path, content in default_files.items():
            self.files[path] = ContextFile(path, content)

    def open(self, path: str) -> ContextFile:
        """打开文件,如果文件不存在则创建空文件"""
        if path not in self.files:
            self.files[path] = ContextFile(path, "")
        return self.files[path].open()

    def read(self, path: str) -> str:
        f = self.open(path)
        try:
            return f.read()
        finally:
            f.close()

    def write(self, path: str, data: str):
        f = self.open(path)
        try:
            f.write(data)
            self.dirty.add(path)
        finally:
            f.close()

    def append(self, path: str, data: str):
        f = self.open(path)
        try:
            f.append(data)
            self.dirty.add(path)
        finally:
            f.close()

    def list_dir(self, path: str = "/ctx") -> list:
        """列出某个目录下的文件与子目录"""
        prefix = path.rstrip("/") + "/"
        result = set()
        for p in self.files:
            if p.startswith(prefix):
                relative = p[len(prefix):]
                top = relative.split("/")[0]
                result.add(top)
        return sorted(result)

    def search(self, keyword: str) -> list:
        """按关键词搜索上下文内容"""
        hits = []
        for path, f in self.files.items():
            if keyword in f.content:
                hits.append(path)
        return hits

    def sync(self, base_dir: str = "./ctx_disk"):
        """将内存中的上下文同步到磁盘"""
        os.makedirs(base_dir, exist_ok=True)
        for path, f in self.files.items():
            # 将 /ctx/system/prompt.md 转为 ./ctx_disk/system/prompt.md
            relative = path.lstrip("/")
            disk_path = Path(base_dir) / relative
            disk_path.parent.mkdir(parents=True, exist_ok=True)
            disk_path.write_text(f.content, encoding="utf-8")
        self.dirty.clear()
        print(f"[sync] 已同步 {len(self.files)} 个文件到 {base_dir}")

    def snapshot(self, snapshot_dir: str = "./ctx_snapshots"):
        """创建当前上下文快照"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        target = os.path.join(snapshot_dir, f"snapshot_{timestamp}")
        os.makedirs(target, exist_ok=True)
        for path, f in self.files.items():
            relative = path.lstrip("/")
            disk_path = Path(target) / relative
            disk_path.parent.mkdir(parents=True, exist_ok=True)
            disk_path.write_text(f.content, encoding="utf-8")
        print(f"[snapshot] 快照已保存到 {target}")
        return target

    def export_meta(self, path: str = "./ctx_meta.json"):
        """将上下文元数据导出为 JSON,便于其他工具消费"""
        meta = {
            "root": self.root,
            "file_count": len(self.files),
            "total_chars": sum(len(f.content) for f in self.files.values()),
            "files": [f.to_dict() for f in self.files.values()],
        }
        with open(path, "w", encoding="utf-8") as fp:
            json.dump(meta, fp, ensure_ascii=False, indent=2)
        print(f"[meta] 元数据已导出到 {path}")


if __name__ == "__main__":
    vfs = ContextVFS()

    print("=== 初始目录结构 ===")
    for item in vfs.list_dir():
        print(f"/ctx/{item}")

    print("\n=== 读取用户偏好 ===")
    print(vfs.read("/ctx/memory/user_profile.md"))

    print("\n=== 追加一条执行记录 ===")
    vfs.append("/ctx/session/progress.md", "\n新增:写入上下文 VFS。")
    print(vfs.read("/ctx/session/progress.md"))

    print("\n=== 搜索包含“文件系统”的文件 ===")
    for hit in vfs.search("文件系统"):
        print(hit)

    print("\n=== 同步到磁盘 ===")
    vfs.sync()

    print("\n=== 创建快照 ===")
    vfs.snapshot()

运行这个脚本,预期输出如下:

=== 初始目录结构 ===
system
memory
workspace
session

=== 读取用户偏好 ===
用户偏好:Python 技术栈,注重代码可读性。

=== 追加一条执行记录 ===
已完成:目录设计。
待办:编码实现。
新增:写入上下文 VFS。

=== 搜索包含“文件系统”的文件 ===
/ctx/workspace/code/main.py
/ctx/session/plan.md

=== 同步到磁盘 ===
[sync] 已同步 8 个文件到 ./ctx_disk

=== 创建快照 ===
[snapshot] 快照已保存到 ./ctx_snapshots/snapshot_20250101_120000

4.2 生成 Prompt:只选择必要上下文

有了文件系统,我们可以按需生成 Prompt。下面这个示例展示如何只选取“系统提示词 + 用户偏好 + 当前计划”三条信息来组织 Prompt,而不是把整个上下文全部发送给模型。

# 文件路径:build_prompt.py
from context_vfs import ContextVFS


def build_prompt(vfs: ContextVFS, task: str) -> str:
    sections = []

    system_prompt = vfs.read("/ctx/system/prompt.md")
    sections.append(f"[系统指令]\n{system_prompt}")

    tools = vfs.read("/ctx/system/tools.md")
    sections.append(f"[可用工具]\n{tools}")

    user_profile = vfs.read("/ctx/memory/user_profile.md")
    sections.append(f"[用户偏好]\n{user_profile}")

    plan = vfs.read("/ctx/session/plan.md")
    sections.append(f"[当前计划]\n{plan}")

    sections.append(f"[本次任务]\n{task}")

    return "\n\n".join(sections)


if __name__ == "__main__":
    vfs = ContextVFS()
    prompt = build_prompt(vfs, "请实现一个上下文检索工具")
    print(prompt)

执行后,模型收到的上下文是高度结构化的,而不是一长串无差别聊天记录。这个思路也是 AI Agent 开发中“上下文工程(Context Engineering)”的核心——不是把所有东西都交给模型,而是精心设计交给模型的内容。

4.3 上下文压缩与恢复

当某个上下文文件过大,比如对话历史累积到几万字,我们可以对它做压缩,并保留原始快照以便恢复。

# 文件路径:compress_context.py
import re
from context_vfs import ContextVFS


def compress_content(content: str, max_lines: int = 100) -> str:
    """简化的上下文压缩:保留头尾,中间摘要化"""
    lines = content.strip().splitlines()
    if len(lines) <= max_lines:
        return content

    head = lines[: max_lines // 2]
    tail = lines[-max_lines // 2:]

    # 统计中间被折叠的行数
    folded_count = len(lines) - max_lines
    summary = [
        f"\n[中间 {folded_count} 行已折叠]",
        "摘要:对话主体部分被自动压缩,如需完整内容请读取原始快照。",
    ]

    return "\n".join(head + summary + tail)


if __name__ == "__main__":
    vfs = ContextVFS()

    # 模拟一个非常大的会话日志
    long_log = "\n".join([f"第 {i} 行:用户输入与模型回复的详细内容" for i in range(1, 300)])
    vfs.write("/ctx/session/chat.log", long_log)

    # 压缩前
    original = vfs.read("/ctx/session/chat.log")
    print(f"压缩前行数:{len(original.splitlines())}")

    # 创建快照
    snapshot_dir = vfs.snapshot()

    # 压缩并写回
    compressed = compress_content(original, max_lines=60)
    vfs.write("/ctx/session/chat.log", compressed)
    print(f"压缩后行数:{len(compressed.splitlines())}")

    # 同步到磁盘
    vfs.sync()

这里的关键点是:压缩不是覆盖原文件,而是先生成快照,再写压缩版本。一旦 Agent 需要查看被折叠的细节,可以从快照目录中恢复原始日志。这种“先快照、再压缩”的方式,比直接在上下文窗口里强制截断要安全得多。

5. 常见问题与排查思路

在实现和使用上下文文件系统时,会遇到下面几类典型问题。我们整理成表格,方便快速定位。

问题现象 常见原因 解决思路
Agent 启动时恢复上下文失败 快照目录不存在或快照文件损坏 检查快照路径是否存在,校验快照元数据,考虑保留最近 N 份快照
不同会话之间内存状态冲突 多个 Agent 实例同时读写同一路径 在路径上添加命名空间或会话 ID,如 /ctx/session/{session_id}/
上下文文件越来越大 长期追加日志,但没有归档策略 设计归档任务:旧日志移入 /ctx/logs/archive/ ,主文件保持精简
压缩后上下文丢失关键细节 压缩策略太激进,直接截断 先创建快照再压缩;压缩时保留头部规则和尾部最新状态
Agent 读取了无用上下文导致 Token 超限 Prompt 构建时没有按需选择 read 精确指定文件,而不是一次性读取整个目录列表
同步到磁盘后元数据不一致 只同步了文件内容,没有同步文件索引 同时导出 JSON 元数据文件(如 export_meta ),再做校验
目录结构混乱,开发人员看不懂 没有约定目录规范 在团队内统一上下文目录规范,像约定代码工程结构一样约定上下文结构
并发写文件时互相覆盖 多个协程或 Agent 同时写入同一个文件 引入文件锁,或者按任务拆分到不同子目录

下面单独说一下最麻烦的“上下文过大导致 Agent 报错终止”的场景。

典型报错现象是:

Agent terminated due to error. You can prompt the model to try again or start a new conversation.

这类错误通常不是因为模型能力不足,而是执行链路中某一环超出了限制。排查顺序建议如下:

  1. 查看日志,确认是上下文长度超限,还是工具执行超时。
  2. 如果上下文超限,统计当前 Prompt 实际包含的 Token 量,找出占用最大的文件。
  3. 将占用大的文件拆分成多个小文件,并在 Prompt 中按任务选择性加载。
  4. 为历史会话开启快照,然后对旧日志进行压缩归档。
  5. 重跑任务,观察错误是否消失。

这里要强调一个原则:不要等到 Agent 报错才处理上下文,而是在 Agent 每轮执行结束后主动做同步和清理。文件系统的意义不在于“出了问题能修复”,而在于“通过结构化管理,从源头避免问题发生”。

6. 最佳实践与工程建议

6.1 路径命名规范

上下文文件系统的路径就是上下文的“地址”,命名规范非常重要。

推荐以下规范:

  • 目录使用小写字母,多个单词用下划线连接,如 user_profile
  • 文件使用小写字母和点号,如 prompt.md session.log
  • 临时文件统一放在 /ctx/session/cache/ ,不要散落在根目录。
  • 长期记忆统一放在 /ctx/memory/ ,和短期工作区严格分离。
  • 涉及多个版本的内容,在文件名中标注版本号,例如 plan_v2.md

一个清晰的命名规范,能让 Agent 在检索时更快地命中目标,也能让调试时人更容易理解目录结构。

6.2 按需加载,而非全量加载

这是上下文文件系统最核心的价值。每次构建 Prompt 之前,应该先明确一个问题:

当前这个任务,模型需要知道哪些信息?

通常 Agent 的任务可以拆成多个子任务,每个子任务只加载自己需要的上下文目录。比如:

  • 代码生成任务:加载 /ctx/workspace/code/ /ctx/memory/decisions.md
  • 测试任务:加载 /ctx/workspace/test/ /ctx/system/tools.md
  • 文档任务:加载 /ctx/workspace/docs/ /ctx/memory/user_profile.md

通过按需加载,可以把原本需要 50k Token 的上下文降低到 10k 甚至更少,同时模型关注度更高,输出质量更好。

6.3 关键节点自动同步

建议在以下时机触发 sync

  • 子任务完成后。
  • 调用外部工具前。
  • 从外部 API 返回结果后。
  • Agent 会话结束前。
  • 压缩上下文之前。

同步操作可能有一定的性能开销,所以不需要每一条消息都同步。重点保证“关键状态不丢失”。

6.4 快照策略

快照是回滚的保障,但不建议每次都全量快照。推荐的策略是:

  • 每次会话结束时创建一次快照。
  • 执行高风险操作(如修改代码、删除文件、发布配置)前创建快照。
  • 设置快照保留数量,比如保留最近 20 份,防止磁盘膨胀。
  • 在快照目录下附带一份 meta.json ,记录快照对应的任务和时间。

6.5 安全与权限意识

上下文文件系统中可能包含敏感信息,比如用户密钥、数据库密码、内部 API 地址。需要注意:

  • 敏感信息放在独立目录,比如 /ctx/secret/ ,并在默认 Prompt 构建时排除。
  • 给不同 Agent 分配不同目录权限,不要所有 Agent 共享同一份完整上下文。
  • 在日志中避免打印完整密钥,脱敏后再输出。
  • 当上下文文件需要跨环境传输时,先检查是否包含敏感字段。

权限思维在 Agent 开发中常常被忽略。一个子 Agent 本来只应该访问测试环境的配置,但如果上下文是全量共享的,它就可能把生产环境的密钥也读进窗口,带来安全风险。

6.6 与现有 Agent 框架的集成思路

OpenViking 这类上下文文件系统,可以嵌入到常见的 Agent 开发框架中。一段典型的集成逻辑是:

# 文件路径:agent_integration.py
from context_vfs import ContextVFS


class AgentEngine:
    def __init__(self):
        self.vfs = ContextVFS()
        self.model_client = None  # 假设接入某个大模型客户端

    def run_task(self, task: str):
        # 1. 从上下文文件系统构建 Prompt
        prompt = self.build_prompt(task)

        # 2. 调用模型
        response = self.model_client.chat(prompt)

        # 3. 将模型输出写回上下文
        self.vfs.append("/ctx/session/chat.log", f"\n用户:{task}\n助手:{response}")

        # 4. 同步到磁盘
        self.vfs.sync()
        return response

    def build_prompt(self, task: str) -> str:
        system_prompt = self.vfs.read("/ctx/system/prompt.md")
        plan = self.vfs.read("/ctx/session/plan.md")
        return f"{system_prompt}\n\n{plan}\n\n任务:{task}"

这样,Agent 的上下文不再是隐形的“临时变量”,而是一个可以随时检查、恢复、迁移的持久化系统。

7. 总结

OpenViking 的核心思想并不复杂:把 Agent 上下文从“对话流”变成“文件系统”。这一抽象带来了四个直接收益:上下文可以按路径精确读取,避免 Token 浪费;上下文可以持久化同步,解决新会话丢失记忆的问题;上下文可以用快照回滚,降低自动压缩带来的风险;上下文可以分层组织,支持多 Agent 协作和权限隔离。

从工程角度看,理解上下文文件系统,本质上是在理解“上下文工程”的落地方式。无论是做 AI 编程助手,还是开发通用 Agent 框架,都需要认真思考一个问题:模型该看到什么,不该看到什么,以及这些信息如何被持久化、检索、压缩和恢复。

建议下一步从三个方面继续深入:第一,研究 Linux VFS 的实际实现,理解文件系统抽象层是怎么处理性能与一致性的;第二,调研主流 Agent 框架(如 Claude Code、各类 Agent harness)是如何管理上下文的,对比它们的优劣;第三,动手改造一个小型 Agent 项目,把上下文结构调整成目录树,并加上同步和快照机制。

如果这篇文章对你有帮助,可以收藏备用。你在做 Agent 开发时遇到过哪些上下文相关的问题?欢迎在评论区分享,大家一起讨论解决方案。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐