Skip to content

VertexAiMemoryBankService sends the model's thought parts to Memory Bank #7850

Closed

Description

@hyeonsang010716

🔴 Required Information

Describe the Bug:
VertexAiMemoryBankService sends the model's thought parts (Part.thought=True) to Memory Bank as conversation content. Both add_session_to_memory and add_events_to_memory pass event.content through unchanged, on the ingest_events path and on the memories.generate path. Memory Bank then extracts memories from the model's private reasoning as if it were part of the conversation, so a guess the model made about the user can be stored as a fact about them.

An event whose only text is a thought is not filtered out either, because _is_empty_content counts any part.text as content.

The other memory services already exclude thoughts: InMemoryMemoryService and VertexAiRagMemoryService (#7473), and SqliteMemoryService (#7691). #7830 reports the same problem in FirestoreMemoryService.

Steps to Reproduce:

  1. Build a session with two model events: one with parts [Part(text="the user sounds anxious, keep the reply short", thought=True), Part(text="Here is the answer.")], and one whose only part is a thought.
  2. Call add_session_to_memory(session) with the agentplatform.Client mocked the way the existing unit tests do.
  3. Inspect the events passed to memory_banks.ingest_events.

Expected Behavior:
The first event is sent with only Here is the answer., and the thought-only event is not sent.

Observed Behavior:
Both events are sent with their thought parts:

901 [('the user sounds anxious, keep the reply short', True), ('Here is the answer.', None)]
902 [('the user sounds anxious, keep the reply short', True)]

add_events_to_memory with a generate-only key such as revision_ttl sends the same content to memories.generate.

Environment Details:

  • ADK Library Version (pip show google-adk): 2.13.0 (also on main at 4f8e4ae)
  • Desktop OS: macOS
  • Python Version (python -V): 3.14.7

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: any thinking model (e.g. Gemini with include_thoughts=True)

🟡 Optional Information

Additional Context:
In src/google/adk/memory/vertex_ai_memory_bank_service.py:

  • _add_events_to_memory_from_events and _add_events_to_memory_via_ingest build each direct-contents event with content=event.content.
  • _should_filter_out_event relies on _is_empty_content, which does not look at part.thought.

I verified this with the mocked client; I don't have a Memory Bank instance.

Minimal Reproduction Code:

import asyncio
from unittest import mock

from google.adk.events.event import Event
from google.adk.memory import VertexAiMemoryBankService
from google.adk.sessions.session import Session
from google.genai import types

THOUGHT = "the user sounds anxious, keep the reply short"


async def main():
  with mock.patch("agentplatform.Client") as client:
    aio = mock.MagicMock()
    aio.memory_banks.ingest_events = mock.AsyncMock()
    client.return_value = mock.MagicMock(aio=aio)
    service = VertexAiMemoryBankService(
        project="p", location="us-central1", agent_engine_id="123"
    )
    session = Session(
        app_name="app",
        user_id="u",
        id="s",
        last_update_time=0,
        events=[
            Event(
                id="901",
                author="agent",
                content=types.Content(
                    role="model",
                    parts=[
                        types.Part(text=THOUGHT, thought=True),
                        types.Part(text="Here is the answer."),
                    ],
                ),
            ),
            Event(
                id="902",
                author="agent",
                content=types.Content(
                    role="model",
                    parts=[types.Part(text=THOUGHT, thought=True)],
                ),
            ),
        ],
    )
    await service.add_session_to_memory(session)
    await asyncio.sleep(0)
    source = aio.memory_banks.ingest_events.call_args.kwargs[
        "direct_contents_source"
    ]
    for event in source.events:
      print(event.event_id, [(p.text, p.thought) for p in event.content.parts])


asyncio.run(main())

How often has this issue occurred?:

  • Always (100%), with thinking models

Activity

  1. added a commit that references this issue on Oct 29, 2026
    733861f
  2. added
    services[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc
    on Oct 30, 2026
  3. surajksharma07 commented on Oct 30, 2026

    @surajksharma07
    Collaborator

    Thanks @hyeonsang010716, really clear report. Reproduced this on 2.13.0 with a mocked client and it hits both the ingest_events and memories.generate paths.

    As a workaround for now, subclass VertexAiMemoryBankService and drop the thought parts from each event before passing it to super()._add_events_to_memory_from_events. That kept them out on both paths and thought-only events got skipped too. Could you check if that works on your side?

    Before #7851 goes for review please give it a good round of testing and make sure the PR covers both paths, drops thought-only events and doesn’t modify the original session events.

  4. hyeonsang010716 commented on Oct 30, 2026

    @hyeonsang010716
    ContributorAuthor

    Thanks for reproducing it. I tried the workaround on main with the mocked client, and it works on both paths: only Here is the answer. is sent, the thought-only event is skipped, and the session's events are unchanged. Since it overrides a private method, it may need updating if that method changes.

    I added tests to #7851 so each of those is checked on both paths: thought parts dropped, thought-only events not sent, and the caller's events left unchanged. All new tests fail on main and pass with the change. Changing the content in place instead of copying it fails the "unchanged" checks on both paths.

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

Metadata

Metadata

Labels

services[Component] This issue is related to runtime services, e.g. sessions, memory, artifacts, etc

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions