GLM-5.2 with smolagents: ToolCallingAgent and CodeAgent
Independent research — not an official Z.ai publication.Identity and provider disclosure
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.
Trace the smolagents verification route
Section titled “Trace the smolagents verification route”- Choose a commercially valid route
- Freeze the tested versions
- Create the provider factory
- Verify direct termination
- Execute one structured tool
- Isolate generated Python
- Add a router fallback
- Inspect the live evidence
- Reconstruct token cost
- Repair layered failures
- Promote the smallest contract
- Audit the sources
- 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:
Python 3.13.13smolagents 1.26.0openai 2.50.0pydantic 2.13.4httpx 0.28.1PyPI 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:
python -m venv .venv. .venv/bin/activatepython -m pip install "smolagents[openai]==1.26.0"python -m pip checkpython -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:
export ZAI_API_KEY="your-Z.ai-api-key"Then create one auditable factory:
import osfrom 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.
Turn final_answer into a deployment probe
Section titled “Turn final_answer into a deployment probe”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:
from smolagents import ToolCallingAgentfrom 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 Noneprint(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:
from smolagents import ToolCallingAgent, toolfrom glm_smolagents import build_zai_model
observed_calls: list[dict[str, str]] = []
@tooldef 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:
from smolagents import CodeAgentfrom 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:
export OPENROUTER_API_KEY="your-OpenRouter-api-key"import osfrom 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”
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.
Budget the hidden framework prompt
Section titled “Budget the hidden framework prompt”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:
input = 6,488 × $1.40 / 1,000,000 = $0.0090832output = 125 × $4.40 / 1,000,000 = $0.0005500total = $0.0096332The 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.
Diagnose nine failures by layer
Section titled “Diagnose nine failures by layer”A 401 appears before the first agent step
Section titled “A 401 appears before the first agent step”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.
AgentGenerationError wraps HTTP 400
Section titled “AgentGenerationError wraps HTTP 400”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.
The run terminates at max_steps
Section titled “The run terminates at max_steps”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.
A supposedly single callback fires twice
Section titled “A supposedly single callback fires twice”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.
Promote a smallest-safe agent contract
Section titled “Promote a smallest-safe agent contract”Use a promotion gate that tests application-visible behavior:
[ ] 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 logsStart 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:
- Hugging Face model classes
- Hugging Face agent guided tour
- Hugging Face agent API reference
- Hugging Face secure code execution
smolagentson PyPI- smolagents v1.26.0 release
- Z.ai API quick start
- Z.ai GLM-5.2 model guide
- Z.ai API pricing
- Z.ai Coding Plan supported tools
- Z.ai Coding Plan usage policy
- OpenRouter GLM-5.2 route
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.
Can smolagents use GLM Coding Plan today?
Section titled “Can smolagents use GLM Coding Plan today?”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.
Is OpenRouter a drop-in failover?
Section titled “Is OpenRouter a drop-in failover?”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.
