scriptling.ai.agent
Agentic AI loop for building AI agents with automatic tool execution. The Agent class handles the complete agentic loop: calling the LLM, executing tool calls, feeding results back, and repeating until the model returns a final answer.
Available Classes & Methods
| Class/Method | Description |
|---|---|
Agent(client, tools, system_prompt, model, memory, mcp_servers, max_tokens, compaction_threshold, request_timeout, extra_body) |
Create an AI agent |
agent.trigger(message, max_iterations) |
One-shot trigger with response |
interact.Agent(...).interact(max_iterations) |
Start an interactive session (subclass provided by scriptling.ai.agent.interact) |
agent.get_messages() |
Get conversation history |
agent.set_messages(messages) |
Set conversation history |
For tool registry documentation, see scriptling.ai.
Quick Start
import scriptling.ai as ai
import scriptling.ai.agent as agent
# Create AI client
client = ai.Client("http://127.0.0.1:1234/v1")
# Create tool registry
tools = ai.ToolRegistry()
tools.add("calculate", "Calculate square root", {"number": "number"}, lambda args: str(args["number"] ** 0.5))
# Create agent
bot = agent.Agent(client, tools=tools, system_prompt="You are a helpful assistant", model="gpt-4")
# One-shot trigger
response = bot.trigger("What is the square root of 144?", max_iterations=10)
print(response.content)For an interactive terminal session, create the agent from scriptling.ai.agent.interact instead (requires scriptling.console); see scriptling.ai.agent.interact:
import scriptling.ai.agent.interact as interact
bot = interact.Agent(client, tools=tools, system_prompt="You are a helpful assistant", model="gpt-4")
bot.interact()Functions
Agent(client, tools=None, system_prompt="", model="", memory=None, mcp_servers=None, max_tokens=32000, compaction_threshold=80, request_timeout=300, extra_body=None)
Creates an AI agent with automatic tool execution.
Parameters:
client(AIClient): AI client instance fromai.Client().tools(ToolRegistry, optional): Tool registry with available tools. Default:None.system_prompt(str, optional): System prompt for the agent. Default:"".model(str, optional): Model to use. Default:"".memory(memory object, optional): Memory store frommemory.new(): see Memory Integration. Default:None.mcp_servers(list, optional): MCP clients frommcp.Client()whose tools and skills the agent can use: see MCP Server Integration. Each must be created with a distinct namespace. Default:None.max_tokens(int, optional): Maximum token budget for the conversation. When estimated token usage reaches the compaction threshold, the conversation history is automatically compacted (summarized). Default:32000.compaction_threshold(int, optional): Percentage ofmax_tokensat which auto-compaction triggers (0-100). For example, withmax_tokens=32000andcompaction_threshold=80, compaction triggers at ~25600 tokens. Default:80.request_timeout(int, optional): Timeout in seconds for each LLM completion request. LLM calls can be slow, especially with tool-calling loops or large contexts. Default:300.extra_body(dict, optional): Provider-specific fields to merge into every request body. Default:None.
Returns: Agent: an agent instance.
import scriptling.ai as ai
import scriptling.ai.agent as agent
client = ai.Client("http://127.0.0.1:1234/v1")
tools = ai.ToolRegistry()
tools.add("reverse", "Reverse text", {"text": "string"}, lambda args: args["text"][::-1])
bot = agent.Agent(
client,
tools=tools,
system_prompt="You are a coding assistant",
model="gpt-4"
)
# With custom compaction settings (compact at 50% of 16k tokens)
bot = agent.Agent(
client,
tools=tools,
max_tokens=16000,
compaction_threshold=50
)
# With provider-specific request body fields
bot = agent.Agent(
client,
tools=tools,
model="glm-4.7",
extra_body={
"thinking": {
"type": "enabled",
"clear_thinking": False
}
}
)agent.trigger(message, max_iterations=1)
Processes a message with the agent, executing tools as needed. Strips <think>...</think> blocks from responses, maintains conversation history, and stops after max_iterations or once the model stops calling tools. Uses the agent’s configured request_timeout for each LLM call.
Parameters:
message(strordict): User message to process.max_iterations(int, optional): Maximum tool call rounds. Default:1.
Returns: dict: the agent’s response message.
response = bot.trigger("What is 2+2?")
print(response.content)
response = bot.trigger("Reverse the word 'hello'", max_iterations=10)
print(response.content)agent.interact(max_iterations=25)
Runs an interactive CLI session. This method exists only on the Agent subclass exported by scriptling.ai.agent.interact (interact.Agent); a plain agent.Agent has no interact(). See scriptling.ai.agent.interact for the full reference.
Parameters:
max_iterations(int, optional): Maximum tool call rounds per message. Default:25.
Returns: None
import scriptling.ai.agent.interact as interact
bot = interact.Agent(client, tools=tools, system_prompt="Coding assistant")
bot.interact()agent.get_messages()
Gets the current conversation history.
Returns: list: list of message dicts.
messages = bot.get_messages()agent.set_messages(messages)
Replaces the conversation history.
Parameters:
messages(list): List of message dicts withroleandcontentkeys.
Returns: None
bot.set_messages([
{"role": "system", "content": "You are helpful"},
{"role": "user", "content": "Hello"},
])Memory Integration
Pass a memory store to Agent via the memory= kwarg. The agent automatically:
- Registers
memory_remember,memory_recall, andmemory_forgetas tools. - Appends memory usage instructions to the system prompt.
- Pre-loads all stored
preferencememories into the system prompt so the LLM has immediate context on the first message without a tool call round-trip.
import scriptling.ai as ai
import scriptling.ai.agent as agent
import scriptling.ai.agent.interact as interact
import scriptling.ai.memory as memory
import scriptling.runtime.kv as kv
client = ai.Client("http://127.0.0.1:1234/v1")
mem = memory.new(kv.open("./memory-db"))
bot = interact.Agent(
client,
model="gpt-4",
system_prompt="You are a helpful assistant.",
memory=mem
)
bot.interact()You can combine memory= with your own tools: the memory tools are added to the existing registry:
tools = ai.ToolRegistry()
tools.add("search", "Search the web", {"query": "string"}, search_handler)
bot = agent.Agent(client, tools=tools, memory=mem, model="gpt-4")
# bot.tool_schemas now contains: search, memory_remember, memory_recall, memory_forgetMemory Tools
When memory= is provided, the following tools are registered automatically:
| Tool | Parameters | Description |
|---|---|---|
memory_remember |
content, type?, importance? |
Store a fact, preference, event or note. |
memory_recall |
query?, limit?, type? |
Search memories by keyword; omit query for recent context. |
memory_forget |
id |
Remove a memory by ID. |
System Prompt Augmentation
The agent appends a ## Memory block to the system prompt explaining when and how to use the memory tools. It also injects a ## Remembered Preferences block containing all stored preference memories, so the LLM has user preferences available immediately.
The original system_prompt you pass is always preserved: the memory content is appended after it.
See scriptling.ai.memory for full memory store documentation.
MCP Server Integration
Pass one or more MCP clients to Agent via the mcp_servers= kwarg and the agent can use everything those servers expose: their tools directly, and their skills on demand. The agent automatically:
- Registers every server tool under its namespaced name (a
searchtool on a client created withnamespace="shop"becomesshop__search), keeping the server’s own input schema verbatim. - Lists the servers’ skills in the system prompt with namespace-qualified URIs, for example
- shop/product-copy: How to write product copy (skill://shop/product-copy/SKILL.md). - Adds a
get_skilltool that fetches a skill’s SKILL.md content on request: the model calls it with the URI from the listing, the agent routes it to the owning server by the URI’s namespace. Supporting files of the same skill route the same way.
import scriptling.ai as ai
import scriptling.ai.agent as agent
import scriptling.mcp as mcp
client = ai.Client("http://127.0.0.1:1234/v1")
# Every server needs its own namespace: it prefixes tool names and routes
# skill URIs back to the owning server.
shop = mcp.Client("https://shop.example.com/mcp", namespace="shop")
docs = mcp.Client("npx", args=["-y", "@example/docs-server"], namespace="docs")
tools = ai.ToolRegistry()
tools.add("lookup_order", "Look up one of our order IDs", {"order_id": "string"}, lookup)
bot = agent.Agent(
client,
tools=tools,
mcp_servers=[shop, docs],
system_prompt="You are a shop assistant.",
model="gpt-4"
)
response = bot.trigger("Find a blue mug, then follow the shop's product-copy skill to describe it", max_iterations=10)
print(response.content)
# Close stdio clients yourself when done; the agent does not own them.
docs.close()Local tools, remote tools and get_skill all live in the same registry, so bot.tool_schemas contains lookup_order, shop__search, docs__fetch_page and get_skill side by side.
Rules and behaviour:
- Every client in
mcp_serversmust be created with a distinctnamespace=:Agent()raises aValueErrorfor a missing or duplicated namespace. - A server without the Skills extension (or with none) simply contributes no skills; its tools still work and no skills section is added.
- If you registered your own
get_skilltool, the agent keeps it and does not add its own. - MCP Apps tools (tools linked to a
ui://resource) come along as ordinary tools: an agent loop has no UI host, so their results are plain text. - Creating the agent contacts every server once (
tools/listplusskills/list); an unreachable server raises atAgent()construction rather than being silently skipped. - The skills listing lives in the system message, so it survives auto-compaction.
A complete runnable example, including a scriptling MCP server with a tool and a skill, is in the scriptling repository at examples/agent-mcp.
Auto-Compaction
The agent automatically compacts conversation history when it grows too large, preventing context window overflow and reducing API costs.
How it works:
- Before each completion call, the agent estimates the token count of the current messages.
- If the estimated tokens reach the compaction threshold (percentage of
max_tokens), the conversation is compacted. - Compaction asks the AI to summarize the conversation so far, preserving key facts and context.
- The history is rebuilt as: system prompt + summary + protected recent context.
- Active tool rounds are preserved so assistant tool calls remain paired with their tool results.
- The agent continues normally with the compacted history.
Parameters:
max_tokens(int): Maximum token budget. Default:32000.compaction_threshold(int): Percentage ofmax_tokensat which compaction triggers. Default:80.
import scriptling.ai as ai
import scriptling.ai.agent as agent
client = ai.Client("http://127.0.0.1:1234/v1")
# Default: compact at 80% of 32000 tokens (25600 tokens)
bot = agent.Agent(client, model="gpt-4")
# Custom: compact at 50% of 16000 tokens (8000 tokens)
bot = agent.Agent(client, model="gpt-4", max_tokens=16000, compaction_threshold=50)Set max_tokens=0 or compaction_threshold=0 to disable auto-compaction entirely.
With LLM-based Deduplication
Pass an AI client to memory.new() to enable intelligent deduplication. When similar memories are found during remember() or compact(), the LLM decides whether to merge them or keep them separate:
client = ai.Client("http://127.0.0.1:1234/v1")
mem = memory.new(kv.open("./memory-db"), ai_client=client, model="qwen3-8b")
bot = agent.Agent(client, model="qwen3-8b", memory=mem)Without an AI client, deduplication is rule-based only (MinHash similarity ≥ 85% auto-merges, otherwise keeps separate).
Tool Handler Interface
Tool handlers receive a dict of arguments and can return any value: complex types are automatically JSON-encoded for the LLM.
def get_time(args):
import datetime
return str(datetime.datetime.now())
def calculate_safe(args):
try:
import math
return str(math.sqrt(args["number"]))
except ValueError as e:
return f"Error: {e}"Thinking Blocks
The agent automatically handles <think>...</think> blocks:
- In
trigger(): strips thinking blocks from responses. - In
interact(): displays thinking in purple, then strips from final output.
import scriptling.ai as ai
result = ai.extract_thinking(response_text)
thinking_blocks = result["thinking"]
clean_content = result["content"]Complete Example
#!/usr/bin/env scriptling
import scriptling.ai as ai
import scriptling.ai.agent as agent
import scriptling.ai.agent.interact as interact
import scriptling.ai.memory as memory
import scriptling.runtime.kv as kv
import os
client = ai.Client("http://127.0.0.1:1234/v1", api_key=os.getenv("OPENAI_API_KEY", ""))
# Tools
tools = ai.ToolRegistry()
tools.add("sqrt", "Calculate square root", {"number": "number"}, lambda args: str(args["number"] ** 0.5))
tools.add("reverse", "Reverse a text string", {"text": "string"}, lambda args: args["text"][::-1])
# Memory
mem = memory.new(kv.open("./memory-db"))
# Agent with tools and memory
bot = interact.Agent(
client,
tools=tools,
memory=mem,
system_prompt="You are a helpful math and text assistant.",
model="gpt-4"
)
bot.interact()Security Considerations
This is an extended library, requiring registration in Go, see Library Registration.
scriptling.ai.agent makes outbound HTTP requests to the configured AI provider, and: when tools are registered: lets the model execute multi-step agentic loops by calling those tools automatically. Never register scriptling.ai.agent for untrusted code: a malicious script can supply a tool registry whose handlers do anything the host process allows. Passing mcp_servers extends that reach to the connected MCP servers: the model can call their tools and read their skills for the duration of the loop. For a full risk breakdown, see the Security Guide and Library Registration.
See Also
- scriptling.ai: AI client and completion functions
- scriptling.ai.agent.interact: Interactive terminal session
- scriptling.ai.memory: Long-term memory store
- scriptling.mcp: MCP client library