@shadowforge0/aquifer-memory 1.5.9 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +23 -0
- package/README.md +96 -73
- package/README_CN.md +659 -0
- package/README_TW.md +680 -0
- package/aquifer.config.example.json +34 -0
- package/consumers/claude-code.js +11 -11
- package/consumers/cli.js +374 -39
- package/consumers/codex-handoff.js +152 -0
- package/consumers/codex.js +1549 -0
- package/consumers/default/daily-entries.js +23 -4
- package/consumers/default/index.js +2 -2
- package/consumers/default/prompts/summary.js +6 -6
- package/consumers/mcp.js +131 -7
- package/consumers/openclaw-ext/index.js +0 -1
- package/consumers/openclaw-plugin.js +44 -4
- package/consumers/shared/config.js +28 -0
- package/consumers/shared/factory.js +2 -0
- package/consumers/shared/ingest.js +1 -1
- package/consumers/shared/normalize.js +14 -3
- package/consumers/shared/recall-format.js +53 -0
- package/consumers/shared/summary-parser.js +151 -0
- package/core/aquifer.js +384 -18
- package/core/finalization-review.js +319 -0
- package/core/insights.js +210 -58
- package/core/mcp-manifest.js +69 -2
- package/core/memory-bootstrap.js +188 -0
- package/core/memory-consolidation.js +1236 -0
- package/core/memory-promotion.js +544 -0
- package/core/memory-recall.js +247 -0
- package/core/memory-records.js +581 -0
- package/core/memory-safety-gate.js +224 -0
- package/core/session-finalization.js +350 -0
- package/core/storage.js +456 -2
- package/docs/getting-started.md +99 -0
- package/docs/postprocess-contract.md +2 -2
- package/docs/setup.md +51 -2
- package/package.json +31 -9
- package/pipeline/normalize/adapters/codex.js +106 -0
- package/pipeline/normalize/detect.js +3 -2
- package/schema/001-base.sql +3 -0
- package/schema/007-v1-foundation.sql +273 -0
- package/schema/008-session-finalizations.sql +50 -0
- package/schema/009-v1-assertion-plane.sql +193 -0
- package/schema/010-v1-finalization-review.sql +160 -0
- package/schema/011-v1-compaction-claim.sql +46 -0
- package/schema/012-v1-compaction-lease.sql +39 -0
- package/schema/013-v1-compaction-lineage.sql +193 -0
- package/scripts/backfill-canonical-key.js +250 -0
- package/scripts/codex-recovery.js +532 -0
- package/consumers/miranda/context-inject.js +0 -119
- package/consumers/miranda/daily-entries.js +0 -224
- package/consumers/miranda/index.js +0 -364
- package/consumers/miranda/instance.js +0 -55
- package/consumers/miranda/llm.js +0 -99
- package/consumers/miranda/profile.json +0 -145
- package/consumers/miranda/prompts/summary.js +0 -303
- package/consumers/miranda/recall-format.js +0 -76
- package/consumers/miranda/render-daily-md.js +0 -186
- package/consumers/miranda/workspace-files.js +0 -91
- package/scripts/drop-entity-state-history.sql +0 -17
- package/scripts/drop-insights.sql +0 -12
- package/scripts/install-openclaw.sh +0 -59
- package/scripts/queries.json +0 -45
- package/scripts/retro-recall-bench.js +0 -409
- package/scripts/sample-bench-queries.sql +0 -75
package/README_CN.md
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🌊 Aquifer
|
|
4
|
+
|
|
5
|
+
**基于 PostgreSQL 的 AI Agent 长期记忆系统**
|
|
6
|
+
|
|
7
|
+
*Turn 级 embedding、三路 RRF 混合排序、信任评分、实体交叉查询、知识图谱、实体作用域——全部运行在 PostgreSQL + pgvector 上。*
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@shadowforge0/aquifer-memory)
|
|
10
|
+
[](https://www.postgresql.org/)
|
|
11
|
+
[](https://github.com/pgvector/pgvector)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
|
|
14
|
+
[English](README.md) | [繁體中文](README_TW.md) | [简体中文](README_CN.md)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 为什么选 Aquifer?
|
|
21
|
+
|
|
22
|
+
大多数 AI 记忆系统会在旁边挂一个向量数据库。Aquifer 的做法不同:**PostgreSQL 就是记忆本体**。
|
|
23
|
+
|
|
24
|
+
Session、摘要、turn 级 embedding、实体图谱——全部存在同一个数据库里,用同一个连接查询。不需要同步层、没有最终一致性问题、不用额外基础设施。
|
|
25
|
+
|
|
26
|
+
### 跟典型做法的差异
|
|
27
|
+
|
|
28
|
+
| | Aquifer | 典型向量 DB 做法 |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| **存储** | PostgreSQL + pgvector | 独立向量 DB + 应用 DB |
|
|
31
|
+
| **粒度** | Turn 级 embedding(不只是 session 摘要) | Session 或文档分片 |
|
|
32
|
+
| **排序** | 三路 RRF:FTS + session embedding + turn embedding | 单一向量相似度 |
|
|
33
|
+
| **知识图谱** | 内建实体提取与共现关系 | 通常是独立系统 |
|
|
34
|
+
| **多租户** | 每张表都有 `tenant_id`,第一天就内建 | 通常是事后补做 |
|
|
35
|
+
| **依赖** | `pg` + MCP SDK | 多个 SDK |
|
|
36
|
+
|
|
37
|
+
### 有和没有的差别
|
|
38
|
+
|
|
39
|
+
**没有 turn 级记忆——搜索只能命中模糊的摘要:**
|
|
40
|
+
|
|
41
|
+
> 查询:"我们对 auth middleware 做了什么决定?"
|
|
42
|
+
> → 返回一份 2000 字的 session 摘要,里面某处提到了 auth
|
|
43
|
+
|
|
44
|
+
**有 Aquifer——搜索直接命中精确的对话片段:**
|
|
45
|
+
|
|
46
|
+
> 查询:"我们对 auth middleware 做了什么决定?"
|
|
47
|
+
> → 返回那句原话:"旧的 auth middleware 拆掉吧——法务说 session token 存储方式不合规"
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 需求
|
|
52
|
+
|
|
53
|
+
| 组件 | 必需? | 用途 | 示例 |
|
|
54
|
+
|------|--------|------|------|
|
|
55
|
+
| Node.js >= 18 | 是 | Runtime | — |
|
|
56
|
+
| PostgreSQL 15+ | 是 | 存储 session、摘要、实体 | 本地、Docker 或 managed |
|
|
57
|
+
| pgvector extension | 是 | 向量相似度搜索 | `CREATE EXTENSION vector;`(`pgvector/pgvector` Docker image 内置) |
|
|
58
|
+
| Embedding 端点 | 是(recall 用) | Turn + session embedding | Ollama `bge-m3`、OpenAI `text-embedding-3-small`、任何 OpenAI 兼容 API |
|
|
59
|
+
| LLM 端点 | 可选 | `enrich` 阶段的内置摘要 | Ollama、OpenRouter、OpenAI——或自己传 `summaryFn` |
|
|
60
|
+
| `@modelcontextprotocol/sdk` + `zod` | 是(MCP server 用) | MCP 协议 runtime | 已列入 dependencies,自动安装 |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 快速开始(MCP 服务器)
|
|
65
|
+
|
|
66
|
+
两行命令从零到可用的 MCP 记忆服务器——不需要设任何 env。Library API 用法请往下看 [Library API](#library-api)。
|
|
67
|
+
|
|
68
|
+
### 1. 起 stack
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
docker compose up -d
|
|
72
|
+
# PostgreSQL 16 + pgvector 和 Ollama(bge-m3 自动 pull)。
|
|
73
|
+
# 第一次运行会拉 model——`docker compose logs -f ollama-pull` 看进度。
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
已经有 PostgreSQL + pgvector 和 embedding 端点在跑?跳过这步——`quickstart` 会从环境变量读 `DATABASE_URL` / `EMBED_PROVIDER`(如果已设置)。
|
|
77
|
+
|
|
78
|
+
### 2. 验证
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx --yes @shadowforge0/aquifer-memory quickstart
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
就这样。`quickstart` 会自动探测 `localhost:5432` 的 PostgreSQL 和 `localhost:11434` 的 Ollama(步骤 1 起的或你自己的都行),跑 migration、embed 一个测试 session、recall 回来、清干净。看到 `✓ Aquifer is working` 就成功了。
|
|
85
|
+
|
|
86
|
+
长期使用建议装进项目省掉 `npx` 解析开销:`npm install @shadowforge0/aquifer-memory` 然后 `npx aquifer quickstart`。
|
|
87
|
+
|
|
88
|
+
要用 OpenAI 不用 Ollama?跑 `quickstart` 前 `export EMBED_PROVIDER=openai` + `OPENAI_API_KEY=sk-...`——model 默认 `text-embedding-3-small`。
|
|
89
|
+
|
|
90
|
+
### 3. 接到你的 MCP client
|
|
91
|
+
|
|
92
|
+
Claude Code、Claude Desktop 或任何支持 MCP 的 client——放进 `.mcp.json`(项目级)或 `claude_desktop_config.json`:
|
|
93
|
+
|
|
94
|
+
```jsonc
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"aquifer": {
|
|
98
|
+
"command": "npx",
|
|
99
|
+
"args": ["--yes", "@shadowforge0/aquifer-memory", "mcp"],
|
|
100
|
+
"env": {
|
|
101
|
+
"DATABASE_URL": "postgresql://aquifer:aquifer@localhost:5432/aquifer",
|
|
102
|
+
"EMBED_PROVIDER": "ollama",
|
|
103
|
+
"AQUIFER_MEMORY_SERVING_MODE": "legacy"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
或直接跑:`DATABASE_URL=... EMBED_PROVIDER=ollama npx aquifer mcp`。(MCP server 本身对 env 严格——`quickstart` 的 autodetect 是试用路径,不是 production 路径。)
|
|
111
|
+
|
|
112
|
+
第一轮 rollout 先保持 `AQUIFER_MEMORY_SERVING_MODE=legacy`。只有在你要让 `session_recall` 和 `session_bootstrap` 提供 active curated memory 时,才切到 `curated`;`evidence_recall` 会保留为显式 audit/debug 路径。要 rollback 只要把 env 或 config 切回 `legacy`。
|
|
113
|
+
|
|
114
|
+
需要 LLM 摘要、知识图谱、OpenAI embedding 或 reranker?往下看 [环境变量](#环境变量) 和 [docs/setup.md](docs/setup.md)。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Library API
|
|
119
|
+
|
|
120
|
+
### 初始化
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
const { createAquifer } = require('@shadowforge0/aquifer-memory');
|
|
124
|
+
|
|
125
|
+
const aquifer = createAquifer({
|
|
126
|
+
db: 'postgresql://user:pass@localhost:5432/mydb', // 连接字符串或 pg.Pool
|
|
127
|
+
schema: 'memory', // PG schema 名称(默认 'aquifer')
|
|
128
|
+
tenantId: 'default', // 多租户隔离
|
|
129
|
+
embed: {
|
|
130
|
+
fn: async (texts) => embeddings, // 你的 embedding 函数
|
|
131
|
+
dim: 1024, // 可选,维度提示
|
|
132
|
+
},
|
|
133
|
+
llm: {
|
|
134
|
+
fn: async (prompt) => text, // 你的 LLM 函数(内置摘要用)
|
|
135
|
+
},
|
|
136
|
+
entities: {
|
|
137
|
+
enabled: true,
|
|
138
|
+
scope: 'my-app', // 实体命名空间(默认 'default')
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// 执行 migration(可重复执行)
|
|
143
|
+
await aquifer.migrate();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 写入路径:commit + enrich
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// 1. 保存 session
|
|
150
|
+
await aquifer.commit('conv-001', [
|
|
151
|
+
{ role: 'user', content: '我来说说新的 auth 做法...' },
|
|
152
|
+
{ role: 'assistant', content: '了解,所以计划是...' },
|
|
153
|
+
], { agentId: 'main' });
|
|
154
|
+
|
|
155
|
+
// 2. 丰富化:摘要 + turn embedding + 实体提取
|
|
156
|
+
const result = await aquifer.enrich('conv-001', {
|
|
157
|
+
agentId: 'main',
|
|
158
|
+
summaryFn: async (msgs) => ({ summaryText, structuredSummary, entityRaw }),
|
|
159
|
+
entityParseFn: (text) => [{ name, normalizedName, type, aliases }],
|
|
160
|
+
postProcess: async (ctx) => { /* 后处理 hook */ },
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 查询路径:recall
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
const results = await aquifer.recall('auth middleware 决定', {
|
|
168
|
+
agentId: 'main',
|
|
169
|
+
limit: 5,
|
|
170
|
+
entities: ['auth-middleware'], // 可选:实体感知搜索
|
|
171
|
+
entityMode: 'all', // 'any'(加分)或 'all'(硬筛)
|
|
172
|
+
});
|
|
173
|
+
// 返回排序后的 session,使用三路 RRF 融合
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 宿主集成
|
|
179
|
+
|
|
180
|
+
MCP 是主要的集成接口。Agent 宿主连接到 Aquifer MCP server,该 server 暴露八个工具:`session_recall`、`evidence_recall`、`session_feedback`、`memory_feedback`、`feedback_stats`、`session_bootstrap`、`memory_stats`、`memory_pending`。
|
|
181
|
+
|
|
182
|
+
| 集成方式 | 路径 | 状态 | 使用场景 |
|
|
183
|
+
|----------|------|------|----------|
|
|
184
|
+
| MCP server | `consumers/mcp.js` | 主要 | Claude Code、OpenClaw、Codex 及任何支持 MCP 的宿主 |
|
|
185
|
+
| Library API | `createAquifer()` | 主要 | 后端应用、自定义 pipeline、直接 Node.js 调用 |
|
|
186
|
+
| CLI | `consumers/cli.js` | 辅助 | 运维、调试、手动 recall/backfill |
|
|
187
|
+
| OpenCode 导入 | `consumers/opencode.js` | 辅助 | 从 OpenCode 的 SQLite DB 导入 session |
|
|
188
|
+
| OpenClaw 插件 | `consumers/openclaw-plugin.js` | 仅兼容 | 通过 `before_reset` 捕获 session——不用于工具分发 |
|
|
189
|
+
|
|
190
|
+
### Claude Code
|
|
191
|
+
|
|
192
|
+
添加到项目的 `.claude.json` 或用户级 MCP 配置:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"aquifer": {
|
|
198
|
+
"type": "stdio",
|
|
199
|
+
"command": "node",
|
|
200
|
+
"args": ["/path/to/aquifer/consumers/mcp.js"],
|
|
201
|
+
"env": {
|
|
202
|
+
"DATABASE_URL": "postgresql://...",
|
|
203
|
+
"AQUIFER_EMBED_BASE_URL": "http://localhost:11434/v1",
|
|
204
|
+
"AQUIFER_EMBED_MODEL": "bge-m3"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
工具在 Claude Code 中显示为 `mcp__aquifer__session_recall`、`mcp__aquifer__evidence_recall`、`mcp__aquifer__session_bootstrap`、`mcp__aquifer__session_feedback`、`mcp__aquifer__memory_feedback`、`mcp__aquifer__feedback_stats`、`mcp__aquifer__memory_stats`、`mcp__aquifer__memory_pending`。
|
|
212
|
+
|
|
213
|
+
### OpenClaw
|
|
214
|
+
|
|
215
|
+
添加到 `openclaw.json` 的 `mcp.servers` 下:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"mcp": {
|
|
220
|
+
"servers": {
|
|
221
|
+
"aquifer": {
|
|
222
|
+
"command": "node",
|
|
223
|
+
"args": ["/path/to/aquifer/consumers/mcp.js"],
|
|
224
|
+
"env": {
|
|
225
|
+
"DATABASE_URL": "postgresql://...",
|
|
226
|
+
"AQUIFER_EMBED_BASE_URL": "http://localhost:11434/v1",
|
|
227
|
+
"AQUIFER_EMBED_MODEL": "bge-m3"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
工具显示为 `aquifer__session_recall`、`aquifer__evidence_recall`、`aquifer__session_feedback`、`aquifer__memory_feedback`、`aquifer__feedback_stats`、`aquifer__session_bootstrap`、`aquifer__memory_stats`、`aquifer__memory_pending`(宿主自动添加 server 名称前缀)。
|
|
236
|
+
|
|
237
|
+
OpenClaw 插件(`consumers/openclaw-plugin.js`)保留用于通过 `before_reset` 捕获 session,但**不是**推荐的工具分发路径。请使用 MCP。
|
|
238
|
+
|
|
239
|
+
### 其他支持 MCP 的宿主
|
|
240
|
+
|
|
241
|
+
任何支持 MCP stdio 的宿主都可以用相同方式连接——将其指向 `node consumers/mcp.js` 并设置所需的环境变量。MCP server 是规范的外部接口。
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 环境变量
|
|
246
|
+
|
|
247
|
+
| 变量 | 是否必需? | 用途 | 示例 |
|
|
248
|
+
|------|-----------|------|------|
|
|
249
|
+
| `DATABASE_URL` | 是 | PostgreSQL 连接字符串 | `postgresql://user:pass@localhost:5432/mydb` |
|
|
250
|
+
| `AQUIFER_SCHEMA` | 否 | PG schema 名称(默认:`aquifer`) | `memory` |
|
|
251
|
+
| `AQUIFER_TENANT_ID` | 否 | 多租户 key(默认:`default`) | `my-app` |
|
|
252
|
+
| `AQUIFER_EMBED_BASE_URL` | 是(recall 需要) | Embedding API 基础 URL | `http://localhost:11434/v1` |
|
|
253
|
+
| `AQUIFER_EMBED_MODEL` | 是(recall 需要) | Embedding 模型名称 | `bge-m3` |
|
|
254
|
+
| `AQUIFER_EMBED_API_KEY` | 取决于提供商 | 托管 embedding 提供商的 API key | `sk-...` |
|
|
255
|
+
| `AQUIFER_EMBED_DIM` | 否 | Embedding 维度覆盖(自动检测) | `1024` |
|
|
256
|
+
| `AQUIFER_LLM_BASE_URL` | 否 | LLM API 基础 URL(内置摘要用) | `http://localhost:11434/v1` |
|
|
257
|
+
| `AQUIFER_LLM_MODEL` | 否 | LLM 模型名称 | `llama3.1` |
|
|
258
|
+
| `AQUIFER_LLM_API_KEY` | 取决于提供商 | 托管 LLM 提供商的 API key | `sk-...` |
|
|
259
|
+
| `AQUIFER_ENTITIES_ENABLED` | 否 | 启用知识图谱(默认:`false`) | `true` |
|
|
260
|
+
| `AQUIFER_ENTITY_SCOPE` | 否 | 实体命名空间(默认:`default`) | `my-app` |
|
|
261
|
+
| `AQUIFER_RERANK_ENABLED` | 否 | 启用 cross-encoder reranking | `true` |
|
|
262
|
+
| `AQUIFER_RERANK_PROVIDER` | 否 | Reranker 提供商:`tei`、`jina`、`openrouter` | `tei` |
|
|
263
|
+
| `AQUIFER_RERANK_BASE_URL` | 否 | Reranker 端点 | `http://localhost:8080` |
|
|
264
|
+
| `AQUIFER_AGENT_ID` | 否 | 默认 agent ID | `main` |
|
|
265
|
+
| `AQUIFER_MEMORY_SERVING_MODE` | 否 | 对外 serving mode:默认 `legacy`,可 opt-in `curated` | `curated` |
|
|
266
|
+
| `AQUIFER_MEMORY_ACTIVE_SCOPE_KEY` | 否 | recall/bootstrap 默认 active curated scope | `project:aquifer` |
|
|
267
|
+
| `AQUIFER_MEMORY_ACTIVE_SCOPE_PATH` | 否 | curated scope inheritance 的有序路径 | `global,project:aquifer` |
|
|
268
|
+
| `AQUIFER_MIGRATIONS_MODE` | 否 | 启动 handshake 模式:`apply`(默认)、`check`、`off` | `apply` |
|
|
269
|
+
| `AQUIFER_MIGRATION_LOCK_TIMEOUT_MS` | 否 | advisory lock 等待上限,超时抛 `AQ_MIGRATION_LOCK_TIMEOUT`(默认 30000) | `30000` |
|
|
270
|
+
| `AQUIFER_INSIGHTS_DEDUP_MODE` | 否 | insights 语义去重模式:`off`(默认)、`shadow`、`enforce`——此字段 env 盖过代码配置,便于 operator 不重新部署就紧急关停 | `shadow` |
|
|
271
|
+
| `AQUIFER_INSIGHTS_DEDUP_COSINE` | 否 | 语义合并的 cosine 阈值(默认 `0.88`,落在 `[0.75, 0.95]` 外会发出警告) | `0.90` |
|
|
272
|
+
| `AQUIFER_INSIGHTS_DEDUP_CLOSE_BAND_FROM` | 否 | close-band(`dedupNear` metadata)下界,必须严格小于阈值(默认 `0.85`) | `0.82` |
|
|
273
|
+
|
|
274
|
+
完整的环境变量到配置映射见 [consumers/shared/config.js](consumers/shared/config.js)。
|
|
275
|
+
|
|
276
|
+
Curated serving 是 opt-in。若 rollout 时需要回到旧路径,设置 `AQUIFER_MEMORY_SERVING_MODE=legacy` 并重启 MCP/CLI process 即可,不需要破坏性 DB rollback。
|
|
277
|
+
|
|
278
|
+
### Insights 语义去重(1.5.10)
|
|
279
|
+
|
|
280
|
+
当 cron extractor(`scripts/extract-insights-from-recent-sessions.js`)或其它调用方通过 `commitInsight` 写 insights 时,canonical-key 层(1.5.3+)会对 `canonicalClaim + entities` 散列相同的 row 做去重。但 LLM 多次产出的 `canonicalClaim` 未必稳定,所以 1.5.10 增加第二层:`title + body` 做 embedding,在 `(tenant, agent, type)` 范围内的 active row 做比对,top cosine 超过 `AQUIFER_INSIGHTS_DEDUP_COSINE` 就触发 supersede(enforce 模式)或仅记录 would-merge metadata(shadow 模式)。落在 close band(`closeBandFrom ≤ cos < threshold`)会写 `metadata.dedupNear`,不 supersede,便于 operator 调整阈值前先观察。
|
|
281
|
+
|
|
282
|
+
推荐部署顺序:`shadow` 跑一个 weekly cycle,检查 `SELECT metadata->>'shadowMatch' FROM insights WHERE metadata ? 'shadowMatch'` 看有无错误合并,确认没问题再切 `enforce`。Kill-switch:`AQUIFER_INSIGHTS_DEDUP_MODE=off` 后重启。
|
|
283
|
+
|
|
284
|
+
1.5.3 之前的历史 row(`canonical_key_v2 IS NULL`)会被语义层直接捕获,但不进入 canonical 路径;启动警告会提示运行一次性的 backfill:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
DATABASE_URL=... \
|
|
288
|
+
node scripts/backfill-canonical-key.js --schema <schema> --agent <id>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Script 是 idempotent 的(`WHERE canonical_key_v2 IS NULL` 保护),重复运行、与 live writer 并发都安全。
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 架构
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
299
|
+
│ createAquifer(入口) │
|
|
300
|
+
│ 设定 · Migration · Ingest · Recall · Enrich │
|
|
301
|
+
└────────┬──────────┬──────────┬──────────┬───────────────────┘
|
|
302
|
+
│ │ │ │
|
|
303
|
+
┌────▼───┐ ┌────▼────┐ ┌──▼───┐ ┌───▼──────────┐
|
|
304
|
+
│storage │ │hybrid- │ │entity│ │ pipeline/ │
|
|
305
|
+
│ .js │ │rank.js │ │ .js │ │summarize.js │
|
|
306
|
+
└────────┘ └─────────┘ └──────┘ │embed.js │
|
|
307
|
+
│ │ │extract-ent.js │
|
|
308
|
+
┌────▼───────────┐ ┌───▼──┐ │rerank.js │
|
|
309
|
+
│ PostgreSQL │ │ LLM │ └───────────────┘
|
|
310
|
+
│ + pgvector │ │ API │
|
|
311
|
+
└────────────────┘ └──────┘
|
|
312
|
+
|
|
313
|
+
┌───────────────────────────────────┐
|
|
314
|
+
│ schema/ │
|
|
315
|
+
│ 001-base.sql(sessions、 │
|
|
316
|
+
│ summaries、turns、FTS) │
|
|
317
|
+
│ 002-entities.sql(KG) │
|
|
318
|
+
│ 003-trust-feedback.sql(信任评分) │
|
|
319
|
+
└───────────────────────────────────┘
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 文件说明
|
|
323
|
+
|
|
324
|
+
| 文件 | 用途 |
|
|
325
|
+
|------|------|
|
|
326
|
+
| `index.js` | 入口——导出 `createAquifer`、`createEmbedder`、`createReranker` |
|
|
327
|
+
| `core/aquifer.js` | 主 facade:`migrate()`、`commit()`、`recall()`、`enrich()` |
|
|
328
|
+
| `core/storage.js` | Session/摘要/turn 的 CRUD、FTS 搜索、embedding 搜索 |
|
|
329
|
+
| `core/entity.js` | 实体 upsert、mention 追踪、关系图谱、名称规范化 |
|
|
330
|
+
| `core/hybrid-rank.js` | 三路 RRF 融合、时间衰减、信任乘数、实体加分、open-loop 加分 |
|
|
331
|
+
| `pipeline/summarize.js` | LLM 驱动的 session 摘要(结构化输出) |
|
|
332
|
+
| `pipeline/embed.js` | Embedding 客户端(任何 OpenAI 兼容 API) |
|
|
333
|
+
| `pipeline/extract-entities.js` | LLM 驱动的实体提取(12 种类型) |
|
|
334
|
+
| `pipeline/rerank.js` | Cross-encoder reranking(TEI、Jina、OpenRouter) |
|
|
335
|
+
| `pipeline/normalize/` | Session 规范化,过滤 Claude Code / gateway 噪音 |
|
|
336
|
+
| `consumers/opencode.js` | OpenCode SQLite 导入消费者 |
|
|
337
|
+
| `schema/001-base.sql` | DDL:sessions、summaries、turn_embeddings、FTS 索引 |
|
|
338
|
+
| `schema/002-entities.sql` | DDL:entities、mentions、relations、entity_sessions |
|
|
339
|
+
| `schema/003-trust-feedback.sql` | DDL:trust_score 字段、session_feedback 审计表 |
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 核心功能
|
|
344
|
+
|
|
345
|
+
### 三路混合检索(RRF)
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
查询 ──┬── FTS(BM25) ──┐
|
|
349
|
+
├── Session embedding 搜索 ──├── RRF 融合 → 时间衰减 → 实体加分 → 结果
|
|
350
|
+
└── Turn embedding 搜索 ──┘
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
- **全文搜索** — PostgreSQL `tsvector`,支持多语言排序
|
|
354
|
+
- **Session embedding** — 对 session 摘要做 cosine 相似度
|
|
355
|
+
- **Turn embedding** — 对单个 user turn 做 cosine 相似度
|
|
356
|
+
- **Reciprocal Rank Fusion** — 合并三份排名列表(K=60)
|
|
357
|
+
- **时间衰减** — sigmoid 衰减,可配置中点与斜率
|
|
358
|
+
- **实体加分** — 包含查询相关实体的 session 会获得分数提升
|
|
359
|
+
- **信任评分** — 根据明确反馈(helpful/unhelpful)的乘法信任系数
|
|
360
|
+
- **Open-loop 加分** — 有未解决项的 session 获得轻微提升
|
|
361
|
+
- **可选 cross-encoder reranking** — 支持 TEI、Jina、OpenRouter 提供商,对 RRF 结果做二次精排
|
|
362
|
+
|
|
363
|
+
### 实体交叉查询
|
|
364
|
+
|
|
365
|
+
明确指定要搜索的实体时,可以做 AND 语义筛选:
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
const results = await aquifer.recall('auth 决定', {
|
|
369
|
+
entities: ['auth-middleware', 'legal-compliance'],
|
|
370
|
+
entityMode: 'all', // 只返回同时包含两个实体的 session
|
|
371
|
+
});
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
- `entityMode: 'any'`(默认)— 提升匹配任一实体的 session
|
|
375
|
+
- `entityMode: 'all'` — 硬筛:只返回包含所有指定实体的 session
|
|
376
|
+
|
|
377
|
+
### 信任评分与反馈
|
|
378
|
+
|
|
379
|
+
Session 通过明确反馈累积信任分数。低信任的记忆在排序中会被压制,无论相关性多高。
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// 返回结果有用
|
|
383
|
+
await aquifer.feedback('session-id', { verdict: 'helpful' });
|
|
384
|
+
|
|
385
|
+
// 返回结果无关
|
|
386
|
+
await aquifer.feedback('session-id', { verdict: 'unhelpful' });
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
- 不对称:helpful +0.05,unhelpful −0.10(低质量记忆下沉更快)
|
|
390
|
+
- 排序中用乘法:trust=0.5 中性、trust=0 分数减半、trust=1.0 提升 50%
|
|
391
|
+
- 完整审计记录在 `session_feedback` 表
|
|
392
|
+
|
|
393
|
+
### Turn 级 Embedding
|
|
394
|
+
|
|
395
|
+
不只是 session 摘要——Aquifer 对每一条有意义的 user turn 独立 embedding。
|
|
396
|
+
|
|
397
|
+
- 过滤噪音:短消息、斜杠命令、确认语("ok""好""收到")
|
|
398
|
+
- 截断 2000 字符,跳过 5 字符以下的 turn
|
|
399
|
+
- 存储 turn 原文 + embedding + 位置,实现精确检索
|
|
400
|
+
|
|
401
|
+
### 知识图谱
|
|
402
|
+
|
|
403
|
+
内建实体提取与关系追踪:
|
|
404
|
+
|
|
405
|
+
- **12 种实体类型**:person、project、concept、tool、metric、org、place、event、doc、task、topic、other
|
|
406
|
+
- **实体规范化**:NFKC + 同形字映射 + 大小写折叠
|
|
407
|
+
- **共现关系**:无向边,带频率追踪
|
|
408
|
+
- **实体-session 映射**:哪些实体出现在哪些 session
|
|
409
|
+
- **排序加分**:包含相关实体的 session 分数更高
|
|
410
|
+
|
|
411
|
+
### 多租户
|
|
412
|
+
|
|
413
|
+
每张表都包含 `tenant_id`(默认:`'default'`)。隔离在查询层强制执行——不会有跨租户数据泄漏。
|
|
414
|
+
|
|
415
|
+
### Schema 隔离
|
|
416
|
+
|
|
417
|
+
传入 `schema: 'my_app'` 给 `createAquifer()`,所有表都建在该 PostgreSQL schema 下。同一个数据库可以运行多个 Aquifer 实例互不冲突。
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## API 参考
|
|
422
|
+
|
|
423
|
+
### `createAquifer(config)`
|
|
424
|
+
|
|
425
|
+
返回一个 Aquifer 实例。设定:
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
{
|
|
429
|
+
db, // PG 连接字符串或 Pool 实例(必填)
|
|
430
|
+
schema, // PG schema 名称(默认 'aquifer')
|
|
431
|
+
tenantId, // 多租户 key(默认 'default')
|
|
432
|
+
embed: { fn, dim }, // embedding 函数(recall 必需)
|
|
433
|
+
llm: { fn }, // LLM 函数(内置摘要必需)
|
|
434
|
+
entities: {
|
|
435
|
+
enabled, // 启用知识图谱(默认 false)
|
|
436
|
+
scope, // 实体命名空间(默认 'default')
|
|
437
|
+
mergeCall, // 合并实体提取到摘要 LLM 调用(默认 true)
|
|
438
|
+
},
|
|
439
|
+
rank: { rrf, timeDecay, access, entityBoost }, // 权重覆盖
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
#### `aquifer.init()`
|
|
444
|
+
|
|
445
|
+
启动 handshake——把所有 pending migration 收敛干净并返回 StartupEnvelope。Host 在接客前应该 `await` 它。`apply` 模式拿到 `ready=false` 就是叫上层中止 startup 的信号。
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
const envelope = await aquifer.init();
|
|
449
|
+
// { ready, memoryMode: 'rw'|'ro'|'off', migrationMode, pendingMigrations,
|
|
450
|
+
// appliedMigrations, error, durationMs }
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
MCP consumer(`consumers/mcp.js`)已经在 `server.connect()` 前接好 `aquifer.init()`,`apply` 模式下 `ready=false` 直接 non-zero exit。
|
|
454
|
+
|
|
455
|
+
#### `aquifer.listPendingMigrations()` / `aquifer.getMigrationStatus()`
|
|
456
|
+
|
|
457
|
+
通过 table / column signature probe 返回 `{ required, applied, pending, lastRunAt }`;table 走 `pg_tables`,alter-only migration 会用 `information_schema.columns` 判断字段是否存在,不执行任何 DDL。适合 health check 或者 consumer 在 `init()` 前先了解 drift 状态。
|
|
458
|
+
|
|
459
|
+
#### `aquifer.migrate()`
|
|
460
|
+
|
|
461
|
+
执行 SQL migration(幂等)。创建表、索引、trigger 和扩展。底层:advisory lock 从 blocking 改成 `pg_try_advisory_lock` + 250ms poll + `lockTimeoutMs`(默认 30s),超时抛 `AQ_MIGRATION_LOCK_TIMEOUT`。成功返回 `{ ok: true, durationMs, notices, ddlExecuted }`;失败抛 error 带 `err.notices` / `err.failedAt`。大部分 caller 应该走 `aquifer.init()`。
|
|
462
|
+
|
|
463
|
+
#### `aquifer.ensureMigrated()`
|
|
464
|
+
|
|
465
|
+
Lazy idempotent wrapper——第一次调用触发 `migrate()`,之后 no-op。尊重 `migrations.mode`:`check` 只 probe、`off` 直接标 migrated 不动 DB。
|
|
466
|
+
|
|
467
|
+
#### `aquifer.commit(sessionId, messages, opts)`
|
|
468
|
+
|
|
469
|
+
保存 session。返回 `{ id, sessionId, isNew }`。
|
|
470
|
+
|
|
471
|
+
#### `aquifer.enrich(sessionId, opts)`
|
|
472
|
+
|
|
473
|
+
丰富化已 commit 的 session:摘要、turn embedding、实体提取。支持自定义 pipeline(`summaryFn`、`entityParseFn`)和 `postProcess` 后处理 hook。使用乐观锁,卡住超过 10 分钟的 processing session 可被回收。
|
|
474
|
+
|
|
475
|
+
#### `aquifer.recall(query, opts)`
|
|
476
|
+
|
|
477
|
+
三路混合搜索。支持 `entities` + `entityMode` 做实体感知查询。
|
|
478
|
+
|
|
479
|
+
```javascript
|
|
480
|
+
const results = await aquifer.recall('搜索关键字', {
|
|
481
|
+
agentId: 'main',
|
|
482
|
+
limit: 10,
|
|
483
|
+
entities: ['postgres', 'migration'],
|
|
484
|
+
entityMode: 'all',
|
|
485
|
+
weights: { rrf, timeDecay, access, entityBoost },
|
|
486
|
+
});
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### `aquifer.feedback(sessionId, opts)`
|
|
490
|
+
|
|
491
|
+
记录信任反馈。返回 `{ trustBefore, trustAfter, verdict }`。
|
|
492
|
+
|
|
493
|
+
#### `aquifer.bootstrap(opts)`
|
|
494
|
+
|
|
495
|
+
基于时间的 session 上下文加载器,用于新对话启动时获取近期记忆。
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
const context = await aquifer.bootstrap({
|
|
499
|
+
agentId: 'main',
|
|
500
|
+
limit: 5, // 返回 session 数量(默认 5)
|
|
501
|
+
lookbackDays: 14, // 回溯天数(默认 14)
|
|
502
|
+
maxChars: 4000, // 最大字符数截断(默认 4000)
|
|
503
|
+
format: 'text', // 'text' | 'structured' | 'both'
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- 跨 session 去重,避免重复内容
|
|
508
|
+
- Sentinel 过滤,排除系统噪音
|
|
509
|
+
- `maxChars` 截断,确保不超出上下文预算
|
|
510
|
+
|
|
511
|
+
#### `aquifer.insights.commitInsight(opts)` / `recallInsights(query, opts)` / `markStale(id)` / `supersede(oldId, newId)`
|
|
512
|
+
|
|
513
|
+
从 session 窗口蒸馏出来的高阶观察(preference / pattern / frustration / workflow)。用两层 identity 拆身份:**canonical key** 描述这个观察是关于什么(claim identity,LLM 用词飘动不影响它),**idempotency key** 描述 canonical claim 的哪个 revision(body + evidenceWindow)。
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
await aquifer.insights.commitInsight({
|
|
517
|
+
agentId: 'main',
|
|
518
|
+
type: 'preference',
|
|
519
|
+
canonicalClaim: 'mk 写 code 前先看 context', // 必填,短、稳定、不带修辞与例子
|
|
520
|
+
title: '先看 context 再动手',
|
|
521
|
+
body: '…',
|
|
522
|
+
entities: ['mk', 'claude code'],
|
|
523
|
+
sourceSessionIds: ['sess-a', 'sess-b'],
|
|
524
|
+
evidenceWindow: { from: isoString, to: isoString },
|
|
525
|
+
importance: 0.9,
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
写入规则:idempotency 命中返回 existing;同 canonical + 更新 evidence → INSERT + 内联 UPDATE supersede 前一 active;同 canonical + 旧/同 window → INSERT 但不 supersede(back-fill revision);同 canonical + 同 body → stale replay 返回 existing。1.5.6 之前的旧 rows `canonical_key_v2` 保持 NULL 不 retrofit,自然淘汰。
|
|
530
|
+
|
|
531
|
+
#### `aquifer.close()`
|
|
532
|
+
|
|
533
|
+
关闭 PostgreSQL 连接池(仅限 Aquifer 自行创建的 pool)。
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## 设定
|
|
538
|
+
|
|
539
|
+
Aquifer 接受 `db` 连接(字符串或 `pg.Pool`),加上可选的 `embed` 和 `llm` 函数:
|
|
540
|
+
|
|
541
|
+
```javascript
|
|
542
|
+
createAquifer({
|
|
543
|
+
db: 'postgresql://user:pass@localhost/mydb', // 或既有 pg.Pool
|
|
544
|
+
schema: 'aquifer',
|
|
545
|
+
tenantId: 'default',
|
|
546
|
+
embed: {
|
|
547
|
+
fn: myEmbedFn, // async (texts: string[]) => number[][]
|
|
548
|
+
dim: 1024,
|
|
549
|
+
},
|
|
550
|
+
llm: {
|
|
551
|
+
fn: myLlmFn, // async (prompt: string) => string
|
|
552
|
+
},
|
|
553
|
+
entities: {
|
|
554
|
+
enabled: true,
|
|
555
|
+
scope: 'my-app', // 实体命名空间——与 agentId 解耦
|
|
556
|
+
mergeCall: true,
|
|
557
|
+
},
|
|
558
|
+
rank: { rrf: 0.65, timeDecay: 0.25, access: 0.10, entityBoost: 0.18 },
|
|
559
|
+
migrations: {
|
|
560
|
+
mode: 'apply', // 'apply' | 'check' | 'off'
|
|
561
|
+
lockTimeoutMs: 30000, // advisory lock 等待上限,超时抛 AQ_MIGRATION_LOCK_TIMEOUT
|
|
562
|
+
startupTimeoutMs: 60000, // init() 整体 deadline
|
|
563
|
+
onEvent: null, // (e) => void — lifecycle 观察 hook
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### 启动可观察性
|
|
569
|
+
|
|
570
|
+
挂 `migrations.onEvent` 就能不解析 log 拿到 lifecycle。事件名:`init_started`、`check_completed`、`apply_started`、`apply_succeeded`、`apply_failed`。payload 带 `schema` / `mode` / 计划 / `ddlExecuted` / `durationMs`,失败时多 `error` / `failedAt` / `notices`。不挂 listener 就是零成本。
|
|
571
|
+
|
|
572
|
+
### 实体作用域(Entity Scope)
|
|
573
|
+
|
|
574
|
+
`entities.scope` 定义实体身份的命名空间。唯一约束是 `(tenant_id, normalized_name, entity_scope)`——不同 scope 中的同名实体会创建独立的实体。这让实体身份与 `agentId` 解耦,允许多个 agent 共享同一个实体命名空间。
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## 数据库 Schema
|
|
579
|
+
|
|
580
|
+
### 001-base.sql
|
|
581
|
+
|
|
582
|
+
| 表 | 用途 |
|
|
583
|
+
|----|------|
|
|
584
|
+
| `sessions` | 原始对话数据,含 messages(JSONB)、token 数、时间戳 |
|
|
585
|
+
| `session_summaries` | LLM 生成的结构化摘要,含 embedding |
|
|
586
|
+
| `turn_embeddings` | 逐 turn 的 user 消息 embedding,实现精确检索 |
|
|
587
|
+
|
|
588
|
+
重要索引:messages GIN、`tsvector` GiST、embedding ivfflat、tenant/agent/timestamp B-tree。
|
|
589
|
+
|
|
590
|
+
### 002-entities.sql
|
|
591
|
+
|
|
592
|
+
| 表 | 用途 |
|
|
593
|
+
|----|------|
|
|
594
|
+
| `entities` | 规范化命名实体,含类型、别名、频率、entity_scope、可选 embedding |
|
|
595
|
+
| `entity_mentions` | 实体 × session 关联,含 mention 次数与上下文 |
|
|
596
|
+
| `entity_relations` | 共现边(无向,`CHECK src < dst`) |
|
|
597
|
+
| `entity_sessions` | 实体-session 关联,用于加分计算 |
|
|
598
|
+
|
|
599
|
+
重要索引:实体名称 trigram、embedding GiST、`(tenant_id, normalized_name, entity_scope)` 唯一索引。
|
|
600
|
+
|
|
601
|
+
### 003-trust-feedback.sql
|
|
602
|
+
|
|
603
|
+
| 表 | 用途 |
|
|
604
|
+
|----|------|
|
|
605
|
+
| `session_feedback` | 明确反馈审计记录(helpful/unhelpful 判定、信任分数变动) |
|
|
606
|
+
|
|
607
|
+
另在 `session_summaries` 新增 `trust_score` 字段(默认 0.5,范围 0–1)。
|
|
608
|
+
|
|
609
|
+
### 005-entity-state-history.sql *(entities 启用时)*
|
|
610
|
+
|
|
611
|
+
| 表 | 用途 |
|
|
612
|
+
|----|------|
|
|
613
|
+
| `entity_state_history` | 时序 state-change 追踪,partial `UNIQUE (tenant, agent, entity, attribute) WHERE valid_to IS NULL` 强制 at-most-one-current。out-of-order backfill 通过 predecessor/successor overlap 检查 |
|
|
614
|
+
|
|
615
|
+
opt-in pipeline(`createAquifer({stateChanges: {enabled, whitelist, confidenceThreshold, timeoutMs, ...}})`)在 `enrich()` 里跑 LLM 抽取 temporal state 变化;默认 OFF 控 LLM 成本。
|
|
616
|
+
|
|
617
|
+
### 006-insights.sql
|
|
618
|
+
|
|
619
|
+
| 表 | 用途 |
|
|
620
|
+
|----|------|
|
|
621
|
+
| `insights` | 高阶反思:TSTZRANGE evidence window、importance、GIN on source_session_ids、HNSW on 1024-dim embedding,以及 `canonical_key_v2` 非 unique partial index 支撑 canonical/revision dedup contract |
|
|
622
|
+
|
|
623
|
+
重要索引:`idx_insights_canonical_v2_active`(active rows + canonical key 非 null)、`idx_insights_idempotency_key`(revision key unique)。
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## 故障排除
|
|
628
|
+
|
|
629
|
+
**`error: type "vector" does not exist`** — pgvector 扩展未安装。以 superuser 身份执行 `CREATE EXTENSION IF NOT EXISTS vector;`,或使用自带该扩展的 `pgvector/pgvector` Docker 镜像。
|
|
630
|
+
|
|
631
|
+
**`aquifer mcp requires @modelcontextprotocol/sdk and zod`** — 这些现在是常规依赖,应该会自动安装。如果看到此错误,重新执行 `npm install` 确保所有依赖就位。
|
|
632
|
+
|
|
633
|
+
**Recall 没有返回结果** — 确保在 `commit` 之后执行了 `enrich`。原始 session 在丰富化(摘要 + embedding)之前不可搜索。运行 `aquifer stats` 检查摘要和 turn embedding 是否存在。
|
|
634
|
+
|
|
635
|
+
**OpenClaw 工具不可见** — 在 `openclaw.json` 中使用 `mcp.servers.aquifer`,不要用插件。工具显示为 `aquifer__session_recall` 等。插件(`consumers/openclaw-plugin.js`)仅用于 session 捕获。
|
|
636
|
+
|
|
637
|
+
**Embedding 提供商连接被拒** — 检查 `AQUIFER_EMBED_BASE_URL` 是否可达。本地 Ollama 需确保服务正在运行且模型已拉取(`ollama pull bge-m3`)。
|
|
638
|
+
|
|
639
|
+
**启动抛 `AQ_MIGRATION_LOCK_TIMEOUT`** — 有其他 process 持有 `aquifer:<schema>` 的 migration advisory lock。可能是另一个 `aquifer.init()` 在竞争(正常;赢家跑完输家下一次会拿到 `pending=[]`),也可能是某个 crash 掉的 worker 把 lock 留着。调高 `migrations.lockTimeoutMs`,或在确认是哪个 pid 死了之后用 `SELECT pg_terminate_backend(pid) FROM pg_locks WHERE locktype='advisory'` 踢掉。
|
|
640
|
+
|
|
641
|
+
**MCP process 启动就 non-zero exit** — 预期行为:`migrations.mode=apply` 且 `aquifer.init()` 回 `ready=false` 时会 abort。看 stderr 那行 `[aquifer-mcp] startup aborted` 拿 `error.code` / `failedAt`。如果需要回到旧的「lazy migrate 等第一个 tool call」行为,设 `AQUIFER_MIGRATIONS_MODE=check`(自己跑 `migrate()`)或 `=off`。
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## 依赖
|
|
646
|
+
|
|
647
|
+
| 包 | 用途 |
|
|
648
|
+
|----|------|
|
|
649
|
+
| `pg` ≥ 8.13 | PostgreSQL 客户端 |
|
|
650
|
+
| `@modelcontextprotocol/sdk` ≥ 1.29 | MCP server 协议 |
|
|
651
|
+
| `zod` ≥ 3.25 | Schema 验证(MCP 工具) |
|
|
652
|
+
|
|
653
|
+
LLM 和 embedding 调用使用原生 HTTP——不需要额外 SDK。
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
657
|
+
## 许可证
|
|
658
|
+
|
|
659
|
+
MIT
|