Preview
Preview Feature — This feature is currently in preview and under active development. APIs and functionality may change. We recommend testing thoroughly before using in production.
Agents
Agents are autonomous AI systems that can reason, plan, and execute multi-step tasks using tools. Unlike simple LLM calls, agents maintain context, use tools to interact with external systems, and make decisions about what actions to take.
When to Use Agents
Use Agents when you need:
- Multi-step reasoning - Tasks that require planning and multiple actions
- Tool usage - AI that can call APIs, query databases, or interact with systems
- Autonomous decisions - Systems that decide what to do next based on context
- Human oversight - AI with approval gates for sensitive operations
- Persistent memory - Conversations that remember past interactions
Quick Example
Here's a simple customer support agent:
- TypeScript
- Java
- Go
- .NET
import Ductape from '@ductape/sdk';
const ductape = new Ductape({
accessKey: 'your-access-key',
});
// Define the agent
const agent = await ductape.agents.define({
tag: 'support-agent',
name: 'Customer Support Agent',
model: {
provider: 'anthropic',
model: 'claude-sonnet-4-20250514',
temperature: 0.7,
},
systemPrompt: `You are a helpful customer support agent.
You have access to tools to look up orders and customer information.
Always be polite and helpful.`,
tools: [
{
tag: 'lookup-order',
description: 'Look up order details by order ID',
parameters: {
orderId: {
type: 'string',
description: 'The order ID to look up',
required: true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
database: 'orders-db',
event: 'find-order',
params: { id: params.orderId },
});
},
},
{
tag: 'get-customer',
description: 'Get customer information',
parameters: {
customerId: {
type: 'string',
description: 'The customer ID',
required: true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
database: 'customers-db',
event: 'find-customer',
params: { id: params.customerId },
});
},
},
],
});
// Run the agent
const result = await ductape.agents.run({
tag: 'support-agent',
input: 'Can you check the status of order ORD-12345?',
});
console.log('Response:', result.output);
console.log('Tools used:', result.toolCalls.length);
import app.ductape.sdk.Ductape;
import app.ductape.sdk.core.EnvType;
import app.ductape.sdk.core.RequestContext;
RequestContext auth = new RequestContext(null, null, null, null, 'your-access-key');
Ductape ductape = new Ductape(EnvType.PRODUCTION, auth);
// Define the agent
Map<String, Object> agent = ductape.agents.define(Map.of(
"tag", "support-agent",
"name", "Customer Support Agent",
model: Map.of(
"provider", "anthropic",
"model", "claude-sonnet-4-20250514",
"temperature", 0.7
),
systemPrompt: `You are a helpful customer support agent.
You have access to tools to look up orders and customer information.
Always be polite and helpful.`,
tools: [
Map.of(
"tag", "lookup-order",
"description", "Look up order details by order ID",
parameters: Map.of(
orderId: Map.of(
"type", "string",
"description", "The order ID to look up",
"required", true
)
),
handler: async (ctx, params) => Map.of(
return ctx.database.query(Map.of(
"database", "orders-db",
"event", "find-order",
params: Map.of( id: params.orderId )
));
)
),
Map.of(
"tag", "get-customer",
"description", "Get customer information",
parameters: Map.of(
customerId: Map.of(
"type", "string",
"description", "The customer ID",
"required", true
)
),
handler: async (ctx, params) => Map.of(
return ctx.database.query(Map.of(
"database", "customers-db",
"event", "find-customer",
params: Map.of( id: params.customerId )
));
)
),
]
));
// Run the agent
Map<String, Object> result = ductape.agents().run(Map<String, Object>.of(
"tag", "support-agent",
"input", "Can you check the status of order ORD-12345?"
));
System.out.println('"Response", ", result.output);
System.out.println("Tools used:', result.toolCalls.length);
import (
"context"
"github.com/ductape/ductape/sdk/go/core"
ductapesdk "github.com/ductape/ductape/sdk/go/ductape"
)
auth := core.NewRequestContext("", "", "", "", 'your-access-key')
client, err := ductapesdk.New(core.EnvProduction, auth)
if err != nil {
return err
}
// Define the agent
agent := client.agents.define({
"tag": "support-agent",
"name": "Customer Support Agent",
model: {
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"temperature": 0.7,
},
systemPrompt: `You are a helpful customer support agent.
You have access to tools to look up orders and customer information.
Always be polite and helpful.`,
tools: [
{
"tag": "lookup-order",
"description": "Look up order details by order ID",
parameters: {
orderId: {
"type": "string",
"description": "The order ID to look up",
"required": true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
"database": "orders-db",
"event": "find-order",
params: { id: params.orderId },
});
},
},
{
"tag": "get-customer",
"description": "Get customer information",
parameters: {
customerId: {
"type": "string",
"description": "The customer ID",
"required": true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
"database": "customers-db",
"event": "find-customer",
params: { id: params.customerId },
});
},
},
],
});
// Run the agent
result := client.agents.run({
"tag": "support-agent",
"input": "Can you check the status of order ORD-12345?",
});
fmt.Println('"Response": ", result.output);
fmt.Println("Tools used:', result.toolCalls.length);
using Ductape.Sdk;
using Ductape.Sdk.Core;
var auth = new RequestContext(null, null, null, null, 'your-access-key', null);
var ductape = new Ductape(EnvType.Production, auth);
// Define the agent
var agent = await ductape.agents.define({
["tag"] = "support-agent",
["name"] = "Customer Support Agent",
model: {
["provider"] = "anthropic",
["model"] = "claude-sonnet-4-20250514",
["temperature"] = 0.7,
},
systemPrompt: `You are a helpful customer support agent.
You have access to tools to look up orders and customer information.
Always be polite and helpful.`,
tools: [
{
["tag"] = "lookup-order",
["description"] = "Look up order details by order ID",
parameters: {
orderId: {
["type"] = "string",
["description"] = "The order ID to look up",
["required"] = true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
["database"] = "orders-db",
["event"] = "find-order",
params: { id: params.orderId },
});
},
},
{
["tag"] = "get-customer",
["description"] = "Get customer information",
parameters: {
customerId: {
["type"] = "string",
["description"] = "The customer ID",
["required"] = true,
},
},
handler: async (ctx, params) => {
return ctx.database.query({
["database"] = "customers-db",
["event"] = "find-customer",
params: { id: params.customerId },
});
},
},
],
});
// Run the agent
var result = await ductape.agents.run({
["tag"] = "support-agent",
["input"] = "Can you check the status of order ORD-12345?",
});
Console.WriteLine('["Response"] = ", result.output);
Console.WriteLine("Tools used:', result.toolCalls.length);
How Agents Work
Agents follow a ReAct (Reason + Act) pattern:
User Input → OBSERVE → THINK → ACT → EVALUATE → Output
↑ ↓
└────────────────────────┘
(loop until done)
- OBSERVE - Gather context from input, memory, and tools
- THINK - LLM reasons about what to do next
- ACT - Execute tool calls or generate response
- EVALUATE - Check if task is complete or needs more steps
Agent Loop
- TypeScript
- Java
- Go
- .NET
while (!done) {
// 1. Build context
const context = buildContext(input, memory, toolResults);
// 2. Call LLM
const response = await llm.generate(context, tools);
// 3. Execute tools if requested
if (response.toolCalls) {
for (const call of response.toolCalls) {
const result = await executeTool(call);
toolResults.push(result);
}
}
// 4. Check termination conditions
done = response.stopReason === 'end_turn' ||
iterations >= maxIterations ||
tokenUsage >= maxTokens;
}
while (!done) Map.of(
// 1. Build context
Map<String, Object> context = buildContext(input, memory, toolResults);
// 2. Call LLM
Map<String, Object> response = llm.generate(context, tools);
// 3. Execute tools if requested
if (response.toolCalls) Map.of(
for (Map<String, Object> call of response.toolCalls) Map.of(
Map<String, Object> result = executeTool(call);
toolResults.push(result);
)
)
// 4. Check termination conditions
done = response.stopReason === 'end_turn' ||
iterations >= maxIterations ||
tokenUsage >= maxTokens;
)
while (!done) {
// 1. Build context
context := buildContext(input, memory, toolResults);
// 2. Call LLM
response := llm.generate(context, tools);
// 3. Execute tools if requested
if (response.toolCalls) {
for (const call of response.toolCalls) {
result := executeTool(call);
toolResults.push(result);
}
}
// 4. Check termination conditions
done = response.stopReason === 'end_turn' ||
iterations >= maxIterations ||
tokenUsage >= maxTokens;
}
while (!done) {
// 1. Build context
var context = buildContext(input, memory, toolResults);
// 2. Call LLM
var response = await llm.generate(context, tools);
// 3. Execute tools if requested
if (response.toolCalls) {
for (var call of response.toolCalls) {
var result = await executeTool(call);
toolResults.push(result);
}
}
// 4. Check termination conditions
done = response.stopReason === 'end_turn' ||
iterations >= maxIterations ||
tokenUsage >= maxTokens;
}
Core Components
Model Configuration
- TypeScript
- Java
- Go
- .NET
model: {
provider: 'anthropic', // LLM provider
model: 'claude-sonnet-4-20250514', // Model name
temperature: 0.7, // Creativity (0-2)
maxTokens: 4096, // Max response tokens
topP: 0.9, // Nucleus sampling
stopSequences: ['DONE'], // Custom stop sequences
timeout: 30000, // Request timeout (ms)
}
model: Map.of(
"provider", "anthropic", // LLM provider
"model", "claude-sonnet-4-20250514", // Model name
"temperature", 0.7, // Creativity (0-2)
"maxTokens", 4096, // Max response tokens
"topP", 0.9, // Nucleus sampling
stopSequences: ['DONE'], // Custom stop sequences
"timeout", 30000, // Request timeout (ms)
)
model: {
"provider": "anthropic", // LLM provider
"model": "claude-sonnet-4-20250514", // Model name
"temperature": 0.7, // Creativity (0-2)
"maxTokens": 4096, // Max response tokens
"topP": 0.9, // Nucleus sampling
stopSequences: ['DONE'], // Custom stop sequences
"timeout": 30000, // Request timeout (ms)
}
model: {
["provider"] = "anthropic", // LLM provider
["model"] = "claude-sonnet-4-20250514", // Model name
["temperature"] = 0.7, // Creativity (0-2)
["maxTokens"] = 4096, // Max response tokens
["topP"] = 0.9, // Nucleus sampling
stopSequences: ['DONE'], // Custom stop sequences
["timeout"] = 30000, // Request timeout (ms)
}
Supported Providers
| Provider | Models | Status |
|---|---|---|
anthropic | Claude 3.5 Sonnet, Claude 3 Opus/Sonnet/Haiku | Supported |
openai | GPT-4, GPT-4 Turbo, GPT-3.5 Turbo | Supported |
google | Gemini Pro, Gemini Ultra | Coming Soon |
cohere | Command R, Command R+ | Coming Soon |
Tools
Tools are functions the agent can call:
- TypeScript
- Java
- Go
- .NET
tools: [
{
tag: 'search-products',
name: 'Search Products', // Optional display name
description: 'Search for products by name or category',
parameters: {
query: {
type: 'string',
description: 'Search query',
required: true,
},
category: {
type: 'string',
description: 'Product category',
enum: ['electronics', 'clothing', 'home'],
},
limit: {
type: 'number',
description: 'Max results',
default: 10,
},
},
requiresConfirmation: false, // Needs human approval?
timeout: 5000, // Tool timeout (ms)
retries: 2, // Retry on failure
handler: async (ctx, params) => {
// Tool implementation
return results;
},
},
]
tools: [
Map.of(
"tag", "search-products",
"name", "Search Products", // Optional display name
"description", "Search for products by name or category",
parameters: Map.of(
query: Map.of(
"type", "string",
"description", "Search query",
"required", true
),
category: Map.of(
"type", "string",
"description", "Product category",
enum: ['electronics', 'clothing', 'home']
),
limit: Map.of(
"type", "number",
"description", "Max results",
"default", 10
)
),
"requiresConfirmation", false, // Needs human approval?
"timeout", 5000, // Tool timeout (ms)
"retries", 2, // Retry on failure
handler: async (ctx, params) => Map.of(
// Tool implementation
return results;
)
),
]
import "context"
tools: [
{
"tag": "search-products",
"name": "Search Products", // Optional display name
"description": "Search for products by name or category",
parameters: {
query: {
"type": "string",
"description": "Search query",
"required": true,
},
category: {
"type": "string",
"description": "Product category",
enum: ['electronics', 'clothing', 'home'],
},
limit: {
"type": "number",
"description": "Max results",
"default": 10,
},
},
"requiresConfirmation": false, // Needs human approval?
"timeout": 5000, // Tool timeout (ms)
"retries": 2, // Retry on failure
handler: async (ctx, params) => {
// Tool implementation
return results;
},
},
]
tools: [
{
["tag"] = "search-products",
["name"] = "Search Products", // Optional display name
["description"] = "Search for products by name or category",
parameters: {
query: {
["type"] = "string",
["description"] = "Search query",
["required"] = true,
},
category: {
["type"] = "string",
["description"] = "Product category",
enum: ['electronics', 'clothing', 'home'],
},
limit: {
["type"] = "number",
["description"] = "Max results",
["default"] = 10,
},
},
["requiresConfirmation"] = false, // Needs human approval?
["timeout"] = 5000, // Tool timeout (ms)
["retries"] = 2, // Retry on failure
handler: async (ctx, params) => {
// Tool implementation
return results;
},
},
]
Memory
Configure short-term and long-term memory:
- TypeScript
- Java
- Go
- .NET
memory: {
shortTerm: {
maxMessages: 50, // Conversation history limit
truncationStrategy: 'summarize', // 'fifo' | 'sliding_window' | 'summarize'
includeToolResults: true, // Include tool outputs in history
},
longTerm: {
enabled: true,
vectorStore: 'agent-memory', // Vector config tag
retrieveTopK: 5, // Memories to retrieve
minSimilarity: 0.7, // Minimum relevance
autoStore: true, // Auto-store interactions
namespace: 'support-agent', // Memory isolation
},
}
memory: Map.of(
shortTerm: Map.of(
"maxMessages", 50, // Conversation history limit
"truncationStrategy", "summarize", // 'fifo' | 'sliding_window' | 'summarize'
"includeToolResults", true, // Include tool outputs in history
),
longTerm: Map.of(
"enabled", true,
"vectorStore", "agent-memory", // Vector config tag
"retrieveTopK", 5, // Memories to retrieve
"minSimilarity", 0.7, // Minimum relevance
"autoStore", true, // Auto-store interactions
"namespace", "support-agent", // Memory isolation
)
)
memory: {
shortTerm: {
"maxMessages": 50, // Conversation history limit
"truncationStrategy": "summarize", // 'fifo' | 'sliding_window' | 'summarize'
"includeToolResults": true, // Include tool outputs in history
},
longTerm: {
"enabled": true,
"vectorStore": "agent-memory", // Vector config tag
"retrieveTopK": 5, // Memories to retrieve
"minSimilarity": 0.7, // Minimum relevance
"autoStore": true, // Auto-store interactions
"namespace": "support-agent", // Memory isolation
},
}
memory: {
shortTerm: {
["maxMessages"] = 50, // Conversation history limit
["truncationStrategy"] = "summarize", // 'fifo' | 'sliding_window' | 'summarize'
["includeToolResults"] = true, // Include tool outputs in history
},
longTerm: {
["enabled"] = true,
["vectorStore"] = "agent-memory", // Vector config tag
["retrieveTopK"] = 5, // Memories to retrieve
["minSimilarity"] = 0.7, // Minimum relevance
["autoStore"] = true, // Auto-store interactions
["namespace"] = "support-agent", // Memory isolation
},
}
Termination
Control when the agent stops:
- TypeScript
- Java
- Go
- .NET
termination: {
maxIterations: 25, // Max reasoning loops
maxTokens: 50000, // Max total tokens
timeout: 300000, // Max execution time (5 min)
allowManualStop: true, // Allow external stop signal
}
termination: Map.of(
"maxIterations", 25, // Max reasoning loops
"maxTokens", 50000, // Max total tokens
"timeout", 300000, // Max execution time (5 min)
"allowManualStop", true, // Allow external stop signal
)
termination: {
"maxIterations": 25, // Max reasoning loops
"maxTokens": 50000, // Max total tokens
"timeout": 300000, // Max execution time (5 min)
"allowManualStop": true, // Allow external stop signal
}
termination: {
["maxIterations"] = 25, // Max reasoning loops
["maxTokens"] = 50000, // Max total tokens
["timeout"] = 300000, // Max execution time (5 min)
["allowManualStop"] = true, // Allow external stop signal
}
Human-in-the-Loop
Add approval gates for sensitive operations:
- TypeScript
- Java
- Go
- .NET
humanInLoop: {
enabled: true,
alwaysRequireApproval: ['refund-order', 'delete-account'],
approvalTimeout: 300000, // 5 minutes
approvalWebhook: 'https://your-app.com/approve',
}
humanInLoop: Map.of(
"enabled", true,
alwaysRequireApproval: ['refund-order', 'delete-account'],
"approvalTimeout", 300000, // 5 minutes
"approvalWebhook", "https://your-app.com/approve"
)
humanInLoop: {
"enabled": true,
alwaysRequireApproval: ['refund-order', 'delete-account'],
"approvalTimeout": 300000, // 5 minutes
"approvalWebhook": "https://your-app.com/approve",
}
humanInLoop: {
["enabled"] = true,
alwaysRequireApproval: ['refund-order', 'delete-account'],
["approvalTimeout"] = 300000, // 5 minutes
["approvalWebhook"] = "https://your-app.com/approve",
}
Streaming
Stream agent events in real-time:
- TypeScript
- Java
- Go
- .NET
streaming: {
enabled: true,
events: ['thinking_start', 'message_delta', 'tool_start', 'tool_end'],
webhook: 'https://your-app.com/stream',
}
streaming: Map.of(
"enabled", true,
events: ['thinking_start', 'message_delta', 'tool_start', 'tool_end'],
"webhook", "https://your-app.com/stream"
)
streaming: {
"enabled": true,
events: ['thinking_start', 'message_delta', 'tool_start', 'tool_end'],
"webhook": "https://your-app.com/stream",
}
streaming: {
["enabled"] = true,
events: ['thinking_start', 'message_delta', 'tool_start', 'tool_end'],
["webhook"] = "https://your-app.com/stream",
}
Agent Lifecycle
1. Define
Create an agent definition:
- TypeScript
- Java
- Go
- .NET
const agent = await ductape.agents.define({
tag: 'my-agent',
name: 'My Agent',
model: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
systemPrompt: 'You are a helpful assistant.',
tools: [...],
});
Map<String, Object> agent = ductape.agents.define(Map.of(
"tag", "my-agent",
"name", "My Agent",
model: Map.of( "provider", "anthropic", "model", "claude-sonnet-4-20250514" ),
"systemPrompt", "You are a helpful assistant.",
tools: [...]
));
agent := client.agents.define({
"tag": "my-agent",
"name": "My Agent",
model: { "provider": "anthropic", "model": "claude-sonnet-4-20250514" },
"systemPrompt": "You are a helpful assistant.",
tools: [...],
});
var agent = await ductape.agents.define({
["tag"] = "my-agent",
["name"] = "My Agent",
model: { ["provider"] = "anthropic", ["model"] = "claude-sonnet-4-20250514" },
["systemPrompt"] = "You are a helpful assistant.",
tools: [...],
});
2. Register (Optional)
Register with a product for persistence:
- TypeScript
- Java
- Go
- .NET
await ductape.agents.register('my-product', agent);
ductape.agents.register('my-product', agent);
client.agents.register('my-product', agent);
await ductape.agents.register('my-product', agent);
3. Run
Execute the agent:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.agents.run({
tag: 'my-agent',
input: 'User message',
sessionId: 'session-123', // For memory continuity
});
Map<String, Object> result = ductape.agents().run(Map<String, Object>.of(
"tag", "my-agent",
"input", "User message",
"sessionId", "session-123", // For memory continuity
));
result := client.agents.run({
"tag": "my-agent",
"input": "User message",
"sessionId": "session-123", // For memory continuity
});
var result = await ductape.agents.run({
["tag"] = "my-agent",
["input"] = "User message",
["sessionId"] = "session-123", // For memory continuity
});
4. Control
Send signals to running agents:
- TypeScript
- Java
- Go
- .NET
// Stop execution
await ductape.agents.signal({
executionId: result.executionId,
signal: 'stop',
});
// Approve tool call
await ductape.agents.signal({
executionId: result.executionId,
signal: 'approve',
payload: { requestId: 'req-123' },
});
// Stop execution
ductape.agents.signal(Map.of(
executionId: result.executionId,
"signal", "stop"
));
// Approve tool call
ductape.agents.signal(Map.of(
executionId: result.executionId,
"signal", "approve",
payload: Map.of( "requestId", "req-123" )
));
// Stop execution
client.agents.signal({
executionId: result.executionId,
"signal": "stop",
});
// Approve tool call
client.agents.signal({
executionId: result.executionId,
"signal": "approve",
payload: { "requestId": "req-123" },
});
// Stop execution
await ductape.agents.signal({
executionId: result.executionId,
["signal"] = "stop",
});
// Approve tool call
await ductape.agents.signal({
executionId: result.executionId,
["signal"] = "approve",
payload: { ["requestId"] = "req-123" },
});
Execution Results
- TypeScript
- Java
- Go
- .NET
const result = await ductape.agents.run({...});
console.log(result.status); // 'completed' | 'failed' | 'stopped'
console.log(result.output); // Final response text
console.log(result.iterations); // Number of reasoning loops
console.log(result.toolCalls); // List of tool calls made
console.log(result.tokenUsage); // Token usage breakdown
console.log(result.executionTime); // Total time (ms)
console.log(result.conversationHistory); // Full conversation
Map<String, Object> result = ductape.agents().run(Map<String, Object>.of(
...));
System.out.println(result.status); // 'completed' | 'failed' | 'stopped'
System.out.println(result.output); // Final response text
System.out.println(result.iterations); // Number of reasoning loops
System.out.println(result.toolCalls); // List of tool calls made
System.out.println(result.tokenUsage); // Token usage breakdown
System.out.println(result.executionTime); // Total time (ms)
System.out.println(result.conversationHistory); // Full conversation
result := client.agents.run({...});
fmt.Println(result.status); // 'completed' | 'failed' | 'stopped'
fmt.Println(result.output); // Final response text
fmt.Println(result.iterations); // Number of reasoning loops
fmt.Println(result.toolCalls); // List of tool calls made
fmt.Println(result.tokenUsage); // Token usage breakdown
fmt.Println(result.executionTime); // Total time (ms)
fmt.Println(result.conversationHistory); // Full conversation
var result = await ductape.agents.run({...});
Console.WriteLine(result.status); // 'completed' | 'failed' | 'stopped'
Console.WriteLine(result.output); // Final response text
Console.WriteLine(result.iterations); // Number of reasoning loops
Console.WriteLine(result.toolCalls); // List of tool calls made
Console.WriteLine(result.tokenUsage); // Token usage breakdown
Console.WriteLine(result.executionTime); // Total time (ms)
Console.WriteLine(result.conversationHistory); // Full conversation
Platform Integration
Agents can access all Ductape components through the tool context:
| Component | Access | Description |
|---|---|---|
| Actions | ctx.api.run() | Call external APIs |
| Databases | ctx.database.query() | Database operations |
| Graphs | ctx.graph.query() | Graph database queries |
| Storage | ctx.storage.upload() | File operations |
| Notifications | ctx.notification.email() | Send notifications |
| Publishing | ctx.messaging.produce() | Message broker publishing |
| Vectors | ctx.recall() / ctx.remember() | Vector memory |
| Features | ctx.feature.run() | Run features |
Next Steps
- Getting Started - Build your first agent
- Tools - Creating and managing tools
- Memory - Configure agent memory
- Human-in-the-Loop - Add approval gates
- Examples - Real-world agent patterns