chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
# app-platform-memory
|
||||
|
||||
## 模块概述
|
||||
|
||||
app-platform-memory 是一个记忆管理模块,为 AI Agent 提供短期、长期和工作记忆的管理能力。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **多类型记忆支持**:
|
||||
- 短期记忆 (short_term): 用于临时存储对话上下文
|
||||
- 长期记忆 (long_term): 持久化存储重要信息
|
||||
- 工作记忆 (working): 当前任务相关的活跃记忆
|
||||
|
||||
- **记忆管理功能**:
|
||||
- 创建、更新、删除记忆
|
||||
- 记忆搜索和检索
|
||||
- 自动归档过期记忆
|
||||
- 重要性评分机制
|
||||
- 访问频率统计
|
||||
|
||||
- **API 支持**:
|
||||
- RESTful API 接口
|
||||
- Web 管理界面
|
||||
- SDK 集成
|
||||
|
||||
## 子模块说明
|
||||
|
||||
- `app-platform-memory-spec`: 规范定义和常量
|
||||
- `app-platform-memory-sdk`: SDK 和 DTO 定义
|
||||
- `app-platform-memory-domain`: 领域模型和仓储接口
|
||||
- `app-platform-memory-application`: 应用服务层
|
||||
- `app-platform-memory-infra`: 基础设施实现
|
||||
- `app-platform-memory-api`: RESTful API 接口
|
||||
- `app-platform-memory-web`: Web 管理界面
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 添加依赖
|
||||
|
||||
在需要使用记忆功能的模块中添加依赖:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-sdk</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### API 调用示例
|
||||
|
||||
#### 创建记忆
|
||||
|
||||
```bash
|
||||
POST /api/memory
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"memoryType": "short_term",
|
||||
"content": "用户偏好设置",
|
||||
"metadata": {
|
||||
"userId": "user123",
|
||||
"sessionId": "session456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 查询记忆
|
||||
|
||||
```bash
|
||||
GET /api/memory/user/{userId}/type/{memoryType}
|
||||
```
|
||||
|
||||
#### 搜索记忆
|
||||
|
||||
```bash
|
||||
GET /api/memory/search?userId={userId}&keyword={keyword}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 默认配置
|
||||
|
||||
- 短期记忆最大容量: 100 条
|
||||
- 工作记忆最大容量: 50 条
|
||||
- 长期记忆转换阈值: 7 天
|
||||
|
||||
### 自定义配置
|
||||
|
||||
可以在配置文件中覆盖默认值:
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
short-term:
|
||||
max-size: 200
|
||||
working:
|
||||
max-size: 100
|
||||
long-term:
|
||||
threshold-days: 14
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Java 17
|
||||
- Spring Boot
|
||||
- Lombok
|
||||
- Maven
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 构建项目
|
||||
|
||||
```bash
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
mvn test
|
||||
```
|
||||
|
||||
## 版本历史
|
||||
|
||||
- v1.0.0-SNAPSHOT: 初始版本,提供基础记忆管理功能
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-application</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.xspaceagi.memory.api.controller;
|
||||
|
||||
import com.xspaceagi.memory.api.dto.MemoryUnitQuery;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryDeleteDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemorySaveDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitQueryDTO;
|
||||
import com.xspaceagi.memory.sdk.service.IMemoryRpcService;
|
||||
import com.xspaceagi.system.sdk.service.dto.UserAccessKeyDto;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.dto.ReqResult;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ForSandboxAgentController {
|
||||
|
||||
@Resource
|
||||
private IMemoryRpcService iMemoryRpcService;
|
||||
|
||||
@PostMapping("/api/v1/4sandbox/memory/query")
|
||||
public ReqResult<String> query(@RequestBody MemoryUnitQuery memoryUnitQuery) {
|
||||
UserAccessKeyDto userAccessKey = (UserAccessKeyDto) RequestContext.get().getUserAccessKey();
|
||||
MemoryUnitQueryDTO memoryUnitQueryDTO = new MemoryUnitQueryDTO();
|
||||
memoryUnitQueryDTO.setTenantId(RequestContext.get().getTenantId());
|
||||
memoryUnitQueryDTO.setUserId(RequestContext.get().getUserId());
|
||||
memoryUnitQueryDTO.setAgentId(Long.parseLong(userAccessKey.getTargetId()));
|
||||
memoryUnitQueryDTO.setCategories(memoryUnitQuery.getCategories());
|
||||
memoryUnitQueryDTO.setSubCategories(memoryUnitQuery.getSubCategories());
|
||||
memoryUnitQueryDTO.setTags(memoryUnitQuery.getTags());
|
||||
memoryUnitQueryDTO.setQueryType(memoryUnitQuery.getTimeRangeType());
|
||||
memoryUnitQueryDTO.setTimeRange(memoryUnitQuery.getTimeRange());
|
||||
memoryUnitQueryDTO.setLimit(memoryUnitQuery.getLimit() == null ? 10 : memoryUnitQuery.getLimit());
|
||||
String memory = iMemoryRpcService.queryMemoriesMd(memoryUnitQueryDTO, memoryUnitQuery.isFilterSensitive());
|
||||
return ReqResult.success(memory);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/api/v1/4sandbox/memory/save")
|
||||
public ReqResult<String> save(@RequestBody MemorySaveDto memorySaveDto) {
|
||||
UserAccessKeyDto userAccessKey = (UserAccessKeyDto) RequestContext.get().getUserAccessKey();
|
||||
iMemoryRpcService.saveMemories(RequestContext.get().getTenantId(), RequestContext.get().getUserId(), Long.parseLong(userAccessKey.getTargetId()), memorySaveDto);
|
||||
return ReqResult.success();
|
||||
}
|
||||
|
||||
@PostMapping("/api/v1/4sandbox/memory/delete")
|
||||
public ReqResult<String> delete(@RequestBody MemoryDeleteDto memoryDeleteDto) {
|
||||
iMemoryRpcService.deleteMemories(RequestContext.get().getTenantId(), RequestContext.get().getUserId(), memoryDeleteDto.getMemoryIds());
|
||||
return ReqResult.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xspaceagi.memory.api.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class MemoryUnitQuery {
|
||||
|
||||
@Schema(description = "必须严格从上述一级分类中选择,不能自创")
|
||||
private List<String> categories;
|
||||
|
||||
@Schema(description = "必须严格从上述二级分类中选择,不能自创")
|
||||
private List<String> subCategories;
|
||||
|
||||
@Schema(description = "关键词标签列表,分词可以细粒度一点,尽量多生成有助于更精确查找")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "查询范围,可选值为:today|yesterday|recent|long-term|all")
|
||||
private String timeRangeType;
|
||||
|
||||
@Schema(description = "时间范围(非必填),格式为:2023-01-01 00:00:00-2023-01-01 23:59:59")
|
||||
private String timeRange;
|
||||
|
||||
@Schema(description = "限制返回的记忆数量,默认为 10")
|
||||
private Integer limit;
|
||||
|
||||
@Schema(description = "是否过滤敏感信息,默认为 true")
|
||||
private boolean filterSensitive = true;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||
<id>repack</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<!--
|
||||
重新打包需要包含的jar包,这个过程会解压所有的jar包并按照jar的规则重新组合成一个jar包
|
||||
这里需要包含当前这个工程 ,格式是{groupId}:{artifactId}
|
||||
-->
|
||||
<includes>
|
||||
<!-- 包含项目自己 -->
|
||||
<include>com.xspaceagi:app-platform-memory-api</include>
|
||||
<include>com.xspaceagi:app-platform-memory-application</include>
|
||||
<include>com.xspaceagi:app-platform-memory-domain</include>
|
||||
<include>com.xspaceagi:app-platform-memory-infra</include>
|
||||
<include>com.xspaceagi:app-platform-memory-spec</include>
|
||||
</includes>
|
||||
<!-- 打包结果目录:/ 表示target/ -->
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<!-- 是否解压依赖 -->
|
||||
<unpack>true</unpack>
|
||||
<!-- 打包的scope -->
|
||||
<!-- <scope>system</scope>-->
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory-api</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-application</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-domain</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-infra</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-agent-core-sdk</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hankcs</groupId>
|
||||
<artifactId>hanlp</artifactId>
|
||||
<version>portable-1.8.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xspaceagi.memory.app.service;
|
||||
|
||||
import com.xspaceagi.memory.app.service.dto.MemoryExtractDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryMetaData;
|
||||
import com.xspaceagi.memory.sdk.dto.MemorySaveDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 记忆应用服务接口
|
||||
*/
|
||||
public interface MemoryApplicationService {
|
||||
|
||||
/**
|
||||
* 创建记忆
|
||||
*/
|
||||
List<MemoryExtractDto> createMemory(MemoryMetaData memoryData);
|
||||
|
||||
/**
|
||||
* 保存记忆
|
||||
*/
|
||||
void saveMemories(Long tenantId, Long userId, Long agentId, MemorySaveDto memorySaveDto);
|
||||
|
||||
/**
|
||||
* 搜索记忆
|
||||
*/
|
||||
List<MemoryUnitDTO> searchMemories(Long tenantId, Long userId, Long agentId, String userMessage, String context, boolean justKeywordSearch);
|
||||
|
||||
void deleteMemories(Long tenantId, Long userId, List<Long> memoryIds);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.xspaceagi.memory.app.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnit;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitCreateDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitQueryDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitUpdateDTO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 记忆单元服务接口
|
||||
*/
|
||||
public interface MemoryUnitService extends IService<MemoryUnit> {
|
||||
|
||||
/**
|
||||
* 创建记忆单元
|
||||
*
|
||||
* @param createDTO 创建DTO
|
||||
* @return 记忆单元DTO
|
||||
*/
|
||||
MemoryUnitDTO create(MemoryUnitCreateDTO createDTO);
|
||||
|
||||
/**
|
||||
* 更新记忆单元
|
||||
*
|
||||
* @param updateDTO 更新DTO
|
||||
* @return 记忆单元DTO
|
||||
*/
|
||||
MemoryUnitDTO update(MemoryUnitUpdateDTO updateDTO);
|
||||
|
||||
/**
|
||||
* 根据ID查询记忆单元
|
||||
*
|
||||
* @param id ID
|
||||
* @return 记忆单元DTO
|
||||
*/
|
||||
MemoryUnitDTO getById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询记忆单元
|
||||
*
|
||||
* @param queryDTO 查询条件
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> queryList(MemoryUnitQueryDTO queryDTO);
|
||||
|
||||
/**
|
||||
* 根据用户ID和分类查询记忆单元
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param category 分类
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> findByUserIdAndCategory(Long userId, Long agentId, String category, String subCategory);
|
||||
|
||||
/**
|
||||
* 根据代理ID查询记忆单元
|
||||
*
|
||||
* @param agentId 代理ID
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> findByAgentId(Long agentId);
|
||||
|
||||
/**
|
||||
* 根据分类和JSON字段值查询记忆单元
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类(可选)
|
||||
* @param jsonKey JSON中的键
|
||||
* @param jsonValue JSON中的值
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> findByCategoryAndJsonKeyValue(Long tenantId, Long userId, String category, String subCategory, String jsonKey, String jsonValue);
|
||||
|
||||
/**
|
||||
* 根据分类和JSON字段值查询记忆单元(支持模糊匹配)
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类(可选)
|
||||
* @param jsonKey JSON中的键
|
||||
* @param jsonValue JSON中的值(支持模糊匹配)
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> findByCategoryAndJsonKeyValueLike(Long tenantId, Long userId, String category, String subCategory, String jsonKey, String jsonValue);
|
||||
|
||||
/**
|
||||
* 根据JSON多个字段值查询记忆单元
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类(可选)
|
||||
* @param jsonKeyValues JSON键值对 Map<key, value>
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnitDTO> findByCategoryAndJsonKeyValues(Long tenantId, Long userId, String category, String subCategory, Map<String, String> jsonKeyValues);
|
||||
|
||||
/**
|
||||
* 实体转DTO
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return DTO
|
||||
*/
|
||||
MemoryUnitDTO toDTO(MemoryUnit entity);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.xspaceagi.memory.app.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitTagCreateDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitTagDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 记忆单元标签服务接口
|
||||
*/
|
||||
public interface MemoryUnitTagService extends IService<MemoryUnitTag> {
|
||||
|
||||
/**
|
||||
* 创建标签
|
||||
*
|
||||
* @param createDTO 创建DTO
|
||||
* @return 标签DTO
|
||||
*/
|
||||
MemoryUnitTagDTO create(MemoryUnitTagCreateDTO createDTO);
|
||||
|
||||
/**
|
||||
* 实体转DTO
|
||||
*
|
||||
* @param entity 实体
|
||||
* @return DTO
|
||||
*/
|
||||
MemoryUnitTagDTO toDTO(MemoryUnitTag entity);
|
||||
|
||||
|
||||
List<MemoryUnitTagDTO> searchByTagNames(Long userId, List<String> tags);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.xspaceagi.memory.app.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* API Key 数据传输对象
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemoryExtractDto {
|
||||
|
||||
/**
|
||||
* 是否需要保存
|
||||
*/
|
||||
@JsonPropertyDescription("判断是否包含可提取的记忆,有则需要保存")
|
||||
private Boolean shouldSave;
|
||||
|
||||
/**
|
||||
* 一级分类
|
||||
*/
|
||||
@JsonPropertyDescription("一级分类,严格按照提示词中的约定")
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 二级分类
|
||||
*/
|
||||
@JsonPropertyDescription("二级分类,严格按照提示词中的约定")
|
||||
private String subCategory;
|
||||
|
||||
/**
|
||||
* 更新的数据
|
||||
*/
|
||||
@JsonPropertyDescription("提取的记忆键值对,严格按照提示词中约定的键名")
|
||||
private List<KeyValueDto> keyValues;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
@JsonPropertyDescription("关键词标签列表,分词可以细粒度一点,尽量多生成有助于更精确查找,根据上下文以及用户消息提取或扩展标签")
|
||||
private List<String> tags;
|
||||
|
||||
/**
|
||||
* 是否敏感信息
|
||||
*/
|
||||
@JsonPropertyDescription("判断是否包含敏感信息")
|
||||
private Boolean isSensitive;
|
||||
|
||||
//priority 范围 0-10,数字越大优先级越高
|
||||
@JsonPropertyDescription("优先级,范围 0-10,数字越大优先级越高")
|
||||
private Integer priority;
|
||||
|
||||
/**
|
||||
* 更新数据内部类
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class KeyValueDto {
|
||||
|
||||
/**
|
||||
* 键名
|
||||
*/
|
||||
@JsonPropertyDescription("键名,尽量使用已定义的键名")
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
@JsonPropertyDescription("键值(对应的记忆值)")
|
||||
private String value;
|
||||
}
|
||||
|
||||
public Map<String, String> toMap() {
|
||||
if (keyValues == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return keyValues.stream().collect(Collectors.toMap(KeyValueDto::getKey, KeyValueDto::getValue, (k1, k2) -> k1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xspaceagi.memory.app.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class MemoryMergeResultDto {
|
||||
|
||||
@JsonPropertyDescription("更新、新增、追加memoryId对应记忆中的键值对")
|
||||
private List<MergeUnitWithID> updateMemoryKeyValues;
|
||||
|
||||
@JsonPropertyDescription("生成新的记忆")
|
||||
private List<MergeUnit> insertMemories;
|
||||
|
||||
@Data
|
||||
public static class MergeUnitWithID {
|
||||
@JsonPropertyDescription("记忆ID")
|
||||
private Long memoryId;
|
||||
@JsonPropertyDescription("更新(替换原有内容,比如手机号变更等)、新增、追加(在原有内容后面追加新的内容,比如之前个人形象身高180,现在又获取到了新的数据为皮肤黝黑,新内容为“身高180,皮肤黝黑”)对应记忆中的键值对")
|
||||
private Map<String, String> keyValues;
|
||||
@JsonPropertyDescription("关键词标签列表")
|
||||
private List<String> tags;
|
||||
@JsonPropertyDescription("是否为敏感信息")
|
||||
private Boolean isSensitive;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MergeUnit {
|
||||
@JsonPropertyDescription("一级分类")
|
||||
private String category;
|
||||
@JsonPropertyDescription("二级分类")
|
||||
private String subCategory;
|
||||
@JsonPropertyDescription("关键词标签列表")
|
||||
private List<String> tags;
|
||||
@JsonPropertyDescription("记忆中的键值对")
|
||||
private Map<String, String> keyValues;
|
||||
@JsonPropertyDescription("是否为敏感信息")
|
||||
private Boolean isSensitive;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.xspaceagi.memory.app.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class MemoryQueryExtractDto {
|
||||
|
||||
@JsonPropertyDescription("判断是否需要搜索记忆,如果问题不需要查询记忆,shouldSearch 设为 false")
|
||||
private Boolean shouldSearch;
|
||||
@JsonPropertyDescription("必须严格从上述一级分类中选择,不能自创")
|
||||
private List<String> categories;
|
||||
@JsonPropertyDescription("必须严格从上述二级分类中选择,不能自创")
|
||||
private List<String> subCategories;
|
||||
@JsonPropertyDescription("关键词标签列表,分词可以细粒度一点,尽量多生成有助于更精确查找")
|
||||
private List<String> tags;
|
||||
@JsonPropertyDescription("是否需要记忆的历史变更记录,如果问题不需要,requiresHistory 设为 false")
|
||||
private Boolean requiresHistory;
|
||||
@JsonPropertyDescription("查询范围,可选值为:today|yesterday|recent|long-term|all")
|
||||
private String queryType;
|
||||
@JsonPropertyDescription("时间范围(非必填),格式为:2023-01-01 00:00:00-2023-01-01 23:59:59")
|
||||
private String timeRange;
|
||||
@JsonPropertyDescription("限制返回的记忆数量,默认为 10")
|
||||
private Integer limit;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xspaceagi.memory.app.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class MemoryUnitMergeInfo {
|
||||
|
||||
@JsonPropertyDescription("记忆ID,用于更新")
|
||||
private Long memoryId;
|
||||
@JsonPropertyDescription("一级分类")
|
||||
private String category;
|
||||
@JsonPropertyDescription("二级分类")
|
||||
private String subCategory;
|
||||
@JsonPropertyDescription("记忆中的键值对")
|
||||
private Map<String, String> keyValues;
|
||||
@JsonPropertyDescription("创建时间")
|
||||
private Date created;
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
package com.xspaceagi.memory.app.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.hankcs.hanlp.HanLP;
|
||||
import com.xspaceagi.agent.core.sdk.IModelRpcService;
|
||||
import com.xspaceagi.memory.app.service.MemoryApplicationService;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitService;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitTagService;
|
||||
import com.xspaceagi.memory.app.service.dto.MemoryExtractDto;
|
||||
import com.xspaceagi.memory.app.service.dto.MemoryMergeResultDto;
|
||||
import com.xspaceagi.memory.app.service.dto.MemoryQueryExtractDto;
|
||||
import com.xspaceagi.memory.app.service.dto.MemoryUnitMergeInfo;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnit;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag;
|
||||
import com.xspaceagi.memory.sdk.dto.*;
|
||||
import com.xspaceagi.system.spec.common.RequestContext;
|
||||
import com.xspaceagi.system.spec.tenant.thread.TenantFunctions;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MemoryApplicationServiceImpl implements MemoryApplicationService {
|
||||
|
||||
// JSON key validation pattern: only alphanumeric, underscore, and dot allowed
|
||||
private static final Pattern JSON_KEY_PATTERN = Pattern.compile("^[a-zA-Z0-9_.]+$");
|
||||
@Autowired
|
||||
private MemoryUnitTagService memoryUnitTagService;
|
||||
|
||||
/**
|
||||
* Validate JSON keys to prevent SQL injection
|
||||
* Only allows alphanumeric characters, underscore, and dot
|
||||
*/
|
||||
private void validateJsonKeys(Map<String, String> jsonKeyValues) {
|
||||
if (jsonKeyValues == null || jsonKeyValues.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (String key : jsonKeyValues.keySet()) {
|
||||
if (key == null || key.isEmpty()) {
|
||||
throw new IllegalArgumentException("JSON key cannot be null or empty");
|
||||
}
|
||||
if (!JSON_KEY_PATTERN.matcher(key).matches()) {
|
||||
throw new IllegalArgumentException("Invalid JSON key: " + key +
|
||||
". Only alphanumeric characters, underscore, and dot are allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String MEMORY_EXTRACT_PROMPT = """
|
||||
你是一个记忆提取专家,分析用户输入,判断用户意图,提取需要记录的记忆。
|
||||
|
||||
## 用户输入
|
||||
{{user_input}}
|
||||
|
||||
## 当前对话上下文
|
||||
{{conversation_context}}
|
||||
|
||||
## 完整分类体系
|
||||
|
||||
### 一级分类
|
||||
- profile: 用户画像
|
||||
- auth: 认证信息
|
||||
- social: 社交媒体
|
||||
- project: 项目信息
|
||||
- preference: 用户偏好
|
||||
- task: 任务待办
|
||||
- note: 笔记备忘
|
||||
- idea: 创意想法
|
||||
- event: 事件日程
|
||||
- document: 文档资料
|
||||
- code: 代码技术
|
||||
- other: 其他类型
|
||||
|
||||
### 二级分类及可用键名
|
||||
|
||||
#### profile 子分类及键名
|
||||
- personal: name, age, gender, birthday, avatar
|
||||
- professional: company, position, department, title, employee_id
|
||||
- location: city, address, timezone, country, coordinates
|
||||
- contact: phone, email, wechat, telegram, slack
|
||||
- identity: id_card, passport, license, ssn, tax_id
|
||||
- account: account_id, phone, email, platform
|
||||
|
||||
#### auth 子分类及键名
|
||||
- website: url, username, password, login_method
|
||||
- service: service_name, api_key, app_id, app_secret, endpoint
|
||||
- api_key: key, secret, permissions, expiry_date
|
||||
- ssh_key: private_key, public_key, host, username, port
|
||||
- oauth_token: access_token, refresh_token, token_type, expires_in
|
||||
- database: host, port, username, password, database, connection_string
|
||||
|
||||
#### social 子分类及键名
|
||||
- wechat: account_id, nickname, contacts, groups
|
||||
- twitter: username, handle, followers, following
|
||||
- linkedin: profile_url, connections, headline
|
||||
- contacts: name, relation, contact_info, notes
|
||||
- groups: group_name, members, topic, platform
|
||||
|
||||
#### project 子分类及键名
|
||||
- ongoing: name, description, progress, start_date, owner
|
||||
- completed: name, completion_date, summary, outcome
|
||||
- planning: name, timeline, requirements, budget
|
||||
- milestone: name, deadline, status, dependencies
|
||||
- task: project_id, title, assignee, deadline, status
|
||||
|
||||
#### preference 子分类及键名
|
||||
- travel: transport, airline, hotel_type, travel_style
|
||||
- food: cuisine, restrictions, favorites, dislikes
|
||||
- lifestyle: sleep_time, exercise, hobbies, daily_routine
|
||||
- entertainment: music, movies, games, sports
|
||||
- work: working_hours, tools, environment, work_style
|
||||
- general: general_preference, preference_name, value, notes
|
||||
|
||||
#### task 子分类及键名
|
||||
- daily: title, due_date, priority, description
|
||||
- weekly: title, week, status, category
|
||||
- project: project_id, title, assignee, status
|
||||
- urgent: title, deadline, priority, impact
|
||||
- recurring: title, frequency, next_due, pattern
|
||||
|
||||
#### note 子分类及键名
|
||||
- meeting: date, participants, summary, action_items
|
||||
- lecture: topic, key_points, references, instructor
|
||||
- research: subject, findings, sources, methodology
|
||||
- personal: topic, content, tags, created_at
|
||||
|
||||
#### idea 子分类及键名
|
||||
- product: name, description, features, target_users
|
||||
- feature: component, enhancement, benefit, complexity
|
||||
- improvement: area, problem, solution, priority
|
||||
- innovation: concept, potential, challenges, feasibility
|
||||
|
||||
#### event 子分类及键名
|
||||
- meeting: title, time, location, attendees, agenda
|
||||
- appointment: title, time, location, purpose
|
||||
- reminder: title, time, repeat, notification_method
|
||||
- deadline: task, due_date, priority, consequences
|
||||
|
||||
#### document 子分类及键名
|
||||
- contract: title, parties, effective_date, expiry_date
|
||||
- invoice: number, amount, due_date, vendor, status
|
||||
- report: title, date, summary, author
|
||||
- reference: title, author, url, tags, category
|
||||
|
||||
#### code 子分类及键名
|
||||
- snippet: language, description, code, filename
|
||||
- config: service, environment, settings, format
|
||||
- api_doc: endpoint, method, parameters, response_format
|
||||
- deployment: environment, version, changes, deploy_date
|
||||
|
||||
## 任务
|
||||
1. 判断用户输入是否包含需要记忆的信息
|
||||
2. 识别信息的分类(一级)和子分类(二级),必须从上述列表中精确选择
|
||||
3. 提取关键值和对应的键名,必须使用上述列出的可用键名
|
||||
4. 判断是否为敏感信息(密码、密钥等)
|
||||
""";
|
||||
|
||||
private static final String MEMORY_QUERY_PROMPT = """
|
||||
你是一个记忆查询分析专家,分析用户问题,匹配一二级分类、关键词以及时间范围。
|
||||
|
||||
## 用户输入
|
||||
{{user_input}}
|
||||
|
||||
## 当前对话上下文
|
||||
{{conversation_context}}
|
||||
|
||||
## 完整分类体系
|
||||
|
||||
### 一级分类
|
||||
- profile: 用户画像
|
||||
- auth: 认证信息
|
||||
- social: 社交媒体
|
||||
- project: 项目信息
|
||||
- preference: 用户偏好
|
||||
- task: 任务待办
|
||||
- note: 笔记备忘
|
||||
- idea: 创意想法
|
||||
- event: 事件日程
|
||||
- document: 文档资料
|
||||
- code: 代码技术
|
||||
- other: 其他类型
|
||||
|
||||
### 二级分类及可用键名
|
||||
|
||||
#### profile 子分类及键名
|
||||
- personal: name, age, gender, birthday, avatar
|
||||
- professional: company, position, department, title, employee_id
|
||||
- location: city, address, timezone, country, coordinates
|
||||
- contact: phone, email, wechat, telegram, slack
|
||||
- identity: id_card, passport, license, ssn, tax_id
|
||||
- account: account_id, phone, email, platform
|
||||
|
||||
#### auth 子分类及键名
|
||||
- website: url, username, password, login_method
|
||||
- service: service_name, api_key, app_id, app_secret, endpoint
|
||||
- api_key: key, secret, permissions, expiry_date
|
||||
- ssh_key: private_key, public_key, host, username, port
|
||||
- oauth_token: access_token, refresh_token, token_type, expires_in
|
||||
- database: host, port, username, password, database, connection_string
|
||||
|
||||
#### social 子分类及键名
|
||||
- wechat: account_id, nickname, contacts, groups
|
||||
- twitter: username, handle, followers, following
|
||||
- linkedin: profile_url, connections, headline
|
||||
- contacts: name, relation, contact_info, notes
|
||||
- groups: group_name, members, topic, platform
|
||||
|
||||
#### project 子分类及键名
|
||||
- ongoing: name, description, progress, start_date, owner
|
||||
- completed: name, completion_date, summary, outcome
|
||||
- planning: name, timeline, requirements, budget
|
||||
- milestone: name, deadline, status, dependencies
|
||||
- task: project_id, title, assignee, deadline, status
|
||||
|
||||
#### preference 子分类及键名
|
||||
- travel: transport, airline, hotel_type, travel_style
|
||||
- food: cuisine, restrictions, favorites, dislikes
|
||||
- lifestyle: sleep_time, exercise, hobbies, daily_routine
|
||||
- entertainment: music, movies, games, sports
|
||||
- work: working_hours, tools, environment, work_style
|
||||
- general: general_preference, preference_name, value, notes
|
||||
|
||||
#### task 子分类及键名
|
||||
- daily: title, due_date, priority, description
|
||||
- weekly: title, week, status, category
|
||||
- project: project_id, title, assignee, status
|
||||
- urgent: title, deadline, priority, impact
|
||||
- recurring: title, frequency, next_due, pattern
|
||||
|
||||
#### note 子分类及键名
|
||||
- meeting: date, participants, summary, action_items
|
||||
- lecture: topic, key_points, references, instructor
|
||||
- research: subject, findings, sources, methodology
|
||||
- personal: topic, content, tags, created_at
|
||||
|
||||
#### idea 子分类及键名
|
||||
- product: name, description, features, target_users
|
||||
- feature: component, enhancement, benefit, complexity
|
||||
- improvement: area, problem, solution, priority
|
||||
- innovation: concept, potential, challenges, feasibility
|
||||
|
||||
#### event 子分类及键名
|
||||
- meeting: title, time, location, attendees, agenda
|
||||
- appointment: title, time, location, purpose
|
||||
- reminder: title, time, repeat, notification_method
|
||||
- deadline: task, due_date, priority, consequences
|
||||
|
||||
#### document 子分类及键名
|
||||
- contract: title, parties, effective_date, expiry_date
|
||||
- invoice: number, amount, due_date, vendor, status
|
||||
- report: title, date, summary, author
|
||||
- reference: title, author, url, tags, category
|
||||
|
||||
#### code 子分类及键名
|
||||
- snippet: language, description, code, filename
|
||||
- config: service, environment, settings, format
|
||||
- api_doc: endpoint, method, parameters, response_format
|
||||
- deployment: environment, version, changes, deploy_date
|
||||
|
||||
## 任务
|
||||
1. 分析用户问题的意图
|
||||
2. 识别需要查询的记忆分类(从一级分类中精确选择)
|
||||
3. 如果问题匹配某个二级分类,也识别出来
|
||||
4. 提取标签关键词
|
||||
5. 判断是否需要约定时间范围
|
||||
""";
|
||||
|
||||
|
||||
private static final String MEMORY_MERGE_PROMPT = """
|
||||
你是一个记忆合并判断专家,判断新记忆是否应该更新现有记忆。
|
||||
|
||||
## 现有记忆
|
||||
{{existing_memory}}
|
||||
|
||||
## 新记忆内容
|
||||
{{new_content}}
|
||||
|
||||
## 任务
|
||||
1. 识别新旧记忆的关系(重复、更新、相关、独立)
|
||||
2. 如果是更新,判断需要更新的字段(keyValues中的字段)
|
||||
3. 计算相似度评分
|
||||
|
||||
## 字段说明
|
||||
- memoryId 记忆ID,用于更新现有记忆
|
||||
- category 一级记忆分类
|
||||
- subCategory 二级记忆分类
|
||||
- keyValues 一条记忆中对应的键值对
|
||||
- created 已有记忆的创建时间
|
||||
""";
|
||||
|
||||
@Resource
|
||||
private IModelRpcService iModelRpcService;
|
||||
|
||||
@Resource
|
||||
private MemoryUnitService memoryUnitService;
|
||||
|
||||
@Override
|
||||
public List<MemoryExtractDto> createMemory(MemoryMetaData memoryData) {
|
||||
boolean hasRequestContext = RequestContext.get() != null;
|
||||
try {
|
||||
if (!hasRequestContext) {
|
||||
RequestContext<Object> requestContext = RequestContext.builder()
|
||||
.tenantId(memoryData.getTenantId())
|
||||
.userId(memoryData.getUserId())
|
||||
.build();
|
||||
RequestContext.set(requestContext);
|
||||
}
|
||||
return createMemory0(memoryData);
|
||||
} finally {
|
||||
if (!hasRequestContext) {
|
||||
RequestContext.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MemoryExtractDto> createMemory0(MemoryMetaData memoryData) {
|
||||
|
||||
String userPrompt = MEMORY_EXTRACT_PROMPT.replace("{{user_input}}", memoryData.getUserInput()).replace("{{conversation_context}}", memoryData.getContext());
|
||||
List<MemoryExtractDto> memoryExtractResults = iModelRpcService.call("当前时间: " + (new Date()), userPrompt, new ParameterizedTypeReference<List<MemoryExtractDto>>() {
|
||||
});
|
||||
log.debug("memoryExtractResults: {}", memoryExtractResults);
|
||||
if (memoryExtractResults != null) {
|
||||
// Validate JSON keys to prevent SQL injection
|
||||
for (MemoryExtractDto memoryExtractDto : memoryExtractResults) {
|
||||
Map<String, String> keyValues = memoryExtractDto.toMap();
|
||||
validateJsonKeys(keyValues);
|
||||
// 验证重复性
|
||||
List<MemoryUnitDTO> byCategoryAndJsonKeyValues = TenantFunctions.callWithIgnoreCheck(() -> memoryUnitService.findByUserIdAndCategory(memoryData.getUserId(), memoryData.getAgentId(), memoryExtractDto.getCategory(), memoryExtractDto.getSubCategory()));
|
||||
if (byCategoryAndJsonKeyValues.isEmpty()) {
|
||||
MemoryUnitCreateDTO memoryUnitCreateDTO = new MemoryUnitCreateDTO();
|
||||
memoryUnitCreateDTO.setCategory(memoryExtractDto.getCategory());
|
||||
memoryUnitCreateDTO.setSubCategory(memoryExtractDto.getSubCategory());
|
||||
memoryUnitCreateDTO.setKeyValues(memoryExtractDto.toMap());
|
||||
memoryUnitCreateDTO.setTags(memoryExtractDto.getTags());
|
||||
memoryUnitCreateDTO.setIsSensitive(memoryExtractDto.getIsSensitive());
|
||||
memoryUnitCreateDTO.setUserId(memoryData.getUserId());
|
||||
memoryUnitCreateDTO.setAgentId(memoryData.getAgentId());
|
||||
memoryUnitCreateDTO.setTenantId(memoryData.getTenantId());
|
||||
memoryUnitService.create(memoryUnitCreateDTO);
|
||||
continue;
|
||||
}
|
||||
List<MemoryUnitMergeInfo> memoryUnitMergeInfos = byCategoryAndJsonKeyValues.stream().map(memoryUnitDTO -> {
|
||||
MemoryUnitMergeInfo memoryUnitMergeInfo = new MemoryUnitMergeInfo();
|
||||
memoryUnitMergeInfo.setMemoryId(memoryUnitDTO.getId());
|
||||
memoryUnitMergeInfo.setCategory(memoryUnitDTO.getCategory());
|
||||
memoryUnitMergeInfo.setSubCategory(memoryUnitDTO.getSubCategory());
|
||||
JSONObject jsonObject = JSON.parseObject(memoryUnitDTO.getContentJson());
|
||||
if (jsonObject != null) {
|
||||
memoryUnitMergeInfo.setKeyValues(jsonObject.getObject("keyValues", Map.class));
|
||||
}
|
||||
memoryUnitMergeInfo.setCreated(memoryUnitDTO.getCreated());
|
||||
return memoryUnitMergeInfo;
|
||||
}).toList();
|
||||
String mergePrompt = MEMORY_MERGE_PROMPT.replace("{{existing_memory}}", JSON.toJSONString(memoryUnitMergeInfos)).replace("{{new_content}}", JSON.toJSONString(memoryExtractDto));
|
||||
MemoryMergeResultDto memoryMergeResultDto = iModelRpcService.call("当前时间: " + (new Date()), mergePrompt, new ParameterizedTypeReference<MemoryMergeResultDto>() {
|
||||
});
|
||||
log.debug("memoryMergeResultDto: {}", memoryMergeResultDto);
|
||||
if (memoryMergeResultDto != null) {
|
||||
List<MemoryMergeResultDto.MergeUnit> insertMemories = memoryMergeResultDto.getInsertMemories();
|
||||
if (CollectionUtils.isNotEmpty(insertMemories)) {
|
||||
for (MemoryMergeResultDto.MergeUnit insertMemory : insertMemories) {
|
||||
insertMemory(memoryData.getTenantId(), memoryData.getUserId(), memoryData.getAgentId(), insertMemory.getCategory(), insertMemory.getSubCategory(), insertMemory.getTags(), insertMemory.getKeyValues(), insertMemory.getIsSensitive());
|
||||
}
|
||||
}
|
||||
List<MemoryMergeResultDto.MergeUnitWithID> updateMemoryKeyValues = memoryMergeResultDto.getUpdateMemoryKeyValues();
|
||||
if (CollectionUtils.isNotEmpty(updateMemoryKeyValues)) {
|
||||
for (MemoryMergeResultDto.MergeUnitWithID updateMemoryKeyValue : updateMemoryKeyValues) {
|
||||
if (updateMemoryKeyValue.getMemoryId() == null || keyValues == null) {
|
||||
continue;
|
||||
}
|
||||
updateMemory(memoryData.getUserId(), updateMemoryKeyValue.getMemoryId(), updateMemoryKeyValue.getTags(), updateMemoryKeyValue.getKeyValues(), memoryExtractDto.getIsSensitive());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return memoryExtractResults;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void saveMemories(Long tenantId, Long userId, Long agentId, MemorySaveDto memorySaveDto) {
|
||||
if (memorySaveDto.getInsertMemories() != null) {
|
||||
for (MemorySaveDto.InsertUnit insertUnit : memorySaveDto.getInsertMemories()) {
|
||||
insertMemory(tenantId, userId, agentId, insertUnit.getCategory(), insertUnit.getSubCategory(), insertUnit.getTags(), insertUnit.getKeyValues(), insertUnit.getIsSensitive());
|
||||
}
|
||||
}
|
||||
|
||||
if (memorySaveDto.getUpdateMemoryKeyValues() != null) {
|
||||
for (MemorySaveDto.UpdateUnitWithID updateUnitWithID : memorySaveDto.getUpdateMemoryKeyValues()) {
|
||||
if (updateUnitWithID.getMemoryId() == null || updateUnitWithID.getKeyValues() == null) {
|
||||
continue;
|
||||
}
|
||||
updateMemory(userId, updateUnitWithID.getMemoryId(), updateUnitWithID.getTags(), updateUnitWithID.getKeyValues(), updateUnitWithID.getIsSensitive());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertMemory(Long tenantId, Long userId, Long agentId, String category, String subCategory, List<String> tags, Map<String, String> keyValues, Boolean isSensitive) {
|
||||
if (keyValues == null || keyValues.isEmpty() || category == null || subCategory == null) {
|
||||
return;
|
||||
}
|
||||
MemoryUnitCreateDTO memoryUnitCreateDTO = new MemoryUnitCreateDTO();
|
||||
memoryUnitCreateDTO.setCategory(category);
|
||||
memoryUnitCreateDTO.setSubCategory(subCategory);
|
||||
memoryUnitCreateDTO.setKeyValues(keyValues);
|
||||
memoryUnitCreateDTO.setTags(tags);
|
||||
memoryUnitCreateDTO.setIsSensitive(isSensitive);
|
||||
memoryUnitCreateDTO.setUserId(userId);
|
||||
memoryUnitCreateDTO.setAgentId(agentId);
|
||||
memoryUnitCreateDTO.setTenantId(tenantId);
|
||||
memoryUnitService.create(memoryUnitCreateDTO);
|
||||
}
|
||||
|
||||
private void updateMemory(Long userId, Long memoryId, List<String> updateTags, Map<String, String> keyValues, Boolean isSensitive) {
|
||||
MemoryUnitDTO memoryUnitDTO = memoryUnitService.getById(memoryId);
|
||||
if (memoryUnitDTO != null && memoryUnitDTO.getUserId().equals(userId)) {
|
||||
JSONObject jsonObject = JSON.parseObject(memoryUnitDTO.getContentJson());
|
||||
JSONObject oldKeyValues = jsonObject.getJSONObject("keyValues");
|
||||
if (oldKeyValues == null) {
|
||||
oldKeyValues = new JSONObject();
|
||||
}
|
||||
List<String> newTags = new ArrayList<>();
|
||||
JSONArray tags = jsonObject.getJSONArray("tags");
|
||||
if (CollectionUtils.isNotEmpty(tags) && CollectionUtils.isNotEmpty(updateTags)) {
|
||||
updateTags.forEach(tag -> {
|
||||
if (!tags.contains(tag)) {
|
||||
newTags.add(tag);
|
||||
tags.add(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
oldKeyValues.putAll(keyValues);
|
||||
MemoryUnitUpdateDTO updateDTO = new MemoryUnitUpdateDTO();
|
||||
updateDTO.setId(memoryUnitDTO.getId());
|
||||
updateDTO.setUserId(userId);
|
||||
updateDTO.setContentJson(jsonObject.toJSONString());
|
||||
updateDTO.setTags(newTags);
|
||||
updateDTO.setIsSensitive(isSensitive);
|
||||
memoryUnitService.update(updateDTO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMemories(Long tenantId, Long userId, List<Long> memoryIds) {
|
||||
if (memoryIds == null || memoryIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LambdaUpdateWrapper<MemoryUnit> updateWrapper = new LambdaUpdateWrapper<>();
|
||||
updateWrapper.eq(MemoryUnit::getUserId, userId);
|
||||
updateWrapper.eq(MemoryUnit::getTenantId, tenantId);
|
||||
updateWrapper.in(MemoryUnit::getId, memoryIds);
|
||||
updateWrapper.set(MemoryUnit::getStatus, "deleted");
|
||||
memoryUnitService.update(updateWrapper);
|
||||
LambdaUpdateWrapper<MemoryUnitTag> memoryUnitTagLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||
memoryUnitTagLambdaUpdateWrapper.in(MemoryUnitTag::getMemoryId, memoryIds);
|
||||
memoryUnitTagService.remove(memoryUnitTagLambdaUpdateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> searchMemories(Long tenantId, Long userId, Long agentId, String inputMessage, String context, boolean justKeywordSearch) {
|
||||
boolean hasRequestContext = RequestContext.get() != null;
|
||||
try {
|
||||
if (!hasRequestContext) {
|
||||
RequestContext<Object> requestContext = RequestContext.builder()
|
||||
.tenantId(tenantId)
|
||||
.userId(userId)
|
||||
.build();
|
||||
RequestContext.set(requestContext);
|
||||
}
|
||||
return searchMemories0(tenantId, userId, agentId, inputMessage, context, justKeywordSearch);
|
||||
} finally {
|
||||
if (!hasRequestContext) {
|
||||
RequestContext.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<MemoryUnitDTO> searchMemories0(Long tenantId, Long userId, Long agentId, String inputMessage, String context, boolean justKeywordSearch) {
|
||||
try {
|
||||
MemoryQueryExtractDto memoryQueryExtractDto = null;
|
||||
if (!justKeywordSearch) {
|
||||
try {
|
||||
String userPrompt = MEMORY_QUERY_PROMPT.replace("{{user_input}}", inputMessage).replace("{{conversation_context}}", context);
|
||||
memoryQueryExtractDto = iModelRpcService.call("当前时间:" + (new Date()), userPrompt, new ParameterizedTypeReference<MemoryQueryExtractDto>() {
|
||||
});
|
||||
if (memoryQueryExtractDto.getShouldSearch() != null && !memoryQueryExtractDto.getShouldSearch()) {
|
||||
return List.of();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (memoryQueryExtractDto == null) {
|
||||
// 预先加载个人信息,其他预先加载
|
||||
// 生成全文检索关键词
|
||||
memoryQueryExtractDto = new MemoryQueryExtractDto();
|
||||
memoryQueryExtractDto.setCategories(List.of("profile"));
|
||||
List<String> tags = new ArrayList<>();
|
||||
HanLP.segment(inputMessage).forEach(term -> tags.add(term.word));
|
||||
tags.removeIf(tag -> tag.length() < 2);
|
||||
memoryQueryExtractDto.setTags(tags);
|
||||
}
|
||||
MemoryUnitQueryDTO memoryUnitQueryDTO = new MemoryUnitQueryDTO();
|
||||
BeanUtils.copyProperties(memoryQueryExtractDto, memoryUnitQueryDTO);
|
||||
memoryUnitQueryDTO.setUserId(userId);
|
||||
memoryUnitQueryDTO.setTenantId(tenantId);
|
||||
memoryUnitQueryDTO.setJustKeywordSearch(justKeywordSearch);
|
||||
memoryUnitQueryDTO.setAgentId(agentId);
|
||||
return memoryUnitService.queryList(memoryUnitQueryDTO);
|
||||
} catch (Exception e) {
|
||||
log.warn("查询记忆失败, userId {}, inputMessage {}", userId, inputMessage, e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.xspaceagi.memory.app.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.xspaceagi.memory.app.service.MemoryApplicationService;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitService;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryMetaData;
|
||||
import com.xspaceagi.memory.sdk.dto.MemorySaveDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitQueryDTO;
|
||||
import com.xspaceagi.memory.sdk.service.IMemoryRpcService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MemoryRpcServiceImpl implements IMemoryRpcService {
|
||||
|
||||
@Resource
|
||||
private MemoryApplicationService memoryApplicationService;
|
||||
|
||||
@Resource
|
||||
private MemoryUnitService memoryUnitService;
|
||||
|
||||
@Override
|
||||
public void createMemory(MemoryMetaData memoryData) {
|
||||
try {
|
||||
log.debug("createMemory: {}", memoryData);
|
||||
memoryApplicationService.createMemory(memoryData);
|
||||
} catch (Exception e) {
|
||||
log.error("createMemory error: {}", e.getMessage(), e);
|
||||
// 由异常处理接管
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String searchMemoriesMd(Long tenantId, Long userId, Long agentId, String inputMessage, String context, boolean justKeywordSearch, boolean filterSensitive) {
|
||||
log.debug("searchMemoriesMd: {}, {}, {}, {}, {}", tenantId, userId, inputMessage, context, justKeywordSearch);
|
||||
List<MemoryUnitDTO> memoryUnitDTOS = memoryApplicationService.searchMemories(tenantId, userId, agentId, inputMessage, context, justKeywordSearch);
|
||||
return toMd(memoryUnitDTOS, filterSensitive);
|
||||
}
|
||||
|
||||
private String toMd(List<MemoryUnitDTO> memoryUnitDTOS, boolean filterSensitive) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
memoryUnitDTOS.forEach(memoryUnitDTO -> {
|
||||
if (filterSensitive && memoryUnitDTO.getIsSensitive()) {
|
||||
return;
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(memoryUnitDTO.getContentJson());
|
||||
JSONObject keyValues = jsonObject.getJSONObject("keyValues");
|
||||
String category = memoryUnitDTO.getCategory();//md的一级标题
|
||||
String subCategory = memoryUnitDTO.getSubCategory();// md的二级标题
|
||||
sb.append("# ").append(category).append("\n");
|
||||
sb.append("## ").append(subCategory).append("\n");
|
||||
keyValues.forEach((key, value) -> sb.append("- ").append(key).append(": ").append(value).append("\n"));
|
||||
sb.append("- memory_id: ").append(memoryUnitDTO.getId()).append("\n");
|
||||
sb.append("- update_time: ").append(memoryUnitDTO.getModified()).append("\n");
|
||||
sb.append("\n");
|
||||
});
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String queryMemoriesMd(MemoryUnitQueryDTO memoryUnitQueryDTO, boolean filterSensitive) {
|
||||
List<MemoryUnitDTO> memoryUnitDTOS = memoryUnitService.queryList(memoryUnitQueryDTO);
|
||||
return toMd(memoryUnitDTOS, filterSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMemories(Long tenantId, Long userId, Long agentId, MemorySaveDto memorySaveDto) {
|
||||
memoryApplicationService.saveMemories(tenantId, userId, agentId, memorySaveDto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMemories(Long tenantId, Long userId, List<Long> memoryIds) {
|
||||
memoryApplicationService.deleteMemories(tenantId, userId, memoryIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.xspaceagi.memory.app.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitService;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitTagService;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnit;
|
||||
import com.xspaceagi.memory.infra.dao.mapper.MemoryUnitMapper;
|
||||
import com.xspaceagi.memory.sdk.dto.*;
|
||||
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
|
||||
import com.xspaceagi.system.spec.exception.BizException;
|
||||
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 记忆单元服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MemoryUnitServiceImpl extends ServiceImpl<MemoryUnitMapper, MemoryUnit> implements MemoryUnitService {
|
||||
|
||||
@Resource
|
||||
private MemoryUnitTagService memoryUnitTagService;
|
||||
|
||||
@Resource
|
||||
private MemoryUnitMapper memoryUnitMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MemoryUnitDTO create(MemoryUnitCreateDTO createDTO) {
|
||||
log.info("创建记忆单元: {}", createDTO);
|
||||
JSONObject contentJson = new JSONObject();
|
||||
contentJson.put("keyValues", createDTO.getKeyValues());
|
||||
contentJson.put("tags", createDTO.getTags());
|
||||
MemoryUnit entity = MemoryUnit.builder()
|
||||
.tenantId(createDTO.getTenantId()) // 这里可以根据实际业务逻辑获取租户ID
|
||||
.userId(createDTO.getUserId())
|
||||
.agentId(createDTO.getAgentId())
|
||||
.category(createDTO.getCategory())
|
||||
.subCategory(createDTO.getSubCategory())
|
||||
.contentJson(contentJson.toJSONString())
|
||||
.isSensitive(createDTO.getIsSensitive() != null ? createDTO.getIsSensitive() : false)
|
||||
.status("active")
|
||||
.build();
|
||||
save(entity);
|
||||
log.info("记忆单元创建成功: {}", entity.getId());
|
||||
if (CollectionUtils.isNotEmpty(createDTO.getTags())) {
|
||||
createDTO.getTags().forEach(tag -> {
|
||||
MemoryUnitTagCreateDTO memoryUnitTagCreateDTO = new MemoryUnitTagCreateDTO();
|
||||
memoryUnitTagCreateDTO.setUserId(createDTO.getUserId());
|
||||
memoryUnitTagCreateDTO.setMemoryId(entity.getId());
|
||||
memoryUnitTagCreateDTO.setTagName(tag);
|
||||
memoryUnitTagService.create(memoryUnitTagCreateDTO);
|
||||
});
|
||||
}
|
||||
return toDTO(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MemoryUnitDTO update(MemoryUnitUpdateDTO updateDTO) {
|
||||
log.info("更新记忆单元: {}", updateDTO);
|
||||
|
||||
MemoryUnit entity = super.getById(updateDTO.getId());
|
||||
if (entity == null) {
|
||||
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.memoryUnitNotFound, updateDTO.getId());
|
||||
}
|
||||
|
||||
if (updateDTO.getCategory() != null) {
|
||||
entity.setCategory(updateDTO.getCategory());
|
||||
}
|
||||
if (updateDTO.getSubCategory() != null) {
|
||||
entity.setSubCategory(updateDTO.getSubCategory());
|
||||
}
|
||||
if (updateDTO.getContentJson() != null) {
|
||||
entity.setContentJson(updateDTO.getContentJson());
|
||||
}
|
||||
if (updateDTO.getIsSensitive() != null) {
|
||||
entity.setIsSensitive(updateDTO.getIsSensitive());
|
||||
}
|
||||
if (updateDTO.getStatus() != null) {
|
||||
entity.setStatus(updateDTO.getStatus());
|
||||
}
|
||||
entity.setModified(new Date());
|
||||
updateById(entity);
|
||||
saveTagRelation(updateDTO.getUserId(), entity.getId(), updateDTO.getTags());
|
||||
|
||||
log.info("记忆单元更新成功: {}", entity.getId());
|
||||
return toDTO(entity);
|
||||
}
|
||||
|
||||
private void saveTagRelation(Long userId, Long memoryId, List<String> tags) {
|
||||
if (CollectionUtils.isNotEmpty(tags)) {
|
||||
tags.forEach(tag -> {
|
||||
MemoryUnitTagCreateDTO memoryUnitTagCreateDTO = new MemoryUnitTagCreateDTO();
|
||||
memoryUnitTagCreateDTO.setUserId(userId);
|
||||
memoryUnitTagCreateDTO.setMemoryId(memoryId);
|
||||
memoryUnitTagCreateDTO.setTagName(tag);
|
||||
try {
|
||||
memoryUnitTagService.create(memoryUnitTagCreateDTO);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryUnitDTO getById(Long id) {
|
||||
MemoryUnit entity = super.getById(id);
|
||||
return entity != null ? toDTO(entity) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> queryList(MemoryUnitQueryDTO queryDTO) {
|
||||
log.info("查询记忆单元列表: {}", queryDTO);
|
||||
List<MemoryUnit> entities;
|
||||
List<Date> timeRange = null;
|
||||
boolean timeRangeQuery = false;
|
||||
try {
|
||||
timeRange = parseTimeRange(queryDTO);
|
||||
timeRangeQuery = timeRange.size() == 2;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
LambdaQueryWrapper<MemoryUnit> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(MemoryUnit::getStatus, "active");
|
||||
queryWrapper.eq(MemoryUnit::getTenantId, queryDTO.getTenantId());
|
||||
queryWrapper.eq(MemoryUnit::getUserId, queryDTO.getUserId());
|
||||
queryWrapper.eq(MemoryUnit::getAgentId, queryDTO.getAgentId());
|
||||
queryWrapper.in(CollectionUtils.isNotEmpty(queryDTO.getCategories()), MemoryUnit::getCategory, queryDTO.getCategories());
|
||||
queryWrapper.in(CollectionUtils.isNotEmpty(queryDTO.getSubCategories()), MemoryUnit::getSubCategory, queryDTO.getSubCategories());
|
||||
queryWrapper.orderByDesc(MemoryUnit::getId);
|
||||
if (timeRangeQuery) {
|
||||
queryWrapper.between(MemoryUnit::getModified, timeRange.get(0), timeRange.get(1));
|
||||
}
|
||||
queryWrapper.last("LIMIT " + (queryDTO.getLimit() == null ? 10 : queryDTO.getLimit()));
|
||||
entities = list(queryWrapper);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(queryDTO.getTags())) {
|
||||
List<MemoryUnit> memoryUnits = memoryUnitMapper.selectWithJoinTags(queryDTO.getUserId(), queryDTO.getAgentId(), queryDTO.getTags(), timeRangeQuery ? timeRange.get(0) : null, timeRangeQuery ? timeRange.get(1) : null, 0L, 10L);
|
||||
// 移除entries中id已包含的
|
||||
memoryUnits.removeIf(memoryUnit -> entities.stream().anyMatch(entity -> entity.getId().equals(memoryUnit.getId())));
|
||||
entities.addAll(memoryUnits);
|
||||
}
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Date> parseTimeRange(MemoryUnitQueryDTO queryDTO) {
|
||||
//查询范围,可选值为:today|yesterday|recent|long-term|all
|
||||
String queryType = queryDTO.getQueryType();
|
||||
//时间范围(非必填),格式为:2023-01-01 00:00:00-2023-01-01 23:59:59
|
||||
//有可能为空
|
||||
String timeRange = queryDTO.getTimeRange();
|
||||
|
||||
List<Date> dates = new ArrayList<>();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
try {
|
||||
// 优先使用自定义时间范围
|
||||
if (StringUtils.isNotBlank(timeRange)) {
|
||||
String[] times = timeRange.split("-");
|
||||
if (times.length == 2) {
|
||||
Date startTime = sdf.parse(times[0]);
|
||||
Date endTime = sdf.parse(times[1]);
|
||||
dates.add(startTime);
|
||||
dates.add(endTime);
|
||||
return dates;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据查询类型计算时间范围
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
Date startTime, endTime;
|
||||
|
||||
switch (queryType) {
|
||||
case "today":
|
||||
// 今天 00:00:00 - 当前时间
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
startTime = calendar.getTime();
|
||||
endTime = new Date();
|
||||
break;
|
||||
|
||||
case "yesterday":
|
||||
// 昨天 00:00:00 - 23:59:59
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
startTime = calendar.getTime();
|
||||
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 23);
|
||||
calendar.set(Calendar.MINUTE, 59);
|
||||
calendar.set(Calendar.SECOND, 59);
|
||||
calendar.set(Calendar.MILLISECOND, 999);
|
||||
endTime = calendar.getTime();
|
||||
break;
|
||||
|
||||
case "recent":
|
||||
// 最近7天
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -7);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
startTime = calendar.getTime();
|
||||
endTime = new Date();
|
||||
break;
|
||||
|
||||
case "long-term":
|
||||
// 最近30天
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -30);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
startTime = calendar.getTime();
|
||||
endTime = new Date();
|
||||
break;
|
||||
|
||||
case "all":
|
||||
// 全部数据,返回空列表
|
||||
return new ArrayList<>();
|
||||
|
||||
default:
|
||||
// 默认返回最近7天
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -7);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
startTime = calendar.getTime();
|
||||
endTime = new Date();
|
||||
break;
|
||||
}
|
||||
|
||||
dates.add(startTime);
|
||||
dates.add(endTime);
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException("时间格式解析失败,正确格式为:yyyy-MM-dd HH:mm:ss-yyyy-MM-dd HH:mm:ss", e);
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> findByUserIdAndCategory(Long userId, Long agentId, String category, String subCategory) {
|
||||
List<MemoryUnit> entities = baseMapper.findByUserIdAndCategory(userId, agentId, category, subCategory);
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> findByAgentId(Long agentId) {
|
||||
List<MemoryUnit> entities = baseMapper.findByAgentId(agentId);
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> findByCategoryAndJsonKeyValue(Long tenantId, Long userId, String category, String subCategory, String jsonKey, String jsonValue) {
|
||||
log.info("根据分类和JSON字段值查询: tenantId={}, userId={}, category={}, subCategory={}, jsonKey={}, jsonValue={}",
|
||||
tenantId, userId, category, subCategory, jsonKey, jsonValue);
|
||||
|
||||
List<MemoryUnit> entities = baseMapper.findByCategoryAndJsonKeyValue(tenantId, userId, category, subCategory, jsonKey, jsonValue);
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> findByCategoryAndJsonKeyValueLike(Long tenantId, Long userId, String category, String subCategory, String jsonKey, String jsonValue) {
|
||||
log.info("根据分类和JSON字段值查询(模糊匹配): tenantId={}, userId={}, category={}, subCategory={}, jsonKey={}, jsonValue={}",
|
||||
tenantId, userId, category, subCategory, jsonKey, jsonValue);
|
||||
|
||||
List<MemoryUnit> entities = baseMapper.findByCategoryAndJsonKeyValueLike(tenantId, userId, category, subCategory, jsonKey, jsonValue);
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitDTO> findByCategoryAndJsonKeyValues(Long tenantId, Long userId, String category, String subCategory, Map<String, String> jsonKeyValues) {
|
||||
log.info("根据分类和JSON多字段值查询: tenantId={}, userId={}, category={}, subCategory={}, jsonKeyValues={}",
|
||||
tenantId, userId, category, subCategory, jsonKeyValues);
|
||||
|
||||
List<MemoryUnit> entities = baseMapper.findByCategoryAndJsonKeyValues(tenantId, userId, category, subCategory, jsonKeyValues);
|
||||
return entities.stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryUnitDTO toDTO(MemoryUnit entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MemoryUnitDTO.builder()
|
||||
.id(entity.getId())
|
||||
.tenantId(entity.getTenantId())
|
||||
.userId(entity.getUserId())
|
||||
.agentId(entity.getAgentId())
|
||||
.category(entity.getCategory())
|
||||
.subCategory(entity.getSubCategory())
|
||||
.contentJson(entity.getContentJson())
|
||||
.isSensitive(entity.getIsSensitive())
|
||||
.status(entity.getStatus())
|
||||
.created(entity.getCreated())
|
||||
.modified(entity.getModified())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.xspaceagi.memory.app.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xspaceagi.memory.app.service.MemoryUnitTagService;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag;
|
||||
import com.xspaceagi.memory.infra.dao.mapper.MemoryUnitTagMapper;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitTagCreateDTO;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitTagDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 记忆单元标签服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MemoryUnitTagServiceImpl extends ServiceImpl<MemoryUnitTagMapper, MemoryUnitTag> implements MemoryUnitTagService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MemoryUnitTagDTO create(MemoryUnitTagCreateDTO createDTO) {
|
||||
log.info("创建记忆标签: {}", createDTO);
|
||||
|
||||
MemoryUnitTag entity = MemoryUnitTag.builder()
|
||||
.tenantId(createDTO.getUserId()) // 这里可以根据实际业务逻辑获取租户ID
|
||||
.userId(createDTO.getUserId())
|
||||
.memoryId(createDTO.getMemoryId())
|
||||
.tagName(createDTO.getTagName())
|
||||
.created(new Date())
|
||||
.build();
|
||||
|
||||
save(entity);
|
||||
log.info("记忆标签创建成功: {}", entity.getId());
|
||||
return toDTO(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MemoryUnitTagDTO> searchByTagNames(Long userId, List<String> tags) {
|
||||
if (tags == null || tags.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<MemoryUnitTag> list = list(new QueryWrapper<MemoryUnitTag>().eq("user_id", userId).in("tag_name", tags));
|
||||
return list.stream().map(this::toDTO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryUnitTagDTO toDTO(MemoryUnitTag entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MemoryUnitTagDTO.builder()
|
||||
.id(entity.getId())
|
||||
.tenantId(entity.getTenantId())
|
||||
.userId(entity.getUserId())
|
||||
.memoryId(entity.getMemoryId())
|
||||
.tagName(entity.getTagName())
|
||||
.created(entity.getCreated())
|
||||
.modified(entity.getModified())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-domain</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-spec</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 其他原有依赖保持不变 -->
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-sdk</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.xspaceagi.memory.domain.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 记忆领域模型
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Memory {
|
||||
|
||||
/**
|
||||
* 记忆ID
|
||||
*/
|
||||
private String memoryId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 记忆类型 (short_term, long_term, working)
|
||||
*/
|
||||
private String memoryType;
|
||||
|
||||
/**
|
||||
* 记忆内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 扩展元数据
|
||||
*/
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 记忆状态 (active, archived, deleted)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 重要性评分 (0.0-1.0)
|
||||
*/
|
||||
private Double importanceScore;
|
||||
|
||||
/**
|
||||
* 访问次数
|
||||
*/
|
||||
private Integer accessCount;
|
||||
|
||||
/**
|
||||
* 最后访问时间
|
||||
*/
|
||||
private LocalDateTime lastAccessTime;
|
||||
|
||||
/**
|
||||
* 标签列表
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 检查记忆是否过期
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
return expireTime != null && LocalDateTime.now().isAfter(expireTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查记忆是否活跃
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return "active".equals(status) && !isExpired();
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加访问次数
|
||||
*/
|
||||
public void incrementAccessCount() {
|
||||
this.accessCount = (this.accessCount == null ? 0 : this.accessCount) + 1;
|
||||
this.lastAccessTime = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.xspaceagi.memory.domain.repository;
|
||||
|
||||
import com.xspaceagi.memory.domain.model.Memory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 记忆仓储接口
|
||||
*/
|
||||
public interface MemoryRepository {
|
||||
|
||||
/**
|
||||
* 保存记忆
|
||||
*/
|
||||
Memory save(Memory memory);
|
||||
|
||||
/**
|
||||
* 根据ID查找记忆
|
||||
*/
|
||||
Optional<Memory> findById(String memoryId);
|
||||
|
||||
/**
|
||||
* 根据用户ID和记忆类型查找记忆列表
|
||||
*/
|
||||
List<Memory> findByUserIdAndType(String userId, String memoryType);
|
||||
|
||||
/**
|
||||
* 根据会话ID查找记忆列表
|
||||
*/
|
||||
List<Memory> findBySessionId(String sessionId);
|
||||
|
||||
/**
|
||||
* 查找过期的记忆
|
||||
*/
|
||||
List<Memory> findExpiredMemories(LocalDateTime currentTime);
|
||||
|
||||
/**
|
||||
* 根据重要性分数查找记忆
|
||||
*/
|
||||
List<Memory> findByImportanceScoreGreaterThan(Double minScore);
|
||||
|
||||
/**
|
||||
* 删除记忆
|
||||
*/
|
||||
void deleteById(String memoryId);
|
||||
|
||||
/**
|
||||
* 批量删除记忆
|
||||
*/
|
||||
void batchDelete(List<String> memoryIds);
|
||||
|
||||
/**
|
||||
* 更新记忆状态
|
||||
*/
|
||||
void updateStatus(String memoryId, String status);
|
||||
|
||||
/**
|
||||
* 统计用户记忆数量
|
||||
*/
|
||||
long countByUserIdAndType(String userId, String memoryType);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-infra</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-domain</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>system-sdk</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.xspaceagi.memory.infra.dao.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 记忆单元实体
|
||||
* @TableName memory_unit
|
||||
*/
|
||||
@TableName(value = "memory_unit")
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class MemoryUnit {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@TableField(value = "_tenant_id")
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 代理ID
|
||||
*/
|
||||
private Long agentId;
|
||||
|
||||
/**
|
||||
* 一级分类
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 二级分类
|
||||
*/
|
||||
private String subCategory;
|
||||
|
||||
/**
|
||||
* 内容JSON
|
||||
*/
|
||||
private String contentJson;
|
||||
|
||||
/**
|
||||
* 是否敏感信息(0:否 1:是)
|
||||
*/
|
||||
private Boolean isSensitive;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date created;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date modified;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.xspaceagi.memory.infra.dao.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 记忆单元标签实体
|
||||
* @TableName memory_unit_tag
|
||||
*/
|
||||
@TableName(value = "memory_unit_tag")
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class MemoryUnitTag {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@TableField(value = "_tenant_id")
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 记忆ID
|
||||
*/
|
||||
private Long memoryId;
|
||||
|
||||
/**
|
||||
* 标签名称
|
||||
*/
|
||||
private String tagName;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date created;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date modified;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.xspaceagi.memory.infra.dao.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnit;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 记忆单元Mapper
|
||||
*
|
||||
* @description 针对表【memory_unit(记忆单元表)】的数据库操作Mapper
|
||||
* @createDate 2026-03-06
|
||||
* @Entity com.xspaceagi.memory.infra.dao.entity.MemoryUnit
|
||||
*/
|
||||
public interface MemoryUnitMapper extends BaseMapper<MemoryUnit> {
|
||||
|
||||
/**
|
||||
* 总条数查询
|
||||
*
|
||||
* @param queryMap 筛选条件
|
||||
* @return 总条数
|
||||
*/
|
||||
Long queryTotal(@Param("queryMap") Map<String, Object> queryMap);
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
*
|
||||
* @param queryMap 筛选条件
|
||||
* @param orderColumns 排序
|
||||
* @param startIndex 索引开始位置
|
||||
* @param pageSize 业务大小
|
||||
* @return 列表
|
||||
*/
|
||||
List<MemoryUnit> queryList(@Param("queryMap") Map<String, Object> queryMap,
|
||||
@Param("orderColumns") List<OrderItem> orderColumns,
|
||||
@Param("startIndex") Long startIndex, @Param("pageSize") Long pageSize);
|
||||
|
||||
/**
|
||||
* 根据用户ID和分类查询记忆单元
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> findByUserIdAndCategory(@Param("userId") Long userId, @Param("agentId") Long agentId, @Param("category") String category, @Param("subCategory") String subCategory);
|
||||
|
||||
/**
|
||||
* 根据代理ID查询记忆单元
|
||||
*
|
||||
* @param agentId 代理ID
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> findByAgentId(@Param("agentId") Long agentId);
|
||||
|
||||
/**
|
||||
* 根据分类和JSON字段值查询记忆单元(精确匹配)
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类
|
||||
* @param jsonKey JSON中的键
|
||||
* @param jsonValue JSON中的值
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> findByCategoryAndJsonKeyValue(
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("userId") Long userId,
|
||||
@Param("category") String category,
|
||||
@Param("subCategory") String subCategory,
|
||||
@Param("jsonKey") String jsonKey,
|
||||
@Param("jsonValue") String jsonValue
|
||||
);
|
||||
|
||||
/**
|
||||
* 根据分类和JSON字段值查询记忆单元(模糊匹配)
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类
|
||||
* @param jsonKey JSON中的键
|
||||
* @param jsonValue JSON中的值
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> findByCategoryAndJsonKeyValueLike(
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("userId") Long userId,
|
||||
@Param("category") String category,
|
||||
@Param("subCategory") String subCategory,
|
||||
@Param("jsonKey") String jsonKey,
|
||||
@Param("jsonValue") String jsonValue
|
||||
);
|
||||
|
||||
/**
|
||||
* 根据分类和JSON多个字段值查询记忆单元
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param userId 用户ID
|
||||
* @param category 一级分类
|
||||
* @param subCategory 二级分类
|
||||
* @param jsonKeyValues JSON键值对 Map<key, value>
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> findByCategoryAndJsonKeyValues(
|
||||
@Param("tenantId") Long tenantId,
|
||||
@Param("userId") Long userId,
|
||||
@Param("category") String category,
|
||||
@Param("subCategory") String subCategory,
|
||||
@Param("jsonKeyValues") Map<String, String> jsonKeyValues
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* 根据用户ID、代理ID和标签查询记忆单元
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param agentId 代理ID
|
||||
* @param tags 标签列表
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param startIndex 索引开始位置
|
||||
* @param pageSize 业务大小
|
||||
* @return 记忆单元列表
|
||||
*/
|
||||
List<MemoryUnit> selectWithJoinTags(@Param("userId") Long userId,
|
||||
@Param("agentId") Long agentId,
|
||||
@Param("tags") List<String> tags,
|
||||
@Param("startTime") Date startTime,
|
||||
@Param("endTime") Date endTime,
|
||||
@Param("startIndex") Long startIndex,
|
||||
@Param("pageSize") Long pageSize);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.xspaceagi.memory.infra.dao.mapper;
|
||||
|
||||
import com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* 记忆单元标签Mapper
|
||||
* @description 针对表【memory_unit_tag(记忆单元标签表)】的数据库操作Mapper
|
||||
* @createDate 2026-03-06
|
||||
* @Entity com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag
|
||||
*/
|
||||
public interface MemoryUnitTagMapper extends BaseMapper<MemoryUnitTag> {
|
||||
|
||||
/**
|
||||
* 根据记忆ID查询标签列表
|
||||
*
|
||||
* @param memoryId 记忆ID
|
||||
* @return 标签列表
|
||||
*/
|
||||
List<MemoryUnitTag> findByMemoryId(@Param("memoryId") Long memoryId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查询标签列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 标签列表
|
||||
*/
|
||||
List<MemoryUnitTag> findByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据标签名称查询标签列表
|
||||
*
|
||||
* @param tagName 标签名称
|
||||
* @return 标签列表
|
||||
*/
|
||||
List<MemoryUnitTag> findByTagName(@Param("tagName") String tagName);
|
||||
|
||||
/**
|
||||
* 根据记忆ID删除标签
|
||||
*
|
||||
* @param memoryId 记忆ID
|
||||
* @return 删除数量
|
||||
*/
|
||||
int deleteByMemoryId(@Param("memoryId") Long memoryId);
|
||||
|
||||
/**
|
||||
* 批量插入标签
|
||||
*
|
||||
* @param tags 标签列表
|
||||
* @return 插入数量
|
||||
*/
|
||||
int batchInsert(@Param("tags") List<MemoryUnitTag> tags);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.xspaceagi.memory.infra.dao.mapper.MemoryUnitMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.xspaceagi.memory.infra.dao.entity.MemoryUnit">
|
||||
<id property="id" column="id"/>
|
||||
<result property="tenantId" column="_tenant_id"/>
|
||||
<result property="userId" column="user_id"/>
|
||||
<result property="agentId" column="agent_id"/>
|
||||
<result property="category" column="category"/>
|
||||
<result property="subCategory" column="sub_category"/>
|
||||
<result property="contentJson" column="content_json"/>
|
||||
<result property="isSensitive" column="is_sensitive"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="created" column="created"/>
|
||||
<result property="modified" column="modified"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询条件 -->
|
||||
<sql id="base_where">
|
||||
<if test="queryMap.tenantId != null">
|
||||
and _tenant_id = #{queryMap.tenantId}
|
||||
</if>
|
||||
<if test="queryMap.userId != null">
|
||||
and user_id = #{queryMap.userId}
|
||||
</if>
|
||||
<if test="queryMap.agentId != null">
|
||||
and agent_id = #{queryMap.agentId}
|
||||
</if>
|
||||
<if test="queryMap.category != null and queryMap.category != ''">
|
||||
and category = #{queryMap.category}
|
||||
</if>
|
||||
<if test="queryMap.subCategory != null and queryMap.subCategory != ''">
|
||||
and sub_category = #{queryMap.subCategory}
|
||||
</if>
|
||||
<if test="queryMap.isSensitive != null">
|
||||
and is_sensitive = #{queryMap.isSensitive}
|
||||
</if>
|
||||
<if test="queryMap.status != null and queryMap.status != ''">
|
||||
and status = #{queryMap.status}
|
||||
</if>
|
||||
<if test="queryMap.keyword != null and queryMap.keyword != ''">
|
||||
and (category like concat('%',#{queryMap.keyword},'%')
|
||||
or sub_category like concat('%',#{queryMap.keyword},'%')
|
||||
or content_json like concat('%',#{queryMap.keyword},'%'))
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<!-- 总条数查询 -->
|
||||
<select id="queryTotal" resultType="java.lang.Long">
|
||||
select count(*)
|
||||
from memory_unit
|
||||
<where>
|
||||
<include refid="base_where"></include>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- 列表查询 -->
|
||||
<select id="queryList" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
<where>
|
||||
<include refid="base_where"></include>
|
||||
</where>
|
||||
<choose>
|
||||
<when test="orderColumns != null and orderColumns.size > 0">
|
||||
<foreach collection="orderColumns" item="entity" index="index" separator="," open="order by " close="">
|
||||
<if test="entity != null">
|
||||
${entity.column}
|
||||
<if test="!entity.asc">
|
||||
desc
|
||||
</if>
|
||||
</if>
|
||||
</foreach>
|
||||
</when>
|
||||
<otherwise>
|
||||
order by created desc
|
||||
</otherwise>
|
||||
</choose>
|
||||
limit #{startIndex}, #{pageSize}
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID和分类查询 -->
|
||||
<select id="findByUserIdAndCategory" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
where user_id = #{userId}
|
||||
<if test="agentId != null">
|
||||
and agent_id = #{agentId}
|
||||
</if>
|
||||
and category = #{category}
|
||||
and sub_category = #{subCategory}
|
||||
and status = 'active'
|
||||
order by created desc limit 50
|
||||
</select>
|
||||
|
||||
<!-- 根据代理ID查询 -->
|
||||
<select id="findByAgentId" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
where agent_id = #{agentId}
|
||||
and status = 'active'
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据分类和JSON字段值查询(精确匹配) -->
|
||||
<select id="findByCategoryAndJsonKeyValue" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
where _tenant_id = #{tenantId}
|
||||
and user_id = #{userId}
|
||||
and category = #{category}
|
||||
<if test="subCategory != null and subCategory != ''">
|
||||
and sub_category = #{subCategory}
|
||||
</if>
|
||||
and JSON_CONTAINS(content_json, JSON_QUOTE(#{jsonValue}), '$.${jsonKey}')
|
||||
and status = 'active'
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据分类和JSON字段值查询(模糊匹配) -->
|
||||
<select id="findByCategoryAndJsonKeyValueLike" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
where _tenant_id = #{tenantId}
|
||||
and user_id = #{userId}
|
||||
and category = #{category}
|
||||
<if test="subCategory != null and subCategory != ''">
|
||||
and sub_category = #{subCategory}
|
||||
</if>
|
||||
and JSON_EXTRACT(content_json, '$.${jsonKey}') LIKE CONCAT('%', #{jsonValue}, '%')
|
||||
and status = 'active'
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据分类和JSON多个字段值查询 -->
|
||||
<select id="findByCategoryAndJsonKeyValues" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit
|
||||
where _tenant_id = #{tenantId}
|
||||
and user_id = #{userId}
|
||||
and category = #{category}
|
||||
<if test="subCategory != null and subCategory != ''">
|
||||
and sub_category = #{subCategory}
|
||||
</if>
|
||||
<if test="jsonKeyValues != null and jsonKeyValues.size > 0">
|
||||
and
|
||||
<trim prefix="" prefixOverrides="and">
|
||||
<foreach collection="jsonKeyValues" item="value" index="key" separator=" and ">
|
||||
JSON_CONTAINS(content_json, JSON_QUOTE(#{value}), '$.keyValues.${key}')
|
||||
</foreach>
|
||||
</trim>
|
||||
</if>
|
||||
and status = 'active'
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID、代理ID和标签查询记忆单元 -->
|
||||
<select id="selectWithJoinTags" resultMap="BaseResultMap">
|
||||
select distinct mu.*
|
||||
from memory_unit mu
|
||||
inner join memory_unit_tag mut on mu.id = mut.memory_id
|
||||
<where>
|
||||
<if test="userId != null">
|
||||
and mu.user_id = #{userId}
|
||||
</if>
|
||||
<if test="agentId != null">
|
||||
and mu.agent_id = #{agentId}
|
||||
</if>
|
||||
<if test="tags != null and tags.size > 0">
|
||||
and mut.tag_name in
|
||||
<foreach collection="tags" item="tag" open="(" separator="," close=")">
|
||||
#{tag}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="startTime != null">
|
||||
and mu.created >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
and mu.created <= #{endTime}
|
||||
</if>
|
||||
and mu.status = 'active'
|
||||
</where>
|
||||
order by mu.created desc
|
||||
<if test="startIndex != null and pageSize != null">
|
||||
limit #{startIndex}, #{pageSize}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.xspaceagi.memory.infra.dao.mapper.MemoryUnitTagMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.xspaceagi.memory.infra.dao.entity.MemoryUnitTag">
|
||||
<id property="id" column="id"/>
|
||||
<result property="tenantId" column="_tenant_id"/>
|
||||
<result property="userId" column="user_id"/>
|
||||
<result property="memoryId" column="memory_id"/>
|
||||
<result property="tagName" column="tag_name"/>
|
||||
<result property="created" column="created"/>
|
||||
<result property="modified" column="modified"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据记忆ID查询标签列表 -->
|
||||
<select id="findByMemoryId" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit_tag
|
||||
where memory_id = #{memoryId}
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据用户ID查询标签列表 -->
|
||||
<select id="findByUserId" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit_tag
|
||||
where user_id = #{userId}
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据标签名称查询标签列表 -->
|
||||
<select id="findByTagName" resultMap="BaseResultMap">
|
||||
select *
|
||||
from memory_unit_tag
|
||||
where tag_name = #{tagName}
|
||||
order by created desc
|
||||
</select>
|
||||
|
||||
<!-- 根据记忆ID删除标签 -->
|
||||
<delete id="deleteByMemoryId">
|
||||
delete from memory_unit_tag
|
||||
where memory_id = #{memoryId}
|
||||
</delete>
|
||||
|
||||
<!-- 批量插入标签 -->
|
||||
<insert id="batchInsert">
|
||||
insert into memory_unit_tag (_tenant_id, user_id, memory_id, tag_name, created)
|
||||
values
|
||||
<foreach collection="tags" item="tag" separator=",">
|
||||
(#{tag.tenantId}, #{tag.userId}, #{tag.memoryId}, #{tag.tagName}, #{tag.created})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,153 @@
|
||||
# 记忆管理模块 - 数据字典
|
||||
|
||||
## 表1: memory_unit (记忆单元表)
|
||||
|
||||
### 表说明
|
||||
用于存储AI Agent的记忆单元数据,支持多种类型的记忆分类和管理。
|
||||
|
||||
### 字段说明
|
||||
|
||||
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| id | BIGINT(20) | 是 | AUTO_INCREMENT | 主键ID,自增 |
|
||||
| _tenant_id | BIGINT(20) | 是 | - | 租户ID,多租户隔离标识 |
|
||||
| user_id | BIGINT(20) | 是 | - | 用户ID,记忆所属用户 |
|
||||
| agent_id | BIGINT(20) | 否 | NULL | 代理ID,关联的AI代理 |
|
||||
| category | VARCHAR(50) | 是 | - | 一级分类,如:工作、学习、生活等 |
|
||||
| sub_category | VARCHAR(100) | 否 | NULL | 二级分类,更细粒度的分类 |
|
||||
| content_json | JSON | 否 | NULL | 记忆内容,JSON格式存储结构化数据 |
|
||||
| is_sensitive | TINYINT(1) | 是 | 0 | 是否敏感信息:0-否,1-是 |
|
||||
| status | ENUM | 是 | 'active' | 状态:active-活跃,archived-已归档,deleted-已删除 |
|
||||
| created | DATETIME | 是 | CURRENT_TIMESTAMP | 创建时间 |
|
||||
| modified | DATETIME | 是 | CURRENT_TIMESTAMP ON UPDATE | 修改时间 |
|
||||
|
||||
### 索引说明
|
||||
|
||||
| 索引名 | 字段 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| PRIMARY | id | PRIMARY | 主键索引 |
|
||||
| idx_tenant_user | _tenant_id, user_id | INDEX | 租户用户联合索引,支持多租户查询 |
|
||||
| idx_agent | agent_id | INDEX | 代理ID索引,支持按代理查询 |
|
||||
| idx_category | category, sub_category | INDEX | 分类联合索引,支持分类查询 |
|
||||
| idx_status | status | INDEX | 状态索引,支持状态筛选 |
|
||||
| idx_created | created | INDEX | 创建时间索引,支持时间范围查询 |
|
||||
|
||||
### 字段约束
|
||||
|
||||
- `status` 字段只能为:'active', 'archived', 'deleted'
|
||||
- `is_sensitive` 只能为 0 或 1
|
||||
- `_tenant_id` 和 `user_id` 必须有值,用于数据隔离
|
||||
|
||||
### 使用场景
|
||||
|
||||
1. **短期记忆**: 临时存储对话上下文
|
||||
2. **长期记忆**: 持久化存储重要信息
|
||||
3. **工作记忆**: 当前任务相关的活跃记忆
|
||||
4. **敏感信息**: 标记需要特殊保护的数据
|
||||
|
||||
---
|
||||
|
||||
## 表2: memory_unit_tag (记忆单元标签表)
|
||||
|
||||
### 表说明
|
||||
用于为记忆单元添加标签,支持灵活的分类和检索。
|
||||
|
||||
### 字段说明
|
||||
|
||||
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| id | BIGINT(20) | 是 | AUTO_INCREMENT | 主键ID,自增 |
|
||||
| _tenant_id | BIGINT(20) | 是 | - | 租户ID,多租户隔离标识 |
|
||||
| user_id | BIGINT(20) | 是 | - | 用户ID,标签创建者 |
|
||||
| memory_id | BIGINT(20) | 是 | - | 记忆ID,关联到memory_unit表 |
|
||||
| tag_name | VARCHAR(100) | 是 | - | 标签名称 |
|
||||
| created | DATETIME | 是 | CURRENT_TIMESTAMP | 创建时间 |
|
||||
| modified | DATETIME | 是 | CURRENT_TIMESTAMP ON UPDATE | 修改时间 |
|
||||
|
||||
### 索引说明
|
||||
|
||||
| 索引名 | 字段 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| PRIMARY | id | PRIMARY | 主键索引 |
|
||||
| idx_tenant_user | _tenant_id, user_id | INDEX | 租户用户联合索引 |
|
||||
| idx_memory | memory_id | INDEX | 记忆ID索引,支持按记忆查询标签 |
|
||||
| idx_tag | tag_name | INDEX | 标签名称索引,支持按标签查询 |
|
||||
|
||||
### 外键约束
|
||||
|
||||
| 约束名 | 字段 | 关联表 | 关联字段 | 级联操作 |
|
||||
|--------|------|--------|----------|----------|
|
||||
| fk_memory_tag | memory_id | memory_unit | id | ON DELETE CASCADE |
|
||||
|
||||
### 级联删除说明
|
||||
当删除 memory_unit 表中的记录时,会自动删除 memory_unit_tag 表中对应的标签记录。
|
||||
|
||||
---
|
||||
|
||||
## 表关系
|
||||
|
||||
```
|
||||
memory_unit (1) ──────< (N) memory_unit_tag
|
||||
一对多关系
|
||||
一个记忆可以有多个标签
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 业务规则
|
||||
|
||||
### 1. 数据隔离
|
||||
- 所有数据按 `_tenant_id` 和 `user_id` 进行隔离
|
||||
- 查询时必须包含租户和用户条件
|
||||
|
||||
### 2. 状态管理
|
||||
- 新创建的记忆默认状态为 'active'
|
||||
- 删除操作采用软删除,将状态设置为 'deleted'
|
||||
- 过期记忆应归档为 'archived'
|
||||
|
||||
### 3. 标签规则
|
||||
- 一个记忆可以有多个标签
|
||||
- 标签名称不区分大小写(建议在应用层处理)
|
||||
- 重复标签由应用层控制
|
||||
|
||||
### 4. 敏感信息处理
|
||||
- 标记为敏感的信息需要特殊处理
|
||||
- 敏感信息不应在日志中显示完整内容
|
||||
|
||||
---
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 分区策略
|
||||
对于大数据量场景,建议按时间或租户进行分区:
|
||||
```sql
|
||||
-- 按创建时间分区(可选)
|
||||
ALTER TABLE memory_unit PARTITION BY RANGE (TO_DAYS(created)) (
|
||||
PARTITION p_old VALUES LESS THAN (TO_DAYS('2026-01-01')),
|
||||
PARTITION p_current VALUES LESS THAN (MAXVALUE)
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 查询优化
|
||||
- 使用覆盖索引减少回表
|
||||
- 避免SELECT *,只查询需要的字段
|
||||
- 合理使用LIMIT分页
|
||||
|
||||
### 3. 数据清理
|
||||
- 定期清理已删除超过30天的数据
|
||||
- 清理孤立的标签数据
|
||||
- 定期执行ANALYZE TABLE和OPTIMIZE TABLE
|
||||
|
||||
---
|
||||
|
||||
## 数据迁移注意事项
|
||||
|
||||
1. **字符集**: 使用 utf8mb4 以支持emoji等特殊字符
|
||||
2. **时区**: 确保应用服务器和数据库时区一致
|
||||
3. **JSON字段**: MySQL 5.7+ 支持JSON类型,低版本需要使用TEXT
|
||||
4. **外键**: 如需禁用外键检查(批量导入时):
|
||||
```sql
|
||||
SET FOREIGN_KEY_CHECKS=0;
|
||||
-- 执行导入操作
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
```
|
||||
@@ -0,0 +1,243 @@
|
||||
# 记忆管理模块 - MySQL表创建脚本使用说明
|
||||
|
||||
## 📁 文件说明
|
||||
|
||||
### 1. `memory_schema.sql` - 完整版脚本
|
||||
**包含内容:**
|
||||
- 完整的表创建语句
|
||||
- 示例数据插入
|
||||
- 常用查询示例
|
||||
- 数据清理和维护脚本
|
||||
- 性能优化建议
|
||||
|
||||
**适用场景:** 开发环境搭建、功能测试、学习参考
|
||||
|
||||
### 2. `memory_tables_simple.sql` - 简化版脚本
|
||||
**包含内容:**
|
||||
- 纯净的表创建语句
|
||||
- 必要的索引和约束
|
||||
|
||||
**适用场景:** 生产环境部署、快速部署
|
||||
|
||||
### 3. `verify_tables.sql` - 验证脚本
|
||||
**包含内容:**
|
||||
- 表结构检查
|
||||
- 索引验证
|
||||
- 外键约束检查
|
||||
- 数据完整性检查
|
||||
|
||||
**适用场景:** 部署后验证、故障排查
|
||||
|
||||
### 4. `DATA_DICTIONARY.md` - 数据字典
|
||||
**包含内容:**
|
||||
- 详细的字段说明
|
||||
- 索引说明
|
||||
- 业务规则
|
||||
- 性能优化建议
|
||||
|
||||
**适用场景:** 开发参考、文档维护
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用步骤
|
||||
|
||||
### 步骤1: 选择合适的脚本
|
||||
```bash
|
||||
# 开发环境(推荐)
|
||||
mysql -u root -p < memory_schema.sql
|
||||
|
||||
# 生产环境
|
||||
mysql -u root -p < memory_tables_simple.sql
|
||||
```
|
||||
|
||||
### 步骤2: 验证表创建
|
||||
```bash
|
||||
mysql -u root -p your_database < verify_tables.sql
|
||||
```
|
||||
|
||||
### 步骤3: 检查结果
|
||||
登录MySQL查看:
|
||||
```sql
|
||||
USE your_database;
|
||||
SHOW TABLES LIKE 'memory_unit%';
|
||||
DESCRIBE memory_unit;
|
||||
DESCRIBE memory_unit_tag;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 表结构概览
|
||||
|
||||
### memory_unit (记忆单元表)
|
||||
存储AI Agent的各种记忆数据,支持分类、标签、状态管理。
|
||||
|
||||
**核心字段:**
|
||||
- `id`: 主键
|
||||
- `_tenant_id`, `user_id`: 多租户隔离
|
||||
- `category`, `sub_category`: 分类体系
|
||||
- `content_json`: JSON格式存储记忆内容
|
||||
- `status`: 状态管理
|
||||
|
||||
### memory_unit_tag (记忆标签表)
|
||||
为记忆单元添加灵活的标签支持。
|
||||
|
||||
**核心字段:**
|
||||
- `id`: 主键
|
||||
- `memory_id`: 关联记忆单元(外键)
|
||||
- `tag_name`: 标签名称
|
||||
|
||||
**关系:** 一个记忆可以有多个标签(一对多)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 高级配置
|
||||
|
||||
### 字符集设置
|
||||
```sql
|
||||
-- 确保使用 utf8mb4 支持emoji
|
||||
ALTER DATABASE your_database CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
```
|
||||
|
||||
### 性能优化
|
||||
```sql
|
||||
-- 分析表优化查询
|
||||
ANALYZE TABLE memory_unit;
|
||||
ANALYZE TABLE memory_unit_tag;
|
||||
|
||||
-- 优化表空间
|
||||
OPTIMIZE TABLE memory_unit;
|
||||
OPTIMIZE TABLE memory_unit_tag;
|
||||
```
|
||||
|
||||
### 备份恢复
|
||||
```bash
|
||||
# 备份
|
||||
mysqldump -u root -p your_database memory_unit memory_unit_tag > backup.sql
|
||||
|
||||
# 恢复
|
||||
mysql -u root -p your_database < backup.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 数据示例
|
||||
|
||||
### 记忆单元示例
|
||||
```json
|
||||
{
|
||||
"title": "AI项目开发",
|
||||
"description": "负责AI代理平台的开发工作",
|
||||
"priority": "高",
|
||||
"tags": ["重要", "正在进行"],
|
||||
"metadata": {
|
||||
"start_date": "2026-01-01",
|
||||
"end_date": "2026-06-30"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 标签示例
|
||||
```
|
||||
重要、正在进行、技术、学习、运动、日常
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. 外键约束
|
||||
- `memory_unit_tag.memory_id` 有外键约束指向 `memory_unit.id`
|
||||
- 删除记忆会自动删除相关标签(CASCADE)
|
||||
|
||||
### 2. 级联删除
|
||||
如需临时禁用外键检查(批量操作时):
|
||||
```sql
|
||||
SET FOREIGN_KEY_CHECKS=0;
|
||||
-- 执行批量操作
|
||||
SET FOREIGN_KEY_CHECKS=1;
|
||||
```
|
||||
|
||||
### 3. JSON字段
|
||||
- MySQL 5.7+ 原生支持JSON类型
|
||||
- 提供JSON函数:JSON_EXTRACT、JSON_SEARCH等
|
||||
- 自动验证JSON格式
|
||||
|
||||
### 4. 状态管理
|
||||
- 新建记录默认状态为 'active'
|
||||
- 删除采用软删除(设置status为'deleted')
|
||||
- 定期清理归档数据
|
||||
|
||||
---
|
||||
|
||||
## 🔍 常用查询
|
||||
|
||||
### 查询用户的活跃记忆
|
||||
```sql
|
||||
SELECT * FROM memory_unit
|
||||
WHERE user_id = 1001
|
||||
AND status = 'active'
|
||||
ORDER BY created DESC;
|
||||
```
|
||||
|
||||
### 查询记忆及其标签
|
||||
```sql
|
||||
SELECT m.*, t.tag_name
|
||||
FROM memory_unit m
|
||||
LEFT JOIN memory_unit_tag t ON m.id = t.memory_id
|
||||
WHERE m.user_id = 1001
|
||||
AND m.status = 'active';
|
||||
```
|
||||
|
||||
### 按标签查询记忆
|
||||
```sql
|
||||
SELECT DISTINCT m.*
|
||||
FROM memory_unit m
|
||||
INNER JOIN memory_unit_tag t ON m.id = t.memory_id
|
||||
WHERE t.tag_name = '重要'
|
||||
AND m.status = 'active';
|
||||
```
|
||||
|
||||
### 统计用户记忆数量(按分类)
|
||||
```sql
|
||||
SELECT category, COUNT(*) as count
|
||||
FROM memory_unit
|
||||
WHERE user_id = 1001
|
||||
AND status = 'active'
|
||||
GROUP BY category;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 故障排查
|
||||
|
||||
### 问题1: 外键约束错误
|
||||
```sql
|
||||
-- 检查外键状态
|
||||
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
WHERE CONSTRAINT_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'memory_unit_tag';
|
||||
```
|
||||
|
||||
### 问题2: 字符编码问题
|
||||
```sql
|
||||
-- 检查字符集设置
|
||||
SHOW VARIABLES LIKE 'character%';
|
||||
SHOW VARIABLES LIKE 'collation%';
|
||||
```
|
||||
|
||||
### 问题3: JSON格式错误
|
||||
```sql
|
||||
-- 验证JSON数据
|
||||
SELECT id, JSON_VALID(content_json) as is_valid
|
||||
FROM memory_unit
|
||||
WHERE content_json IS NOT NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有问题,请参考:
|
||||
1. `DATA_DICTIONARY.md` - 详细的数据字典
|
||||
2. `verify_tables.sql` - 表结构验证脚本
|
||||
3. 项目文档:`README.md`
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>app-platform-memory-sdk</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Swagger注解 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class MemoryDeleteDto {
|
||||
|
||||
@Schema(description = "记忆ID列表")
|
||||
private List<Long> memoryIds;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 记忆数据DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemoryMetaData {
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 代理ID
|
||||
*/
|
||||
private Long agentId;
|
||||
|
||||
/**
|
||||
* 上下文
|
||||
*/
|
||||
private String context;
|
||||
|
||||
/**
|
||||
* 用户输入
|
||||
*/
|
||||
private String userInput;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API Key 数据传输对象
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MemorySaveDto {
|
||||
|
||||
@Schema(description = "更新、新增、追加memoryId对应记忆中的键值对")
|
||||
private List<UpdateUnitWithID> updateMemoryKeyValues;
|
||||
|
||||
@Schema(description = "生成新的记忆")
|
||||
private List<InsertUnit> insertMemories;
|
||||
|
||||
@Data
|
||||
public static class UpdateUnitWithID {
|
||||
|
||||
@Schema(description = "记忆ID")
|
||||
private Long memoryId;
|
||||
|
||||
@Schema(description = "更新(替换原有内容,比如手机号变更等)、新增、追加(在原有内容后面追加新的内容,比如之前个人形象身高180,现在又获取到了新的数据为皮肤黝黑,新内容为“身高180,皮肤黝黑”)对应记忆中的键值对")
|
||||
private Map<String, String> keyValues;
|
||||
|
||||
@JsonPropertyDescription("关键词标签列表")
|
||||
private List<String> tags;
|
||||
|
||||
@JsonPropertyDescription("是否为敏感信息")
|
||||
private Boolean isSensitive;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class InsertUnit {
|
||||
|
||||
@Schema(description = "一级分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "二级分类")
|
||||
private String subCategory;
|
||||
|
||||
@Schema(description = "记忆中的键值对")
|
||||
private Map<String, String> keyValues;
|
||||
|
||||
@JsonPropertyDescription("关键词标签列表")
|
||||
private List<String> tags;
|
||||
|
||||
@JsonPropertyDescription("是否为敏感信息")
|
||||
private Boolean isSensitive;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 记忆单元创建DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "记忆单元创建DTO", description = "创建记忆单元")
|
||||
public class MemoryUnitCreateDTO {
|
||||
private Long tenantId;
|
||||
private Long userId;
|
||||
private Long agentId;
|
||||
private String category;
|
||||
private String subCategory;
|
||||
private Map<String, String> keyValues;
|
||||
private List<String> tags;
|
||||
private Boolean isSensitive;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 记忆单元DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ApiModel(value = "记忆单元DTO", description = "记忆单元数据传输对象")
|
||||
public class MemoryUnitDTO {
|
||||
|
||||
@ApiModelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "租户ID")
|
||||
private Long tenantId;
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "代理ID")
|
||||
private Long agentId;
|
||||
|
||||
@ApiModelProperty(value = "一级分类")
|
||||
private String category;
|
||||
|
||||
@ApiModelProperty(value = "二级分类")
|
||||
private String subCategory;
|
||||
|
||||
@ApiModelProperty(value = "内容JSON")
|
||||
private String contentJson;
|
||||
|
||||
@ApiModelProperty(value = "是否敏感信息(0:否 1:是)")
|
||||
private Boolean isSensitive;
|
||||
|
||||
@ApiModelProperty(value = "状态,active、deleted")
|
||||
private String status;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@ApiModelProperty(value = "修改时间")
|
||||
private Date modified;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 记忆单元查询DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "记忆单元查询DTO", description = "记忆单元查询条件")
|
||||
public class MemoryUnitQueryDTO {
|
||||
private Long tenantId;
|
||||
private Long userId;
|
||||
private Long agentId;
|
||||
@JsonPropertyDescription("必须严格从上述一级分类中选择,不能自创")
|
||||
private List<String> categories;
|
||||
@JsonPropertyDescription("必须严格从上述二级分类中选择,不能自创")
|
||||
private List<String> subCategories;
|
||||
@JsonPropertyDescription("关键词标签列表,分词可以细粒度一点,尽量多生成有助于更精确查找")
|
||||
private List<String> tags;
|
||||
@JsonPropertyDescription("是否需要记忆的历史变更记录,如果问题不需要,requiresHistory 设为 false")
|
||||
private Boolean requiresHistory;
|
||||
@JsonPropertyDescription("查询范围,可选值为:today|yesterday|recent|long-term|all")
|
||||
private String queryType;
|
||||
@JsonPropertyDescription("时间范围(非必填),格式为:2023-01-01 00:00:00-2023-01-01 23:59:59")
|
||||
private String timeRange;
|
||||
@JsonPropertyDescription("限制返回的记忆数量,默认为 10")
|
||||
private Integer limit;
|
||||
boolean justKeywordSearch;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 记忆单元标签创建DTO
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "记忆单元标签创建DTO", description = "创建记忆单元标签")
|
||||
public class MemoryUnitTagCreateDTO {
|
||||
|
||||
@ApiModelProperty(value = "用户ID", required = true)
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "记忆ID", required = true)
|
||||
private Long memoryId;
|
||||
|
||||
@ApiModelProperty(value = "标签名称", required = true)
|
||||
private String tagName;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 记忆单元标签DTO
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ApiModel(value = "记忆单元标签DTO", description = "记忆单元标签数据传输对象")
|
||||
public class MemoryUnitTagDTO {
|
||||
|
||||
@ApiModelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "租户ID")
|
||||
private Long tenantId;
|
||||
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
@ApiModelProperty(value = "记忆ID")
|
||||
private Long memoryId;
|
||||
|
||||
@ApiModelProperty(value = "标签名称")
|
||||
private String tagName;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@ApiModelProperty(value = "修改时间")
|
||||
private Date modified;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.xspaceagi.memory.sdk.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 记忆单元更新DTO
|
||||
*/
|
||||
@Data
|
||||
public class MemoryUnitUpdateDTO {
|
||||
|
||||
private Long id;
|
||||
private Long userId;
|
||||
@ApiModelProperty(value = "一级分类")
|
||||
private String category;
|
||||
|
||||
@ApiModelProperty(value = "二级分类")
|
||||
private String subCategory;
|
||||
|
||||
@ApiModelProperty(value = "内容JSON")
|
||||
private String contentJson;
|
||||
|
||||
@ApiModelProperty(value = "是否敏感信息")
|
||||
private Boolean isSensitive;
|
||||
|
||||
@ApiModelProperty(value = "状态")
|
||||
private String status;
|
||||
|
||||
private List<String> tags;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xspaceagi.memory.sdk.service;
|
||||
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryMetaData;
|
||||
import com.xspaceagi.memory.sdk.dto.MemorySaveDto;
|
||||
import com.xspaceagi.memory.sdk.dto.MemoryUnitQueryDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IMemoryRpcService {
|
||||
|
||||
void createMemory(MemoryMetaData memoryData);
|
||||
|
||||
String searchMemoriesMd(Long tenantId, Long userId, Long agentId, String inputMessage, String context, boolean justKeywordSearch, boolean filterSensitive);
|
||||
|
||||
String queryMemoriesMd(MemoryUnitQueryDTO memoryUnitQueryDTO, boolean filterSensitive);
|
||||
|
||||
void saveMemories(Long tenantId, Long userId, Long agentId, MemorySaveDto memorySaveDto);
|
||||
|
||||
void deleteMemories(Long tenantId, Long userId, List<Long> memoryIds);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-spec</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>system-sdk</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xspaceagi.memory.spec;
|
||||
|
||||
/**
|
||||
* Memory模块常量定义
|
||||
*/
|
||||
public class MemoryConstants {
|
||||
|
||||
/**
|
||||
* 记忆类型
|
||||
*/
|
||||
public static final class MemoryType {
|
||||
public static final String SHORT_TERM = "short_term";
|
||||
public static final String LONG_TERM = "long_term";
|
||||
public static final String WORKING = "working";
|
||||
}
|
||||
|
||||
/**
|
||||
* 记忆状态
|
||||
*/
|
||||
public static final class MemoryStatus {
|
||||
public static final String ACTIVE = "active";
|
||||
public static final String ARCHIVED = "archived";
|
||||
public static final String DELETED = "deleted";
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
public static final class DefaultConfig {
|
||||
public static final int MAX_SHORT_TERM_MEMORY_SIZE = 100;
|
||||
public static final int MAX_WORKING_MEMORY_SIZE = 50;
|
||||
public static final long LONG_TERM_MEMORY_THRESHOLD = 7 * 24 * 60 * 60 * 1000L; // 7天
|
||||
}
|
||||
|
||||
private MemoryConstants() {
|
||||
// 私有构造函数,防止实例化
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-web</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-application</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,32 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||
<id>repack</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<!--
|
||||
重新打包需要包含的jar包,这个过程会解压所有的jar包并按照jar的规则重新组合成一个jar包
|
||||
这里需要包含当前这个工程 ,格式是{groupId}:{artifactId}
|
||||
-->
|
||||
<includes>
|
||||
<!-- 包含项目自己 -->
|
||||
<include>com.xspaceagi:app-platform-memory-web</include>
|
||||
<include>com.xspaceagi:app-platform-memory-application</include>
|
||||
<include>com.xspaceagi:app-platform-memory-domain</include>
|
||||
<include>com.xspaceagi:app-platform-memory-infra</include>
|
||||
<include>com.xspaceagi:app-platform-memory-spec</include>
|
||||
</includes>
|
||||
<!-- 打包结果目录:/ 表示target/ -->
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<!-- 是否解压依赖 -->
|
||||
<unpack>true</unpack>
|
||||
<!-- 打包的scope -->
|
||||
<!-- <scope>system</scope>-->
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>app-platform-memory-web</artifactId>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>app-platform-memory-web</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-modules</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>app-platform-memory</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties/>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>system-sdk</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-application</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-infra</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-domain</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-spec</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-api</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-sdk</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xspaceagi</groupId>
|
||||
<artifactId>app-platform-memory-web</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
<module>app-platform-memory-application</module>
|
||||
<module>app-platform-memory-domain</module>
|
||||
<module>app-platform-memory-infra</module>
|
||||
<module>app-platform-memory-spec</module>
|
||||
<module>app-platform-memory-api</module>
|
||||
<module>app-platform-memory-web</module>
|
||||
<module>app-platform-memory-sdk</module>
|
||||
</modules>
|
||||
</project>
|
||||
Reference in New Issue
Block a user