Building reliable AI agents requires three things from your model: accurate function calling, the ability to reason across many steps without losing context, and an economics that does not bankrupt you mid-project. GLM 5.2 (api model name glm-4-plus) checks all three boxes. It supports OpenAI-compatible tool use, runs parallel function calls in a single response, holds up to 1,048,576 tokens in context, and is priced at $1.40 per million input tokens. This tutorial walks you through everything you need to wire up a production-grade agent — from a single tool call to a full multi-step ReAct loop — with working Python code at every stage.
Why Agentic Use Cases Push Most Models to Their Limits
The pain point every agent developer runs into is not the first tool call — it is what happens when an agent needs to coordinate ten tools across twenty turns while holding a 50-page knowledge base in context. That is when GPT-4o context limits become roadblocks and per-token costs start compounding in ways that make multi-step loops economically painful.
The 1M-token context window in GLM 5.2 is not a marketing number — it directly changes what is architecturally possible. You can pass an entire codebase, a full document corpus, or dozens of previous tool results into a single context without chunking or retrieval hacks. At $1.40 per million input tokens, even a 200K-token agent loop costs less than $0.30 per run. For document-heavy tasks — legal review, financial analysis, code auditing — that combination is a genuine unlock that more expensive models cannot match at scale.
Why GLM 5.2 Is the Right Foundation for Agents
Before diving into code, here is what makes glm-4-plus specifically well-suited for agentic work:
- OpenAI-compatible tool spec — the
toolsparameter format is identical to the OpenAI API, so any existing agent code (LangChain, LlamaIndex, AutoGen, CrewAI) works with a two-line change:base_urlandmodel. - Parallel function calls — the model can return multiple
tool_callsin a single response, letting you fetch data from several sources simultaneously rather than sequentially. - Reliable instruction-following — a GPQA Diamond score of 89% and SWE-bench Pro at 62.1% reflect the model's ability to reason carefully and follow complex multi-step instructions.
- JSON mode — set
response_format={"type": "json_object"}and the model guarantees a parseable JSON output, critical for structured agent state management. - MIT-licensed open weights — the model weights are available on HuggingFace under the MIT license, so self-hosted deployments are an option for latency-sensitive or data-private workloads.
For full API setup details, see the GLM 5.2 API guide which covers authentication, endpoints, and streaming configuration.
Setting Up: Client and Environment
Install the openai package — no separate GLM SDK needed:
pip install openaiConfigure the client to point at Zhipu's API:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ZHIPU_API_KEY"],
base_url="https://open.bigmodel.cn/api/paas/v4/",
)
MODEL = "glm-4-plus"Set ZHIPU_API_KEY in your environment. Everything else in this tutorial uses this client object.
If you want to experiment with GLM 5.2 before writing any code, glm5.app lets you run prompts, test tool calls interactively, and see live responses — useful for prototyping a tool schema before committing it to production code.
Step 1: A Single Function Call
The building block of any agent is a single tool call. Define a tool schema, pass it in the tools parameter, and let the model decide whether to call it.
import json
tools = [
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Returns the latest closing price for a given stock ticker symbol.",
"parameters": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The stock ticker, e.g. AAPL or TSLA",
}
},
"required": ["ticker"],
},
},
}
]
messages = [
{"role": "system", "content": "You are a financial research assistant."},
{"role": "user", "content": "What is the current price of AAPL?"},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
if message.tool_calls:
call = message.tool_calls[0]
print(f"Tool called: {call.function.name}")
print(f"Arguments: {call.function.arguments}")
else:
print(message.content)The model returns a tool_calls list rather than a text answer when it determines a tool is appropriate. Your application then executes the real function (an actual API call, database query, or calculation) and feeds the result back.
Step 2: Closing the Loop — Returning Tool Results
An agent loop is simply a while cycle: call the model, check if it wants a tool, execute the tool, append the result to messages, and call the model again. Here is a complete single-tool loop:
def run_agent(user_message: str, tools: list, tool_map: dict) -> str:
"""
Simple agent loop. tool_map maps function names to Python callables.
Returns the model's final text response.
"""
messages = [
{"role": "system", "content": "You are a helpful research assistant."},
{"role": "user", "content": user_message},
]
while True:
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
# No tool calls — model is done reasoning, return final answer
if not message.tool_calls:
return message.content
# Append the assistant's tool-call message to history
messages.append(message)
# Execute each tool call and append results
for call in message.tool_calls:
fn = tool_map.get(call.function.name)
if fn is None:
result = f"Error: unknown function {call.function.name}"
else:
args = json.loads(call.function.arguments)
result = fn(**args)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
}
)
# Loop back — model will reason over the tool resultsThe key detail is the role: "tool" message. Each tool result must carry the matching tool_call_id so the model can correlate results with calls when multiple tools are invoked simultaneously.
Step 3: Parallel Tool Calls
GLM 5.2 can return multiple tool calls in a single response. This lets you fetch independent data sources simultaneously instead of waiting for each one to complete before asking for the next. The code change is minor — you already handle a list of tool_calls — but the performance difference in production is significant when tools involve I/O.
import concurrent.futures
def run_agent_parallel(user_message: str, tools: list, tool_map: dict) -> str:
messages = [
{"role": "system", "content": "You are a market analysis assistant. "
"When you need multiple data points, fetch them in parallel."},
{"role": "user", "content": user_message},
]
while True:
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
if not message.tool_calls:
return message.content
messages.append(message)
# Execute all tool calls in parallel
def execute_call(call):
fn = tool_map.get(call.function.name)
if fn is None:
return call.id, f"Error: unknown function {call.function.name}"
args = json.loads(call.function.arguments)
return call.id, fn(**args)
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(execute_call, message.tool_calls))
for call_id, result in results:
messages.append(
{
"role": "tool",
"tool_call_id": call_id,
"content": str(result),
}
)When asked "Compare the revenue of AAPL, MSFT, and GOOGL," the model can emit three get_revenue calls simultaneously. Your thread pool executes them in parallel, and you return all three results in one batch. The wall-clock time is the slowest single call, not the sum of all three.
Step 4: Multi-Step ReAct Agent with JSON State
For tasks that require planning — "research this topic, write a structured report, check it against these constraints" — a ReAct (Reason + Act) pattern gives the model space to think before each action. Combine it with response_format={"type": "json_object"} to get structured intermediate state you can log, inspect, and resume.
REACT_SYSTEM = """You are a research agent using the ReAct pattern.
At each step, respond with a JSON object:
{
"thought": "your reasoning about what to do next",
"action": "tool_name or 'finish'",
"action_input": { ...tool arguments... },
"final_answer": "only present when action is 'finish'"
}
Never skip the thought field. Be explicit about why you chose each action."""
def react_agent(task: str, tools: list, tool_map: dict, max_steps: int = 10) -> str:
messages = [
{"role": "system", "content": REACT_SYSTEM},
{"role": "user", "content": task},
]
for step in range(max_steps):
response = client.chat.completions.create(
model=MODEL,
messages=messages,
response_format={"type": "json_object"},
# Pass tools as context but let the model decide via JSON
)
raw = response.choices[0].message.content
state = json.loads(raw)
print(f"\nStep {step + 1} thought: {state.get('thought', '')}")
action = state.get("action")
if action == "finish":
return state.get("final_answer", "")
fn = tool_map.get(action)
if fn is None:
tool_result = f"Error: unknown action '{action}'"
else:
tool_result = fn(**state.get("action_input", {}))
messages.append({"role": "assistant", "content": raw})
messages.append(
{"role": "user", "content": f"Observation: {tool_result}\nContinue."}
)
return "Max steps reached without finishing."The JSON mode guarantee means json.loads(raw) will not throw a parse error mid-loop. With a 1M-token context, the full reasoning trace — every thought, action, and observation — stays in context across all ten steps without truncation.
Step 5: Framework Integration
If you are already using a framework, the integration is two lines:
LangChain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="glm-4-plus",
openai_api_key=os.environ["ZHIPU_API_KEY"],
openai_api_base="https://open.bigmodel.cn/api/paas/v4/",
)
# Use llm in any LangChain agent, chain, or tool-calling wrapperAutoGen
config_list = [
{
"model": "glm-4-plus",
"api_key": os.environ["ZHIPU_API_KEY"],
"base_url": "https://open.bigmodel.cn/api/paas/v4/",
}
]
# Pass config_list to AssistantAgent or UserProxyAgent as llm_configLlamaIndex
from llama_index.llms.openai import OpenAI as LlamaOpenAI
llm = LlamaOpenAI(
model="glm-4-plus",
api_key=os.environ["ZHIPU_API_KEY"],
api_base="https://open.bigmodel.cn/api/paas/v4/",
)All three frameworks send standard OpenAI-format tool schemas under the hood, and GLM 5.2 handles them without modification.
Choosing the Right GLM Model for Your Agent
Zhipu offers several models alongside glm-4-plus. For agent workflows, the tradeoffs look like this:
| Model | Best For | Input Price |
|---|---|---|
glm-4-plus (GLM 5.2) | Complex reasoning, long contexts, multi-step agents | $1.40 / 1M tokens |
glm-4-air | Lightweight routing, cheap sub-agents | $0.10 / 1M tokens (approx) |
glm-4-long | Long-document ingestion pre-processing | $1.00 / 1M tokens (approx) |
embedding-3 | Semantic search, RAG retrieval | Embedding pricing |
A common production pattern is a tiered architecture: glm-4-air as a fast router that classifies intent and dispatches to glm-4-plus only for tasks requiring deep reasoning. This keeps average cost low while preserving quality where it matters.
Production Considerations
Error handling and retries. Tool results can be malformed or empty. Wrap tool execution in try/except and return a structured error string — the model handles "Error: API timeout" gracefully and can decide to retry or take an alternative path.
Context management. At 1M tokens, GLM 5.2 rarely needs aggressive pruning. Still, for very long-running agents, consider summarizing old observations with a cheap model before the context fills. Keep the last N full tool results and a summary of everything older.
Streaming tool calls. Pass stream=True to get tool call deltas as they arrive. This is useful for showing the user that work is in progress before the tool result comes back. The streaming format for tool calls matches the OpenAI streaming spec exactly.
Rate limits and cost monitoring. At 158 tokens per second (Artificial Analysis benchmark), throughput is solid for single-user agents. For batch agent workloads, use Zhipu's batch API to process many tasks overnight at reduced cost.
Next Steps
The patterns in this tutorial — single tool call, parallel execution, ReAct JSON loop, framework integration — cover the majority of production agent architectures. What varies is the tool set you bring: web search, database queries, code execution, file I/O, external APIs.
GLM 5.2's 1M-token context and OpenAI-compatible interface mean you can iterate quickly, keeping the full conversation history and all tool results in context without wrestling with retrieval or chunking. The pricing makes multi-step loops economically viable at production scale.
Start building and testing your agent tools at glm5.app — the playground lets you prototype tool schemas and observe model behavior before wiring up your backend.
Sources
- Zhipu AI official API documentation: https://open.bigmodel.cn/dev/api
- GLM-4 model card and MIT license (HuggingFace): https://huggingface.co/THUDM/glm-4
- Artificial Analysis GLM-4-Plus benchmark page: https://artificialanalysis.ai/models/glm-4-plus
- Zhipu AI pricing page: https://open.bigmodel.cn/pricing
- OpenAI function calling reference (compatible format): https://platform.openai.com/docs/guides/function-calling

