Skip to content

GLM-5.2 with smolagents: ToolCallingAgent and CodeAgent

Independent research — not an official Z.ai publication.Identity and provider disclosure

A GLM-5.2 model core routes work to a smolagents ToolCallingAgent, a container-bounded CodeAgent and an OpenRouter fallback

Original editorial diagram of the verified paths. It is not a Hugging Face, Z.ai or OpenRouter product screen. The later terminal image is rendered from the sanitized live-run receipts.

Hugging Face’s smolagents makes two choices unusually explicit. ToolCallingAgent asks a model for structured tool calls. CodeAgent asks a model for Python actions and then executes them. GLM-5.2 can drive both paradigms through the framework’s OpenAIModel, but a three-line provider object is not yet a safe agent.

The missing decisions are commercial eligibility, termination, tool authorization, code isolation, usage measurement and failure handling. They matter immediately. Our one-sentence direct task consumed 976 input tokens because the framework had to teach the model its agent protocol. The one-tool task consumed 3,500 input tokens. The visible prompt alone cannot predict the bill.

This guide gives you the exact provider factory, two minimal agents, the observed receipts, an OpenRouter alternative and nine diagnostic branches. Every network result came from a digest-pinned, task-owned Docker container; the host received no Python package.

  1. Choose a commercially valid route
  2. Freeze the tested versions
  3. Create the provider factory
  4. Verify direct termination
  5. Execute one structured tool
  6. Isolate generated Python
  7. Add a router fallback
  8. Inspect the live evidence
  9. Reconstruct token cost
  10. Repair layered failures
  11. Promote the smallest contract
  12. Audit the sources
  13. Resolve practical questions

Put commercial eligibility before provider syntax

Section titled “Put commercial eligibility before provider syntax”

An OpenAI-compatible JSON shape does not mean every key can be used at every endpoint. Z.ai publishes a general metered API and a Coding Plan endpoint, while OpenRouter publishes a separate gateway:

Route Base URL Model ID Status in this guide
Z.ai pay-as-you-go https://api.z.ai/api/paas/v4 glm-5.2 Live tested
Z.ai Coding Plan https://api.z.ai/api/coding/paas/v4 glm-5.2 Not called; framework unsupported
OpenRouter https://openrouter.ai/api/v1 z-ai/glm-5.2 Live tested; downstream host not pinned

Z.ai’s current Tool Integration page says Coding Plan benefits are limited to its named tools. The list included Claude Code, OpenCode, Pi, Cline, Roo Code, Crush, Goose, OpenClaw, Hermes and others, but not smolagents. Its usage policy separately warns against subscription use in unsupported SDK scenarios.

Therefore the correct “Coding Plan configuration” for this framework is a stop, not a speculative code block. Use the general API for a smolagents application. If subscription quota is your requirement, move the task to a supported client such as the tested OpenCode setup or Claude Code setup. Do not paste a Coding Plan key into an arbitrary SDK merely because its endpoint speaks Chat Completions.

Pin the exact smolagents wheel and runtime

Section titled “Pin the exact smolagents wheel and runtime”

The successful container used this set:

Versions captured on July 29, 2026
Python 3.13.13
smolagents 1.26.0
openai 2.50.0
pydantic 2.13.4
httpx 0.28.1

PyPI lists smolagents 1.26.0 for Python 3.10 and newer, with an openai extra. Hugging Face’s v1.26.0 release is dated May 29, 2026. Pinning is important because agent constructors, executor choices, model-parameter forwarding and result objects have changed across releases.

Install in an isolated environment:

Install the tested adapter
python -m venv .venv
. .venv/bin/activate
python -m pip install "smolagents[openai]==1.26.0"
python -m pip check
python -c "import smolagents; print(smolagents.__version__)"

For an evidence-grade reproduction, use the read-only Docker profile described in this guide. The project keeps the exact command and probe in its private evidence archive; the public sanitized result JSON contains no credential or hidden transcript.

Construct a provider object that exposes every assumption

Section titled “Construct a provider object that exposes every assumption”

Set credentials outside Python:

Required environment variable
export ZAI_API_KEY="your-Z.ai-api-key"

Then create one auditable factory:

glm_smolagents.py
import os
from smolagents import OpenAIModel
MODEL_ID = "glm-5.2"
PAYGO_BASE = "https://api.z.ai/api/paas/v4"
def build_zai_model() -> OpenAIModel:
return OpenAIModel(
model_id=MODEL_ID,
api_base=PAYGO_BASE,
api_key=os.environ["ZAI_API_KEY"],
temperature=0,
max_tokens=256,
extra_body={"thinking": {"type": "disabled"}},
client_kwargs={
"timeout": 120.0,
"max_retries": 0,
},
)

Hugging Face’s OpenAIModel reference documents model_id, api_base and api_key for an OpenAI-compatible server. Its guided tour says model kwargs are forwarded to the completion call. That is why extra_body can carry Z.ai’s explicit thinking switch.

These values are acceptance-test defaults, not universal production choices. temperature=0 narrows variability. The 256-token cap bounds a marker or small tool turn. Disabled thinking avoids paying for a large reasoning trace when the expected answer is mechanically checkable. Raise caps and enable reasoning only after measuring the task.

Z.ai’s GLM-5.2 guide publishes a 1M context, 128K maximum output, function calls, caching and structured output. Those are model capabilities. The framework’s system prompt, tool schemas, transcript, gateway limits and your own memory policy still determine the usable window.

An agent run should terminate through the framework’s contract, not merely print plausible text. This minimal probe has no business tool. The model must call the built-in final_answer action:

verify_direct.py
from smolagents import ToolCallingAgent
from glm_smolagents import build_zai_model
agent = ToolCallingAgent(
tools=[],
model=build_zai_model(),
verbosity_level=0,
)
result = agent.run(
(
"Call final_answer with exactly "
"SMOLAGENTS_DIRECT_OK and no other characters."
),
max_steps=2,
return_full_result=True,
)
assert result.state == "success"
assert str(result.output) == "SMOLAGENTS_DIRECT_OK"
assert result.token_usage.input_tokens is not None
print(result.output)

Our result was success, with the exact marker after 7,897 ms. The framework reported 976 input and 16 output tokens. This proves authentication, endpoint, model ID, smolagents message conversion and final-answer parsing together. Importing the object or saving a config file proves none of those boundaries.

Keep max_steps in every acceptance test. If final termination fails, an unbounded retry loop can turn a small compatibility check into a quota incident.

Prove one callback executed, not merely proposed

Section titled “Prove one callback executed, not merely proposed”

A model saying “I will call the tool” is not a tool test. The callback must record the exact argument:

verify_tool.py
from smolagents import ToolCallingAgent, tool
from glm_smolagents import build_zai_model
observed_calls: list[dict[str, str]] = []
@tool
def lookup_fixture(ticket_id: str) -> str:
"""Look up one synthetic fixture ticket.
Args:
ticket_id: Exact fixture ticket identifier.
"""
observed_calls.append({"ticket_id": ticket_id})
return "owner=orchid;port=7442"
agent = ToolCallingAgent(
tools=[lookup_fixture],
model=build_zai_model(),
verbosity_level=0,
)
result = agent.run(
(
"Call lookup_fixture exactly once with ticket_id ORCHID-7442. "
"Then call final_answer with exactly "
"SMOLAGENTS_TOOL_OK owner=orchid port=7442."
),
max_steps=3,
return_full_result=True,
)
assert observed_calls == [{"ticket_id": "ORCHID-7442"}]
assert str(result.output) == "SMOLAGENTS_TOOL_OK owner=orchid port=7442"

The decorator derives a schema from the name, type hints, return type, docstring and Args: block. The official tool guide warns that these fields become instructions inside the model’s system prompt. Ambiguous documentation is therefore a behavior bug, not cosmetic prose.

The live callback ran exactly once and returned the required final marker after 10,337 ms. The run reported 3,500 input and 46 output tokens. We archived only the callback receipt and aggregate counts—not the agent’s internal step transcript or hidden reasoning.

For a real tool, validate arguments again at the execution boundary. Add allowlists, idempotency keys, timeouts and a human approval step for writes. Treat model-generated strings and remote tool output as untrusted data.

Confine Python actions to an outer container

Section titled “Confine Python actions to an outer container”

CodeAgent is more expressive because its actions are Python. That also changes the threat model. Hugging Face describes the local executor as restricted, but its guided tour still warns that the model generates executable code and recommends an isolated executor for stronger boundaries.

Our exact code case used executor_type="local" inside the task-owned Docker container:

verify_code_inside_container.py
from smolagents import CodeAgent
from glm_smolagents import build_zai_model
agent = CodeAgent(
tools=[],
model=build_zai_model(),
additional_authorized_imports=[],
executor_type="local",
verbosity_level=0,
)
result = agent.run(
(
"Use Python to calculate 37 * 113. Then call final_answer "
"with exactly SMOLAGENTS_CODE_OK=4181."
),
max_steps=3,
return_full_result=True,
)
assert str(result.output) == "SMOLAGENTS_CODE_OK=4181"

It passed in 3,186 ms with 2,012 input and 63 output tokens. The container mounted this repository read-only, published no port and was removed after the run.

Do not copy executor_type="local" onto a laptop and call that equivalent. Either run the whole application in a disposable container with a minimal read-only mount, as we did, or follow the secure code-execution guide and select a supported isolated executor. Never mount a Docker socket, credential directory, home directory or production dataset merely to make a demo convenient.

Keep a router profile separate from the first-party route

Section titled “Keep a router profile separate from the first-party route”

A fallback needs its own model slug, key and policy:

Router credential
export OPENROUTER_API_KEY="your-OpenRouter-api-key"
glm_smolagents_openrouter.py
import os
from smolagents import OpenAIModel
def build_openrouter_model() -> OpenAIModel:
return OpenAIModel(
model_id="z-ai/glm-5.2",
api_base="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
temperature=0,
max_tokens=256,
extra_body={
"reasoning": {
"effort": "minimal",
"exclude": True,
}
},
client_kwargs={
"timeout": 120.0,
"max_retries": 0,
},
)

The OpenRouter model page publishes the z-ai/glm-5.2 slug and explains that its gateway is OpenAI-compatible. OpenRouter may route across downstream providers unless you add a provider policy. That operational difference is why the router profile should not silently replace the first-party factory.

Our exact SMOLAGENTS_OPENROUTER_OK marker passed in 5,763 ms with 983 input and 52 output tokens. We did not include that usage in the Z.ai cost reconstruction because the downstream route and billing basis were not pinned. GLM52.ai has no affiliate relationship with OpenRouter in this article.

Read the July 29 receipts without overgeneralizing

Section titled “Read the July 29 receipts without overgeneralizing”
Sanitized Docker receipt showing passing GLM-5.2 smolagents direct, typed-tool, CodeAgent and OpenRouter checks plus an expected HTTP 400 invalid-model control

The image is rendered from the archived JSON, not a staged provider dashboard. Keys, request IDs, provider messages, step transcripts and hidden reasoning are intentionally omitted.

Acceptance case Result Elapsed Framework usage
Direct final_answer exact marker 7,897 ms 976 in / 16 out
One structured callback exact argument and marker 10,337 ms 3,500 in / 46 out
Container code action exact calculation marker 3,186 ms 2,012 in / 63 out
OpenRouter route exact marker 5,763 ms 983 in / 52 out
Invalid model expected AgentGenerationError bounded HTTP 400

These are single observations, not latency percentiles. The direct and OpenRouter paths are different commercial routes. The tool task has a larger schema and additional model turn. It is not valid to rank providers or agent types from these five numbers.

The tool result contained four stored framework steps but only one observed fixture callback. Framework steps can include setup and final-answer bookkeeping. Count side effects at the callback or downstream service, not by guessing from the memory length.

Download the machine-readable test result or reproduce the route with the pinned versions, provider factories and acceptance snippets above. The exact probe and container recipe remain in the project’s private evidence archive.

Z.ai’s price table listed $1.40 per million fresh input tokens, $0.26 per million cached input tokens and $4.40 per million output tokens on the test date.

The three direct cases used 6,488 input and 125 output tokens:

One-run arithmetic, not an invoice
input = 6,488 × $1.40 / 1,000,000 = $0.0090832
output = 125 × $4.40 / 1,000,000 = $0.0005500
total = $0.0096332

The visible user strings were tiny. Most input came from the agent protocol, tool schema, code instructions and accumulated steps. This is the important smolagents cost lesson: price a run from provider-reported usage, not prompt characters.

For production forecasting, measure at least:

  • accepted results per task, not requests alone;
  • input and output tokens per agent step;
  • tool-call retries and duplicate side effects;
  • cached versus fresh input;
  • latency and failure rates by provider route;
  • context growth across long-lived memory;
  • human-review cost after unsafe or malformed actions.

The GLM-5.2 cost calculator can model a token budget, while the prompt-caching guide explains why a repeated prefix is not automatically billed as a cache hit.

Confirm that ZAI_API_KEY exists in the same process that constructs OpenAIModel. Then verify the general base URL exactly. Do not log the key, put it in a screenshot or bake it into an image. A Coding Plan key and a general API key represent different products; a syntactically valid key can still be wrong for the route.

Start with the literal model ID glm-5.2. Our deliberate glm-5.2-intentional-invalid control produced HTTP 400 wrapped as AgentGenerationError. Log the sanitized exception type and status, but avoid archiving full provider bodies if they may include request identifiers or account data.

Check whether the model called the built-in final_answer. Keep a small step cap while diagnosing. Simplify the task, disable unnecessary tools and ask for one exact termination value. Raising max_steps first can hide a parser or instruction problem and multiply cost.

The model narrates a tool call instead of invoking it

Section titled “The model narrates a tool call instead of invoking it”

Use ToolCallingAgent, give the function complete type hints and a docstring with a clear Args: section, and remove competing tools. Verify the callback receipt. Natural-language narration is not execution.

Make writes idempotent and reject duplicate operation IDs. Maintain a server-side execution ledger. Prompt wording like “exactly once” is useful for a test but cannot enforce transactional semantics.

CodeAgent can see more host state than intended

Section titled “CodeAgent can see more host state than intended”

Stop the run. The local executor inherits the process boundary in which it runs. Move the whole process into a disposable container or select a documented remote/container executor. Mount only the fixture directory, read-only where possible; do not share the home directory or Docker socket.

A 1M model meets a smaller application limit

Section titled “A 1M model meets a smaller application limit”

The model card’s context length is not the same as your gateway, framework or memory budget. Count the system prompt, tools, observations and generated code. Compact or summarize memory before the provider rejects it. Add a preflight token ceiling and leave output headroom.

Router behavior changes without a source diff

Section titled “Router behavior changes without a source diff”

Record the OpenRouter slug, downstream-provider policy, date, model response metadata and acceptance result. A general router may change providers, availability or economics. If deterministic hosting matters, constrain it or use the first-party route.

An SDK request seems to consume Coding Plan

Section titled “An SDK request seems to consume Coding Plan”

Do not infer authorization from HTTP success. Recheck the current supported tool list and subscription policy. For smolagents on the test date, the safe action is to stop and use the general API. If Z.ai later adds the framework, rerun a versioned acceptance test before changing this page.

Use a promotion gate that tests application-visible behavior:

Minimal promotion checklist
[ ] exact package versions are recorded
[ ] provider, base URL and model ID are explicit
[ ] selected key is eligible for the product route
[ ] final_answer succeeds within a bounded step count
[ ] each tool has a schema, allowlist and timeout
[ ] one test records the exact callback argument
[ ] write tools are idempotent and approval-gated
[ ] generated Python runs outside the host trust boundary
[ ] provider-reported token usage is retained
[ ] router and first-party results remain distinguishable
[ ] errors are sanitized without hiding the HTTP class
[ ] secrets, hidden reasoning and raw private transcripts stay out of logs

Start with ToolCallingAgent when the job is dispatching a small set of atomic tools. Choose CodeAgent only when code composition materially helps and you can supply a real isolation boundary. The official agent reference describes both constructors; it does not choose your risk tolerance.

The separate GLM-5.2 tool-calling guide examines raw API controls and adversarial tool-choice behavior. Use it when you need to know what the provider actually enforces below the framework.

Verify every claim against the evidence ledger

Section titled “Verify every claim against the evidence ledger”

Primary documentation and dated artifacts used here:

The reproducible probe and full sanitized result live under docs/evidence/glm-5-2-smolagents-2026-07-29/. The experiment proves one dated configuration. It does not certify arbitrary tools, long contexts, parallel actions, provider regions, future versions or production safety.

Answer practical smolagents deployment questions

Section titled “Answer practical smolagents deployment questions”

Should a beginner choose ToolCallingAgent or CodeAgent?

Section titled “Should a beginner choose ToolCallingAgent or CodeAgent?”

Start with ToolCallingAgent when each permitted action can be expressed as a small typed function. It gives you a clearer schema and avoids generated Python. Use CodeAgent for composition, loops or data transforms only after you have an isolated executor and a reason the structured approach is insufficient.

Not under the policy reviewed on July 29, 2026. The framework was absent from Z.ai’s officially supported list, so this guide did not call the Coding Plan endpoint. Use the metered API, or move the work to a supported coding client.

Why did a one-line task use nearly a thousand input tokens?

Section titled “Why did a one-line task use nearly a thousand input tokens?”

The framework supplies an agent system prompt, tool protocol and termination instructions in addition to your visible task. Tool schemas and prior steps increase that input further. Budget from returned token usage.

Does the test prove arbitrary Python is safe?

Section titled “Does the test prove arbitrary Python is safe?”

No. It proves a multiplication action inside a disposable outer Docker container with no published port and a read-only repository mount. Running the local executor directly on a workstation would be a different and weaker boundary.

It is configuration-compatible through OpenAIModel, and our exact marker passed. It is not operationally identical: the model slug, key, billing and downstream routing differ. Keep separate telemetry and acceptance results.

Can the agent use the full one-million-token window?

Section titled “Can the agent use the full one-million-token window?”

GLM-5.2 publishes a 1M context, but the usable amount is the smallest limit across model, route, framework and application. Reserve output headroom and count tools, observations, generated code and memory before sending a long request.