---
title: "harness - 会话与心智记忆"
---


会话（Session）保存一次对话的上下文，心智记忆（Memory）则在多次对话之间长期留存关键事实。两者配合，让 Agent 既有"短期记忆"也有"长期记忆"。

### 1、会话提供者（sessionProvider）

`sessionProvider` 是构建引擎的必填项，决定会话如何创建与存储。

```java
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
 .sessionProvider(InMemoryAgentSession::of) // 内存会话（进程级，重启即失）
 .build();
```

`AgentSessionProvider` 是一个函数式接口（`@FunctionalInterface`），唯一的抽象方法是 `AgentSession getSession(String instanceId)`，因此可以直接使用方法引用或 Lambda：

```java
// 方法引用
.sessionProvider(MySessionProvider::new)

// Lambda（每次返回新会话）
.sessionProvider(sessionId -> new InMemoryAgentSession(sessionId))
```

**获取/使用会话：**

```java
AgentSession session = engine.getSession("default"); // 按实例 id 获取

engine.prompt("hello")
 .session(session) // 不传则为临时会话（不留历史）
 .call();
```

**内置会话实现：**

| 实现 | 说明 |
|------|------|
| `InMemoryAgentSession` | 基于内存的会话（进程级别，重启即失），提供多个静态工厂：`of()`、`of(sessionId)`、`of(sessionId, maxMessages)`、`of(FlowContext)` |
| `FileAgentSession` | 基于文件的会话（带内存缓存层），消息以 NDJSON 格式持久化到磁盘，快照以 JSON 持久化，适用于需要重启恢复的场景 |
| `RedisAgentSession` | 基于 Redis 的会话，适用于分布式多实例共享会话的场景 |

需要自定义持久化方案时，实现 `AgentSessionProvider` 接口（函数式），把会话落到文件或数据库即可。

会话的运行态数据包括消息历史和执行流快照。使用 `FileAgentSession` 时消息默认落在 `{harnessHome}/sessions/` 下（由 `getHarnessSessions()` 路径决定），每条会话独立存储为 `{sessionId}.messages.ndjson` 和 `{sessionId}.snapshot.json`。

**会话快照（Snapshot）：**

`AgentSession` 接口扩展了会话快照能力：

```java
// 同步/更新执行快照（Flow 引擎状态）
void updateSnapshot();

// 获取会话上下文（FlowContext）
FlowContext getContext();

// 获取当前状态快照
default FlowContext getSnapshot() {
    return getContext();
}
```

快照机制使 Agent 在执行流（Flow）被中断后可恢复现场，配合 `FileAgentSession` 等持久化实现可在进程重启后恢复会话。

**内置会话提供者快捷工厂：**

可直接用 `AgentSessionProvider` 接口的函数式特性快速构造：

```java
// 文件会话提供者（持久化到 {harnessHome}/sessions/）
AgentSessionProvider fileSessionProvider = sessionId ->
    new FileAgentSession(sessionId, engine.getHarnessSessions());
```

### 2、会话窗口与上下文压缩

为控制上下文长度，引擎提供两层机制（详见 [《harness - 配置参考》](/article/1427)）：

- **`sessionWindowSize`**：新指令携带几条历史消息（默认 8）。
- **`compressionMaxMessages`**：消息条数超阈值时触发压缩（默认 40 条）。
- **`compressionMaxContextRatio`**：上下文总长度占比超阈值时触发压缩（默认 0.75，即上下文占用模型窗口 75% 时触发）。
- **`compressionModel`**：可选，指定专用的压缩模型（不配置则使用当前对话模型）。
- **`compressionInterceptor`**：可选，自定义压缩拦截器，覆盖默认的上下文摘要策略。

配置方式：

```java
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
 .sessionProvider(InMemoryAgentSession::of)
 .sessionWindowSize(8)
 .compressionThreshold(40, 0.75)
 .compressionModel("deepseek-v3")
 .build();
```

也可在运行时动态调整：

```java
engine.setSessionWindowSize(12);
```

### 3、心智记忆（Memory）

心智记忆让 Agent 把用户偏好、项目规约等关键事实长期保存，并在需要时检索召回。需满足两个条件：`memoryEnabled=true`（默认开启）且已通过 `memoryProvider(...)` 配置记忆方案。

心智记忆能力由内置 `MemoryTalent` 提供，对应工具权限名为 `memory`（但实际暴露多个工具：`memory_extract`、`memory_recall`、`memory_search`、`memory_consolidate`、`memory_prune`），需在工具权限配置中确保已授权。

#### 3.1 架构分层

```
MemorySolutionProvider (Provider 接口: 按工作区返回方案)
  └─ MemorySolution (组合接口)
       ├─ MemoryStorer  (物理持久化 + TTL 管理)
       └─ MemorySearcher (语义检索 + 热记忆提取)
```

- **`MemorySolutionProvider`**：工厂/提供者接口，根据运行上下文（如工作目录 `__cwd`）返回对应的 `MemorySolution` 实例。同时提供 `getScopesDefault()`（默认 "workspace"）和 `getScopesDescription()` 方法，供 MemoryTalent 动态展示作用域说明。

- **`MemorySolution`**：组合了存储与搜索能力，构成完整的长期记忆解决闭环。还提供了统一的 TTL 策略：
  ```java
  default int computeTtl(int importance) {
      if (importance >= 10) return -1;      // 永久
      if (importance >= 5) return 2592000;   // 30 天
      return 604800;                          // 7 天
  }
  ```

#### 3.2 配置记忆方案

通过 `HarnessEngine.Builder.memoryProvider(MemorySolutionProvider)` 注入：

```java
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
 .sessionProvider(InMemoryAgentSession::of)
 .memoryProvider(new MyMemoryProvider())
 .build();
```

**自定义 MemorySolutionProvider 示例：**

`MemorySolutionProvider` 是一个接口，只有一个方法需要实现：

```java
public interface MemorySolutionProvider {
    String SHARED_USER_ID = "shared";
    MemorySolution get(String __cwd);              // 按工作区返回方案
    
    default String getScopesDefault() {            // 默认作用域
        return "workspace";
    }
    
    default String getScopesDescription() {        // 作用域说明
        return "存储作用域: workspace(工作区,默认) 或 user(用户全局)。跨项目的通用认知用 user 域。";
    }
}
```

**MD 方案实现（开箱即用，零外部依赖）：**

框架内置的 `MemorySolutionMdImpl` 构造器接受 `Map<String, Path>`（作用域→路径映射），使用 `LinkedHashMap` 保证迭代顺序（低→高优先级，后者覆盖前者）：

```java
public class MyMemoryProvider implements MemorySolutionProvider {
    private final Map<String, MemorySolution> cached = new ConcurrentHashMap<>();
    
    @Override
    public MemorySolution get(String __cwd) {
        return cached.computeIfAbsent(__cwd, k -> {
            // 按作用域划分目录：workspace 级存工作区，user 级存用户主目录
            Map<String, Path> scopeMap = new LinkedHashMap<>();
            scopeMap.put("user", Paths.get(System.getProperty("user.home"), ".demo/memory/"));
            scopeMap.put("workspace", Paths.get(k, ".demo/memory/"));
            return new MemorySolutionMdImpl(scopeMap);
        });
    }
}
```

注意：`MemorySolutionMdImpl` **只接受 `Map<String, Path>` 构造器**，作用是域→目录映射；`user` 域优先级低于 `workspace` 域（前者在后者的 LinkedHashMap 中排在前面），同级 Key 写入时 `workspace` 覆盖 `user`；读取时按作用域合并。

实际上，MD 方案内部 Store 和 Search 共享同一份内存数据，启动时全量加载已有 MD 文件，写入同时更新搜索索引，后台定期清理过期条目（默认每小时清理一次）。

#### 3.3 记忆 Provider 的默认路径

通过 `HarnessOptions` 的 `getHarnessMemory()` 可以获取记忆数据的默认存储路径（`{harnessHome}memory/`），供自定义 Provider 实现时使用：

```java
String getHarnessMemory() {
    return harnessHome + "memory/";
}
```

#### 3.4 进阶记忆 Provider 适配

除 MD 外，框架还提供以下内置存储与搜索实现，可按需组合自己的 `MemorySolution`：

| 类别 | 实现类 | 说明 |
|------|--------|------|
| **Storer** | `MemoryStorerMdImpl` | 基于 MD 文件的持久化 |
| **Storer** | `MemoryStorerRedisImpl` | 基于 Redis 的持久化 |
| **Storer** | `MemoryStorerRogueImpl` | 基于 Rogue KV 存储的持久化 |
| **Searcher** | `MemorySearcherMdImpl` | 基于内存的全文检索 |
| **Searcher** | `MemorySearcherLuceneImpl` | 基于 Lucene 的全文检索 |
| **Searcher** | `MemorySearcherRepositoryImpl` | 基于向量库的语义检索 |

### 4、MemoryTalent 配置

通过 `HarnessEngine.Builder` 提供了一组针对 MemoryTalent 的配置方法，可在构建阶段微调记忆行为：

#### 4.1 Builder 级配置

```java
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
 .sessionProvider(InMemoryAgentSession::of)
 .memoryProvider(new MyMemoryProvider())
 .memoryEnabled(true)              // 启用心智记忆（默认 true）
 .memoryRelevanceCount(6)          // 按语义匹配的记忆条数（默认 6）
 .memoryPriorityCount(5)           // 按重要度兜底的记忆条数（默认 5）
 .memorySummaryLength(80)          // listAll 视图的摘要截断长度（默认 80）
 .build();
```

各配置项的含义：

| 配置 | 默认值 | 说明 |
|------|--------|------|
| `memoryEnabled` | `true` | 是否启用心智记忆。设为 `false` 时 MemoryTalent 被禁用 |
| `memoryRelevanceCount` | `6` | 按当前用户输入做语义检索的记忆条数。总注入预算 = relevanceCount + priorityCount。小窗口模型建议 3-4，大窗口模型建议 8-10 |
| `memoryPriorityCount` | `5` | 按重要度（importance>=5）兜底的记忆条数。语义匹配不足时保证核心认知不丢；search 未用完的预算自动流转给 priorityCount |
| `memorySummaryLength` | `80` | `memory_search('*')` 列表视图中每个条目的摘要截断长度。仅影响列表展示，注入路径使用完整内容 |

#### 4.2 运行时动态调整

可在运行时动态调整记忆行为：

```java
// 构建引擎后调整
engine.setMemoryRelevanceCount(8);
engine.setMemoryPriorityCount(3);
engine.setMemorySummaryLength(120);
```

#### 4.3 运行时动态开关

可在运行时动态开启或关闭心智记忆功能（同时更新 Options 配置和 MemoryTalent 的 enabled 状态）：

```java
engine.setMemoryEnabled(true);   // 运行时开启心智记忆
engine.setMemoryEnabled(false);  // 运行时关闭心智记忆
```

关闭后，MemoryTalent 会被动态禁用，不再参与后续对话的认知注入。

### 5、记忆能力

配置记忆方案后，Agent 可自主进行 5 种操作：

| 工具名 | 能力 | 说明 |
|--------|------|------|
| `memory_extract` | **提取与覆盖** | 存入事实/偏好/进度。同名 Key 会返回旧记录供对比，信息有变则覆盖写入。包含近似 Key 探测（防止碎片化）和碎片密度检测（低分碎片超 5 条提示整合） |
| `memory_recall` | **精确召回** | 通过 Key 获取该条目的完整细节（内容、时间、重要度） |
| `memory_search` | **语义检索** | 用自然语言找回相关记忆；传入 `'*'` 列出全部条目索引（Key + 摘要），用于回答"记住了哪些" |
| `memory_consolidate` | **认知升维** | 将多个碎片整合为高层洞察并清理冗余。新洞察自动赋最高重要度（10，永久保留）。支持原地升维（newKey 复用 keys_to_merge 中的某个 Key） |
| `memory_prune` | **记忆修剪** | 删除错误、重复或过时的认知，同时清理存储主体和检索索引 |

记忆条目带重要度评分（1-10），Agent 在 `getInstruction()` 注入时采用**相关性 + 热度混合策略**：

1. **步骤 A**：按当前用户输入做语义检索（`relevanceCount` 条）
2. **步骤 B**：热记忆兜底（取剩余预算，检索重要度 >= 5 的高质量条目）
3. 两者按 Key 去重合并后注入到 LLM 的 system prompt 中

通过 `MemoryTalent.relevanceInjection(boolean)` 可控制画像注入策略：
- `true`（默认）：语义检索 + 热记忆混合注入
- `false`：仅注入热记忆（记忆量极大或检索延迟较高时可用）

### 6、作用域隔离

心智记忆支持作用域隔离，通过 `MemorySolutionProvider` 的描述方法向 LLM 呈现：

- **`workspace`**（默认）：工作区隔离，不同项目的记忆互不干扰
- **`user`**：用户全局，跨项目的通用认知共享

通过 `MemoryTalent.sessionIsolation(boolean)` 可控制是否按会话 ID 隔离用户标识（存储时 userId 为 sessionId 而非 `shared`），适用于多租户场景：

```java
// 在 HarnessEngine 构建时不可直接配置，需自定义 MemoryTalent
// 默认 sessionIsolation=false（所有会话共享用户身份）
```

### 7、完整配置示例

```java
HarnessEngine engine = HarnessEngine.of("work", ".soloncode/")
 .sessionProvider(InMemoryAgentSession::of)
 .memoryProvider(new MyMemoryProvider())
 .memoryEnabled(true)
 .memoryRelevanceCount(6)
 .memoryPriorityCount(5)
 .memorySummaryLength(80)
 .sessionWindowSize(8)
 .compressionThreshold(40, 0.75)
 .build();

// 运行时关闭心智记忆
engine.setMemoryEnabled(false);

// 运行时调整记忆检索数量
engine.setMemoryRelevanceCount(10);

// 获取 MemoryTalent 实例直接操作（如注册拦截器）
engine.getMemoryTalent(); // 返回 MemoryTalent 实例
```
