feat(memory): reduce corpus write serialization with exact apply#2210
Open
huangruiteng wants to merge 54 commits into
Open
feat(memory): reduce corpus write serialization with exact apply#2210huangruiteng wants to merge 54 commits into
huangruiteng wants to merge 54 commits into
Conversation
|
Persistent review updated to latest commit 2f09aa9 |
PR Code Suggestions ✨Explore these optional code suggestions:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds server-side pieces for faster agent-memory corpus preparation while preserving the normal per-trajectory experience semantics and Memory V2 graph consistency.
The practical target is Vaka / TAU-style agent-memory iteration: clients can submit many session commits concurrently, while the server owns file-level safety, patch shape, graph cleanup, retry telemetry, and bounded apply backpressure. This PR no longer includes batch experience consolidation or a batch provider; the acceleration path is now concurrent session commits + concurrent same-session per-trajectory experience phases + operation-exact apply window.
Product behavior changes only for operation-exact phases:
memory.operation_exact_apply_window_secondsnow defaults to10.0s.operation_exactapply locks.memory.agent_experience_per_trajectory_max_concurrencylets same-session per-trajectory experience phases run concurrently when operation-exact apply is enabled.memory.long_term_extraction_enabled=falsecan skip standard long-term user/tool/skill memories while preserving agent memories for agent-memory-only corpus builds.corpus_session_commit_concurrency, expected lock modes, long-term extraction mode, and apply-window settings.Design Boundary
This PR is deliberately not a semantic reconcile system.
supersedes/ delete is treated as a graph rewrite, not ordinary metadata: peerlinks/backlinksthat point at superseded experiences are migrated to the replacement URI, stale old-URI edges are cleaned before delete, deleted-link endpoints are included in operation-exact lock/write sets, superseded target reads are version-tracked so late endpoint drift triggers stale-read retry, andsupersedesis only consumed after the target is resolved.The intended contract is: clients may be aggressive about concurrency; server-side Memory V2 should provide safe apply semantics and telemetry without asking every client to implement its own serial discipline.
What Changed
memory.operation_exact_apply_window_seconds, default10.0.memory.agent_experience_per_trajectory_max_concurrency, default4.StrPatch(blocks=[...])for bothmerge_op=patchandmerge_op=replacestring fields when old field content is available.MemoryUpdaterapply a structured string patch throughPatchOpeven if the schema field is normallymerge_op=replace.links/backlinks, cleans remaining old-URI edges for delete operations, includes deleted-link endpoints in operation-exact lock/write sets, and tracks superseded target reads for exact-apply stale retry.supersedesas a graph rewrite:supersedesstrings are supported, while an exact raw name is tried first so valid filenames containing separators still work.memory.long_term_extraction_enabled, defaulttrue.GET /api/v1/stats/memory-graphto let clients inspect memory graph health after concurrent corpus writes, including memory type counts, source links, backlinks, broken endpoints, missing reciprocal links, and violation samples.Validation Signal
Small Throughput Probe
Small TAU-2 retail corpus-prepare probe, cached train transcripts, 8 successful sessions, wait timeout 3600s.
Phase times below are server telemetry attribution. They should not be added to task wall time directly, and the tree-control run did not record a complete
otherphase. The tree row therefore reports the lock bucket that explains the missingotherbottleneck.otherphaseRead: this is a corpus-prepare throughput signal, not a benchmark-score claim. Experience and trajectory phases were already clean in this probe; the long tail was standard long-term memory extraction, especially tools updates that were still emitted as plain-string patches. The conversion layer addresses that patch-shape problem directly; the latest patch also removes batch consolidation and instead allows same-session per-trajectory experience phases to run concurrently.
Full Retail Corpus Graph Check
Cached TAU-2 retail train transcripts, success-only agent-memory corpus build,
corpus_session_commit_concurrency=4,operation_exact_apply_window_seconds=10.0, long-term extraction disabled.supersedesThe full patched run validates the main invariant: source lineage and replacement cleanup are handled by the server apply path, not by best-effort post-apply metadata edits.
That full run also surfaced two multi-target
supersedesstrings from the extractor, for example one field naming several older experience candidates. The latest commit adds server-side parsing for that edge: try the exact raw target first, then split comma/semicolon/newline candidates; resolve every valid target; inherit all source trajectory links; and only treat the operation as invalid when no target can be resolved. This multi-target parser and replacement-link migration are covered by targeted tests; I did not rerun the full 59-session corpus after these small graph-rewrite follow-ups. The latest follow-up also records the superseded file base digest during graph rewrite, so if the old card gains new links before lock/apply, exact apply retries with a refreshed cleanup plan rather than mutating an endpoint that was not part of the original lock set.TAU Runner Contract
The TAU runner now supports and records the intended client-side write mode:
--strict-preflightchecks the running OpenViking config before a matrix run. The cached corpus manifest recordscorpus_session_commit_concurrency,corpus_prepare_mode, stable input-order rows, and the expected OpenViking memory config so mismatched reruns fail fast instead of silently reusing an incompatible corpus.Follow-up / Open Questions
supersedesnames with stable candidate URI/id when the extractor can expose replace candidates safely.stalenessas telemetry / evaluation-policy language for now: record source policy, patch base, apply version, window wait/order, and retry/conflict reason before turning it into a product default budget.Tests
Latest validation:
source /Users/bytedance/Documents/agent-harness/scripts/load_local_env.sh && uv run --with ruff ruff check openviking/session/compressor_v2.py tests/session/memory/test_compressor_v2.py openviking/session/memory/memory_updater.py tests/session/memory/test_memory_updater.pysource /Users/bytedance/Documents/agent-harness/scripts/load_local_env.sh && uv run --with ruff ruff format --check openviking/session/compressor_v2.py tests/session/memory/test_compressor_v2.py openviking/session/memory/memory_updater.py tests/session/memory/test_memory_updater.pyOPENVIKING_CONFIG_FILE=tests/api_test/ov.conf.template .venv/bin/python -m pytest tests/session/memory/test_compressor_v2.py::test_source_trajectory_links_attach_before_exact_lock tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_tracks_deleted_file_version_for_exact_retry tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_consumes_field_only_after_resolved tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_migrates_peer_links_to_replacement_uri tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_accepts_multiple_replaced_experiences tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_keeps_partial_multi_target_resolution tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_prefers_exact_name_before_splitting tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_retries_when_prefetched_target_disappears tests/session/memory/test_compressor_v2.py::test_resolve_supersedes_unresolved_target_marks_operations_invalid tests/session/memory/test_memory_updater.py::TestMemoryUpdater::test_apply_operations_cleans_peer_backlinks_before_delete tests/session/memory/test_memory_updater.py::TestMemoryUpdater::test_apply_operations_migrates_replacement_links_and_cleans_old_uri tests/session/memory/test_memory_updater.py::TestMemoryUpdater::test_apply_operations_heals_preserved_forward_links_on_upsert tests/session/memory/test_memory_updater.py::TestMemoryUpdater::test_apply_operations_cleans_links_added_after_delete_snapshot(14 passed).venv/bin/python -m compileall -q openviking/session/compressor_v2.py tests/session/memory/test_compressor_v2.py openviking/session/memory/memory_updater.py tests/session/memory/test_memory_updater.pyOPENVIKING_CONFIG_FILE=$(mktemp-empty-config) .venv/bin/python -m pytest tests/server/test_api_stats_memory_graph.py -q(2 passed)OPENVIKING_CONFIG_FILE=tests/api_test/ov.conf.template .venv/bin/python -m pytest tests/server/test_api_stats_memory_graph.py tests/client/test_rebuild_clients.py -q(12 passed)