Skip to content

Down the line SequentialAgent has access to all previous conversations #2207

@jorism1993

Description

@jorism1993

Describe the bug
Down the line SequentialAgent has access to all previous conversations through llm_request.contents, instead of running as a standalone agent. Even when setting include_contents='none', it still has access to the last llm_request.contents part, leading to context rot.

To Reproduce

from typing import Optional

from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.agents.callback_context import CallbackContext
from google.genai.types import Content, Part
from google.adk.models import LlmResponse, LlmRequest

import asyncio

GEMINI_MODEL = "gemini-2.5-flash"

# Constants for session management
APP_NAME = 'code_pipeline_app'
USER_ID = 'test_user'
SESSION_ID = 'code_pipeline_session'


# --- Define the Callback Function ---
def set_last_user_message_callback(
        callback_context: CallbackContext,
        llm_request: LlmRequest
) -> Optional[LlmResponse]:
    """Inspects/modifies the LLM request or skips the call."""

    # last_user_message = ""
    last_message = llm_request.contents[-1].parts[0]

    import pdb
    pdb.set_trace()

    return None


# --- 1. Define Sub-Agents for Each Pipeline Stage ---

# Code Writer Agent
code_writer_agent = LlmAgent(
    name="CodeWriterAgent",
    model=GEMINI_MODEL,
    instruction="""You are a Python Code Generator.
Based *only* on the user's request, write Python code that fulfills the requirement.
Output *only* the complete Python code block, enclosed in <code> tags (<code>python ... </code>). 
Do not add any other text before or after the code block.
""",
    description="Writes initial Python code based on a specification.",
    output_key="generated_code"
)

# Code Reviewer Agent
code_reviewer_agent = LlmAgent(
    name="CodeReviewerAgent",
    model=GEMINI_MODEL,
    instruction="""You are an expert Python Code Reviewer. 
    Your task is to provide constructive feedback on the provided code.

    **Code to Review:**
    <code>
    {generated_code}
    </code>

**Review Criteria:**
1.  **Correctness:** Does the code work as intended? Are there logic errors?
2.  **Readability:** Is the code clear and easy to understand? Follows PEP 8 style guidelines?
3.  **Efficiency:** Is the code reasonably efficient? Any obvious performance bottlenecks?
4.  **Edge Cases:** Does the code handle potential edge cases or invalid inputs gracefully?
5.  **Best Practices:** Does the code follow common Python best practices?

**Output:**
Provide your feedback as a concise, bulleted list. Focus on the most important points for improvement.
If the code is excellent and requires no changes, simply state: "No major issues found."
Output *only* the review comments or the "No major issues" statement.
""",
    description="Reviews code and provides feedback.",
    output_key="review_comments",
)

# Code Refactorer Agent
code_refactorer_agent = LlmAgent(
    name="CodeRefactorerAgent",
    model=GEMINI_MODEL,
    instruction="""You are a Python Code Refactoring AI.
Your goal is to improve the given Python code based on the provided review comments.

  **Original Code:**
  <code>
  {generated_code}
  </code>

  **Review Comments:**
  {review_comments}

**Task:**
Carefully apply the suggestions from the review comments to refactor the original code.
If the review comments state "No major issues found," return the original code unchanged.
Ensure the final code is complete, functional, and includes necessary imports and docstrings.

**Output:**
Output *only* the final, refactored Python code block, enclosed in <code> tags (<code>python ... </code>). 
Do not add any other text before or after the code block.
""",
    description="Refactors code based on review comments.",
    output_key="refactored_code",
    before_model_callback=set_last_user_message_callback,
    include_contents='none',
)

# --- 2. Create the SequentialAgent ---
code_pipeline_agent = SequentialAgent(
    name="CodePipelineAgent",
    sub_agents=[code_writer_agent, code_reviewer_agent, code_refactorer_agent],
    description="Executes a sequence of code writing, reviewing, and refactoring.",
)

# For ADK tools compatibility, the root agent must be named `root_agent`
root_agent = code_pipeline_agent

# --- 3. Initialize Session Service and Create Session ---
session_service = InMemorySessionService()

# Create session for the pipeline
# Session creation is asynchronous; it will be performed inside `main()` where we can `await` the coroutine.

# Create runner
pipeline_runner = Runner(
    agent=code_pipeline_agent,
    app_name=APP_NAME,
    session_service=session_service
)


# --- 4. Define Pipeline Interaction Logic ---
async def run_code_pipeline(query: str):
    """Runs the code pipeline and prints detailed results for each stage."""
    print(f"\n>>> Running Code Pipeline | Query: {query}")

    user_content = Content(role='user', parts=[Part(text=query)])

    final_response_content = "No final response received."

    # Run the pipeline
    async for event in pipeline_runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=user_content):
        # Uncomment for detailed event logging
        # print(f"Event: {event.type}, Author: {event.author}")

        if event.is_final_response() and event.content and event.content.parts:
            final_response_content = event.content.parts[0].text

    print(f"<<< Pipeline Final Response: {final_response_content}")

    # Get the current session to inspect intermediate results
    current_session = await session_service.get_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)

    if current_session is None:
        print("Warning: Session not found when retrieving stage results.")
        return

    # Print results from each stage
    print("\n--- Pipeline Stage Results ---")

    # Generated Code
    generated_code = current_session.state.get("generated_code")
    if generated_code:
        print("1. Generated Code:")
        print(generated_code)
        print("-" * 50)

    # Review Comments
    review_comments = current_session.state.get("review_comments")
    if review_comments:
        print("2. Review Comments:")
        print(review_comments)
        print("-" * 50)

    # Refactored Code
    refactored_code = current_session.state.get("refactored_code")
    if refactored_code:
        print("3. Refactored Code:")
        print(refactored_code)
        print("-" * 50)


# --- 5. Main Execution ---
async def main():
    print("=== Code Pipeline Demo ===")

    # Ensure the session exists before running the pipeline.
    # If the session already exists, this call will simply retrieve it.
    await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)

    # Test with different code requests
    await run_code_pipeline("Write some code to add two numbers together in Python.")

    # print("\n" + "=" * 60 + "\n")
    #
    # await run_code_pipeline("Create a function that calculates the factorial of a number using recursion.")
    #
    # print("\n" + "=" * 60 + "\n")
    #
    # await run_code_pipeline("Write a class for a simple calculator with basic arithmetic operations.")


if __name__ == "__main__":
    asyncio.run(main())

See code example to run. Inspect variables in the callback

Expected behavior
I expect the agent to run as a standalone agent, and pass information through the {} in the prompts. I expect it to not have access to the previous prompts and answers.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementation

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions