An agent becomes useful when a chat model can choose an action, receive the action's result, and continue toward a final response. LangChain provides that model-and-tool loop through create_agent, so a small deterministic program can prove the complete execution path before an application connects to a hosted model or an external service.

The local program uses a small deterministic BaseChatModel implementation. Its first call requests a temperature-conversion tool, while its second call reads the resulting ToolMessage and formats that value into the final answer. This removes API keys and network responses from the smoke test while still exercising the agent's real tool node and message state.

The final state must contain the requested tool name, its calculated value, and the agent reply. A provider-backed chat model can replace the deterministic model later without changing the tool definition, create_agent call, or message-shaped input.

Steps to create a LangChain agent:

  1. Upgrade LangChain in the active project environment.
    $ python -m pip install --upgrade langchain

    LangChain requires Python 3.10 or newer. Provider integrations remain separate packages when the deterministic model is replaced.
    Related: How to install LangChain with pip

  2. Create agent_demo.py with the message imports and temperature tool.
    agent_demo.py
    from typing import Any
     
    from langchain.agents import create_agent
    from langchain.messages import AIMessage, ToolMessage
    from langchain.tools import tool
    from langchain_core.callbacks import CallbackManagerForLLMRun
    from langchain_core.language_models.chat_models import BaseChatModel
    from langchain_core.messages import BaseMessage
    from langchain_core.outputs import ChatGeneration, ChatResult
     
     
    @tool
    def convert_celsius_to_fahrenheit(celsius: float) -> float:
        """Convert a Celsius temperature to Fahrenheit."""
        return celsius * 9 / 5 + 32
  3. Add the deterministic tool-result model below the temperature tool.
    class ToolResultModel(BaseChatModel):
        @property
        def _llm_type(self) -> str:
            return "tool-result-model"
     
        def bind_tools(self, tools, *, tool_choice=None, **kwargs):
            return self
     
        def _generate(
            self,
            messages: list[BaseMessage],
            stop: list[str] | None = None,
            run_manager: CallbackManagerForLLMRun | None = None,
            **kwargs: Any,
        ) -> ChatResult:
            tool_message = next(
                (
                    message
                    for message in reversed(messages)
                    if isinstance(message, ToolMessage)
                ),
                None,
            )
     
            if tool_message is None:
                response = AIMessage(
                    content="",
                    tool_calls=[
                        {
                            "name": "convert_celsius_to_fahrenheit",
                            "args": {"celsius": 20},
                            "id": "call_temperature",
                        }
                    ],
                )
            else:
                fahrenheit = float(tool_message.content)
                response = AIMessage(
                    content=(
                        "20 degrees Celsius equals "
                        f"{fahrenheit:g} degrees Fahrenheit."
                    )
                )
     
            return ChatResult(generations=[ChatGeneration(message=response)])
     
     
    model = ToolResultModel()

    The bind_tools() override supplies the binding surface expected by create_agent. The deterministic branches make this a repeatable local agent test, not a substitute for a provider model in production.

  4. Append the agent construction below the model definition.
    agent = create_agent(
        model=model,
        tools=[convert_celsius_to_fahrenheit],
        system_prompt="Use the temperature tool for conversions.",
    )
  5. Append the invocation and message-state inspection below the agent definition.
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "Convert 20 C to Fahrenheit."}]}
    )
     
    tool_call = next(
        call
        for message in result["messages"]
        if isinstance(message, AIMessage)
        for call in message.tool_calls
    )
    tool_message = next(
        message for message in result["messages"] if isinstance(message, ToolMessage)
    )
    final_reply = result["messages"][-1].content
    expected_reply = (
        "20 degrees Celsius equals "
        f"{float(tool_message.content):g} degrees Fahrenheit."
    )
    assert final_reply == expected_reply, "Agent reply did not use the tool result."
     
    print(f"Tool called: {tool_call['name']}")
    print(f"Tool result: {tool_message.content}")
    print(f"Agent reply: {final_reply}")
  6. Review the completed agent_demo.py program before execution.
    agent_demo.py
    from typing import Any
     
    from langchain.agents import create_agent
    from langchain.messages import AIMessage, ToolMessage
    from langchain.tools import tool
    from langchain_core.callbacks import CallbackManagerForLLMRun
    from langchain_core.language_models.chat_models import BaseChatModel
    from langchain_core.messages import BaseMessage
    from langchain_core.outputs import ChatGeneration, ChatResult
     
     
    @tool
    def convert_celsius_to_fahrenheit(celsius: float) -> float:
        """Convert a Celsius temperature to Fahrenheit."""
        return celsius * 9 / 5 + 32
     
     
    class ToolResultModel(BaseChatModel):
        @property
        def _llm_type(self) -> str:
            return "tool-result-model"
     
        def bind_tools(self, tools, *, tool_choice=None, **kwargs):
            return self
     
        def _generate(
            self,
            messages: list[BaseMessage],
            stop: list[str] | None = None,
            run_manager: CallbackManagerForLLMRun | None = None,
            **kwargs: Any,
        ) -> ChatResult:
            tool_message = next(
                (
                    message
                    for message in reversed(messages)
                    if isinstance(message, ToolMessage)
                ),
                None,
            )
     
            if tool_message is None:
                response = AIMessage(
                    content="",
                    tool_calls=[
                        {
                            "name": "convert_celsius_to_fahrenheit",
                            "args": {"celsius": 20},
                            "id": "call_temperature",
                        }
                    ],
                )
            else:
                fahrenheit = float(tool_message.content)
                response = AIMessage(
                    content=(
                        "20 degrees Celsius equals "
                        f"{fahrenheit:g} degrees Fahrenheit."
                    )
                )
     
            return ChatResult(generations=[ChatGeneration(message=response)])
     
     
    model = ToolResultModel()
     
    agent = create_agent(
        model=model,
        tools=[convert_celsius_to_fahrenheit],
        system_prompt="Use the temperature tool for conversions.",
    )
     
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "Convert 20 C to Fahrenheit."}]}
    )
     
    tool_call = next(
        call
        for message in result["messages"]
        if isinstance(message, AIMessage)
        for call in message.tool_calls
    )
    tool_message = next(
        message for message in result["messages"] if isinstance(message, ToolMessage)
    )
    final_reply = result["messages"][-1].content
    expected_reply = (
        "20 degrees Celsius equals "
        f"{float(tool_message.content):g} degrees Fahrenheit."
    )
    assert final_reply == expected_reply, "Agent reply did not use the tool result."
     
    print(f"Tool called: {tool_call['name']}")
    print(f"Tool result: {tool_message.content}")
    print(f"Agent reply: {final_reply}")
  7. Run the completed agent program to verify tool execution and the final reply.
    $ python agent_demo.py
    Tool called: convert_celsius_to_fahrenheit
    Tool result: 68.0
    Agent reply: 20 degrees Celsius equals 68 degrees Fahrenheit.

    The model builds the final line from the received ToolMessage. The equality assertion raises an error if the completed reply no longer matches that tool result.