# Issues about the entry points of RAG knowledge

**URL:** <https://forum.langchain.com/t/issues-about-the-entry-points-of-rag-knowledge/3052>\
**Category:** LangGraph\
**Tags:** intro-to-langgraph, python-help\
**Created:** [February 28, 2026, 1:08pm UTC](https://forum.langchain.com/t/issues-about-the-entry-points-of-rag-knowledge/3052 "2026-02-28T13:08:48Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Huimin-station](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/huimin-station/32/2466_2.png) [@Huimin-station](https://forum.langchain.com/u/Huimin-station)\
**Post date:** [February 28, 2026, 1:08pm UTC](https://forum.langchain.com/t/issues-about-the-entry-points-of-rag-knowledge/3052/1 "2026-02-28T13:08:48Z")

</div>

```python
class MessageState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]
    knowledge: list[AIMessage]

```

As follows, am I supposed to directly concatenate the messages in state with my knowledge and send them?

But after the model receives my information, it always assumes that the knowledge was actively told to it by me, just like user input. The model always says, ‘According to the information you provided…’ but I don’t know the relevant knowledge. Even if I wrapped it with AImessage.

```python
async def model_with_tools_node(state:dict):
    model_list_get = await model_list()
    return {
        "messages":[
            model_list_get[1].invoke([SystemMessage(content=model_with_tools_prompt)]+state["messages"]+state["knowledge"])
        ]
    }

```

**Or maybe it’s just because of my prompt?**

---

<div class="post-metadata">

**Author:** ![Huimin-station](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/huimin-station/32/2466_2.png) [@Huimin-station](https://forum.langchain.com/u/Huimin-station)\
**Post date:** [February 28, 2026, 1:30pm UTC](https://forum.langchain.com/t/issues-about-the-entry-points-of-rag-knowledge/3052/2 "2026-02-28T13:30:27Z")

</div>

**What confuses me is that, clearly it’s AI messages, yet the model still thinks the knowledge is provided by the user?**

---

<div class="post-metadata">

**Author:** ![pawel-twardziak](https://yyz1.discourse-cdn.com/flex007/user_avatar/forum.langchain.com/pawel-twardziak/32/960_2.png) [@pawel-twardziak](https://forum.langchain.com/u/pawel-twardziak)\
**Post date:** [February 28, 2026, 4:47pm UTC](https://forum.langchain.com/t/issues-about-the-entry-points-of-rag-knowledge/3052/3 "2026-02-28T16:47:55Z")

</div>

Hi @Huimin-station

the root mistake is storing retrieved knowledge as `AIMessage`.

AIMessage semantically means “the LLM said this in a prior turn.” When you store retrieved knowledge as AIMessage and then append it after state[“messages”], you construct this message sequence for the model:

SystemMessage → “You are a helpful assistant…”  
HumanMessage → “What is X?”  
AIMessage → [retrieved knowledge chunk 1]  
AIMessage → [retrieved knowledge chunk 2]

From the model’s perspective, the conversation is already complete - a Human turn followed, by AI responses. It cannot tell why it’s being invoked again, so it tries to reconcile the context by reattributing those AIMessage chunks to the user. That’s exactly why it says “According to the information you provided…” - it’s trying to make narrative sense of content it found in an unexpected position.

LangChain message roles:

**type** - **role** - **meaning**  
SystemMessage - system - Application-level instructions/context  
HumanMessage - user - What the human said  
AIMessage - assistant - What the model previously said  
ToolMessage - tool - Output of a tool call

**Solution 1: Inject into SystemMessage**

Models universally understand system role as application context, not user-provided data:

```py
async def model_with_tools_node(state: dict):
    model_list_get = await model_list()
  
    knowledge_text = "\n\n".join(
        msg.content for msg in state.get("knowledge", [])
    )
  
    system_content = model_with_tools_prompt
    if knowledge_text:
        system_content += (
            "\n\nUse the following retrieved context to answer the user's question:\n\n"
            + knowledge_text
        )
  
    return {
        "messages": [
            model_list_get[1].invoke(
                [SystemMessage(content=system_content)] + state["messages"]
            )
        ]
    }

```

The model will never say “you provided” because SystemMessage is clearly application-injected. See the LangSmith RAG [quickstart](https://docs.langchain.com/langsmith/observability-quickstart#3-define-your-application).

**Solution 2: Use the Official Agentic RAG Pattern with ToolMessage**

If you’re building an agent (which the node-based LangGraph structure suggests), use the proper tool call flow. Retrieved content should arrive as ToolMessage it’s the correct semantic type for “something a tool returned”:

SystemMessage → “You are a helpful assistant…”  
HumanMessage → “What is X?”  
AIMessage → [tool\_call: retrieve(“X”)]  
ToolMessage → [retrieved content] ← unambiguously a tool result

Aalso in the official LangGraph RAG tutorial:

```py
retriever_tool = create_retriever_tool(retriever, "retrieve_docs", "...")
retrieve = ToolNode([retriever_tool])
workflow.add_node("retrieve", retrieve)

```

`ToolNode` handles wrapping results in ToolMessage automatically. The model correctly understands this as tool-sourced knowledge.

**Solution 3: Middleware Pattern (augment the HumanMessage)**

The LangChain RAG chains [docs](https://docs.langchain.com/oss/python/langchain/rag#returning-source-documents) show a `before_model` middleware that augments the user’s message directly with the retrieved context:

```py
class RetrieveDocumentsMiddleware(AgentMiddleware[State]):
    def before_model(self, state):
        last_message = state["messages"][-1]
        docs = vector_store.similarity_search(last_message.text)
        augmented = f"{last_message.text}\n\nContext:\n{'\n\n'.join(d.page_content for d in
 docs)}"
        return {"messages": [last_message.model_copy(update={"content": augmented})]}

agent = create_agent(model, tools=[], middleware=[RetrieveDocumentsMiddleware()])

```

* * *

## Summary

| Symptom | Root cause | Fix |
| --- | --- | --- |
| “According to information you provided…” | AIMessage placement creates a fake completed exchange; model reattributes content to the user | Inject into SystemMessage |
| Knowledge placed after `state["messages"]` | Appended after the HumanMessage, looks like prior AI turns | Keep only `state["messages"]` in the call; merge knowledge into SystemMessage |
| `knowledge: list[AIMessage]` | Wrong message type for retrieved context | Store as plain text and inject into system prompt, or use ToolMessage via ToolNode |

## References

- [LangChain RAG chains (Python)](https://docs.langchain.com/oss/python/langchain/rag)
- [Build a custom RAG agent with LangGraph (Python)](https://docs.langchain.com/oss/python/langgraph/agentic-rag)
- [LangChain messages guide](https://docs.langchain.com/oss/python/langchain/messages)
- [LangSmith observability quickstart - RAG example](https://docs.langchain.com/langsmith/observability-quickstart)
- [langgraph/examples/rag/langgraph\_agentic\_rag.ipynb at main · langchain-ai/langgraph · GitHub](https://github.com/langchain-ai/langgraph/blob/main/examples/rag/langgraph_agentic_rag.ipynb)
- [langgraph/libs/prebuilt/langgraph/prebuilt/chat\_agent\_executor.py at main · langchain-ai/langgraph · GitHub](https://github.com/langchain-ai/langgraph/blob/main/libs/prebuilt/langgraph/prebuilt/chat_agent_executor.py)
