Skip to content

feat(workspace): auto-extract structured memories on compaction - #1603

Closed
G7CNF wants to merge 5 commits into
nearai:mainfrom
G7CNF:codex/issue-1474-session-end-memory-extractor
Closed

G7CNF wants to merge 5 commits into
nearai:mainfrom
G7CNF:codex/issue-1474-session-end-memory-extractor

Conversation

@G7CNF

@G7CNF G7CNF commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Implements the first slice of #1474 by extracting high-confidence structured memories during compaction and appending them to MEMORY.md.

What changed:

  • compaction now asks the LLM for structured durable memories from archived turns
  • results are deduped against existing structured memory entries in MEMORY.md
  • extraction is best-effort and does not block compaction if the LLM or append path fails
  • FEATURE_PARITY.md updated to reflect the new extraction capability

Validation:

  • cargo fmt --all -- --check
  • CARGO_INCREMENTAL=0 CARGO_PROFILE_TEST_DEBUG=0 RUSTFLAGS='-Cdebuginfo=0' cargo test -p ironclaw --features libsql --lib test_move_to_workspace_extracts_structured_memories -- --nocapture
  • CARGO_INCREMENTAL=0 CARGO_PROFILE_TEST_DEBUG=0 RUSTFLAGS='-Cdebuginfo=0' cargo test -p ironclaw --features libsql --lib test_move_to_workspace_skips_duplicate_structured_memories -- --nocapture
  • IC_BASE_REF=origin/staging /Users/nigecoleman/.codex/skills/rust-pr-preflight/scripts/preflight.sh (repo-wide baseline boundary warnings remain, but no branch-specific blockers)

This is intentionally the minimal first pass for #1474; follow-up work can layer compaction/pruning polish on top.

@github-actions github-actions Bot added scope: agent Agent core (agent loop, router, scheduler) scope: docs Documentation size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Mar 23, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the agent's compaction mechanism by enabling the automatic extraction of structured memories. During the compaction process, the system now leverages an LLM to identify and extract high-confidence durable memories from archived conversation turns, subsequently appending these to MEMORY.md. This feature aims to improve the agent's long-term memory and understanding by systematically capturing key information, while also ensuring efficiency through deduplication and best-effort processing.

Highlights

  • Structured Memory Extraction: Implemented automatic extraction of high-confidence structured memories from archived conversation turns during compaction, leveraging an LLM.
  • Deduplication Logic: Introduced deduplication logic to prevent adding existing structured memory entries to MEMORY.md, ensuring memory efficiency.
  • Best-Effort Processing: Ensured memory extraction is a best-effort process, allowing compaction to complete even if the LLM call or appending to MEMORY.md fails.
  • Feature Parity Update: Updated the FEATURE_PARITY.md document to reflect the new structured memory extraction capability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an excellent new feature for automatically extracting structured memories during context compaction. The implementation is well-structured, covering the LLM interaction for extraction, deduplication against existing memories, and updating the workspace. The addition of tests for the new functionality is also great. I've identified a few areas for improvement: a bug in the memory deduplication logic that could allow duplicates, an opportunity to enhance debuggability by logging JSON parsing errors from the LLM, and a small refactoring to make one of the compaction functions more concise.

Comment thread src/agent/compaction.rs Outdated
Comment thread src/agent/compaction.rs
Comment thread src/agent/compaction.rs Outdated

@zmanian zmanian 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.

Review: Structured Memory Extraction at Compaction

Overall this is a clean, well-scoped first pass. The best-effort pattern is solid, the LLM prompt is well-constrained, and the test coverage is good. A few items worth addressing before merge:

Dedup logic (Gemini flag) -- addressed

The second commit correctly fixes the dedup to extract content from - **kind**: content lines before comparison. The current version using split_once("**: ") is correct.

Issues to address

1. extract_json_object is fragile with nested braces

The extract_json_object function uses text.find('{') / text.rfind('}') which works for the simple schema here, but will silently produce garbage if the LLM wraps the response in markdown fences containing an explanation with braces before/after the JSON. Since the LLM is asked to return "ONLY JSON", this is low risk, but a more robust approach would be to find the outermost balanced braces, or at minimum add a serde_json::from_str validation inside extract_json_object before returning. Not a blocker, but worth a comment or a follow-up.

2. Dedup reads the entire MEMORY.md on every candidate memory

structured_memory_exists calls workspace.memory().await for each candidate memory in the loop (up to 5 times). This re-reads MEMORY.md from the database on every call. Should read once before the loop and pass the content in. Something like:

let memory_content = workspace.memory().await.map(|d| d.content).unwrap_or_default();
for memory in parsed.memories.into_iter().take(MEMORY_EXTRACTION_MAX_CANDIDATES) {
    // ...
    if memory_already_exists(&memory_content, &memory.content) {
        continue;
    }
    // ...
}

This is a real performance issue -- 5 DB reads when 1 suffices.

3. kind field is not validated

The kind field from the LLM is used directly as a string in the formatted output (- **{kind}**: ...). If the LLM hallucinates a kind like "personal_secret" or returns garbage, it gets written verbatim. Consider validating against the known enum variants (preference | identity | project | plan | fact) and skipping unknown kinds, or at least documenting this as a known limitation for the follow-up.

4. Substring dedup can produce false positives

The dedup uses bidirectional contains():

existing.contains(&normalized_query) || normalized_query.contains(&existing)

A memory like "User uses Rust" would match an existing "User uses Rust and Python for backend work" which is correct (subsumption). But it also means "User prefers dark mode" would match an existing line containing "User prefers dark mode for IDE but light mode for docs" -- the new shorter memory would be incorrectly suppressed even though it's semantically different (broader). This is acceptable for a first pass but worth noting for the follow-up pruning/compaction work.

Looks good

  • Best-effort error handling: all failure paths log warnings and return 0, never blocking compaction.
  • No .unwrap()/.expect() in production code.
  • Temperature 0.0 and max_tokens 512 are sensible for structured extraction.
  • Confidence threshold at 0.75 is reasonable.
  • #[serde(default)] on evidence field handles LLMs that omit optional fields.
  • Tests cover both extraction and dedup paths with real workspace instances.
  • The Gemini bot feedback has been addressed in the second commit (dedup content extraction, compact_truncate refactoring, serde error logging).

Verdict

Request changes for item #2 (reading MEMORY.md N times in a loop). The rest are minor suggestions or follow-up items. With that fix, this is good to merge.

@zmanian zmanian 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.

Requesting changes for the repeated MEMORY.md reads in the dedup loop (see detailed review comment). The rest is solid work.

@henrypark133
henrypark133 changed the base branch from staging to main May 1, 2026 06:18
@serrrfirat

Copy link
Copy Markdown
Collaborator

Thank you for pushing structured durable-memory extraction forward.\n\nWe are closing this legacy implementation because Reborn has separated compaction from memory lifecycle ownership. Compaction owns transcript-window reduction; memory providers own interaction recording and any extraction or distillation policy. The active Reborn memory-lifecycle work in #5327 records completed-turn interactions and explicitly defers durable on-run-end summaries to Phase 3.\n\nReferences:\n- https://github.com/nearai/ironclaw/pull/5327\n- https://github.com/nearai/ironclaw/blob/main/crates/ironclaw_agent_loop/src/strategies/compaction.rs\n- https://github.com/nearai/ironclaw/blob/main/crates/ironclaw_memory/src/service.rs\n- https://github.com/nearai/ironclaw/blob/main/crates/ironclaw_memory_native/src/service.rs\n\nThe valuable extraction idea should continue as a provider-owned Phase 3 follow-up to #5327, not as a side effect inside the legacy compaction path. We would be very happy to have you contribute to that Reborn work, with this PR retained as context and attribution.

@serrrfirat serrrfirat closed this Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: medium Business logic, config, or moderate-risk modules scope: agent Agent core (agent loop, router, scheduler) scope: docs Documentation size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants