Skip to content

新增用户长记忆链路并注入聊天上下文#1

Merged
Choco-emmm merged 8 commits into
masterfrom
copilot/add-long-memory-system
May 13, 2026
Merged

新增用户长记忆链路并注入聊天上下文#1
Choco-emmm merged 8 commits into
masterfrom
copilot/add-long-memory-system

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 13, 2026

本 PR 在聊天链路中新增用户长记忆能力:从新日记中抽取长期画像属性并落库,在聊天时将这些背景知识注入上下文;同时补齐按关键词 / 日期检索历史日记摘要的 Function Calling 支持,帮助模型在需要更早历史时主动查询。

在原始实现基础上,已根据审查反馈补充长记忆链路的安全与一致性修复,包括:

  • 为长记忆同步补上事务化执行,并调整为先完成 upsert、再清理旧记录,避免写入失败时丢失已有画像
  • attributeKey / attributeValue 增加长度限制、字符过滤、单行化与兜底转义,保证落库和 prompt 注入可控
  • 将长记忆注入聊天上下文的格式改为“背景事实 JSON 列表”,并显式声明其不是指令,降低 prompt injection 风险
  • 将未登录场景统一为 BAD_REQUEST,避免 ChatController 直接调用时返回 500

测试与校验已完成:

  • 使用 Java 21 运行后端 ./mvnw test 通过
  • 已补充 / 更新后端测试,覆盖函数注册、历史日记检索、长记忆 prompt 生成与安全回归场景
  • 代码审查与 CodeQL 安全校验已重新通过

Copilot AI and others added 3 commits May 13, 2026 09:27
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/1913171a-3d6b-403b-a06e-34e4582a8616

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/1913171a-3d6b-403b-a06e-34e4582a8616

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/1913171a-3d6b-403b-a06e-34e4582a8616

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Copilot AI and others added 3 commits May 13, 2026 09:43
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/f30dae57-1e8a-4ae2-9648-c0c291488a2a

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/f30dae57-1e8a-4ae2-9648-c0c291488a2a

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/f30dae57-1e8a-4ae2-9648-c0c291488a2a

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 在后端聊天链路中新增“用户长记忆”能力:从新日记中抽取长期画像属性并落库,在聊天时将这些背景知识注入 system prompt;同时补齐“按关键词/日期检索历史日记摘要”的 Function Calling 支持,帮助模型在需要更早历史时主动查询。

Changes:

  • 新增 user_profile_memory 表与对应 Entity/Mapper,并实现 MemoryExtractionService:从日记内容抽取并同步长期画像属性。
  • 新增 DiarySearchRequest/DiarySearchResultDiaryService.searchOwnDiarySummaries,并在 AIConfiguration 注册 diarySearchFunctionChatService 开启函数能力。
  • 更新聊天与日记相关测试,覆盖函数注册、长记忆 prompt 生成、历史日记检索行为。

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
CLAUDE.md 更新项目结构/数据库与聊天链路说明,补充长记忆注入与最新迁移版本信息
backend/moodcopilot/src/test/java/com/moodcopilot/diary/DiaryRecommendationServiceTest.java 增加日记摘要检索的单测,并适配 DiaryService 新构造参数
backend/moodcopilot/src/test/java/com/moodcopilot/diary/DiaryControllerMockMvcTest.java 适配 runAiAnalysis 方法签名变更并增强验证
backend/moodcopilot/src/test/java/com/moodcopilot/ai/MemoryExtractionServiceTest.java 新增长记忆抽取与 prompt 构造的单测
backend/moodcopilot/src/test/java/com/moodcopilot/ai/ChatServiceTest.java 新增聊天/回复路径对 diarySearchFunction 注册的单测
backend/moodcopilot/src/test/java/com/moodcopilot/ai/ChatControllerMockMvcTest.java 适配 ChatController 注入长记忆背景并覆盖接口行为
backend/moodcopilot/src/main/resources/db/migration/V1_14__add_user_profile_memory.sql 新增 user_profile_memory 表结构与索引/外键
backend/moodcopilot/src/main/java/com/moodcopilot/mapper/UserProfileMemoryMapper.java 新增 UserProfileMemory 的 MyBatis-Plus Mapper
backend/moodcopilot/src/main/java/com/moodcopilot/entity/UserProfileMemoryEntity.java 新增 user_profile_memory 实体定义
backend/moodcopilot/src/main/java/com/moodcopilot/diary/DiaryService.java 新增历史日记摘要检索;AI 分析后触发长记忆抽取同步;构造函数增加依赖
backend/moodcopilot/src/main/java/com/moodcopilot/diary/DiarySearchResult.java 新增检索结果 record(含日期与 snippet)
backend/moodcopilot/src/main/java/com/moodcopilot/diary/DiarySearchRequest.java 新增检索请求 record(keyword/startDate/endDate)
backend/moodcopilot/src/main/java/com/moodcopilot/diary/DiaryController.java 适配 runAiAnalysis 新签名并传入 userId
backend/moodcopilot/src/main/java/com/moodcopilot/config/AIConfiguration.java 注册 diarySearchFunction FunctionCallback;更新聊天 system prompt 指引模型调用函数
backend/moodcopilot/src/main/java/com/moodcopilot/ai/MemoryExtractionService.java 新增长记忆抽取、清洗与同步落库;生成聊天注入用背景 prompt
backend/moodcopilot/src/main/java/com/moodcopilot/ai/DiarySearchFunctionSupport.java 新增函数名常量封装
backend/moodcopilot/src/main/java/com/moodcopilot/ai/ChatService.java chat/reply 支持注入长记忆背景并开启 diarySearchFunction
backend/moodcopilot/src/main/java/com/moodcopilot/ai/ChatController.java 在 chat/reply 前读取长记忆背景并传入 ChatService
backend/moodcopilot/mvnw 引入 Maven Wrapper 脚本以便统一构建/测试入口

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +57 to +72
@Async("aiExecutor")
public void extractAndSyncMemory(Long userId, String diaryContent) {
try {
List<UserProfileMemoryEntity> existing = listUserMemories(userId);
String prompt = buildExtractionUserPrompt(diaryContent, existing);
String json = analysisChatClient.prompt()
.system(MEMORY_EXTRACTION_PROMPT)
.user(prompt)
.call()
.content();
MemoryExtractionResponse response = objectMapper.readValue(json, MemoryExtractionResponse.class);
syncMemories(userId, existing, sanitizeAttributes(response.attributes()));
} catch (Exception e) {
log.warn("长记忆提取失败,userId={}: {}", userId, e.getMessage());
}
}
Comment on lines +106 to +123
private List<MemoryAttribute> sanitizeAttributes(List<MemoryAttribute> attributes) {
if (attributes == null || attributes.isEmpty()) {
return List.of();
}
Map<String, MemoryAttribute> deduped = new LinkedHashMap<>();
for (MemoryAttribute attribute : attributes) {
if (attribute == null || attribute.attributeKey() == null || attribute.attributeValue() == null) {
continue;
}
String key = attribute.attributeKey().trim();
String value = attribute.attributeValue().trim();
if (key.isEmpty() || value.isEmpty()) {
continue;
}
deduped.put(key, new MemoryAttribute(key, value));
}
return List.copyOf(deduped.values());
}
Comment on lines +74 to +85
public String buildUserMemoryPrompt() {
List<UserProfileMemoryEntity> memories = listUserMemories(currentUser().getId());
if (memories.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder("以下是用户已经形成的长期背景知识:\n");
for (UserProfileMemoryEntity memory : memories) {
sb.append("- ").append(memory.getAttributeKey()).append(":")
.append(memory.getAttributeValue()).append("\n");
}
return sb.toString();
}
Comment on lines +154 to +160
private UserEntity currentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal() instanceof UserEntity user) {
return user;
}
throw new IllegalStateException("用户未登录");
}
@Choco-emmm
Copy link
Copy Markdown
Owner

@copilot 审查pr,并查看与本地的区别,若无问题则合并到master

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot 审查pr,并查看与本地的区别,若无问题则合并到master

我已按 0f2af04 复查:本地工作树干净,且与远端 PR 分支无差异;export JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 && cd backend/moodcopilot && ./mvnw test 已通过。当前不建议合并到 master,仍有阻塞问题:MemoryExtractionService 的删后重写缺少事务保护、长记忆内容直接拼接到 prompt 存在注入/超长风险、未登录时 buildUserMemoryPrompt() 可能返回 500。此次仅做代码审查,无 UI 变更可截图。若需要,我下一步可以直接把这几个问题修掉再提请合并。

@Choco-emmm
Copy link
Copy Markdown
Owner

@copilot 修掉问题

@Choco-emmm
Copy link
Copy Markdown
Owner

@copilot 都可以修

Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/7c4bd1be-dfcf-419d-a1cb-aaa674aa0e2a

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Choco-emmm/MoodCopilot/sessions/7c4bd1be-dfcf-419d-a1cb-aaa674aa0e2a

Co-authored-by: Choco-emmm <266224693+Choco-emmm@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot 都可以修

已修完并推到 75c8fdb。这次补上了长记忆同步的安全写入顺序与事务执行、属性长度/字符清洗、prompt 背景事实结构化与兜底转义、以及未登录时统一返回 BAD_REQUEST。JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 && cd backend/moodcopilot && ./mvnw test 已通过;并行代码审查与 CodeQL 也已重新跑过。本次无 UI 变更。

@Choco-emmm
Copy link
Copy Markdown
Owner

@copilot 修完之后可以合并到主分支然后拉到本地来了

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 13, 2026

@copilot 修完之后可以合并到主分支然后拉到本地来了

当前分支已经可合并,最新修复提交是 75c8fdb。后端测试(Java 21 下 ./mvnw test)、代码审查和 CodeQL 都已通过。受当前环境限制我不能直接替你合并到主分支,但你现在可以在 GitHub 上合并这个 PR,然后在本地拉取最新主分支。本次无 UI 变更。

@Choco-emmm Choco-emmm merged commit d3fd3e0 into master May 13, 2026
1 check passed
@Choco-emmm Choco-emmm deleted the copilot/add-long-memory-system branch May 13, 2026 15:32
Choco-emmm added a commit that referenced this pull request May 15, 2026
- 记忆解绑:删除日记不再触发全量画像重建,改为用户手动管理
- 新增 GET/DELETE /api/memory 接口 + SettingsPage 记忆管理面板
- 清理 MemoryExtractionService 重建相关方法(~400行),保留增量更新
- 修复报告缓存过期时自动调用 AI 生成总结的问题
- 修复 AI 聊天回复中泄露内部编号(#1/#5)的 prompt 问题
- 修复手机端聊天消息偶发丢失(saveToBackend race condition)
- 修复报告生成按钮无 loading 反馈的体验问题
- 修复日记编辑模式下原文与编辑框重复回显
- 修复后端时区为 Asia/Shanghai(Dockerfile JVM + JDBC + Jackson)
Choco-emmm added a commit that referenced this pull request May 23, 2026
Choco-emmm added a commit that referenced this pull request May 23, 2026
- 记忆解绑:删除日记不再触发全量画像重建,改为用户手动管理
- 新增 GET/DELETE /api/memory 接口 + SettingsPage 记忆管理面板
- 清理 MemoryExtractionService 重建相关方法(~400行),保留增量更新
- 修复报告缓存过期时自动调用 AI 生成总结的问题
- 修复 AI 聊天回复中泄露内部编号(#1/#5)的 prompt 问题
- 修复手机端聊天消息偶发丢失(saveToBackend race condition)
- 修复报告生成按钮无 loading 反馈的体验问题
- 修复日记编辑模式下原文与编辑框重复回显
- 修复后端时区为 Asia/Shanghai(Dockerfile JVM + JDBC + Jackson)
Choco-emmm added a commit that referenced this pull request May 23, 2026
- 记忆解绑:删除日记不再触发全量画像重建,改为用户手动管理
- 新增 GET/DELETE /api/memory 接口 + SettingsPage 记忆管理面板
- 清理 MemoryExtractionService 重建相关方法(~400行),保留增量更新
- 修复报告缓存过期时自动调用 AI 生成总结的问题
- 修复 AI 聊天回复中泄露内部编号(#1/#5)的 prompt 问题
- 修复手机端聊天消息偶发丢失(saveToBackend race condition)
- 修复报告生成按钮无 loading 反馈的体验问题
- 修复日记编辑模式下原文与编辑框重复回显
- 修复后端时区为 Asia/Shanghai(Dockerfile JVM + JDBC + Jackson)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants