update
This commit is contained in:
@@ -0,0 +1,767 @@
|
||||
# AgentCore.jl - Tools Deep Dive
|
||||
|
||||
## Tool Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Tool Layer │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ AgentTool │
|
||||
│ - name: String (identifier) │
|
||||
│ - label: String (display name) │
|
||||
│ - description: String (what it does) │
|
||||
│ - parameters: JSON schema │
|
||||
│ - execute::Function (main logic) │
|
||||
│ - prepare_arguments::Union{Function, Nothing} │
|
||||
│ - execution_mode::Union{ToolExecutionMode, Nothing} │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ BashTool │ │ ReadTool │ │ WriteTool │
|
||||
│ - bash() │ │ - read() │ │ - write() │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
┌─────────────┐
|
||||
│ EditTool │
|
||||
│ - edit() │
|
||||
└─────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Tool Execution Flow │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Assistant Message
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ AssistantMessage: │
|
||||
│ content: [ │
|
||||
│ TextContent("I'll check the files..."), │
|
||||
│ ToolCall("bash", {command: "ls -la"}), │
|
||||
│ ToolCall("read", {path: "README.md"}) │
|
||||
│ ] │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ AgentLoop.executeToolCalls() │
|
||||
│ - Extract ToolCalls from message content │
|
||||
│ - Determine execution mode (sequential/parallel) │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
├─► executeToolCallsSequential()
|
||||
│ (for tools that require order)
|
||||
│
|
||||
└─► executeToolCallsParallel()
|
||||
(for independent tools)
|
||||
|
||||
│
|
||||
├─► prepareToolCall()
|
||||
│ - before_tool_call hook (optional)
|
||||
│ - validate arguments
|
||||
│ - prepare arguments (optional)
|
||||
│
|
||||
├─► execute()
|
||||
│ - Tool-specific logic
|
||||
│ - Return AgentToolResult
|
||||
│
|
||||
├─► finalizeExecutedToolCall()
|
||||
│ - after_tool_call hook (optional)
|
||||
│
|
||||
└─► createToolResultMessage()
|
||||
- Emit ToolResultMessage
|
||||
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ ToolResultMessage │
|
||||
│ - tool_call_id: "ref to original ToolCall" │
|
||||
│ - tool_name: "bash" │
|
||||
│ - content: [TextContent("file1.md\nfile2.md\n")] │
|
||||
│ - is_error: false │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ AgentState.messages.append(tool_result) │
|
||||
│ - Next turn: LLM sees tool results │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Built-in Tools
|
||||
|
||||
### 1. BashTool
|
||||
|
||||
```julia
|
||||
struct BashToolOptions{TContext}
|
||||
command_prefix::Union{String, Nothing}
|
||||
prepare::Union{BashPrepare{TContext}, Nothing}
|
||||
end
|
||||
|
||||
struct BashPrepare{TContext}
|
||||
function::Function
|
||||
context::TContext
|
||||
signal::Union{Any, Nothing}
|
||||
end
|
||||
|
||||
struct BashToolDetails
|
||||
truncation::Union{Any, Nothing}
|
||||
full_output_path::Union{String, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
#### createBashTool()
|
||||
|
||||
```julia
|
||||
function createBashTool{TContext}(options::Union{BashToolOptions{TContext}, Nothing}=nothing)
|
||||
return AgentTool(
|
||||
"bash",
|
||||
"bash",
|
||||
"Execute a bash command in the current working directory.",
|
||||
Dict{String, Any}(),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Execute command
|
||||
result = executeBashCommand(params, signal, on_update)
|
||||
|
||||
# Return result
|
||||
return AgentToolResult(
|
||||
[TextContent(result.output)],
|
||||
BashToolDetails(result.truncation, result.full_path),
|
||||
nothing,
|
||||
nothing,
|
||||
result.terminate,
|
||||
)
|
||||
end,
|
||||
nothing, # prepare_arguments
|
||||
nothing, # execution_mode (default: use config)
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
**Parameters Schema**:
|
||||
```json
|
||||
{
|
||||
"command": "string",
|
||||
"timeout": "number (optional)",
|
||||
"cwd": "string (optional)",
|
||||
"env": "object (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```julia
|
||||
# Create tool
|
||||
bash_tool = createBashTool()
|
||||
|
||||
# Agent receives command
|
||||
tool_call = ToolCall("tool", "tc1", "bash", Dict(
|
||||
"command" => "ls -la",
|
||||
"timeout" => 30
|
||||
), nothing)
|
||||
|
||||
# Execute
|
||||
result = bash_tool.execute(
|
||||
"tc1",
|
||||
Dict("command" => "ls -la", "timeout" => 30),
|
||||
nothing,
|
||||
on_update, # Callback for streaming output
|
||||
nothing,
|
||||
)
|
||||
|
||||
# Result
|
||||
AgentToolResult(
|
||||
[TextContent("total 12\n-rw-r--r-- 1 user user 100 Jan 1 file1.md\n-rw-r--r-- 1 user user 200 Jan 2 file2.md\n")],
|
||||
BashToolDetails(truncation_info, nothing),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
```
|
||||
|
||||
### 2. ReadTool
|
||||
|
||||
```julia
|
||||
struct ReadToolOptions{TContext}
|
||||
max_size::Union{Int64, Nothing}
|
||||
max_lines::Union{Int64, Nothing}
|
||||
image_processor::Union{ReadImageProcessor, Nothing}
|
||||
prepare::Union{ReadPrepare{TContext}, Nothing}
|
||||
end
|
||||
|
||||
struct ReadImageProcessor
|
||||
function::Function
|
||||
context::Any
|
||||
end
|
||||
|
||||
struct ReadImageProcessorResult
|
||||
content::Vector{MessageContent}
|
||||
usage::Union{Usage, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
#### createReadTool()
|
||||
|
||||
```julia
|
||||
function createReadTool{TContext}(options::Union{ReadToolOptions{TContext}, Nothing}=nothing)
|
||||
return AgentTool(
|
||||
"read",
|
||||
"read",
|
||||
"Read a file from the file system.",
|
||||
Dict{String, Any}(),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Read file
|
||||
result = readFileSystem(params, signal, options)
|
||||
|
||||
# Process content
|
||||
content = if isImage(params.path)
|
||||
# Image processing
|
||||
image_result = options.image_processor.function(result.path, context)
|
||||
image_result.content
|
||||
else
|
||||
# Text content
|
||||
[TextContent(result.content)]
|
||||
end
|
||||
|
||||
return AgentToolResult(
|
||||
content,
|
||||
ReadToolDetails(result.size, result.truncated, result.full_path),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
**Parameters Schema**:
|
||||
```json
|
||||
{
|
||||
"path": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```julia
|
||||
# Create tool
|
||||
read_tool = createReadTool()
|
||||
|
||||
# Agent requests to read file
|
||||
tool_call = ToolCall("tool", "tc2", "read", Dict(
|
||||
"path" => "src/main.jl"
|
||||
), nothing)
|
||||
|
||||
# Execute
|
||||
result = read_tool.execute("tc2", Dict("path" => "src/main.jl"), nothing, nothing, nothing)
|
||||
|
||||
# Result
|
||||
AgentToolResult(
|
||||
[TextContent("module Main\nfunction main()\n println(\"Hello\")\nend\nend\n")],
|
||||
ReadToolDetails(1234, false, "/path/to/src/main.jl"),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
```
|
||||
|
||||
### 3. WriteTool
|
||||
|
||||
```julia
|
||||
struct WriteToolInput
|
||||
path::String
|
||||
content::String
|
||||
end
|
||||
```
|
||||
|
||||
#### createWriteTool()
|
||||
|
||||
```julia
|
||||
function createWriteTool{TContext}(options::Union{WriteToolOptions{TContext}, Nothing}=nothing)
|
||||
return AgentTool(
|
||||
"write",
|
||||
"write",
|
||||
"Write content to a file.",
|
||||
Dict{String, Any}(),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Write file
|
||||
result = writeToFile(params, signal)
|
||||
|
||||
return AgentToolResult(
|
||||
[TextContent(result.message)],
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
**Parameters Schema**:
|
||||
```json
|
||||
{
|
||||
"path": "string",
|
||||
"content": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```julia
|
||||
# Create tool
|
||||
write_tool = createWriteTool()
|
||||
|
||||
# Agent wants to write file
|
||||
tool_call = ToolCall("tool", "tc3", "write", Dict(
|
||||
"path" => "output.txt",
|
||||
"content" => "Hello World"
|
||||
), nothing)
|
||||
|
||||
# Execute
|
||||
result = write_tool.execute("tc3", Dict(
|
||||
"path" => "output.txt",
|
||||
"content" => "Hello World"
|
||||
), nothing, nothing, nothing)
|
||||
|
||||
# Result
|
||||
AgentToolResult(
|
||||
[TextContent("File written: output.txt (11 bytes)")],
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
```
|
||||
|
||||
### 4. EditTool
|
||||
|
||||
```julia
|
||||
struct EditToolInput
|
||||
path::String
|
||||
find::String
|
||||
replacement::String
|
||||
end
|
||||
|
||||
struct EditToolDetails
|
||||
edits::Vector{Edit}
|
||||
before_content::String
|
||||
after_content::String
|
||||
end
|
||||
```
|
||||
|
||||
#### createEditTool()
|
||||
|
||||
```julia
|
||||
function createEditTool{TContext}(options::Union{EditToolOptions{TContext}, Nothing}=nothing)
|
||||
return AgentTool(
|
||||
"edit",
|
||||
"edit",
|
||||
"Edit a file by finding and replacing text.",
|
||||
Dict{String, Any}(),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Read file
|
||||
before_content = read(params.path)
|
||||
|
||||
# Apply edit
|
||||
after_content = replace(before_content, params.find => params.replacement)
|
||||
|
||||
# Write file
|
||||
write(params.path, after_content)
|
||||
|
||||
return AgentToolResult(
|
||||
[TextContent("Edit applied successfully")],
|
||||
EditToolDetails([Edit(params.find, params.replacement)], before_content, after_content),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
**Parameters Schema**:
|
||||
```json
|
||||
{
|
||||
"path": "string",
|
||||
"find": "string",
|
||||
"replacement": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```julia
|
||||
# Create tool
|
||||
edit_tool = createEditTool()
|
||||
|
||||
# Agent wants to replace text
|
||||
tool_call = ToolCall("tool", "tc4", "edit", Dict(
|
||||
"path" => "README.md",
|
||||
"find" => "v1.0.0",
|
||||
"replacement" => "v2.0.0"
|
||||
), nothing)
|
||||
|
||||
# Execute
|
||||
result = edit_tool.execute("tc4", Dict(
|
||||
"path" => "README.md",
|
||||
"find" => "v1.0.0",
|
||||
"replacement" => "v2.0.0"
|
||||
), nothing, nothing, nothing)
|
||||
|
||||
# Result
|
||||
AgentToolResult(
|
||||
[TextContent("Edit applied: README.md")],
|
||||
EditToolDetails([Edit("v1.0.0", "v2.0.0")], "Version 1.0.0", "Version 2.0.0"),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
```
|
||||
|
||||
## Tool Execution Hooks
|
||||
|
||||
### before_tool_call
|
||||
|
||||
```julia
|
||||
struct BeforeToolCallContext
|
||||
assistant_message::AssistantMessage
|
||||
tool_call::ToolCall
|
||||
args::Any
|
||||
context::AgentContext
|
||||
end
|
||||
|
||||
struct BeforeToolCallResult
|
||||
block::Union{Bool, Nothing}
|
||||
reason::Union{String, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```julia
|
||||
function myBeforeToolCall(context, signal)
|
||||
tool_name = context.tool_call.name
|
||||
|
||||
# Block dangerous commands
|
||||
if tool_name == "bash" && contains(context.args["command"], "rm -rf /")
|
||||
return BeforeToolCallResult(
|
||||
true,
|
||||
"Blocking dangerous command: rm -rf /"
|
||||
)
|
||||
end
|
||||
|
||||
# Log tool execution
|
||||
println("Executing tool: $tool_name")
|
||||
|
||||
return nothing # Allow execution
|
||||
end
|
||||
|
||||
# Configure agent
|
||||
agent = Agent(Dict(
|
||||
:beforeToolCall => myBeforeToolCall,
|
||||
))
|
||||
```
|
||||
|
||||
### after_tool_call
|
||||
|
||||
```julia
|
||||
struct AfterToolCallContext
|
||||
assistant_message::AssistantMessage
|
||||
tool_call::ToolCall
|
||||
args::Any
|
||||
result::AgentToolResult
|
||||
is_error::Bool
|
||||
context::AgentContext
|
||||
end
|
||||
|
||||
struct AfterToolCallResult
|
||||
content::Union{Vector{MessageContent}, Nothing}
|
||||
details::Union{Any, Nothing}
|
||||
is_error::Union{Bool, Nothing}
|
||||
usage::Union{Usage, Nothing}
|
||||
terminate::Union{Bool, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```julia
|
||||
function myAfterToolCall(context, signal)
|
||||
tool_name = context.tool_call.name
|
||||
|
||||
# Modify bash output
|
||||
if tool_name == "bash"
|
||||
# Add timestamp to output
|
||||
new_content = [
|
||||
TextContent("[Executed at $(Dates.now())]\n"),
|
||||
context.result.content[1],
|
||||
]
|
||||
return AfterToolCallResult(
|
||||
content = new_content,
|
||||
details = context.result.details,
|
||||
is_error = context.is_error,
|
||||
usage = context.result.usage,
|
||||
terminate = context.result.terminate,
|
||||
)
|
||||
end
|
||||
|
||||
return nothing # Use original result
|
||||
end
|
||||
|
||||
# Configure agent
|
||||
agent = Agent(Dict(
|
||||
:afterToolCall => myAfterToolCall,
|
||||
))
|
||||
```
|
||||
|
||||
### prepare_next_turn
|
||||
|
||||
```julia
|
||||
struct PrepareNextTurnContext
|
||||
message::AssistantMessage
|
||||
tool_results::Vector{ToolResultMessage}
|
||||
context::AgentContext
|
||||
new_messages::Vector{AgentMessage}
|
||||
end
|
||||
|
||||
struct AgentLoopTurnUpdate
|
||||
context::Union{AgentContext, Nothing}
|
||||
model::Union{Model, Nothing}
|
||||
thinking_level::Union{ThinkingLevel, Nothing}
|
||||
end
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```julia
|
||||
function myPrepareNextTurn(context, signal)
|
||||
# Check if we should use a different model
|
||||
last_message = context.message
|
||||
tool_results = context.tool_results
|
||||
|
||||
# If tool execution had errors, use more capable model
|
||||
has_errors = any(r -> r.is_error, tool_results)
|
||||
if has_errors
|
||||
return AgentLoopTurnUpdate(
|
||||
context = context.context,
|
||||
model = Model("gpt-4", "GPT-4", "openai", "openai", "", ...),
|
||||
thinking_level = THINKING_HIGH,
|
||||
)
|
||||
end
|
||||
|
||||
return nothing # Keep current settings
|
||||
end
|
||||
|
||||
# Configure agent
|
||||
agent = Agent(Dict(
|
||||
:prepareNextTurn => myPrepareNextTurn,
|
||||
))
|
||||
```
|
||||
|
||||
## Tool Execution Modes
|
||||
|
||||
### Sequential Execution
|
||||
|
||||
```julia
|
||||
# Tools run one at a time, in order
|
||||
# Use case: Tools that modify shared state
|
||||
|
||||
# Configure tool
|
||||
bash_tool = AgentTool(
|
||||
"bash",
|
||||
"bash",
|
||||
"Execute bash command",
|
||||
...,
|
||||
execute,
|
||||
nothing,
|
||||
EXECUTION_SEQUENTIAL, # Force sequential
|
||||
)
|
||||
|
||||
# Or configure globally
|
||||
agent = Agent(Dict(
|
||||
:toolExecution => EXECUTION_SEQUENTIAL,
|
||||
))
|
||||
```
|
||||
|
||||
**Example Scenario**:
|
||||
```julia
|
||||
# Sequential execution (correct order)
|
||||
|
||||
1. Tool 1: create_directory("build/")
|
||||
└─ Creates build/ directory
|
||||
|
||||
2. Tool 2: write("build/app.js", "...")
|
||||
└─ Writes file to build/
|
||||
|
||||
(If parallel: might fail because build/ doesn't exist yet)
|
||||
```
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
```julia
|
||||
# Tools run concurrently
|
||||
# Use case: Independent operations
|
||||
|
||||
# Default behavior
|
||||
agent = Agent(Dict(
|
||||
:toolExecution => EXECUTION_PARALLEL, # Default
|
||||
))
|
||||
```
|
||||
|
||||
**Example Scenario**:
|
||||
```julia
|
||||
# Parallel execution (independent operations)
|
||||
|
||||
1. Tool 1: read("README.md") ─────┐
|
||||
2. Tool 2: read("CHANGELOG.md") ─┼─► Run simultaneously
|
||||
3. Tool 3: read("LICENSE") ──────┘
|
||||
|
||||
(Parallel: All three read operations can happen at once)
|
||||
(Sequential: Would wait for each read to complete)
|
||||
```
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Example: Database Tool
|
||||
|
||||
```julia
|
||||
function createDatabaseTool()
|
||||
return AgentTool(
|
||||
"database",
|
||||
"database",
|
||||
"Execute SQL queries against the database.",
|
||||
Dict{String, Any}(
|
||||
"type" => "object",
|
||||
"properties" => Dict(
|
||||
"query" => Dict("type" => "string"),
|
||||
"params" => Dict("type" => "array", "items" => Dict("type" => "string")),
|
||||
),
|
||||
"required" => ["query"],
|
||||
),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Execute query
|
||||
query = params["query"]
|
||||
result = executeQuery(query)
|
||||
|
||||
# Format output
|
||||
output = formatQueryResult(result)
|
||||
|
||||
return AgentToolResult(
|
||||
[TextContent(output)],
|
||||
Dict("rows_affected" => result.rows_affected),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end,
|
||||
nothing,
|
||||
EXECUTION_SEQUENTIAL,
|
||||
)
|
||||
end
|
||||
|
||||
# Usage
|
||||
db_tool = createDatabaseTool()
|
||||
agent = Agent(Dict(:tools => [db_tool]))
|
||||
```
|
||||
|
||||
### Example: HTTP Request Tool
|
||||
|
||||
```julia
|
||||
function createHTTPTool()
|
||||
return AgentTool(
|
||||
"http",
|
||||
"http",
|
||||
"Make HTTP requests.",
|
||||
Dict{String, Any}(
|
||||
"type" => "object",
|
||||
"properties" => Dict(
|
||||
"url" => Dict("type" => "string"),
|
||||
"method" => Dict("type" => "string", "enum" => ["GET", "POST", "PUT", "DELETE"]),
|
||||
"body" => Dict("type" => "string"),
|
||||
"headers" => Dict("type" => "object"),
|
||||
),
|
||||
"required" => ["url", "method"],
|
||||
),
|
||||
(tool_call_id, params, signal, on_update, context) -> begin
|
||||
# Make request
|
||||
url = params["url"]
|
||||
method = params["method"]
|
||||
body = get(params, "body", nothing)
|
||||
headers = get(params, "headers", Dict())
|
||||
|
||||
response = makeHTTPRequest(method, url, body, headers)
|
||||
|
||||
return AgentToolResult(
|
||||
[TextContent(response.body)],
|
||||
Dict(
|
||||
"status_code" => response.status_code,
|
||||
"headers" => response.headers,
|
||||
),
|
||||
nothing,
|
||||
nothing,
|
||||
nothing,
|
||||
)
|
||||
end,
|
||||
nothing,
|
||||
EXECUTION_PARALLEL,
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```julia
|
||||
using AgentCore
|
||||
|
||||
# 1. Create tools
|
||||
bash_tool = createBashTool()
|
||||
read_tool = createReadTool()
|
||||
write_tool = createWriteTool()
|
||||
|
||||
# 2. Configure hooks
|
||||
before_hook = (context, signal) -> begin
|
||||
println("About to execute: $(context.tool_call.name)")
|
||||
return nothing
|
||||
end
|
||||
|
||||
after_hook = (context, signal) -> begin
|
||||
if context.is_error
|
||||
println("Tool failed: $(context.tool_call.name)")
|
||||
else
|
||||
println("Tool completed: $(context.tool_call.name)")
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
# 3. Create agent
|
||||
agent = Agent(Dict(
|
||||
:systemPrompt => "You are a helpful assistant with file system access.",
|
||||
:tools => [bash_tool, read_tool, write_tool],
|
||||
:beforeToolCall => before_hook,
|
||||
:afterToolCall => after_hook,
|
||||
))
|
||||
|
||||
# 4. Run conversation
|
||||
prompt(agent, "List files in current directory and read the first one")
|
||||
|
||||
# 5. Agent will:
|
||||
# - Execute bash("ls -la") tool
|
||||
# - Parse output to find first file
|
||||
# - Execute read("path/to/file") tool
|
||||
# - Return content to user
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use sequential execution** for tools that depend on shared state
|
||||
2. **Use parallel execution** for independent operations
|
||||
3. **Implement before_tool_call hook** for logging and validation
|
||||
4. **Implement after_tool_call hook** for result modification
|
||||
5. **Use prepare_next_turn hook** for dynamic model/thinking level changes
|
||||
6. **Return terminate=true** from tool when agent should stop
|
||||
7. **Include usage statistics** in tool results when possible
|
||||
Reference in New Issue
Block a user