<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmad Al Dibo</title>
    <description>The latest articles on DEV Community by Ahmad Al Dibo (@ahmadaldibo).</description>
    <link>https://dev.to/ahmadaldibo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4189507%2Ffa5a271f-e4aa-443b-9483-19d49608a345.png</url>
      <title>DEV Community: Ahmad Al Dibo</title>
      <link>https://dev.to/ahmadaldibo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmadaldibo"/>
    <language>en</language>
    <item>
      <title>Build a Reproducible Local Agent Tool with ArcLM (No GPU Required)</title>
      <dc:creator>Ahmad Al Dibo</dc:creator>
      <pubDate>Fri, 16 Oct 2026 20:37:34 +0000</pubDate>
      <link>https://dev.to/ahmadaldibo/build-a-reproducible-local-agent-tool-with-arclm-no-gpu-required-46kl</link>
      <guid>https://dev.to/ahmadaldibo/build-a-reproducible-local-agent-tool-with-arclm-no-gpu-required-46kl</guid>
      <description>&lt;h1&gt;
  
  
  Build a Reproducible Local Agent Tool with ArcLM (No GPU Required)
&lt;/h1&gt;

&lt;p&gt;An AI agent becomes useful when it can do more than generate text. It needs a way to call functions, validate their inputs, execute operations, and return structured results.&lt;/p&gt;

&lt;p&gt;But giving an agent access to tools introduces engineering questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What arguments can a tool accept?&lt;/li&gt;
&lt;li&gt;How do you prevent an agent from running indefinitely?&lt;/li&gt;
&lt;li&gt;How can you inspect which tools were called?&lt;/li&gt;
&lt;li&gt;What happens when a tool needs permission to modify a file?&lt;/li&gt;
&lt;li&gt;How do you avoid exposing sensitive information in diagnostic output?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this tutorial, we'll build a small calculator agent with &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM" rel="noopener noreferrer"&gt;ArcLM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll define a typed Python function, register it as a tool, execute a bounded agent loop, inspect its trace, and examine how permissions and redaction fit into the design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No GPU or external LLM API is required.&lt;/strong&gt; We'll use a deterministic fake model adapter so that the same example can be reproduced without API credentials or the variability of a real language model.&lt;/p&gt;

&lt;p&gt;The fake model is only a test adapter. It demonstrates the tool-calling pipeline, not the reasoning capabilities of an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install ArcLM
&lt;/h2&gt;

&lt;p&gt;Create and activate a Python virtual environment, then install the lightweight ArcLM package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="n"&gt;\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On macOS or Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install ArcLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;arclm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent runtime does not require PyTorch for this example. You don't need a GPU, a downloaded language model, or an API key.&lt;/p&gt;

&lt;p&gt;For the implementation details, see the &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM/blob/cleanup-2.0/examples/10_agents_tools/01_custom_tool_agent.py" rel="noopener noreferrer"&gt;ArcLM agent tools example&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Define a Typed Tool
&lt;/h2&gt;

&lt;p&gt;Let's start with a simple function that adds two integers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utilities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add two integers.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are only a few lines of implementation, but the metadata matters.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@tool&lt;/code&gt; decorator exposes the function as an ArcLM tool. Its type hints describe the expected arguments and return type, while the docstring supplies a description.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;category&lt;/code&gt; field groups the tool with related functionality.&lt;/p&gt;

&lt;p&gt;The permission and side-effect declarations describe its operational behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ToolPermission.PURE&lt;/code&gt; identifies an operation that doesn't require access to external resources.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SideEffectLevel.NONE&lt;/code&gt; declares that the operation has no side effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The function itself remains ordinary Python. ArcLM supplies the tool abstraction around it.&lt;/p&gt;

&lt;p&gt;This distinction is useful when building larger systems: the business logic can remain independent of the agent runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Register the Tool
&lt;/h2&gt;

&lt;p&gt;ArcLM provides &lt;code&gt;ToolRegistry&lt;/code&gt; for managing tools by name.&lt;/p&gt;

&lt;p&gt;Create a registry and register the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ToolRegistry&lt;/span&gt;

&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;registered_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add_numbers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registered_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registered_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registered_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registry gives you a consistent way to retrieve registered tools and inspect their metadata.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;input_schema&lt;/code&gt; describes the expected arguments. An agent adapter can use this information when deciding how to formulate a tool call.&lt;/p&gt;

&lt;p&gt;This is more explicit than simply exposing arbitrary Python functions to a model.&lt;/p&gt;

&lt;p&gt;A schema can describe what a function accepts, but it doesn't prove that a tool is safe. You still need to validate arguments and control access to operations with side effects.&lt;/p&gt;

&lt;p&gt;ArcLM's tool abstraction includes argument validation before invoking the underlying function. See &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM/blob/cleanup-2.0/src/arclm/agents/tool.py" rel="noopener noreferrer"&gt;&lt;code&gt;src/arclm/agents/tool.py&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Build a Deterministic Agent
&lt;/h2&gt;

&lt;p&gt;A real language model would decide when to call &lt;code&gt;add_numbers&lt;/code&gt; and what arguments to provide. That introduces an external dependency and potentially nondeterministic behavior.&lt;/p&gt;

&lt;p&gt;For this tutorial, we'll remove that variable by implementing a minimal fake model.&lt;/p&gt;

&lt;p&gt;It will do two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request a call to &lt;code&gt;add_numbers&lt;/code&gt; with the arguments &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Read the structured tool result and return a final response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a file named &lt;code&gt;calculator_agent.py&lt;/code&gt; and use the following complete example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AgentRunner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ModelResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ToolRegistry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redact_sensitive_text&lt;/span&gt;


&lt;span class="c1"&gt;# 1. Define a typed tool.
&lt;/span&gt;
&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utilities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add two integers.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;


&lt;span class="c1"&gt;# 2. Register the tool.
&lt;/span&gt;
&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# 3. Create a deterministic fake model adapter.
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeterministicFakeModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ModelResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="nc"&gt;ToolCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call_1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add_numbers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Read the actual result returned by the tool.
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ModelResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The sum is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ModelResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;error_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The calculator tool failed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ModelResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing_tool_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;error_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No calculator result was received.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# 4. Configure and run the agent.
&lt;/span&gt;
&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calculator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use the available calculator tool to add integers.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;DeterministicFakeModel&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_tools&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Add 2 and 5.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent run failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# 5. Inspect the result and execution trace.
&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_calls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace_events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_type&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="c1"&gt;# 6. Demonstrate sensitive-text redaction.
&lt;/span&gt;
&lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_key=demo-secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;redact_sensitive_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python calculator_agent.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is intended to produce a successful calculator run followed by a redaction demonstration. Execute it with your installed ArcLM version and capture the actual terminal output before publishing the tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens inside the agent loop?
&lt;/h3&gt;

&lt;p&gt;The execution follows a sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The runner sends the conversation and available tool descriptions to the fake model.&lt;/li&gt;
&lt;li&gt;The model returns a structured &lt;code&gt;ToolCall&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The runner checks whether the tool exists and is allowed.&lt;/li&gt;
&lt;li&gt;ArcLM validates the arguments and invokes the function.&lt;/li&gt;
&lt;li&gt;The tool result is added to the session.&lt;/li&gt;
&lt;li&gt;The fake model reads that result and returns a final response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that the second response derives its answer from the returned tool result rather than hardcoding the number &lt;code&gt;7&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This makes the example useful for examining the interaction between an agent, a tool, and a model adapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Why Bound the Agent Loop?
&lt;/h2&gt;

&lt;p&gt;The line below is an important part of the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AgentRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent that repeatedly requests tools without reaching a final response can consume resources indefinitely unless execution is constrained.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;max_steps&lt;/code&gt; limits the number of model-response steps in a run. In this example, two steps are sufficient: one tool request and one final response.&lt;/p&gt;

&lt;p&gt;The limit is not a guarantee that every run will succeed. The model could request a nonexistent tool, return an invalid response, or fail to produce a final answer.&lt;/p&gt;

&lt;p&gt;ArcLM represents execution outcomes through structured result objects, which lets the caller inspect success and failure without relying exclusively on printed text.&lt;/p&gt;

&lt;p&gt;You can explore the implementation in &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM/blob/cleanup-2.0/src/arclm/agents/runner.py" rel="noopener noreferrer"&gt;&lt;code&gt;src/arclm/agents/runner.py&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Add Permission and Side-Effect Controls
&lt;/h2&gt;

&lt;p&gt;A calculator is a convenient first tool because it doesn't modify external state.&lt;/p&gt;

&lt;p&gt;A file-writing tool is different. It may overwrite data, create files in unintended locations, or expose information to another process.&lt;/p&gt;

&lt;p&gt;ArcLM separates permission declarations from side-effect classification.&lt;/p&gt;

&lt;p&gt;For example, a tool that writes to a local workspace could declare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filesystem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ToolPermission&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WRITE_LOCAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SideEffectLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVERSIBLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Write a report to an approved workspace location.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Implement explicit path validation and file-writing policy here.
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classification example, not a file-writing implementation. A production tool also needs to validate its destination, restrict allowed paths, handle errors, and define overwrite behavior.&lt;/p&gt;

&lt;p&gt;The distinction between the two metadata fields is important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;permission&lt;/code&gt; declares the kind of access a tool requires.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;side_effect&lt;/code&gt; describes the operational consequences of the tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Declaring a side effect alone does not enforce approval.&lt;/strong&gt; ArcLM's approval policy uses the tool's permission classification and the configured &lt;code&gt;ToolPolicy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By default, permissions such as &lt;code&gt;WRITE_LOCAL&lt;/code&gt;, &lt;code&gt;READ_NETWORK&lt;/code&gt;, &lt;code&gt;EXTERNAL_ACTION&lt;/code&gt;, and &lt;code&gt;SENSITIVE&lt;/code&gt; require approval. The default approval handler denies operations that require approval unless an appropriate handler is configured.&lt;/p&gt;

&lt;p&gt;For a real application, provide a &lt;code&gt;ToolApprovalHandler&lt;/code&gt; through &lt;code&gt;AgentRunner(approval_handler=...)&lt;/code&gt; and define how approval is requested, granted, denied, and audited.&lt;/p&gt;

&lt;p&gt;Never treat a descriptive tool schema as a substitute for access control.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Redact Sensitive Information
&lt;/h2&gt;

&lt;p&gt;Tool outputs and execution logs can contain credentials, tokens, or other sensitive values.&lt;/p&gt;

&lt;p&gt;ArcLM provides a redaction utility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;redact_sensitive_text&lt;/span&gt;

&lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_key=demo-secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;safe_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;redact_sensitive_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;safe_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The intended transformation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api_key=[REDACTED]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArcLM also redacts tool-result payloads when trace redaction is enabled by the agent's security policy.&lt;/p&gt;

&lt;p&gt;However, redaction has limits. It detects supported patterns; it cannot guarantee that every secret or item of personal information will be recognized.&lt;/p&gt;

&lt;p&gt;It is also important to distinguish tool-result redaction from complete session sanitization. For example, the session records tool-call arguments. You should not pass real credentials as tool arguments simply because result redaction is enabled.&lt;/p&gt;

&lt;p&gt;For sensitive workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid putting secrets into prompts and tool arguments.&lt;/li&gt;
&lt;li&gt;Keep credentials in an appropriate secret-management mechanism.&lt;/li&gt;
&lt;li&gt;Minimize what you include in diagnostic output.&lt;/li&gt;
&lt;li&gt;Test redaction with representative examples from your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The relevant implementation is available in &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM/blob/cleanup-2.0/src/arclm/security/redaction.py" rel="noopener noreferrer"&gt;&lt;code&gt;src/arclm/security/redaction.py&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Inspect the Trace
&lt;/h2&gt;

&lt;p&gt;A final answer tells you what the agent returned. A trace helps explain how it reached that outcome.&lt;/p&gt;

&lt;p&gt;The example collects the trace event names using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;trace_events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_type&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runner records events such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;run_started&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;model_responded&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tool_requested&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tool_started&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tool_completed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;step_completed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run_completed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These events help answer practical debugging questions.&lt;/p&gt;

&lt;p&gt;Did the model request a tool? Did the runner start it? Did it complete? Did the agent reach a final response or exhaust its step limit?&lt;/p&gt;

&lt;p&gt;For the current example, inspecting event names is enough. In a larger application, you can also examine the structured trace and result metadata.&lt;/p&gt;

&lt;p&gt;Before persisting traces, decide what information should be retained, who can access it, and how sensitive data should be handled.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Example Demonstrates
&lt;/h2&gt;

&lt;p&gt;This small example separates several responsibilities that are often mixed together in agent implementations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools define capabilities.&lt;/strong&gt; A typed function exposes a specific operation with a discoverable schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The registry manages available tools.&lt;/strong&gt; Tools can be registered and selected explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The adapter defines model interaction.&lt;/strong&gt; Our deterministic adapter demonstrates the protocol without requiring a provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The runner controls execution.&lt;/strong&gt; It validates tool calls, coordinates results, and limits the number of steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions and redaction address different security concerns.&lt;/strong&gt; Permissions govern access and approval, while redaction reduces exposure of recognized sensitive values.&lt;/p&gt;

&lt;p&gt;This is not a production-ready autonomous agent, nor does it demonstrate real language-model reasoning. It is a small, inspectable foundation for understanding how tool-calling systems fit together.&lt;/p&gt;

&lt;p&gt;If you want to go further, start with the &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM" rel="noopener noreferrer"&gt;ArcLM repository&lt;/a&gt; and inspect the canonical &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM/blob/cleanup-2.0/examples/10_agents_tools/01_custom_tool_agent.py" rel="noopener noreferrer"&gt;custom tool agent example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next experiment is to replace the fake adapter with a real model adapter while keeping the same tool boundary, step limit, permission policy, and trace inspection.&lt;/p&gt;

&lt;p&gt;That separation is the useful part: you can change the model backend without having to redesign the tool's Python implementation.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>llm</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Install ArcLM, Tokenize Text, and Run a Tiny Language Model</title>
      <dc:creator>Ahmad Al Dibo</dc:creator>
      <pubDate>Fri, 16 Oct 2026 20:27:37 +0000</pubDate>
      <link>https://dev.to/ahmadaldibo/how-to-install-arclm-tokenize-text-and-run-a-tiny-language-model-431f</link>
      <guid>https://dev.to/ahmadaldibo/how-to-install-arclm-tokenize-text-and-run-a-tiny-language-model-431f</guid>
      <description>&lt;h1&gt;
  
  
  How to Install ArcLM, Tokenize Text, and Run a Tiny Language Model
&lt;/h1&gt;

&lt;p&gt;Want to experiment with language models in Python without starting by downloading a multi-gigabyte model?&lt;/p&gt;

&lt;p&gt;You can begin with something much smaller: tokenize a sentence, create a tiny neural network, and inspect the model's output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM" rel="noopener noreferrer"&gt;ArcLM&lt;/a&gt; is a Python framework for working with language models. Its 2.0 architecture separates lightweight utilities from compute-intensive functionality, so you can install the core package without making PyTorch a mandatory dependency.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll go from installation to a model forward pass.&lt;/p&gt;

&lt;p&gt;By the end, you'll know how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install ArcLM without PyTorch.&lt;/li&gt;
&lt;li&gt;Convert text into token IDs.&lt;/li&gt;
&lt;li&gt;Configure and save model settings.&lt;/li&gt;
&lt;li&gt;Run a tiny native causal language model.&lt;/li&gt;
&lt;li&gt;Use the ArcLM CLI to check your installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; Python 3.12, a terminal, and basic knowledge of Python. You can complete the first three steps without a GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install ArcLM
&lt;/h2&gt;

&lt;p&gt;Start by creating a virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Activate it on Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;venv&lt;/span&gt;&lt;span class="n"&gt;\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or activate it on macOS and Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install ArcLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;arclm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core installation includes dependencies such as NumPy and SentencePiece without requiring PyTorch.&lt;/p&gt;

&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;arclm&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arclm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_version&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the version used in this tutorial, the output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important point is that you can start exploring the framework without installing the runtime needed for native model execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Tokenize Your First Sentence
&lt;/h2&gt;

&lt;p&gt;Language models do not process ordinary text directly. They operate on numerical representations.&lt;/p&gt;

&lt;p&gt;A tokenizer converts text into tokens and maps those tokens to integer IDs.&lt;/p&gt;

&lt;p&gt;ArcLM provides a simple word tokenizer that makes this process easy to inspect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.tokenizers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tokenizer&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arc lm trains tiny models on local text about language models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_vocab&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arc lm trains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Token IDs:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vocabulary size:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_vocab_size&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Decoded tokens:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The observed output on ArcLM 2.0.0 was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token IDs: [2, 3, 4]
Vocabulary size: 11
Decoded tokens: ['arc', 'lm', 'trains']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Tokenizer(max_vocab=64)&lt;/code&gt; creates a tokenizer with a maximum vocabulary size of 64.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build(text)&lt;/code&gt; constructs a vocabulary from the supplied text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encode_text()&lt;/code&gt; converts words into integer IDs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;decode()&lt;/code&gt; maps those IDs back to tokens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This tokenizer is intentionally simple. It does not automatically lowercase text, so &lt;code&gt;"Arc"&lt;/code&gt; and &lt;code&gt;"arc"&lt;/code&gt; can be treated as different tokens. Words outside the vocabulary map to an unknown token.&lt;/p&gt;

&lt;p&gt;For more flexible subword tokenization, ArcLM also provides SentencePiece support.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Configure a Model Without PyTorch
&lt;/h2&gt;

&lt;p&gt;Configuration and execution are separate concerns.&lt;/p&gt;

&lt;p&gt;You can define settings before installing the dependency required to execute a neural network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.configs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;embed_dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_dim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;32
16
cpu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The settings describe aspects of a model or training workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;embed_dim&lt;/code&gt;: the embedding dimension.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;block_size&lt;/code&gt;: the configured sequence or context length.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;num_epochs&lt;/code&gt;: the number of training epochs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;device&lt;/code&gt;: the configured execution device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also save the configuration to YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.configs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;save_config_yaml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load_config_yaml&lt;/span&gt;

&lt;span class="nf"&gt;save_config_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arclm-config.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;loaded_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_config_yaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arclm-config.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loaded_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embed_dim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to keep experiment settings separate from your application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Run a Tiny Native Language Model
&lt;/h2&gt;

&lt;p&gt;Now let's execute a neural network.&lt;/p&gt;

&lt;p&gt;This step requires PyTorch. Install ArcLM with its training extra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"arclm[train]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need a CPU-only PyTorch installation, use the command provided by the official &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch installation selector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, create &lt;code&gt;model_demo.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.tokenizers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tokenizer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arclm.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ArcLM&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arc lm trains tiny models on local text about language models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_vocab&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ArcLM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_vocab_size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;embed_dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_blocks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arc lm trains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;input_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Logits shape:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Parameters:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_num_parameters&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python model_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The observed CPU output for this configuration was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logits shape: (1, 3, 11)
Parameters: 3531
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the output shape
&lt;/h3&gt;

&lt;p&gt;The input tensor has three dimensions of information in the processing pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch size: &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sequence length: &lt;code&gt;3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vocabulary size: &lt;code&gt;11&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The input tensor itself has shape &lt;code&gt;(1, 3)&lt;/code&gt;. The output logits have shape &lt;code&gt;(1, 3, 11)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each position in the input sequence has an associated vector of scores over the vocabulary. These scores are called &lt;strong&gt;logits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They are not probabilities by themselves. A generation procedure can process the logits to select a next token according to its decoding strategy.&lt;/p&gt;

&lt;p&gt;Also notice that the model contains only 3,531 parameters. It is a teaching example, not a practical replacement for a pretrained LLM.&lt;/p&gt;

&lt;p&gt;The goal is to understand the processing path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text → Token IDs → Neural Network → Logits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have now executed a native causal language model without downloading a pretrained chat model.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Check the ArcLM CLI
&lt;/h2&gt;

&lt;p&gt;ArcLM also provides a command-line interface.&lt;/p&gt;

&lt;p&gt;To see the available commands, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;arclm &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can alternatively use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; arclm &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For runtime diagnostics, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;arclm doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;doctor&lt;/code&gt; command is designed to inspect the environment without requiring PyTorch. This is useful when you want to check the core installation before adding the training dependency.&lt;/p&gt;

&lt;p&gt;ArcLM's CLI includes commands for configuration, data, run inspection, caching, security, and model workflows. Commands for training, evaluation, generation, and chat require the appropriate optional dependencies.&lt;/p&gt;

&lt;p&gt;For this introduction, &lt;code&gt;--help&lt;/code&gt; and &lt;code&gt;doctor&lt;/code&gt; are enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PyTorch is missing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a model-related import raises an &lt;code&gt;OptionalDependencyError&lt;/code&gt;, install the training extra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"arclm[train]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The tokenizer returns unknown tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The words may not exist in the tokenizer's vocabulary. Build the vocabulary with more representative text, use consistent text normalization, or explore subword tokenization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model rejects the input sequence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check the configured &lt;code&gt;block_size&lt;/code&gt;. The sequence length must be compatible with the model's context-length configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;arclm&lt;/code&gt; command is unavailable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Confirm that your virtual environment is active and that ArcLM is installed inside it. You can also use &lt;code&gt;python -m arclm --help&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;In a few steps, we installed ArcLM, converted text into token IDs, created a configuration, and ran a tiny causal language model.&lt;/p&gt;

&lt;p&gt;We also saw why separating the lightweight core from optional compute dependencies can be useful: you can begin exploring language-model tooling before committing to a full machine-learning installation.&lt;/p&gt;

&lt;p&gt;There is an important distinction between running a randomly initialized model and using a pretrained LLM. This tutorial demonstrates the mechanics of tokenization and a forward pass, not how to build a useful conversational model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next step:&lt;/strong&gt; load a pretrained model and explore text generation.&lt;/p&gt;

&lt;p&gt;You can find ArcLM's source code and continue exploring the project on &lt;a href="https://github.com/Ahmad-Al-Dibo/ArcLM" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you try the examples, share your results or questions in the comments.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
