A powerful Go framework for building intelligent agents with LLM integration, tool execution, and multi-agent collaboration.
- Features
- Installation
- Development
- Quick Start
- Core Concepts
- System Agents
- Built-in Tools
- Advanced Features
- Plugins
- Complete Example
- Environment Variables
- Project Structure
- Contributing
- License
- π Simple Agent Creation - Create AI agents with just a few lines of code
- π§ Extensible Tool System - Build custom tools with automatic validation
- π₯ Multi-Agent Teams - Orchestrate teams of specialized agents
- π Streaming Responses - Real-time streaming of agent responses
- πΎ Conversation Persistence - Built-in conversation history storage
- π Multiple LLM Providers - Support for OpenAI, DeepSeek, TogetherAI, and OpenAI-compatible APIs
- π Web Automation - Navigate, interact with, and extract content from web pages using headless browser
- π Session Management - Automatic browser session cleanup and resource management
go get github.com/thinktwiceco/agent-forgeRun unit tests using the test script:
./scripts/test.sh --unitThis runs all unit tests with race detection and coverage reporting.
Run the linter using the lint script:
./scripts/lint.shNote: You'll need to install golangci-lint first. See installation instructions below.
Using the official installation script (recommended):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latestMake sure $(go env GOPATH)/bin is in your PATH:
export PATH=$PATH:$(go env GOPATH)/binUsing Go install:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latestUsing package managers:
- macOS:
brew install golangci-lint - Linux (snap):
sudo snap install golangci-lint
package main
import (
"context"
"fmt"
"github.com/thinktwiceco/agent-forge/src/agents"
"github.com/thinktwiceco/agent-forge/src/llms"
)
func main() {
ctx := context.Background()
// Create an LLM engine
multiModelLLM, err := llms.NewOpenAILLMBuilder("togetherai").
SetModel(llms.TOGETHERAI_Llama3170BInstructTurbo).
SetCtx(ctx).
Build()
if err != nil {
panic(err)
}
llm := multiModelLLM.MainModel()
// Create an agent
agent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "Assistant",
SystemPrompt: "You are a helpful AI assistant.",
MainAgent: true,
})
// Chat with the agent
responseCh := agent.ChatStream("Hello! How can you help me?", "")
// Process streaming response
for chunk := range responseCh.Start() {
if chunk.Content != "" {
fmt.Print(chunk.Content)
}
}
}Agents can be created programmatically using AgentConfig or from YAML configuration files. See the Builder Configuration Guide for details on creating agents from configuration files.
Agents are created using AgentConfig:
agent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm, // Required: LLM engine
AgentName: "my-agent", // Required: Agent name
Description: "Agent description", // Optional
SystemPrompt: "You are...", // Optional
Tools: []llms.Tool{}, // Optional: Available tools
MainAgent: true, // Optional: Is this the main agent?
Persistence: "json", // Optional: Enable conversation history
Plugins: []core.Plugin{}, // Optional: Plugins
Tone: "keep-it-short", // Optional: Response tone
Trace: "response", // Optional: Trace identifier
CanExpand: true, // Optional: Enable tool/agent expansion
SubAgents: []*core.SubAgent{}, // Optional: Sub-agents for delegation
})
// Add system agents after creation
reasoningAgent := agents.ReasoningAgent(llm)
agent.AddSystemAgent(reasoningAgent)Tools extend agent capabilities:
import "github.com/thinktwiceco/agent-forge/src/core"
calculatorTool := &core.Tool{
Name: "calculate",
Description: "Performs mathematical calculations",
Parameters: []core.Parameter{
{
Name: "expression",
Type: "string",
Description: "Mathematical expression to evaluate",
Required: true,
},
},
Handler: func(agentContext map[string]any, args map[string]any) llms.ToolReturn {
expression := args["expression"].(string)
result := evaluate(expression)
return core.NewSuccessResponse(result)
},
}
// Add tool to agent
agent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "calculator-agent",
Tools: []llms.Tool{calculatorTool},
})Create specialized agents and coordinate them:
// Create specialized custom sub-agents
customAgent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "custom-agent",
Description: "Breaks down complex problems",
SystemPrompt: "You are a specialized agent.",
})
// Convert to sub-agent
customSubAgent := customAgent.AgentAsSubAgent()
// Create main agent
mainAgent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "coordinator",
MainAgent: true,
SubAgents: []*core.SubAgent{customSubAgent},
})
// Add system agents (see System Agents section)
reasoningAgent := agents.ReasoningAgent(llm)
mainAgent.AddSystemAgent(reasoningAgent)
// The delegate tool is automatically added when sub-agents are presentSystem agents are pre-defined specialized agents that can be added to your main agent. They provide common functionality like reasoning analysis, OS operations, Git operations, coding assistance, web automation, and vector database operations.
The reasoning agent analyzes questions before the main agent responds, helping identify ambiguities, detect assumptions, and guide objective responses.
// Create and add reasoning agent
reasoningAgent := agents.ReasoningAgent(llm)
agent.AddSystemAgent(reasoningAgent)Use cases:
- Analyzing ambiguous questions
- Detecting missing information
- Providing guidance for objective responses
- Flagging assumptions before responding
The OS agent handles file system operations and OS-related tasks within a restricted directory.
// Create and add OS agent with root directory
osAgent := agents.OsAgent(llm, "/path/to/root")
agent.AddSystemAgent(osAgent)Use cases:
- Reading and writing files
- File system operations
- Executing OS-level commands
- Managing file resources
Note: The OS agent operates within a restricted root directory for security. All file paths are validated to prevent directory traversal.
The Web agent provides web navigation, automation, and content extraction capabilities using a headless browser.
// Create and add Web agent with working directory
webAgent := agents.WebAgent(llm, "/path/to/working/dir")
agent.AddSystemAgent(webAgent)Use cases:
- Web navigation and page interaction
- Form filling and clicking elements
- Screenshot capture
- Content extraction and saving
- JavaScript execution
- Browser history navigation
Available actions:
navigate: Navigate to a URL (automatically adds https:// if scheme is missing)click: Click elements by CSS selector (with optional wait for visibility)type: Type text into input fields (with optional field clearing)screenshot: Capture page or element screenshots (saves to temp directory if path not provided)get_content: Extract page content as HTML, plain text, or titlesave_content: Save page content to file inworkingDir/webdirectory (preferred when vector indexing is available)wait: Wait for elements to appear or pages to load (configurable timeout)back/forward: Navigate browser historyevaluate: Execute JavaScript code and return resultsclose: Close browser session and free resources
Note:
- The Web agent maintains browser context across tool calls, preserving cookies, session, and history
- Browser sessions are automatically cleaned up after 5 minutes of inactivity
- When a vector/indexing system is available (check for "system-vector" in sub-agents), use
save_contentinstead ofget_contentto enable the workflow: Save β Index β Semantic Search - After saving content with
save_content, the main agent can delegate to the vector agent to index it for semantic search capabilities
The Git agent provides Git repository operations and version control capabilities.
// Create and add Git agent with repository root
gitAgent := agents.GitAgent(llm, "/path/to/repo")
agent.AddSystemAgent(gitAgent)Use cases:
- Git repository operations (add, commit, push, pull)
- Branch management
- Viewing git status and logs
- Managing version control
The coding agent specializes in code generation, analysis, and manipulation within a codebase.
// Create and add coding agent with codebase root
codingAgent := agents.CodingAgent(codingLLM, "/path/to/codebase")
agent.AddSystemAgent(codingAgent)Use cases:
- Code generation and modification
- Code analysis and review
- Refactoring assistance
- Codebase navigation
Note: The coding agent can use a different LLM engine optimized for code tasks.
The vector agent provides semantic search and document indexing capabilities using vector databases.
// Create and add vector agent with vector DB and embedding generator
vectorAgent := agents.VectorAgent(llm, vectorDB, embeddingGenerator)
agent.AddSystemAgent(vectorAgent)Use cases:
- Semantic document search
- Document indexing and storage
- Knowledge base queries
- Similarity searches
Note: Requires a vector database (e.g., Milvus) and an embedding generator (e.g., OpenAI embeddings) to be initialized separately.
Agent Forge includes several built-in tools that can be easily integrated with your agents. These tools provide common functionality for file system operations, Git operations, database access, and API interactions.
The file system tool provides safe file operations within a restricted root directory.
import "github.com/thinktwiceco/agent-forge/src/tools/fs"
fsTool := fs.NewFsTool("/path/to/root")Available operations: Read, write, list, delete files and directories.
The Git tool enables Git operations within a repository.
import "github.com/thinktwiceco/agent-forge/src/tools/git"
gitTool := git.NewGitTool("/path/to/repo")Available operations: Status, diff, log, add, commit, push, branch, reset, checkout, clone.
The Postgres tool provides secure database operations with table whitelisting.
import "github.com/thinktwiceco/agent-forge/src/tools/postgres"
pgTool := postgres.NewPostgresTool(
"postgresql://user:pass@localhost:5432/db",
"read", // or "write" for INSERT/UPDATE/DELETE
[]string{"users", "products"}, // allowed tables
[]string{"public"}, // allowed schemas
)Modes: read (SELECT only), write (full DML access).
The API tool enables agents to make HTTP API calls to configured endpoints with authentication and validation support.
import "github.com/thinktwiceco/agent-forge/src/tools/api"
// Define endpoints
endpoints := []api.Endpoint{
{
Name: "get_user",
URL: "https://api.example.com/users/{user_id}",
Method: "GET",
Description: "Get user by ID",
URLParameters: `- user_id: string - The user ID`,
},
{
Name: "create_post",
URL: "https://api.example.com/posts",
Method: "POST",
Description: "Create a new post",
Payload: `- title: string - Post title
- content: string - Post content`,
},
}
// Optional: Create authentication hook
authHook := func(url string, headers map[string]string, body string) (map[string]string, error) {
headers["Authorization"] = "Bearer " + os.Getenv("API_TOKEN")
return headers, nil
}
// Create API tool
apiTool := api.NewApiTool("my_api", endpoints, authHook)Features:
- Dynamic endpoint discovery - Agent automatically sees all available endpoints
- URL parameter substitution (e.g.,
/users/{user_id}) - Query parameter support (e.g.,
?limit=10&offset=0) - Request body support for POST/PUT/PATCH
- Authentication hooks for adding auth headers without exposing secrets
- Per-endpoint validation for parameter safety
- YAML configuration support
YAML Configuration:
agent:
tools:
- name: "api"
endpoints:
- name: "get_user"
url: "https://api.example.com/users/{user_id}"
method: "GET"
description: "Get user by ID"
urlParameters: |
- user_id: string - The user ID
validator: "validate_positive_id" # Optional
onApiCallHook: "add_auth_token" # OptionalParameter Validation:
Register validators to ensure parameters are safe before making API calls:
// Register validators
api.RegisterValidator("validate_positive_id",
api.ValidatePositiveIntParam("user_id"))
api.RegisterValidator("validate_body_size",
api.ValidateBodyMaxSize(10000)) // 10KB limit
// Register authentication hooks
api.RegisterHook("add_auth_token", func(url string, headers map[string]string, body string) (map[string]string, error) {
headers["Authorization"] = "Bearer " + getAPIToken()
return headers, nil
})Built-in validators:
ValidatePositiveIntParam- Ensures parameters are positive integersValidateRequiredParams- Ensures parameters are present and non-emptyValidateBodyMaxSize- Limits request body size
Examples:
- API Tool Documentation - Comprehensive guide
- Pokemon API Example - Working integration with PokeAPI
The web browser tool provides web automation capabilities using a headless browser.
import "github.com/thinktwiceco/agent-forge/src/tools/web"
webTool := web.NewWebTool("/path/to/working/dir")Available actions: Navigate, click, fill forms, extract content, take screenshots, execute JavaScript.
The vector database tool enables semantic search over indexed documents.
import "github.com/thinktwiceco/agent-forge/src/tools/vector"
vectorTool := vector.NewVectorTool(vectorDB, embeddingGenerator)Available actions: Index documents, semantic search, list documents, delete documents.
All agent responses are streamed in real-time:
responseCh := agent.ChatStream("What is the capital of France?")
for chunk := range responseCh.Start() {
switch chunk.Type {
case llms.TypeContent:
fmt.Print(chunk.Content)
case llms.TypeToolExecuting:
fmt.Printf("Executing: %s\n", chunk.ToolExecuting.Name)
case llms.TypeToolResult:
fmt.Printf("Result: %s\n", chunk.ToolResults[0].Result)
}
}Enable conversation history storage to maintain context across sessions. The system supports multiple concurrent conversations per agent.
agent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "persistent-agent",
Persistence: "json", // Stores history as JSON files in data/conversations/
})
// Start a new conversation (pass empty string as chatId)
// Returns the responses and the generated chatId
responseCh := agent.ChatStream("Hello!", "")
chatId := responseCh.ChatId() // Save this for later!
// Continue an existing conversation
// Pass the chatId to resume context
responseCh = agent.ChatStream("Continue...", chatId)Start New Conversation:
POST /api/server/{agentName}/chat
{
"message": "Hello!"
}
# Response includes "chatId" in every chunkResume Conversation:
POST /api/server/{agentName}/chat?conversationId={uuid}
{
"message": "Continue..."
}Hooks are registered through plugins. Plugins can register hooks for various lifecycle events:
// Hooks are registered via plugins, not directly on agents
// See the Plugins section for more information on creating custom pluginsAvailable hook events include:
EventAgentInitialization- Before agent initializationEventAgentInitialized- After agent initializationEventBeforeToolExecution- Before tool executionEventToolExecution- After tool executionEventNewUserMessage- When a new user message is receivedEventNewAssistantMessage- When assistant generates a messageEventNewChunk- For each streaming chunk- And more (see
core.Eventsfor complete list)
Plugins extend agent functionality by providing tools, hooks, and system prompt enhancements.
- Logger Plugin - Configurable output formatting for agent responses
- Todo Plugin - Task management and todo list functionality
See the Plugins README for more information on creating custom plugins.
package main
import (
"context"
"fmt"
"github.com/thinktwiceco/agent-forge/src/agents"
"github.com/thinktwiceco/agent-forge/src/core"
"github.com/thinktwiceco/agent-forge/src/llms"
)
func main() {
ctx := context.Background()
// Initialize LLM
multiModelLLM, err := llms.NewOpenAILLMBuilder("togetherai").
SetModel(llms.TOGETHERAI_Llama3170BInstructTurbo).
SetCtx(ctx).
Build()
if err != nil {
panic(err)
}
llm := multiModelLLM.MainModel()
// Create a calculator tool
calcTool := &core.Tool{
Name: "calculate",
Description: "Performs mathematical calculations",
Parameters: []core.Parameter{
{Name: "expression", Type: "string", Required: true},
},
Handler: func(agentContext map[string]any, args map[string]any) llms.ToolReturn {
expr := args["expression"].(string)
// Your calculation logic here
return core.NewSuccessResponse("Result: 42")
},
}
// Create agent with tools
agent := agents.NewAgent(&agents.AgentConfig{
LLMEngine: llm,
AgentName: "MathAssistant",
SystemPrompt: "You are a helpful math assistant.",
Tools: []llms.Tool{calcTool},
MainAgent: true,
Persistence: "json",
})
// Add reasoning system agent
reasoningAgent := agents.ReasoningAgent(llm)
agent.AddSystemAgent(reasoningAgent)
// Chat with the agent
responseCh := agent.ChatStream("What is 15 multiplied by 23?", "")
for chunk := range responseCh.Start() {
if chunk.Content != "" {
fmt.Print(chunk.Content)
}
}
}AF_TOGETHERAI_API_KEY- API key for TogetherAIAF_DEEPSEEK_API_KEY- API key for DeepSeekAF_OPENAI_API_KEY- API key for OpenAIAF_LOG_LEVEL- Logging level (DEBUG, INFO, WARN, ERROR). Default: INFO
Set via .env file or system environment variables. Environment variables take precedence over .env file values.
src/
βββ agents/ # Agent implementation
βββ llms/ # LLM engine implementations
βββ core/ # Core interfaces and implementations
βββ tools/ # Tool implementations
βββ persistence/ # Conversation persistence
βββ integrations/ # External integrations (Milvus, embeddings)
βββ plugins/ # Plugin system
βββ logger/ # Logger plugin
βββ todo/ # Todo plugin
The project includes a Python SDK for interacting with the AgentForge server.
- Documentation: Python SDK README
- Build Script: Use
scripts/build-python-sdk.shto compile the required Go server binaries for the SDK.
Contributions are welcome! Please feel free to submit a Pull Request.
[Add your license here]
