Skip to content

middleware and Custom state_schema are mutually exclusive in create_agent() #33217

Description

Checked other resources

  • This is a feature request, not a bug report or usage question.
  • I added a clear and descriptive title that summarizes the feature request.
  • I used the GitHub search to find a similar feature request and didn't find it.
  • I checked the LangChain documentation and API reference to see if this feature already exists.
  • This is not related to the langchain-community package.

Feature Description

When using create_agent(), the middleware parameter and state_schema parameter cannot be used together. This creates a significant limitation for RAG applications that need both:

  1. Custom state - to store retrieved documents for citation purposes
  2. Middleware - for message summarization to manage context window

The assertion at line 1150 in langchain/agents/react_agent.py enforces this:

assert state_schema is None  # noqa: S101

Use Case

Building a RAG agent that needs to:

  • Retrieve documents from a vector store
  • Provide citations by accessing the retrieved documents after agent execution
  • Manage conversation history with summarization to fit within context windows

Proposed Solution

  1. Design Rationale: Why are middleware and state_schema mutually exclusive? Is there a technical limitation or just an implementation detail?

  2. Feature Request: Could middleware support custom state schemas? For example:

    agent = create_agent(
        model=llm,
        tools=tools,
        middleware=[SummarizationMiddleware(model=llm)],
        state_schema=CustomState,  # Should work together
        context_schema=Context,
    )

Alternatives Considered

1. Tool Message Artifacts (as suggested in docs)

@tool(response_format="content_and_artifact")
def retrieve_context(query: str):
    retrieved_docs = vector_store.similarity_search(query, k=2)
    serialized = "\n\n".join(...)
    return serialized, retrieved_docs

Issue: When multiple tools exist, you must navigate the message history to find the correct ToolMessage with the retrieval artifacts. This becomes fragile and complex.

2. Custom State via Command

def search_knowledge_base(
    state: Annotated[RAGState, InjectedState],
    tool_call_id: Annotated[str, InjectedToolCallId],
) -> Command:
    docs = await retrieve_documents(...)
    return Command(update={
        "retrieved_documents": docs,
        "messages": [ToolMessage(content, tool_call_id=tool_call_id)]
    })

Issue: This requires state_schema=RAGState in create_agent(), which is incompatible with middleware=[SummarizationMiddleware(...)].

3. Post-Model Hook

Could potentially attach documents to message metadata, but still doesn't solve the core limitation.

Additional Context

This limitation affects any RAG application that needs:

  • Clean access to retrieved documents for citations
  • Conversation history management
  • Multiple tools (where artifact navigation becomes complex)

Would appreciate guidance on:

  • The reasoning behind this design decision
  • Any plans to support both features together
  • Recommended patterns for this common RAG use case

Metadata

Metadata

Assignees

No one assigned

    Labels

    externalfeature requestRequest for an enhancement / additional functionality

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions