Skip to main content
This lesson walks through a compact agent demo that connects a search tool (Tavily) with an LLM (ChatOpenAI). The goal is to demonstrate how message history, a prompt with an agent scratchpad, tools, and a runnable agent fit together to handle multi-step and context-aware queries. Some module names or imports may change over time—if you run into import errors, consult the latest LangChain Python SDK docs: https://python.langchain.com/en/latest/.

Overview

  • Build a chat prompt that includes a system instruction, a conversation history placeholder, the user input placeholder, and an agent_scratchpad placeholder.
  • Create an LLM (ChatOpenAI) and attach the Tavily search tool for web results.
  • Wrap an AgentExecutor with RunnableWithMessageHistory so each session can preserve history and the agent scratchpad.
  • Invoke the runnable agent with per-session IDs to support follow-up questions.

Key components

Imports and basic setup

We need imports for prompt templates, message history, runnables, the LLM, the Tavily search tool, and agent utilities. The agent_scratchpad placeholder is used as the agent’s temporary workspace.
Explanation:
  • ChatPromptTemplate.from_messages(...) composes the prompt with system instructions, a chat_history placeholder, the user input placeholder ({input}), and the agent_scratchpad.
  • search is the Tavily search tool used by the agent to retrieve web results.
  • MessagesPlaceholder and ChatMessageHistory enable in-memory chat history persistence for a session.
Using MessagesPlaceholder("agent_scratchpad") gives the agent a workspace to append intermediate reasoning and tool calls. This helps the LLM and the tool orchestrator maintain context across a single turn and between turns when history is preserved.
Be careful with API keys in code or logs. Prefer environment variables, secrets managers, or encrypted stores. If a tool reads the key from the environment, do not hardcode it in production.

Create LLM, tools, and the message history

Create the LLM instance, assemble the tools list, and initialize an in-memory ChatMessageHistory for this demo.

Agent creation and wrapping with message history

  • Use create_tool_calling_agent to construct a tool-calling agent with the LLM, tools, and prompt.
  • Use AgentExecutor to manage execution and orchestrate tool calls.
  • Wrap the executor with RunnableWithMessageHistory to provide session-aware message history retrieval.
The example below uses a simple lambda that returns the same ChatMessageHistory for any session_id. In production, map each session_id to its own persisted ChatMessageHistory (Redis, database, etc.).

Invoking the agent (examples)

When invoking the runnable agent, pass the user input using the input key and include a config that carries a session_id. RunnableWithMessageHistory uses this session_id to look up and persist the chat history across calls. Say hello to the agent:
Possible returned structure (example):
Ask the agent a question that requires search:
Expected (example) output:
Follow-up question that uses conversation context and search:
Expected (example) output:

Example: compute days until the tournament

A follow-up that relies on context and a date calculation:
You may observe the LLM returning an incorrect or imprecise numeric answer (for example, saying “17 days” when the current date or calculation is not accurate). This highlights a limitation.
LLMs are not always reliable for precise arithmetic or time-based calculations unless you explicitly delegate the calculation to a deterministic tool (like a Python REPL or a date utility). For exact answers (e.g., “days until a date”), add a deterministic tool to the agent that performs the arithmetic and returns the correct result.
  • A plain search tool typically returns raw documents, snippets, or URLs.
  • The agent can call the search tool, aggregate results, and ask the LLM to synthesize a concise, user-friendly answer.
  • With session-aware message history, follow-up questions that refer to earlier turns are handled naturally because the prompt includes previous chat and the agent scratchpad.

Extending this agent

To make the agent more robust and capable of deterministic computation:
  • Add a Python REPL or date-calculation tool so the agent can delegate numeric or date arithmetic to a deterministic environment.
  • Persist ChatMessageHistory per session_id using Redis, a database, or another storage backend for production usage.
  • Add more retrieval tools or structured data sources (APIs, knowledge bases) to broaden the agent’s factual coverage.

Conclusion and next steps

In this lesson we built a session-aware agent that:
  • Uses a search tool (Tavily) to fetch web results.
  • Uses an LLM (ChatOpenAI) to synthesize and present answers.
  • Persists message history via RunnableWithMessageHistory.
  • Uses an agent_scratchpad in the prompt to manage intermediate reasoning and tool calls.
Next steps:
  • Add a deterministic tool (Python REPL or date calculator) to handle precise calculations.
  • Implement a per-session persistent store for chat history (e.g., Redis).
  • Explore multi-tool orchestration and richer prompts to improve answer reliability.

Watch Video