|
| 1 | +--- |
| 2 | +title: AI时代如何临摹项目:Vault跨项目持久化存储系统 |
| 3 | +date: 2026-04-06 |
| 4 | +tags: [AI, HagiCode, Vault, 知识管理, 持久化存储] |
| 5 | +--- |
| 6 | + |
| 7 | +## AI时代如何临摹项目:Vault跨项目持久化存储系统 |
| 8 | + |
| 9 | +> 在AI辅助开发时代,如何让AI助手更好地理解我们的学习资源?HagiCode项目通过Vault系统实现了一个统一的、可被AI理解的知识存储抽象层,让临摹项目的学习效率大幅提升。 |
| 10 | +
|
| 11 | +## 背景 |
| 12 | + |
| 13 | +在AI时代,开发者学习新技术和架构的方式正在发生深刻变化。"临摹项目"——即深入研究和学习优秀开源项目的代码、架构和设计模式——已经成为一种高效的学习方法。相比传统的读书、看视频,直接阅读和运行高质量的开源项目能让你更快地理解真实世界的工程实践。 |
| 14 | + |
| 15 | +只是这种学习方式也面临着不少挑战。 |
| 16 | + |
| 17 | +学习资料太分散了。你的笔记可能在Obsidian里,代码仓库散落在各个文件夹,AI助手的对话历史又是一个独立的数据孤岛。当你想让AI帮你分析某个项目时,得手动复制代码片段、整理上下文,这个过程相当繁琐。 |
| 18 | + |
| 19 | +其实更麻烦的是上下文断裂。AI助手无法直接访问你的本地学习资源,每次对话都得重新提供背景信息。而且临摹的代码仓库更新很快,手动同步容易出错,多个学习项目之间也难以共享知识。 |
| 20 | + |
| 21 | +这些问题本质上都是"数据孤岛"导致的。如果能有一个统一的存储抽象层,让AI助手能够理解和访问你的所有学习资源,问题就迎刃而解了。 |
| 22 | + |
| 23 | +## 关于 HagiCode |
| 24 | + |
| 25 | +本文分享的Vault系统,正是我们在开发 [HagiCode](https://hagicode.com) 过程中实践出来的解决方案。HagiCode 是一个AI代码助手项目,在我们的日常开发中,经常需要学习和参考各种开源项目。为了让AI助手更好地理解这些学习资源,我们设计了Vault跨项目持久化存储系统。 |
| 26 | + |
| 27 | +这套方案已经在HagiCode中经过了实际验证,如果你也面临类似的知识管理难题,希望这些经验能给你一些启发。毕竟,有些坑踩过了,总得留下点什么给后来的人。 |
| 28 | + |
| 29 | +## Vault系统设计理念 |
| 30 | + |
| 31 | +Vault系统的核心思想很简单:创建一个统一的、可被AI理解的知识存储抽象层。从实现角度来看,系统具有几个关键特征。 |
| 32 | + |
| 33 | +### 多类型支持 |
| 34 | + |
| 35 | +系统支持四种vault类型,分别对应不同的使用场景: |
| 36 | + |
| 37 | +```typescript |
| 38 | +// folder:通用文件夹类型 |
| 39 | +export const DEFAULT_VAULT_TYPE = 'folder'; |
| 40 | + |
| 41 | +// coderef:专门用于临摹代码项目的类型 |
| 42 | +export const CODEREF_VAULT_TYPE = 'coderef'; |
| 43 | + |
| 44 | +// obsidian:与Obsidian笔记软件集成 |
| 45 | +export const OBSIDIAN_VAULT_TYPE = 'obsidian'; |
| 46 | + |
| 47 | +// system-managed:系统自动管理的vault |
| 48 | +export const SYSTEM_MANAGED_VAULT_TYPE = 'system-managed'; |
| 49 | +``` |
| 50 | + |
| 51 | +其中 **coderef** 类型是HagiCode中最常用的。它专门为临摹代码项目设计,提供了标准化的目录结构和AI可读的元数据描述。 |
| 52 | + |
| 53 | +### 持久化存储机制 |
| 54 | + |
| 55 | +Vault的注册表以JSON格式持久化存储,确保配置在应用重启后仍然可用: |
| 56 | + |
| 57 | +```csharp |
| 58 | +public class VaultRegistryStore : IVaultRegistryStore |
| 59 | +{ |
| 60 | + private readonly string _registryFilePath; |
| 61 | + |
| 62 | + public VaultRegistryStore(IConfiguration configuration, ILogger<VaultRegistryStore> logger) |
| 63 | + { |
| 64 | + var dataDir = configuration["DataDir"] ?? "./data"; |
| 65 | + var absoluteDataDir = Path.IsPathRooted(dataDir) |
| 66 | + ? dataDir |
| 67 | + : Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), dataDir)); |
| 68 | + |
| 69 | + _registryFilePath = Path.Combine(absoluteDataDir, "personal-data", "vaults", "registry.json"); |
| 70 | + } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +这种设计的好处是简单可靠。JSON格式人类可读,便于调试和手动修改;文件系统存储避免了数据库的复杂性,降低了系统的依赖。毕竟,有时候简单的反而是最好的。 |
| 75 | + |
| 76 | +### AI上下文集成 |
| 77 | + |
| 78 | +最关键的是,系统能够自动将vault信息注入到AI提案的上下文中: |
| 79 | + |
| 80 | +```typescript |
| 81 | +export function buildTargetVaultsText( |
| 82 | + vaults: VaultForText[], |
| 83 | + template: VaultPromptTemplate = DEFAULT_VAULT_PROMPT_TEMPLATE, |
| 84 | +): string { |
| 85 | + const readOnlyVaults = vaults.filter((vault) => vault.accessType === 'read'); |
| 86 | + const editableVaults = vaults.filter((vault) => vault.accessType === 'write'); |
| 87 | + |
| 88 | + if (readOnlyVaults.length === 0 && editableVaults.length === 0) { |
| 89 | + return ''; |
| 90 | + } |
| 91 | + |
| 92 | + const sections = [ |
| 93 | + buildVaultSection(readOnlyVaults, template.reference), |
| 94 | + buildVaultSection(editableVaults, template.editable), |
| 95 | + ].filter(Boolean); |
| 96 | + |
| 97 | + return `\n\n### ${template.heading}\n\n${sections.join('\n')}`; |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +这样就实现了一个重要的功能:AI助手能够自动理解可用的学习资源,无需用户手动提供上下文。这倒也算是一种默契了。 |
| 102 | + |
| 103 | +## CodeRef Vault的标准化结构 |
| 104 | + |
| 105 | +对于coderef类型的vault,HagiCode提供了一套标准化的目录结构: |
| 106 | + |
| 107 | +``` |
| 108 | +my-coderef-vault/ |
| 109 | +├── index.yaml # vault元数据描述 |
| 110 | +├── AGENTS.md # AI助手的操作指南 |
| 111 | +├── docs/ # 存放学习笔记和文档 |
| 112 | +└── repos/ # 通过Git子模块管理临摹的代码仓库 |
| 113 | +``` |
| 114 | + |
| 115 | +创建vault时,系统会自动初始化这个结构: |
| 116 | + |
| 117 | +```csharp |
| 118 | +private async Task EnsureCodeRefStructureAsync( |
| 119 | + string vaultName, |
| 120 | + string physicalPath, |
| 121 | + ICollection<VaultBootstrapDiagnosticDto> diagnostics, |
| 122 | + CancellationToken cancellationToken) |
| 123 | +{ |
| 124 | + Directory.CreateDirectory(physicalPath); |
| 125 | + |
| 126 | + var indexPath = Path.Combine(physicalPath, CodeRefIndexFileName); |
| 127 | + var docsPath = Path.Combine(physicalPath, CodeRefDocsDirectoryName); |
| 128 | + var reposPath = Path.Combine(physicalPath, CodeRefReposDirectoryName); |
| 129 | + |
| 130 | + // 创建标准目录结构 |
| 131 | + if (!Directory.Exists(docsPath)) |
| 132 | + { |
| 133 | + Directory.CreateDirectory(docsPath); |
| 134 | + } |
| 135 | + |
| 136 | + if (!Directory.Exists(reposPath)) |
| 137 | + { |
| 138 | + Directory.CreateDirectory(reposPath); |
| 139 | + } |
| 140 | + |
| 141 | + // 创建AGENTS.md指南 |
| 142 | + await EnsureCodeRefAgentsDocumentAsync(physicalPath, cancellationToken); |
| 143 | + |
| 144 | + // 创建index.yaml元数据 |
| 145 | + await WriteCodeRefIndexDocumentAsync(indexPath, mergedDocument, cancellationToken); |
| 146 | +} |
| 147 | +``` |
| 148 | + |
| 149 | +这套结构的设计也是有讲究的: |
| 150 | + |
| 151 | +- **docs/** 目录存放你的学习笔记,可以用Markdown格式记录对代码的理解、架构分析、踩坑经验等 |
| 152 | +- **repos/** 目录通过Git子模块管理临摹的仓库,而不是直接复制代码。这样既能保持代码同步,又能节省空间 |
| 153 | +- **index.yaml** 包含vault的元数据,让AI助手快速理解这个vault的用途和内容 |
| 154 | +- **AGENTS.md** 是专门写给AI助手看的指南,说明如何处理这个vault中的内容 |
| 155 | + |
| 156 | +或许这样组织起来,AI也能更容易理解你的想法吧。 |
| 157 | + |
| 158 | +## 系统管理的自动初始化 |
| 159 | + |
| 160 | +除了手动创建vault,HagiCode还支持系统自动管理的vault: |
| 161 | + |
| 162 | +```csharp |
| 163 | +public async Task<IReadOnlyList<VaultRegistryEntry>> EnsureAllSystemManagedVaultsAsync( |
| 164 | + CancellationToken cancellationToken = default) |
| 165 | +{ |
| 166 | + var definitions = GetAllResolvedDefinitions(); |
| 167 | + var entries = new List<VaultRegistryEntry>(definitions.Count); |
| 168 | + |
| 169 | + foreach (var definition in definitions) |
| 170 | + { |
| 171 | + entries.Add(await EnsureResolvedSystemManagedVaultAsync(definition, cancellationToken)); |
| 172 | + } |
| 173 | + |
| 174 | + return entries; |
| 175 | +} |
| 176 | +``` |
| 177 | + |
| 178 | +系统会自动创建和管理以下vault: |
| 179 | + |
| 180 | +- **hagiprojectdata**:项目数据存储,用于保存项目的配置和状态 |
| 181 | +- **personaldata**:个人数据存储,用于保存用户的偏好设置 |
| 182 | +- **hbsprompt**:提示词模板库,用于管理常用的AI提示词 |
| 183 | + |
| 184 | +这些vault在系统启动时自动初始化,无需用户手动配置。毕竟,有些事情交给系统去做就好了,人类何必操心呢。 |
| 185 | + |
| 186 | +## 访问控制机制 |
| 187 | + |
| 188 | +一个重要的设计是访问控制。系统将vault分为两种访问类型: |
| 189 | + |
| 190 | +```typescript |
| 191 | +export interface VaultForText { |
| 192 | + id: string; |
| 193 | + name: string; |
| 194 | + type: string; |
| 195 | + physicalPath: string; |
| 196 | + accessType: 'read' | 'write'; // 关键:区分只读和可编辑 |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +- **reference(只读)**:AI仅用于分析和理解,不能修改内容。适用于参考的开源项目、文档等 |
| 201 | +- **editable(可编辑)**:AI可以根据任务需要修改内容。适用于你的笔记、草稿等 |
| 202 | + |
| 203 | +这种区分很重要。它让AI知道哪些内容是"只读参考",哪些是"可以动手改的",避免了误操作风险。毕竟,谁也不想自己的心血被无意中改没了。 |
| 204 | + |
| 205 | +## 实战:创建和使用Vault |
| 206 | + |
| 207 | +看完了原理,咱们来看看实际怎么用。 |
| 208 | + |
| 209 | +### 创建CodeRef Vault |
| 210 | + |
| 211 | +以下是一个完整的前端调用示例: |
| 212 | + |
| 213 | +```typescript |
| 214 | +const createCodeRefVault = async () => { |
| 215 | + const response = await VaultService.postApiVaults({ |
| 216 | + requestBody: { |
| 217 | + name: "React Learning Vault", |
| 218 | + type: "coderef", |
| 219 | + physicalPath: "/Users/developer/vaults/react-learning", |
| 220 | + gitUrl: "https://github.com/facebook/react.git" |
| 221 | + } |
| 222 | + }); |
| 223 | + |
| 224 | + // 系统会自动: |
| 225 | + // 1. 克隆React仓库到vault/repos/react |
| 226 | + // 2. 创建docs/目录用于笔记 |
| 227 | + // 3. 生成index.yaml元数据 |
| 228 | + // 4. 创建AGENTS.md指南文件 |
| 229 | + |
| 230 | + return response; |
| 231 | +}; |
| 232 | +``` |
| 233 | + |
| 234 | +这个API调用会完成一系列操作:创建目录结构、初始化Git子模块、生成元数据文件等。你只需要提供基本信息,剩下的交给系统处理。其实这样也挺省心的。 |
| 235 | + |
| 236 | +### 在AI提案中使用Vault |
| 237 | + |
| 238 | +创建好vault后,就可以在AI提案中引用它了: |
| 239 | + |
| 240 | +```typescript |
| 241 | +const proposal = composeProposalChiefComplaint({ |
| 242 | + chiefComplaint: "帮我分析React的并发渲染机制", |
| 243 | + repositories: [ |
| 244 | + { id: "react", gitUrl: "https://github.com/facebook/react.git" } |
| 245 | + ], |
| 246 | + vaults: [ |
| 247 | + { |
| 248 | + id: "react-learning", |
| 249 | + name: "React Learning Vault", |
| 250 | + type: "coderef", |
| 251 | + physicalPath: "/vaults/react-learning", |
| 252 | + accessType: "read" // AI只能读取,不能修改 |
| 253 | + } |
| 254 | + ], |
| 255 | + quickRequestText: "重点关注fiber架构和scheduler实现" |
| 256 | +}); |
| 257 | +``` |
| 258 | + |
| 259 | +系统会自动将vault信息注入到AI的上下文中,让AI知道你有哪些学习资源可用。AI能理解你的想法,这倒也算是一种难得的默契了。 |
| 260 | + |
| 261 | +## 最佳实践和注意事项 |
| 262 | + |
| 263 | +在使用Vault系统的过程中,我们总结了一些经验教训。 |
| 264 | + |
| 265 | +### 路径安全 |
| 266 | + |
| 267 | +系统会严格校验路径,防止路径穿越攻击: |
| 268 | + |
| 269 | +```csharp |
| 270 | +private static string ResolveFilePath(string vaultRoot, string relativePath) |
| 271 | +{ |
| 272 | + var rootPath = EnsureTrailingSeparator(Path.GetFullPath(vaultRoot)); |
| 273 | + var combinedPath = Path.GetFullPath(Path.Combine(rootPath, relativePath)); |
| 274 | + if (!combinedPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) |
| 275 | + { |
| 276 | + throw new BusinessException(VaultRelativePathTraversalCode, |
| 277 | + "Vault file paths must stay inside the registered vault root."); |
| 278 | + } |
| 279 | + return combinedPath; |
| 280 | +} |
| 281 | +``` |
| 282 | + |
| 283 | +这点很重要。如果你在自定义vault路径,一定要确保路径在允许的范围内,否则系统会拒绝操作。安全这东西,怎么强调都不过分。 |
| 284 | + |
| 285 | +### Git子模块管理 |
| 286 | + |
| 287 | +CodeRef vault推荐使用Git子模块而非直接复制代码: |
| 288 | + |
| 289 | +```csharp |
| 290 | +private static string BuildCodeRefAgentsContent() |
| 291 | +{ |
| 292 | + return """ |
| 293 | + # CodeRef Vault Guide |
| 294 | +
|
| 295 | + Repositories under `repos/` should be maintained through Git submodules |
| 296 | + rather than copied directly into the vault root. |
| 297 | +
|
| 298 | + Keep this structure stable so assistants and tools can understand the vault quickly. |
| 299 | + """ + Environment.NewLine; |
| 300 | +} |
| 301 | +``` |
| 302 | + |
| 303 | +这样做有几个好处:保持代码与上游同步、节省磁盘空间、便于管理多个版本的代码。毕竟,谁愿意一遍遍地重复下载同样的东西呢。 |
| 304 | + |
| 305 | +### 文件预览限制 |
| 306 | + |
| 307 | +为了防止性能问题,系统限制了文件大小和类型: |
| 308 | + |
| 309 | +```csharp |
| 310 | +private const int FileEnumerationLimit = 500; |
| 311 | +private const int PreviewByteLimit = 256 * 1024; // 256KB |
| 312 | +``` |
| 313 | + |
| 314 | +如果你的vault包含大量文件或超大文件,可能会影响预览功能的性能。这种情况下可以考虑分批处理或使用专门的搜索工具。毕竟,有些东西太大了,反而不好处理。 |
| 315 | + |
| 316 | +### 诊断信息 |
| 317 | + |
| 318 | +创建vault时会返回诊断信息,帮助调试: |
| 319 | + |
| 320 | +```csharp |
| 321 | +List<VaultBootstrapDiagnosticDto> bootstrapDiagnostics = []; |
| 322 | + |
| 323 | +if (IsCodeRefVaultType(normalizedType)) |
| 324 | +{ |
| 325 | + bootstrapDiagnostics = await EnsureCodeRefBootstrapAsync( |
| 326 | + normalizedName, |
| 327 | + normalizedPhysicalPath, |
| 328 | + normalizedGitUrl, |
| 329 | + cancellationToken); |
| 330 | +} |
| 331 | +``` |
| 332 | + |
| 333 | +如果创建失败,可以查看诊断信息了解具体原因。出错了就看看诊断信息,这倒也是一种解决问题的方法。 |
| 334 | + |
| 335 | +## 总结 |
| 336 | + |
| 337 | +Vault系统通过统一的存储抽象层,解决了AI时代临摹项目的核心痛点: |
| 338 | + |
| 339 | +- **知识集中管理**:所有学习资源集中在一个地方,不再散落各处 |
| 340 | +- **AI上下文自动注入**:AI助手能够自动理解可用的学习资源,无需手动提供上下文 |
| 341 | +- **跨项目知识复用**:多个学习项目之间可以共享和复用知识 |
| 342 | +- **标准化目录结构**:提供一致的目录结构,降低学习成本 |
| 343 | + |
| 344 | +这套方案在HagiCode项目中已经经过了实际验证。如果你也在做AI辅助开发相关的工具,或者面临类似的知识管理问题,希望这些经验能给你一些参考。 |
| 345 | + |
| 346 | +其实技术方案的价值不在于有多复杂,而在于能不能解决实际问题。Vault系统的核心思想很简单——就是建立一个统一的、AI可理解的知识存储层。但正是这个简单的抽象,让我们的开发效率提升了不少。 |
| 347 | + |
| 348 | +有时候,简单的反而是最好的。毕竟,复杂的东西往往藏着更多的坑...... |
| 349 | + |
| 350 | +## 参考资料 |
| 351 | + |
| 352 | +- HagiCode 项目地址:[github.com/HagiCode-org/site](https://github.com/HagiCode-org/site) |
| 353 | +- HagiCode 官网:[hagicode.com](https://hagicode.com) |
| 354 | +- HagiCode 安装文档:[docs.hagicode.com/installation/docker-compose](https://docs.hagicode.com/installation/docker-compose) |
| 355 | +- Obsidian 官网:[obsidian.md](https://obsidian.md) |
| 356 | +- Git 子模块文档:[git-scm.com/docs/gitsubmodules](https://git-scm.com/docs/gitsubmodules) |
| 357 | + |
| 358 | +--- |
| 359 | + |
| 360 | +如果本文对你有帮助,欢迎来 GitHub 给个 Star,或者访问官网了解更多关于HagiCode的信息。公测已开始,现在安装即可体验完整的AI代码助手功能。 |
| 361 | + |
| 362 | +或许,你也可以试试看...... |
| 363 | + |
| 364 | +## 版权说明 |
| 365 | + |
| 366 | +感谢您的阅读,如果您觉得本文有用,欢迎点赞、收藏和分享支持。 |
| 367 | +本内容采用人工智能辅助协作,最终内容由作者审核并确认。 |
| 368 | +- 本文作者: [newbe36524](https://www.newbe.pro) |
| 369 | +- 原文链接: [https://docs.hagicode.com/blog/2026-04-06-vault-persistent-storage-for-ai-era/](https://docs.hagicode.com/blog/2026-04-06-vault-persistent-storage-for-ai-era/) |
| 370 | +- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! |
0 commit comments