GLM 5.2 Agentic Workflows: Function Calling, Tool Use, and Multi-Step Tasks

GLM 5.2 Agentic Workflows: Function Calling, Tool Use, and Multi-Step Tasks

Build production AI agents with GLM 5.2 using function calling, parallel tool execution, and multi-step reasoning — complete Python examples included.

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 tools parameter format is identical to the OpenAI API, so any existing agent code (LangChain, LlamaIndex, AutoGen, CrewAI) works with a two-line change: base_url and model.
  • Parallel function calls — the model can return multiple tool_calls in 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 openai

Configure 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 results

The 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 wrapper

AutoGen

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_config

LlamaIndex

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:

ModelBest ForInput Price
glm-4-plus (GLM 5.2)Complex reasoning, long contexts, multi-step agents$1.40 / 1M tokens
glm-4-airLightweight routing, cheap sub-agents$0.10 / 1M tokens (approx)
glm-4-longLong-document ingestion pre-processing$1.00 / 1M tokens (approx)
embedding-3Semantic search, RAG retrievalEmbedding 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

Zacznij korzystać z GLM 5 już dziś

Wypróbuj GLM 5 za darmo — rozumowanie, kodowanie, agenci i generowanie obrazów w jednej platformie.