Skip to content

perf(atomic-memory): send each Source body once to extraction - #2163

Open
jasondeng1997 wants to merge 1 commit into
oceanbase:masterfrom
jasondeng1997:perf/atomic-memory-evidence-dedup
Open

jasondeng1997 wants to merge 1 commit into
oceanbase:masterfrom
jasondeng1997:perf/atomic-memory-evidence-dedup

Conversation

@jasondeng1997

Copy link
Copy Markdown
Contributor

What this changes

project_atomic_memory_evidence() built one AtomicMemoryEvidence per Source with two independent body carriers: content, and a full model_dump() of the stored Source in source_metadata. The model therefore received every Source body two or three times, and the repeated copies spent the atomic_memory_input_tokens_limit budget on text it had already read.

source_metadata is now compacted at the projection site by _compact_source_metadata(): it keeps Source provenance and drops only the values that repeat the body.

Representation Body copies before after
native (ContentSource via HTTP capture_content_source) content + source_metadata.content content
observation (SourceObservation via the SDK transport) content + source_metadata.payload.content + the text-evidence projection content

Measured with character_token_estimator() on the complete AtomicMemoryExtractionInput for one 3,982-character Source (996 tokens on its own):

Representation before after cut Sources fitting 24,000
native 2,142 tokens 1,143 tokens −46.6 % 11 → 20
observation (text projection) 3,243 tokens 1,195 tokens −63.2 % 7 → 20

The remaining overhead is provenance, not body: 147 tokens for a native Source and 199 for an observation.

What is kept

  • evidence_id, source_ref, journal_position, content, via_artifact, and the AtomicMemoryEvidence schema are unchanged.
  • Every non-body metadata key survives: top-level metadata, payload.metadata, name, description, definition_version, materialization, working_note, source_type, definition_fingerprint, and any further payload key.
  • wire_content is dropped only when it is the same value as the body. A structured wire body is distinct evidence and stays.
  • A second projection keeps its keys; only a content key inside its value is removed.
  • The text-evidence projection entry itself is dropped, but only when content is that projection — the entry then carries nothing the response does not already have.
  • Source storage, the journal, and Artifact lineage are untouched: this changes what the extraction prompt is given, not what the Runtime keeps.

Tests

tests/builtin/artifacts/atomic_memory/                        90 passed, 3 skipped
tests/test_extraction_capabilities.py
tests/e2e/test_extraction_diagnostics.py
tests/e2e/test_atomic_memory_generation_merge.py

New tests/builtin/artifacts/atomic_memory/test_extraction_evidence.py pins the invariant "one Source body, one copy" for all four shapes — native, native with a repeated wire_content, observation with a text projection, observation without one — and asserts the surviving provenance field by field, so a future compaction cannot delete evidence while passing the body-count check.

ruff check, ruff format --check and ty check are clean on both files.

Out of scope

Nothing else in #2137 is required for this: there is no contract, config or storage change. _reduce_window() behaviour is unchanged — fewer redundant bytes simply mean it triggers less often for the same batch.

`project_atomic_memory_evidence` gave the model two independent body carriers: `content`, and a
full dump of the stored Source in `source_metadata`. Every Source body therefore arrived twice, and
three times for an observation, where `payload.content` and the text-evidence projection repeat it
again. Those copies spend the input token budget on text the model has already read, so a batch
crosses `atomic_memory_input_tokens_limit` and `_reduce_window()` bisects it instead of extracting.

Compact the metadata at the projection site. Only values that repeat the body are removed:
`ContentSource.content`, `payload.content`, the `content` key inside a projection value, and
`wire_content` when it is the same value as the body. Every other provenance key stays, including
`metadata`, `payload.metadata`, `definition_version` and `working_note`.

One 3,982-character Source, measured on the complete extraction input with the character estimator:
2,142 -> 1,143 tokens for a native Source (46.6% less) and 3,243 -> 1,195 for an observation (63.2%
less). At 24,000 tokens that is 11 -> 20 Sources per window for the HTTP capture path.

Source storage, the journal and Artifact lineage are untouched; this changes what the prompt sees.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 08:37

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

if not isinstance(value, SourceObservation):
body = dumped.get("content")
dumped.pop("content", None)
if body is not None and dumped.get("wire_content") == body:

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.

[P2] 结构化 wire_content 仍把同一份正文送两遍,标题的「每份 Source 只送一次」只对字符串 wire 成立

:146 的判据是 dumped.get("wire_content") == body,即 dict 与 str 比较。真实写入路径是
records.create_source 的 content = _canonical_source_text(content)(RFC 8785 规范化 JSON)+
wire_content = <原始 JsonValue>;集成侧传的是结构化 JSON,因此这里恒为 False,整个 wire_content
连同其中的正文一起留在 source_metadata 里,而 content 已经是同一段文本的规范化 JSON。

探针 probes/probe_structured_wire.py(同一 Head 树,走 project_atomic_memory_evidence 公开入口后
AtomicMemoryExtractionInput.model_dump_json() 计数):

  • wire_content = {"text": <body>, "kind": "note"} ⇒ 正文出现 2 次,input 738 bytes(wire_content 保留);
  • wire_content = <body>(字符串)⇒ 1 次,553 bytes;
  • wire_content = {"messages": [...]}(不含正文)⇒ 0 次,580 bytes。

test_wire_bytes_that_carry_something_else_survive 用的正是第三种,所以用例自洽,但覆盖不到真实采集形态。

建议:按「wire 的规范化文本是否等于 content」判定(复用 _canonical_source_text / rfc8785.dumps),
而不是比较对象与字符串;并补一条 {"text": <body>, ...} 结构性 wire 的用例。

另需协调:#2139 修改的是同一个 project_atomic_memory_evidence 与同一插入点,去重边界与本 PR 不一致
(本 PR 无条件删 payload.content,并对任意 projection 的 value.content 一律剥离;#2139 仅在等于投影正文时删、
其余 projection 完整保留;结构化 wire_content 反而是 #2139 删、本 PR 保留:同场景实测 #2139 为 1 次 / 586 bytes)。
两者只能合入其一,后合的一方会在同段落必然冲突,建议在 PR 正文写明与 #2139、Issue #2137 的协调结论。

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

Please preserve distinct Source evidence during compaction. The current implementation can omit facts from extraction while still advancing the Source cursor.

continue
projected = projection.get("value")
if isinstance(projected, dict):
projection = {**projection, "value": {key: item for key, item in projected.items() if key != "content"}}

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.

[P2] Preserve content that differs from the extraction body

This drops content from every projection by name, and line 153 does the same to payload.content, even when those values differ from the selected text evidence. With a valid registered Source whose text projection is Use canary deployment., raw content is The deployment region must be eu-west-1., and another projection's content is The deployment budget is 37 units., submitting to SQLite and then flushing removes both independent facts from the extractor input while advancing the cursor to 1. Both facts survive at base 88899876.

Only remove values proven redundant with the actual extraction body. The native branch at line 145 needs the same safeguard when adapter.read() returns a different value.

@AlexStocks AlexStocks added the 1.4.1 v1.4.1 release tracking label Oct 21, 2026

This branch has not been deployed

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

Labels

1.4.1 v1.4.1 release tracking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants