Skip to content

Chapter 4 · Messages and the tool-calling loop ​

Introduce a model while keeping tools limited to fixed public course data. Understand the full cycle: model requests a tool, the tool returns a result, and the model responds again.

Mechanics and design choices ​

MessagesState includes message merging. bind_tools exposes schemas to the model, ToolNode executes calls and produces matching tool messages, and tools_condition inspects the latest model message for tool_calls. Tool results must match call IDs. The OpenAI-compatible endpoint used here must actually support tool calling; compatibility does not guarantee every feature.

Example ​

python
import os
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.prebuilt import ToolNode, tools_condition

@tool
def course_hours(course: str) -> str:
    """Return estimated hours for a known course."""
    return {"langgraph": "12 hours", "deepagents": "10 hours"}.get(course.lower(), "Unknown course")

model = ChatOpenAI(
    model=os.environ["MODEL_NAME"],
    api_key=os.environ["OPENAI_API_KEY"],
    base_url=os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1"),
).bind_tools([course_hours])

def assistant(state: MessagesState):
    return {"messages": [model.invoke(state["messages"])]}

builder = StateGraph(MessagesState)
builder.add_node("assistant", assistant)
builder.add_node("tools", ToolNode([course_hours]))
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", tools_condition)
builder.add_edge("tools", "assistant")
graph = builder.compile()
result = graph.invoke(
    {"messages": [{"role": "user", "content": "Use course_hours to find the hours for langgraph."}]},
    {"recursion_limit": 12},
)
for message in result["messages"]:
    print(message.type, message.content)

Run and verify ​

Also install langchain-openai. Configure MODEL_NAME, OPENAI_API_KEY, and optionally OPENAI_BASE_URL. Execution calls a paid external model. Verify a tool message and 12 hours; a natural-language answer alone is insufficient evidence of tool use.

Pitfalls ​

Do not promote tool output into higher-priority instructions. Validate arguments and authorization in code. Model output is not permission; paid or mutating operations need an approval boundary.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What does bind_tools do?

2. What must tool responses match?

3. Does text generation guarantee tool calling?

4. When does the tool loop exit?

Hands-on exercises ​

  1. Test course_hours.invoke for known and unknown courses.
  2. Record message types and verify the model runs after tool output.
  3. Add another read-only tool and verify selection instead of guessing.

Official references ​

Verified on: 2026-09-17.