Skip to content

feat(memory): add memory_session_context tool for session-start context injection#1132

Open
shiloong wants to merge 1 commit into
alibaba:mainfrom
shiloong:feat/memory/session-context
Open

feat(memory): add memory_session_context tool for session-start context injection#1132
shiloong wants to merge 1 commit into
alibaba:mainfrom
shiloong:feat/memory/session-context

Conversation

@shiloong

Copy link
Copy Markdown
Collaborator

Description

Implement A-7: Session-Start 上下文注入.

Add memory_session_context tool that assembles historical context for a new session by combining:

  1. Recent session summaries — reads facts/summary/*.md files (produced by consolidation Rule 6), sorts by created_at descending, includes top N (default 5)
  2. High-confidence facts — scans other facts/<category>/ directories for facts with confidence >= 0.8, sorted by confidence descending

Returns a markdown-formatted context string (capped at 8KB) suitable for injection into the agent's system prompt at session start.

Usage

Designed to be called by copilot-shell's autoRecallHook (PR #1121) at session start, or manually by any MCP client:

memory_session_context(limit=5)  → "# Recent Sessions\n\n## ...\n\n# Key Memories\n\n## ..."

Implementation

  • New file: src/tools/session_context.rs (272 lines)
  • Uses safe_fs::read_to_string for sandbox-safe reads
  • Frontmatter parser reuses the YAML-quote pattern from existing tools
  • Char-boundary-safe preview truncation (CJK-aware)
  • Unit tests: frontmatter parsing, preview truncation, no-frontmatter case

Related Issue

no-issue: A-7 Session-Start context injection

Type of Change

  • New feature (non-breaking change that adds functionality)

Scope

  • memory (agent-memory)

Checklist

  • cargo clippy --all-targets -- -D warnings passes
  • cargo fmt --check passes
  • cargo test passes (148 tests: 135 lib + 3 profile + 10 integration)
  • Lock files are up to date

@Forrest-ly Forrest-ly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR #1132 Review — feat(memory): add memory_session_context tool for session-start context injection

本 PR 添加 memory_session_context 工具,在 session 启动时注入历史上下文。扫描 facts/summary/ 获取最近 session 摘要,扫描其他 facts// 获取高置信度事实(confidence >= 0.8),输出上限 8KB 的 markdown 文本。

259 行新增,代码质量在最近系列 PR 中最好:正确使用 safe_fs::read_to_string 读文件、frontmatter 解析器支持 YAML unquote、preview 截断是 char-boundary-safe 的。


发现

  1. src/agent-memory/src/tools/session_context.rs:~85 — WalkDir 扫描整个 mount root 但只需要 facts/ 子目录 (CONFIRMED, 中)

for entry in WalkDir::new(&svc.mount.root) // ← 扫描整棵树
.follow_links(false)
.into_iter()
.filter_entry(|e| !e.path().starts_with(&meta_dir))
{
// ...
if !rel.starts_with("facts/") {
continue; // ← 跳过所有非 facts/ 文件
}

filter_entry 仅排除 meta_dir,不排除 notes/、MEMORY.md 等其他顶级目录。WalkDir 会递归进入 notes/observed/ 的每一层,为每个文件计算 rel_path 后才 continue。对于有 1000 条 notes、50 条 facts 的存储,浪费 95% 的迭代。

此工具定位为 session-start 路径(每次会话启动调用),性能敏感。应改为 WalkDir::new(svc.mount.root.join("facts")) 直接扫描目标目录,或在 filter_entry 中裁剪非 facts/ 子树:

.filter_entry(|e| {
!e.path().starts_with(&meta_dir)
&& (e.depth() == 0 || e.path().starts_with(&facts_dir))
})

  1. src/agent-memory/src/tools/session_context.rs:~151 — high_confidence.truncate(limit) 与 summaries 共享同一 limit 参数,但文档只提及 summaries (CONFIRMED, 低-中)

summaries.truncate(limit);
// ...
high_confidence.truncate(limit);

工具描述说 "Optional limit (default 5) controls max summaries",但 limit 同样截断 high_confidence。调用 memory_session_context(limit=2) 会同时限制 summaries 和 facts 各为 2 条。如果用户想多看 facts 但少看
summaries,无法做到。应分离为两个参数,或文档明确说明 limit 同时约束两者。

  1. src/agent-memory/src/tools/session_context.rs:~163 — 空 title 的 summary 生成畸形 markdown (CONFIRMED, 低)

let section = format!(
"## {}\n_session: {} | created: {}_\n\n{}\n\n",
s.title, // ← 可能为空字符串
s.session_id, // ← 可能为空字符串
s.created_at,
take_preview(&s.body)
);

若 summary 文件缺少 title frontmatter 字段,title 默认为 "",生成:

session: | created: 2026-06-25T...

空的 ## 标题在 markdown 渲染中不可见但占据结构位。应 fallback 到文件名:
let title = fm.get("title").cloned()
.filter(|t| !t.is_empty())
.unwrap_or_else(|| rel.clone());

  1. src/agent-memory/src/tools/session_context.rs:~155 — bytes_used 不包含 section headers("# Recent Sessions\n\n" 等),8KB 上限非精确 (CONFIRMED, 低)

out.push_str("# Recent Sessions\n\n");
// bytes_used 未递增
for s in &summaries {
if bytes_used >= MAX_CONTEXT_BYTES { break; }

实际输出可超过 MAX_CONTEXT_BYTES 约 38 字节(两个 header 总长)。对 8KB 上限来说可忽略不计,但若上限将来调低或上游有严格 budget 检查,可能溢出。

…xt injection

Signed-off-by: Shile Zhang <shile.zhang@linux.alibaba.com>
@shiloong shiloong force-pushed the feat/memory/session-context branch from 6c2bb60 to 2fb73e2 Compare June 25, 2026 10:30
@shiloong

Copy link
Copy Markdown
Collaborator Author

Review 修复回复

1. 扫描整个 mount root — ✅ 已修复

改为 WalkDir::new(svc.mount.root.join("facts")),直接扫描 facts/ 目录,不再遍历 notes/ 等无关子树。移除了 meta_dir 过滤(不再需要)。

2. limit 共享 — ⚠️ 设计决策

limit 同时约束 summaries 和 facts,简化 API。文档已说明 "controls max summaries",后续可分离为两个参数。当前默认 5 + 5 = 10 条在 8KB 上限内足够。

3. 空 title 畸形 markdown — ✅ 已修复

title fallback 到文件路径:fm.get("title").filter(|t| !t.is_empty()).unwrap_or_else(|| rel.clone())

4. bytes_used 不含 headers — ⚠️ 已知

headers("# Recent Sessions\n\n")约 20 bytes,对 8KB 上限影响 <0.3%。后续可精确计算。

CI: fmt ✅ clippy ✅ 135 tests ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants