stable-harness 0.0.7 → 0.0.9
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/README.md +10 -0
- package/docs/0.1.0-p0-runtime-control-plane-plan.zh.md +171 -0
- package/docs/0.1.0-retry-policy.zh.md +87 -0
- package/docs/0.1.0-stable-runtime-development-roadmap.zh.md +393 -0
- package/docs/0.1.0-tool-guard-benchmark.zh.md +42 -0
- package/docs/adapter-contract.md +199 -0
- package/docs/architecture/backend-comparison.md +41 -0
- package/docs/architecture/runtime-events.md +263 -0
- package/docs/architecture/runtime-events.zh.md +248 -0
- package/docs/architecture/system-architecture.zh.md +435 -0
- package/docs/compatibility-matrix.md +139 -0
- package/docs/engineering-rules.md +111 -0
- package/docs/evaluation/0.1.0-bfcl-targeted-model-matrix.zh.md +1632 -0
- package/docs/evaluation/0.1.0-bfcl-targeted-review-matrix.zh.md +1952 -0
- package/docs/evaluation/0.1.0-bfcl-tool-guard.zh.md +1427 -0
- package/docs/granite-tool-calling-comparison.zh.md +206 -0
- package/docs/guides/getting-started.md +126 -0
- package/docs/guides/index.md +40 -0
- package/docs/guides/integration-guide.md +126 -0
- package/docs/guides/operator-runbook.md +153 -0
- package/docs/guides/workspace-authoring.md +212 -0
- package/docs/implementation-blueprint.md +233 -0
- package/docs/memory/0.1.0-memory-design.zh.md +719 -0
- package/docs/memory/0.1.0-step-09-deepagents-native-memory.zh.md +146 -0
- package/docs/memory/0.1.0-step-09-langmem-shaped-provider.zh.md +169 -0
- package/docs/memory/0.1.0-step-09-memory-adapter-projection.zh.md +123 -0
- package/docs/memory/0.1.0-step-09-memory-contract.zh.md +169 -0
- package/docs/memory/0.1.0-step-09-memory-governance-approval.zh.md +143 -0
- package/docs/memory/0.1.0-step-09-memory-lifecycle-hooks.zh.md +150 -0
- package/docs/memory/0.1.0-step-09-memory-maintenance-boundary.zh.md +118 -0
- package/docs/memory/0.1.0-step-09-memory-persistence-boundary.zh.md +118 -0
- package/docs/product/adoption-playbook.md +145 -0
- package/docs/product/market-positioning.md +137 -0
- package/docs/product-boundary.md +258 -0
- package/docs/protocols/http-runtime.md +37 -0
- package/docs/protocols/langgraph-compatible.md +107 -0
- package/docs/protocols/openai-compatible.md +121 -0
- package/docs/tooling/0.1.0-bettercall-tool-quality.zh.md +231 -0
- package/package.json +3 -1
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
# 0.1.0 Memory 设计说明
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
`stable-harness` 的 memory 设计目标是让 agent workspace 拥有可控、可插拔、可长期测试的长期记忆能力,同时不重写 DeepAgents 的执行语义。
|
|
6
|
+
|
|
7
|
+
当前设计同时支持两类 memory:
|
|
8
|
+
|
|
9
|
+
1. `LangMem`:`stable-harness` 主路径。由 runtime 在 run 前召回、run 后抽取,交给 LangMem-style service 持久化到 SQLite 和 vector index。
|
|
10
|
+
2. `deepagentsMem`:DeepAgents 原生 memory passthrough。通过 DeepAgents 的 `memory` 参数暴露 `/memories/<id>.md`,由 DeepAgents 自己读取或写入 memory 文件。
|
|
11
|
+
|
|
12
|
+
这两类 memory 使用同一组 workspace Memory YAML 定义,但通过四个 boolean flag 独立控制读写。
|
|
13
|
+
|
|
14
|
+
## 非目标
|
|
15
|
+
|
|
16
|
+
- 不把 memory 做成领域路由规则。
|
|
17
|
+
- 不在 core runtime 中 hardcode EasyNet、股票、Kubernetes、release 等 downstream 逻辑。
|
|
18
|
+
- 不把 vector store 当成唯一事实源。
|
|
19
|
+
- 不让 DeepAgents native memory 和 LangMem structured memory 互相依赖。
|
|
20
|
+
- 不依赖 system prompt 作为唯一治理手段。
|
|
21
|
+
- 不在 adapter 中重写 DeepAgents 的 planning、delegation、filesystem、memory 执行语义。
|
|
22
|
+
|
|
23
|
+
## 核心概念
|
|
24
|
+
|
|
25
|
+
| 概念 | 说明 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `Memory` YAML | 声明一个 memory profile,例如 `workspace`、`user`、`agent`、`session`。 |
|
|
28
|
+
| `profile` | 决定 namespace 和抽取边界。常见值为 `workspace`、`user`。 |
|
|
29
|
+
| `LangMem` | 后台抽取、去重、持久化、vector recall 的主 memory path。 |
|
|
30
|
+
| `deepagentsMem` | DeepAgents 原生 file memory passthrough。 |
|
|
31
|
+
| `memory_records` | SQLite 中的结构化 memory 事实表。 |
|
|
32
|
+
| `memory_vectors` | SQLite 中的 embedding JSON 表,用于 vector search。 |
|
|
33
|
+
| `metadata.memoryWrite=false` | 单次 request 只 recall,不写回 memory。 |
|
|
34
|
+
|
|
35
|
+
## 四个 Boolean Flag
|
|
36
|
+
|
|
37
|
+
runtime 配置面保持简单,只暴露四个 boolean:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
memory:
|
|
41
|
+
deepagentsMem:
|
|
42
|
+
read: true
|
|
43
|
+
write: true
|
|
44
|
+
LangMem:
|
|
45
|
+
read: true
|
|
46
|
+
write: true
|
|
47
|
+
maintenance:
|
|
48
|
+
enabled: true
|
|
49
|
+
schedule: periodic
|
|
50
|
+
prompt: durable preferences, workspace facts, reusable procedures, prior corrections, stale duplicates, and contradicted memories
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Flag | 默认建议 | 作用 |
|
|
54
|
+
|---|---:|---|
|
|
55
|
+
| `memory.deepagentsMem.read` | `true` | 是否把 enabled Memory YAML 映射成 DeepAgents 原生 `memory: ["/memories/<id>.md"]`。 |
|
|
56
|
+
| `memory.deepagentsMem.write` | `true` | 是否允许 DeepAgents 写 `/memories/**`。为 `false` 时 adapter 增加 write deny permission。 |
|
|
57
|
+
| `memory.LangMem.read` | `true` | 是否在 run 前从 LangMem provider 搜索并召回相关 memory records。 |
|
|
58
|
+
| `memory.LangMem.write` | `true` | 是否在 run 后把 `User input + Agent output` 发给 LangMem provider 抽取并写入。 |
|
|
59
|
+
|
|
60
|
+
单个 memory profile 的开关仍然在 Memory YAML 内:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
apiVersion: stable-harness.dev/v1
|
|
64
|
+
kind: Memory
|
|
65
|
+
metadata:
|
|
66
|
+
name: workspace
|
|
67
|
+
spec:
|
|
68
|
+
provider: langmem
|
|
69
|
+
profile: workspace
|
|
70
|
+
enabled: true
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 推荐默认值
|
|
74
|
+
|
|
75
|
+
生产默认推荐:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
memory:
|
|
79
|
+
deepagentsMem:
|
|
80
|
+
read: true
|
|
81
|
+
write: true
|
|
82
|
+
LangMem:
|
|
83
|
+
read: true
|
|
84
|
+
write: true
|
|
85
|
+
maintenance:
|
|
86
|
+
enabled: true
|
|
87
|
+
schedule: periodic
|
|
88
|
+
prompt: durable preferences, workspace facts, reusable procedures, prior corrections, stale duplicates, and contradicted memories
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
含义:
|
|
92
|
+
|
|
93
|
+
- 默认双写:DeepAgents 原生 file memory 和 LangMem structured memory 都写入。
|
|
94
|
+
- 默认双读:DeepAgents 可以读取 native memory files,LangMem 也会在 run 前做 top-k recall。
|
|
95
|
+
- 两个 memory 的后台整理使用同一个 maintenance prompt 和 schedule,但 target 独立运行,互不依赖。
|
|
96
|
+
- 需要关闭某一路时,只关对应 `read` 或 `write` boolean。
|
|
97
|
+
|
|
98
|
+
## Memory YAML
|
|
99
|
+
|
|
100
|
+
Memory YAML 是可扩展定义,类似 resources/tools/skills 的 runtime inventory。
|
|
101
|
+
|
|
102
|
+
示例:
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
apiVersion: stable-harness.dev/v1
|
|
106
|
+
kind: Memory
|
|
107
|
+
metadata:
|
|
108
|
+
name: workspace
|
|
109
|
+
description: Workspace memory for durable project facts and reusable operating evidence.
|
|
110
|
+
spec:
|
|
111
|
+
provider: langmem
|
|
112
|
+
profile: workspace
|
|
113
|
+
enabled: true
|
|
114
|
+
mode: background
|
|
115
|
+
prompts:
|
|
116
|
+
semantic: |-
|
|
117
|
+
Remember durable workspace facts, repository structure, runtime defaults,
|
|
118
|
+
validation commands, model names, URLs, and sentinel tokens.
|
|
119
|
+
episodic: |-
|
|
120
|
+
Remember important workspace events, validation runs, incidents, migrations,
|
|
121
|
+
release checks, and fixes that are likely to matter later.
|
|
122
|
+
procedural: |-
|
|
123
|
+
Remember reusable operating procedures, recovery steps, release gates,
|
|
124
|
+
full-matrix test commands, and project-specific engineering constraints.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
每个 Memory profile 都可以有自己的 prompt,但输入上下文来自同一次 run:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
User input
|
|
131
|
+
Agent output
|
|
132
|
+
runtime metadata
|
|
133
|
+
profile prompts
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
这样可以让 `workspace` 和 `user` 使用同一段上下文,但分别抽取不同的 memory。
|
|
137
|
+
|
|
138
|
+
## LangMem 主路径
|
|
139
|
+
|
|
140
|
+
LangMem 主路径由 `stable-harness` 控制 lifecycle。
|
|
141
|
+
|
|
142
|
+
### 写入来源
|
|
143
|
+
|
|
144
|
+
`memory_records.content` 不是 client 直接写入,也不是 DeepAgents 自动写入。
|
|
145
|
+
|
|
146
|
+
写入流程是:
|
|
147
|
+
|
|
148
|
+
1. client 或 agent run 产生 `User input` 和 `Agent output`。
|
|
149
|
+
2. `stable-harness` memory layer 把 run context、profile、prompts、namespace 发给 provider。
|
|
150
|
+
3. LangMem service 调 LLM 抽取 durable memory。
|
|
151
|
+
4. SQLite store 做过滤、合并、去重、embedding。
|
|
152
|
+
5. 最终写入 `memory_records` 和 `memory_vectors`。
|
|
153
|
+
|
|
154
|
+
### LangMem 写入 Sequence
|
|
155
|
+
|
|
156
|
+
```mermaid
|
|
157
|
+
sequenceDiagram
|
|
158
|
+
participant Client as Client
|
|
159
|
+
participant Runtime as stable-harness Runtime
|
|
160
|
+
participant Adapter as Backend Adapter
|
|
161
|
+
participant Agent as DeepAgents
|
|
162
|
+
participant MemLayer as Memory Plugin Layer
|
|
163
|
+
participant Service as LangMem Service
|
|
164
|
+
participant LLM as Memory Extraction LLM
|
|
165
|
+
participant Store as SQLite + Vector Store
|
|
166
|
+
|
|
167
|
+
Client->>Runtime: request(input, metadata)
|
|
168
|
+
Runtime->>Adapter: run(adapterContext)
|
|
169
|
+
Adapter->>Agent: invoke(user message)
|
|
170
|
+
Agent-->>Adapter: agent output
|
|
171
|
+
Adapter-->>Runtime: RuntimeOutput
|
|
172
|
+
Runtime->>MemLayer: write-after-run hook
|
|
173
|
+
MemLayer->>Service: propose(namespace, User input + Agent output, prompts)
|
|
174
|
+
Service->>LLM: extract semantic/episodic/procedural memories
|
|
175
|
+
LLM-->>Service: memory candidates
|
|
176
|
+
Service->>Store: upsert + dedupe + embed
|
|
177
|
+
Store-->>Service: MemoryRecord
|
|
178
|
+
Service-->>MemLayer: candidates accepted
|
|
179
|
+
MemLayer-->>Runtime: runtime.memory.plugin.completed event
|
|
180
|
+
Runtime-->>Client: response
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### LangMem 召回 Sequence
|
|
184
|
+
|
|
185
|
+
```mermaid
|
|
186
|
+
sequenceDiagram
|
|
187
|
+
participant Client as Client
|
|
188
|
+
participant Runtime as stable-harness Runtime
|
|
189
|
+
participant MemLayer as Memory Plugin Layer
|
|
190
|
+
participant Service as LangMem Service
|
|
191
|
+
participant Store as SQLite + Vector Store
|
|
192
|
+
participant Adapter as DeepAgents Adapter
|
|
193
|
+
participant Agent as DeepAgents
|
|
194
|
+
|
|
195
|
+
Client->>Runtime: request(input)
|
|
196
|
+
Runtime->>MemLayer: read-before-run hook
|
|
197
|
+
MemLayer->>Service: search(namespace, input)
|
|
198
|
+
Service->>Store: embed query + vector search
|
|
199
|
+
Store-->>Service: relevant MemoryRecords
|
|
200
|
+
Service-->>MemLayer: records
|
|
201
|
+
MemLayer-->>Runtime: pluginMemories context
|
|
202
|
+
Runtime->>Adapter: run(context includes pluginMemories)
|
|
203
|
+
Adapter->>Agent: "Relevant long-term memory" + User request
|
|
204
|
+
Agent-->>Adapter: output using recalled facts
|
|
205
|
+
Adapter-->>Runtime: RuntimeOutput
|
|
206
|
+
Runtime-->>Client: response
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## DeepAgents 原生 Memory
|
|
210
|
+
|
|
211
|
+
DeepAgents 原生 memory 是上游框架自己的 memory primitive。
|
|
212
|
+
|
|
213
|
+
当 `memory.deepagentsMem.read=true` 且 agent 没有显式配置 DeepAgents memory 时,adapter 会把 enabled Memory YAML 映射成:
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
memory:
|
|
217
|
+
- /memories/workspace.md
|
|
218
|
+
- /memories/user.md
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
这表示 DeepAgents 可以读取这些 memory file。它和 LangMem SQLite/vector 不是同一个存储系统。
|
|
222
|
+
|
|
223
|
+
### DeepAgents 原生 Read Sequence
|
|
224
|
+
|
|
225
|
+
```mermaid
|
|
226
|
+
sequenceDiagram
|
|
227
|
+
participant Runtime as stable-harness Runtime
|
|
228
|
+
participant Adapter as DeepAgents Adapter
|
|
229
|
+
participant DA as DeepAgents
|
|
230
|
+
participant FS as DeepAgents Memory Backend
|
|
231
|
+
|
|
232
|
+
Runtime->>Adapter: run(adapterContext)
|
|
233
|
+
Adapter->>Adapter: read memory.deepagentsMem.read
|
|
234
|
+
alt deepagentsMem.read is true
|
|
235
|
+
Adapter->>DA: createDeepAgent(memory=["/memories/workspace.md"])
|
|
236
|
+
DA->>FS: read /memories/workspace.md
|
|
237
|
+
FS-->>DA: markdown memory content
|
|
238
|
+
else deepagentsMem.read is false
|
|
239
|
+
Adapter->>DA: createDeepAgent(memory omitted)
|
|
240
|
+
end
|
|
241
|
+
DA-->>Adapter: output
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### DeepAgents 原生 Write Sequence
|
|
245
|
+
|
|
246
|
+
```mermaid
|
|
247
|
+
sequenceDiagram
|
|
248
|
+
participant DA as DeepAgents
|
|
249
|
+
participant Adapter as DeepAgents Adapter
|
|
250
|
+
participant Policy as Permissions / Middleware
|
|
251
|
+
participant FS as DeepAgents Memory Backend
|
|
252
|
+
|
|
253
|
+
DA->>Adapter: edit_file(/memories/workspace.md)
|
|
254
|
+
Adapter->>Policy: check memory.deepagentsMem.write
|
|
255
|
+
alt deepagentsMem.write is true
|
|
256
|
+
Policy-->>Adapter: allow
|
|
257
|
+
Adapter->>FS: write memory file
|
|
258
|
+
FS-->>DA: write ok
|
|
259
|
+
else deepagentsMem.write is false
|
|
260
|
+
Policy-->>Adapter: deny write /memories/**
|
|
261
|
+
Adapter-->>DA: tool error
|
|
262
|
+
end
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Prompt 控制和 Governance 控制
|
|
266
|
+
|
|
267
|
+
DeepAgents native memory 有两层控制:
|
|
268
|
+
|
|
269
|
+
| 控制层 | 类型 | 能控制什么 | 局限 |
|
|
270
|
+
|---|---|---|---|
|
|
271
|
+
| system prompt | 软控制 | 告诉 agent 什么时候写、写什么格式、不要写什么 | 模型可能忘记、误解或被 prompt injection 影响 |
|
|
272
|
+
| permission / middleware | 硬控制 | 是否允许写 `/memories/**`、是否需要 approval、是否允许某个 path | 只能控制能不能写,不负责判断内容是否高质量 |
|
|
273
|
+
|
|
274
|
+
LangMem 主路径的控制点不同:
|
|
275
|
+
|
|
276
|
+
| 控制层 | 类型 | 能控制什么 |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| Memory YAML prompts | 抽取边界 | semantic/episodic/procedural 各存什么 |
|
|
279
|
+
| profile namespace | 隔离边界 | workspace/user/agent/session 分开 |
|
|
280
|
+
| service filtering | 内容过滤 | 过滤低价值 run instruction,例如 “Reply exactly” |
|
|
281
|
+
| store dedupe | 记录治理 | canonical token、相似度、跨 kind 合并 |
|
|
282
|
+
| runtime flag | 生命周期 | 读写开关、单次 request read-only |
|
|
283
|
+
|
|
284
|
+
## Side-by-side 对比
|
|
285
|
+
|
|
286
|
+
| 维度 | `deepagentsMem` | `LangMem` |
|
|
287
|
+
|---|---|---|
|
|
288
|
+
| 归属 | DeepAgents 上游能力 | stable-harness runtime capability |
|
|
289
|
+
| 存储形态 | memory files / markdown | SQLite `memory_records` + `memory_vectors` |
|
|
290
|
+
| 写入者 | agent 通过 file edit 写 | LangMem service 从 run context 抽取 |
|
|
291
|
+
| 读入方式 | DeepAgents 读取 `/memories/*.md` | runtime 先 vector search,再注入相关 records |
|
|
292
|
+
| 是否动态学习 | 是,agent 自己决定是否写文件 | 是,runtime run 后后台抽取 |
|
|
293
|
+
| 内容质量控制 | prompt + permissions | prompts + filter + dedupe + namespace |
|
|
294
|
+
| 去重 | 需要 agent 自觉或额外 middleware | store 层合并 |
|
|
295
|
+
| 审计 | 文件 diff | record metadata + sourceRef + runtime events |
|
|
296
|
+
| 生产默认 | 开启读写 | 开启读写 |
|
|
297
|
+
| 适合用途 | 轻量 notebook、上游能力测试 | 生产长期记忆主路径 |
|
|
298
|
+
|
|
299
|
+
## 四个 Flag 的行为矩阵
|
|
300
|
+
|
|
301
|
+
| `deepagentsMem.read` | `deepagentsMem.write` | `LangMem.read` | `LangMem.write` | 行为 |
|
|
302
|
+
|---:|---:|---:|---:|---|
|
|
303
|
+
| true | true | true | true | 推荐默认。DeepAgents native memory 和 LangMem 都读写;后台整理保持同 prompt、同 schedule、独立 target。 |
|
|
304
|
+
| false | false | true | true | LangMem-only 模式。DeepAgents 不读写 native memory;LangMem 负责召回和写入。 |
|
|
305
|
+
| true | false | true | true | 额外让 DeepAgents 读 memory files,但不允许写;LangMem 仍是主路径。 |
|
|
306
|
+
| false | false | true | false | 只读 LangMem,用于回归或只想使用已有 memory。 |
|
|
307
|
+
| false | false | false | true | 只写 LangMem,不召回;适合离线采集或影子写入测试。 |
|
|
308
|
+
| false | false | false | false | memory 完全关闭。 |
|
|
309
|
+
|
|
310
|
+
## 单次 Request 只读
|
|
311
|
+
|
|
312
|
+
如果某次请求只是测试 recall,不希望把这次回答再次写回 memory,可传:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"metadata": {
|
|
317
|
+
"memoryWrite": false
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Sequence:
|
|
323
|
+
|
|
324
|
+
```mermaid
|
|
325
|
+
sequenceDiagram
|
|
326
|
+
participant Runtime as Runtime
|
|
327
|
+
participant Service as LangMem Service
|
|
328
|
+
participant Agent as Agent
|
|
329
|
+
|
|
330
|
+
Runtime->>Service: search(namespace, input)
|
|
331
|
+
Service-->>Runtime: recalled records
|
|
332
|
+
Runtime->>Agent: prompt with memory
|
|
333
|
+
Agent-->>Runtime: answer
|
|
334
|
+
alt metadata.memoryWrite is false
|
|
335
|
+
Runtime-->>Service: no propose call
|
|
336
|
+
else default
|
|
337
|
+
Runtime->>Service: propose(run context)
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## 后台整理 Daemon
|
|
342
|
+
|
|
343
|
+
后台整理是 `stable-harness` runtime capability,不属于 DeepAgents 执行语义。它只负责调度和审计,具体整理逻辑由各 memory target 自己实现。
|
|
344
|
+
|
|
345
|
+
原则:
|
|
346
|
+
|
|
347
|
+
- `LangMem` 和 `deepagentsMem` 是两个独立 target。
|
|
348
|
+
- 两个 target 使用同一个 `memory.maintenance.prompt`。
|
|
349
|
+
- 两个 target 使用同一个 schedule。
|
|
350
|
+
- 一个 target 失败不阻塞另一个 target。
|
|
351
|
+
- LangMem target 调 provider `search -> consolidate`。
|
|
352
|
+
- DeepAgents target 通过独立 maintainer 处理 native memory file,不依赖 LangMem records。
|
|
353
|
+
- Skill Candidate Miner target 从长期稳定 memory pattern 中生成候选 Skill,但不直接启用。
|
|
354
|
+
|
|
355
|
+
Sequence:
|
|
356
|
+
|
|
357
|
+
```mermaid
|
|
358
|
+
sequenceDiagram
|
|
359
|
+
participant Daemon as Memory Maintenance Daemon
|
|
360
|
+
participant LangMem as LangMem Target
|
|
361
|
+
participant DeepMem as DeepAgentsMem Target
|
|
362
|
+
participant Provider as LangMem Provider
|
|
363
|
+
participant Store as DeepAgents StoreBackend
|
|
364
|
+
|
|
365
|
+
Daemon->>Daemon: load shared schedule and prompt
|
|
366
|
+
par LangMem maintenance
|
|
367
|
+
Daemon->>LangMem: run(prompt)
|
|
368
|
+
LangMem->>Provider: search(namespace, prompt)
|
|
369
|
+
Provider-->>LangMem: records
|
|
370
|
+
LangMem->>Provider: consolidate(records)
|
|
371
|
+
Provider-->>LangMem: operations
|
|
372
|
+
and DeepAgents native maintenance
|
|
373
|
+
Daemon->>DeepMem: run(prompt)
|
|
374
|
+
DeepMem->>Store: read /memories/*.md
|
|
375
|
+
DeepMem->>Store: compact / dedupe / rewrite files
|
|
376
|
+
Store-->>DeepMem: saved
|
|
377
|
+
and Skill candidate mining
|
|
378
|
+
Daemon->>Daemon: scan stable repeated memory evidence
|
|
379
|
+
Daemon->>Daemon: upsert skill_candidates
|
|
380
|
+
Daemon->>Daemon: create skill_candidate approval
|
|
381
|
+
end
|
|
382
|
+
Daemon->>Daemon: emit maintenance events
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Skill Candidate
|
|
386
|
+
|
|
387
|
+
`SkillCandidate` 是 control-plane 对象,不是普通 memory record。它表示系统从长期稳定、可靠、可验证的 memory evidence 中发现了一个可能值得沉淀成 Skill 的流程。
|
|
388
|
+
|
|
389
|
+
默认规则:
|
|
390
|
+
|
|
391
|
+
- 不写入 `memory_records`。
|
|
392
|
+
- 不自动写入 `resources/skills/**/SKILL.md`。
|
|
393
|
+
- 不自动启用。
|
|
394
|
+
- 只生成 `review_required` candidate。
|
|
395
|
+
- 同时创建 `skill_candidate` approval。
|
|
396
|
+
|
|
397
|
+
推荐存储在 control-plane SQLite:
|
|
398
|
+
|
|
399
|
+
```text
|
|
400
|
+
.stable-harness/control-plane/stable.sqlite
|
|
401
|
+
skill_candidates
|
|
402
|
+
skill_candidate_evidence
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
真实 E2E 使用:
|
|
406
|
+
|
|
407
|
+
```text
|
|
408
|
+
npm run test:skill-mining:e2e
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
测试会启动真实 Python LangMem service,通过远端 `qwen3.5:4b` 在多个 session 中写入长期 memory evidence,再由 maintenance daemon 生成 `review_required` SkillCandidate。
|
|
412
|
+
|
|
413
|
+
## Namespace 规则
|
|
414
|
+
|
|
415
|
+
namespace 由 `workspaceId`、profile、request metadata 决定:
|
|
416
|
+
|
|
417
|
+
| Profile | Namespace 形态 |
|
|
418
|
+
|---|---|
|
|
419
|
+
| `workspace` | `<workspaceId>:workspace:<memoryId>` |
|
|
420
|
+
| `user` | `<workspaceId>:user:<userId>:<memoryId>` |
|
|
421
|
+
| `agent` | `<workspaceId>:agent:<agentId>:<memoryId>` |
|
|
422
|
+
| `session` | `<workspaceId>:session:<sessionId>:<memoryId>` |
|
|
423
|
+
|
|
424
|
+
EasyNet 示例:
|
|
425
|
+
|
|
426
|
+
```text
|
|
427
|
+
easynet:workspace:workspace
|
|
428
|
+
easynet:user:e2e-user:user
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## 存储目录
|
|
432
|
+
|
|
433
|
+
配置和生成数据分开。
|
|
434
|
+
|
|
435
|
+
`stable-harness`:
|
|
436
|
+
|
|
437
|
+
```text
|
|
438
|
+
config/memory/ # Memory YAML
|
|
439
|
+
.stable-harness/memory/langmem/ # generated LangMem data
|
|
440
|
+
.stable-harness/memory/langmem/e2e/ # disposable E2E SQLite
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
EasyNet:
|
|
444
|
+
|
|
445
|
+
```text
|
|
446
|
+
config/memory/ # Memory YAML
|
|
447
|
+
.botbotgo/memory/langmem/ # generated LangMem data
|
|
448
|
+
.botbotgo/memory/langmem/e2e/ # disposable E2E SQLite
|
|
449
|
+
.botbotgo/runtime/ # runtime run records
|
|
450
|
+
.botbotgo/knowledge/ # existing native knowledge DBs
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
生成数据不提交到 git。
|
|
454
|
+
|
|
455
|
+
## SQLite Schema
|
|
456
|
+
|
|
457
|
+
LangMem SQLite service 当前维护两张表:
|
|
458
|
+
|
|
459
|
+
```text
|
|
460
|
+
memory_records
|
|
461
|
+
├─ id
|
|
462
|
+
├─ namespace
|
|
463
|
+
├─ kind
|
|
464
|
+
├─ scope
|
|
465
|
+
├─ status
|
|
466
|
+
├─ content
|
|
467
|
+
├─ summary
|
|
468
|
+
├─ confidence
|
|
469
|
+
├─ tags_json
|
|
470
|
+
├─ sensitivity
|
|
471
|
+
├─ metadata_json
|
|
472
|
+
└─ created_at
|
|
473
|
+
|
|
474
|
+
memory_vectors
|
|
475
|
+
├─ record_id
|
|
476
|
+
├─ namespace
|
|
477
|
+
├─ dimensions
|
|
478
|
+
└─ embedding_json
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
`memory_records` 是事实源,`memory_vectors` 是召回索引。
|
|
482
|
+
|
|
483
|
+
## 去重策略
|
|
484
|
+
|
|
485
|
+
重复来源主要有两类:
|
|
486
|
+
|
|
487
|
+
1. 读取 memory 的回答又被写回,形成派生 memory。
|
|
488
|
+
2. LLM 对同一事实生成中英文、semantic/procedural/episodic 多种表述。
|
|
489
|
+
|
|
490
|
+
当前处理:
|
|
491
|
+
|
|
492
|
+
- 支持 `metadata.memoryWrite=false`,只召回不写回。
|
|
493
|
+
- 过滤低价值内容,例如 `Reply exactly`、`memory stored`、`asked to remember`。
|
|
494
|
+
- 同 namespace 内跨 kind 查找相似 memory。
|
|
495
|
+
- 对 `npm run ...`、`EASYNET_*` 等 canonical token 做强匹配。
|
|
496
|
+
- 相似内容合并到已有 record,而不是新增行。
|
|
497
|
+
|
|
498
|
+
去重 Sequence:
|
|
499
|
+
|
|
500
|
+
```mermaid
|
|
501
|
+
sequenceDiagram
|
|
502
|
+
participant Service as LangMem Service
|
|
503
|
+
participant LLM as Extraction LLM
|
|
504
|
+
participant Store as SQLite Store
|
|
505
|
+
participant Vec as Vector Store
|
|
506
|
+
|
|
507
|
+
Service->>LLM: extract memories
|
|
508
|
+
LLM-->>Service: candidates
|
|
509
|
+
loop each candidate
|
|
510
|
+
Service->>Service: filter low-value run instructions
|
|
511
|
+
Service->>Store: find active records in namespace
|
|
512
|
+
Store-->>Service: existing records
|
|
513
|
+
Service->>Service: compare text similarity + canonical tokens
|
|
514
|
+
alt duplicate found
|
|
515
|
+
Service->>Store: merge into existing memory_record
|
|
516
|
+
Service->>Vec: replace embedding
|
|
517
|
+
else new fact
|
|
518
|
+
Service->>Store: insert memory_record
|
|
519
|
+
Service->>Vec: insert embedding
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## 长期测试
|
|
525
|
+
|
|
526
|
+
长期测试目标是验证多轮重复写入不会让 memory records 无限制增长,并确认后续 run 能使用已经生成的 memory。
|
|
527
|
+
|
|
528
|
+
`stable-harness`:
|
|
529
|
+
|
|
530
|
+
```text
|
|
531
|
+
LANGMEM_E2E_ITERATIONS=3 npm run test:langmem:sqlite:e2e
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
期望:
|
|
535
|
+
|
|
536
|
+
```text
|
|
537
|
+
memoryRecords: 1
|
|
538
|
+
secondOutput includes npm run check:rules
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
EasyNet:
|
|
542
|
+
|
|
543
|
+
```text
|
|
544
|
+
EASYNET_LANGMEM_E2E_ITERATIONS=3 npm run test:langmem:sqlite:e2e
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
期望:
|
|
548
|
+
|
|
549
|
+
```text
|
|
550
|
+
workspaceRecords: 1
|
|
551
|
+
userRecords: 2
|
|
552
|
+
secondOutput includes npm run test:botbotgo:full
|
|
553
|
+
secondOutput includes EASYNET_USER_MEMORY_9137
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
长期测试 Sequence:
|
|
557
|
+
|
|
558
|
+
```mermaid
|
|
559
|
+
sequenceDiagram
|
|
560
|
+
participant Test as E2E Script
|
|
561
|
+
participant Runtime as Runtime
|
|
562
|
+
participant Service as LangMem Service
|
|
563
|
+
participant Store as SQLite Store
|
|
564
|
+
participant Agent as Agent
|
|
565
|
+
|
|
566
|
+
Test->>Service: start with clean E2E SQLite
|
|
567
|
+
loop N iterations
|
|
568
|
+
Test->>Runtime: write request
|
|
569
|
+
Runtime->>Service: propose(run context)
|
|
570
|
+
Service->>Store: upsert/dedupe
|
|
571
|
+
Test->>Runtime: read request with memoryWrite=false
|
|
572
|
+
Runtime->>Service: search(query)
|
|
573
|
+
Service-->>Runtime: stable records
|
|
574
|
+
Runtime->>Agent: recalled memory
|
|
575
|
+
Agent-->>Runtime: exact remembered answer
|
|
576
|
+
end
|
|
577
|
+
Test->>Store: count records
|
|
578
|
+
Store-->>Test: bounded record count
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## 配置示例
|
|
582
|
+
|
|
583
|
+
### 推荐生产默认
|
|
584
|
+
|
|
585
|
+
```yaml
|
|
586
|
+
memory:
|
|
587
|
+
enabled: true
|
|
588
|
+
deepagentsMem:
|
|
589
|
+
read: true
|
|
590
|
+
write: true
|
|
591
|
+
LangMem:
|
|
592
|
+
read: true
|
|
593
|
+
write: true
|
|
594
|
+
maintenance:
|
|
595
|
+
enabled: true
|
|
596
|
+
schedule: periodic
|
|
597
|
+
prompt: durable preferences, workspace facts, reusable procedures, prior corrections, stale duplicates, and contradicted memories
|
|
598
|
+
refs:
|
|
599
|
+
- workspace
|
|
600
|
+
- user
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### LangMem-only 对比测试
|
|
604
|
+
|
|
605
|
+
```yaml
|
|
606
|
+
memory:
|
|
607
|
+
enabled: true
|
|
608
|
+
deepagentsMem:
|
|
609
|
+
read: false
|
|
610
|
+
write: false
|
|
611
|
+
LangMem:
|
|
612
|
+
read: true
|
|
613
|
+
write: true
|
|
614
|
+
refs:
|
|
615
|
+
- workspace
|
|
616
|
+
- user
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### DeepAgents native read-only 对比测试
|
|
620
|
+
|
|
621
|
+
```yaml
|
|
622
|
+
memory:
|
|
623
|
+
enabled: true
|
|
624
|
+
deepagentsMem:
|
|
625
|
+
read: true
|
|
626
|
+
write: false
|
|
627
|
+
LangMem:
|
|
628
|
+
read: true
|
|
629
|
+
write: true
|
|
630
|
+
refs:
|
|
631
|
+
- workspace
|
|
632
|
+
- user
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### 只读 LangMem
|
|
636
|
+
|
|
637
|
+
```yaml
|
|
638
|
+
memory:
|
|
639
|
+
enabled: true
|
|
640
|
+
deepagentsMem:
|
|
641
|
+
read: false
|
|
642
|
+
write: false
|
|
643
|
+
LangMem:
|
|
644
|
+
read: true
|
|
645
|
+
write: false
|
|
646
|
+
refs:
|
|
647
|
+
- workspace
|
|
648
|
+
- user
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## 端到端总览
|
|
652
|
+
|
|
653
|
+
```mermaid
|
|
654
|
+
sequenceDiagram
|
|
655
|
+
participant User as User
|
|
656
|
+
participant Runtime as stable-harness Runtime
|
|
657
|
+
participant LangMem as LangMem Provider
|
|
658
|
+
participant SQLite as SQLite + Vector
|
|
659
|
+
participant Adapter as DeepAgents Adapter
|
|
660
|
+
participant DeepAgents as DeepAgents
|
|
661
|
+
participant Native as DeepAgents Native Memory
|
|
662
|
+
|
|
663
|
+
User->>Runtime: request(input, metadata)
|
|
664
|
+
|
|
665
|
+
alt LangMem.read is true
|
|
666
|
+
Runtime->>LangMem: search(namespace, input)
|
|
667
|
+
LangMem->>SQLite: vector search
|
|
668
|
+
SQLite-->>LangMem: MemoryRecords
|
|
669
|
+
LangMem-->>Runtime: recalled context
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
Runtime->>Adapter: run(pluginMemories)
|
|
673
|
+
|
|
674
|
+
alt deepagentsMem.read is true
|
|
675
|
+
Adapter->>DeepAgents: memory=["/memories/workspace.md", "/memories/user.md"]
|
|
676
|
+
DeepAgents->>Native: read memory files
|
|
677
|
+
Native-->>DeepAgents: markdown memory
|
|
678
|
+
else deepagentsMem.read is false
|
|
679
|
+
Adapter->>DeepAgents: no native memory paths
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
Adapter->>DeepAgents: user request + recalled LangMem context
|
|
683
|
+
DeepAgents-->>Adapter: output
|
|
684
|
+
Adapter-->>Runtime: RuntimeOutput
|
|
685
|
+
|
|
686
|
+
alt deepagentsMem.write is true
|
|
687
|
+
DeepAgents->>Native: may edit /memories/**
|
|
688
|
+
else deepagentsMem.write is false
|
|
689
|
+
Adapter-->>DeepAgents: deny writes to /memories/**
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
alt LangMem.write is true and memoryWrite is not false
|
|
693
|
+
Runtime->>LangMem: propose(User input + Agent output)
|
|
694
|
+
LangMem->>SQLite: upsert/dedupe/vectorize
|
|
695
|
+
else LangMem.write is false or memoryWrite=false
|
|
696
|
+
Runtime-->>LangMem: no write
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
Runtime-->>User: response
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
## 最终建议
|
|
703
|
+
|
|
704
|
+
短期默认:
|
|
705
|
+
|
|
706
|
+
```yaml
|
|
707
|
+
deepagentsMem.read: true
|
|
708
|
+
deepagentsMem.write: true
|
|
709
|
+
LangMem.read: true
|
|
710
|
+
LangMem.write: true
|
|
711
|
+
maintenance.enabled: true
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
长期评估:
|
|
715
|
+
|
|
716
|
+
- 默认双读双写,长期观察重复、污染、文件 diff、profile 边界。
|
|
717
|
+
- 两个 memory 的整理使用同一个 prompt 和 schedule,但 target 独立。
|
|
718
|
+
- 如果 DeepAgents native memory 污染明显,可只关闭 `deepagentsMem.write`。
|
|
719
|
+
- 如果 LangMem recall 成本过高,可只关闭 `LangMem.read`,保留 `LangMem.write` 做后台积累。
|