This commit is contained in:
2026-08-08 19:44:06 +07:00
parent 03e1dd7628
commit 2aa0d1e9a4
4 changed files with 86 additions and 165 deletions
+42 -49
View File
@@ -399,15 +399,49 @@ The framework includes tools that allow the agent to create new tools at runtime
### `writeTool` — Create New Tool Files
The `writeTool` tool generates a new Julia tool file at `src/tools/<name>.jl`. The agent writes the tool, then the agent (or system) restarts so `loadTools("src/tools")` picks it up.
`writeTool` is a **file writer**, not a code generator. The LLM provides the tool logic as `executeCode` (the actual Julia code), and `writeTool` wraps it in the required boilerplate.
**How it works:**
The LLM constructs `writeTool` with:
- **`executeCode`** — the actual tool logic (Julia code body, NOT wrapped in a function)
- **`name`, `label`, `description`** — tool metadata
- **`inputSchema`** — parameter schema in MCP format
- **`validateCode`, `prepareCode`** (optional) — custom validation/preparation logic
`writeTool` produces `src/tools/<name>.jl` by:
1. Converting the `inputSchema` Dict into a Julia `Dict{String,Any}(...)` string literal
2. Indenting `executeCode` with 4 spaces
3. Wrapping it inside a `function executeTool(...)::agentToolResult ... end` template
4. Appending the `getTool()` definition that returns an `agentTool` struct
5. Writing the combined string to disk
**Workflow:**
1. Agent identifies a task that no existing tool can handle
2. Agent calls `writeTool` with a tool specification:
```
LLM decides: "Need a searchWine tool. I'll provide the logic."
LLM calls writeTool:
name: "searchWine"
executeCode: "query = args[\"query\"]\nresult = search(query)\nreturn ..."
writeTool wraps it → src/tools/searchWine.jl:
function executeTool(...)::agentToolResult
query = args["query"] ← LLM code (indented 4 spaces)
result = search(query)
return agentToolResult(...)
end
function getTool()::agentTool
return agentTool(name="searchWine", ...)
end
Restart → loadTools("src/tools") loads searchWine.jl
```
**Example specification:**
```julia
# Agent sends this to writeTool:
Dict(
"name" => "searchWine",
"label" => "Wine Search",
@@ -431,45 +465,6 @@ Dict(
)
```
3. Agent calls `listTools` to check for name collisions (built-in, auto-registered)
4. Agent calls `writeTool` with a unique name
5. Restart agent — `loadTools("src/tools")` picks up the new file
6. Agent's next LLM turn discovers and calls the new tool
**Generated file format:**
```julia
# Auto-generated tool: searchWine
# Generated by writeTool at 2026-08-08T14:00:00
function executeTool(toolCallId::String, args::Dict{String,Any}, signal::Union{Nothing,abortSignal}, onPartialResult::Function)::agentToolResult
query = args["query"]
max_results = get(args, "maxResults", 10)
result = "Found 3 wines matching: $query"
return agentToolResult([textContent(result)], Dict{Any,Any}(), nothing, false)
end
function getTool()::agentTool
return agentTool(
name = "searchWine",
label = "Wine Search",
description = "Search a wine database by name, region, or variety",
inputSchema = Dict{String,Any}(
"type" => "object",
"properties" => Dict(
"query" => Dict("type" => "string", "description" => "Search query"),
"maxResults" => Dict("type" => "integer", "default" => 10)
),
"required" => ["query"]
),
execute = executeTool,
validateRequiredArgs = nothing,
prepareArguments = nothing,
parallelToolExecute = false
)
end
```
**Optional hooks:**
| Field | Description |
@@ -479,7 +474,7 @@ end
### `listTools` — Discover Available Tools
Returns a list of all registered tools with names, labels, and descriptions.
Returns all registered tools. Primarily useful for **collision detection** before creating a new tool via `writeTool` — the LLM checks existing names before picking a unique one.
```julia
# Result from listTools:
@@ -497,20 +492,18 @@ User: "I need to search for wines. Do you have a tool for that?"
# ─── LOOP: Agent realizes no wine search tool exists ─────────────────
[Tool Call] listTools()
# Result: lists all available tools — no collision with existing tools
# LLM generates the tool logic and calls writeTool to write it to disk
[Tool Call] writeTool(name="searchWine", label="Wine Search",
description="Search a wine database by name, region, or variety",
inputSchema={...},
executeCode="query = args[\"query\"]\nresult = \"Found wines...\"\nreturn agentToolResult([textContent(result)], ...)")
# writeTool generates searchWine.jl
# writeTool generates src/tools/searchWine.jl
# ─── SYSTEM RESTARTS ─────────────────────────────────────────────────
# Agent restarts — loadTools("src/tools") picks up searchWine.jl
# searchWine is now available — agent uses it directly
# loadTools("src/tools") loads searchWine.jl alongside all other tools
# ─── Agent calls the new tool ─────────────────────────────────────────
+14 -5
View File
@@ -1,13 +1,12 @@
"""
Tool that generates new Julia tool module files.
Tool that writes new Julia tool module files to disk.
The agent can use this tool when it encounters a task that no existing tool
can handle. Provide the tool's name, label, description, inputSchema, and
execute logic as Julia code. The tool is written to `src/tools/<name>.jl`.
After calling this tool, restart the agent so the new tool is loaded by
`loadTools("src/tools")`. Then call `listTools` to verify the new tool
is available.
After calling this tool, restart the agent so `loadTools("src/tools")` picks
up the new file. The new tool is immediately available.
# Example
@@ -16,6 +15,16 @@ is available.
3. Restart agent — loadTools() picks up the new file
4. Agent calls searchWine with args
# How It Works
writeTool is a **file writer**, not a code generator. The LLM provides the
tool logic as `executeCode`, and writeTool wraps it in Julia boilerplate:
- Converts `inputSchema` Dict into Julia `Dict{String,Any}(...)` string
- Indents `executeCode` with 4 spaces
- Wraps it inside `function executeTool(...)::agentToolResult ... end`
- Appends `getTool()` returning an `agentTool` struct
- Writes the combined string to `src/tools/<name>.jl`
# Important Notes
- The `executeCode` string is embedded literally into the generated tool.
@@ -243,7 +252,7 @@ function getTool()::agentTool
return agentTool(
name = "writeTool",
label = "Create Tool",
description = "Generate a new Julia tool module file at src/tools/<name>.jl. After creation, restart the agent and call listTools to verify the new tool is loaded.",
description = "Write a new Julia tool module file to src/tools/<name>.jl. The LLM provides the tool logic as executeCode; writeTool wraps it in Julia boilerplate and writes the file. Restart the agent to load the new tool.",
inputSchema = Dict{String,Any}(
"type" => "object",
"properties" => Dict(