Build an AI Agent
Agentic workflows need repeatability, modularity, observability and cross-platform support. Dagger provides a way to build reproducible workflows in any language with custom environments, parallel processing, and seamless chaining.
This quickstart will guide you through creating and running your first AI agent with Dagger.
The agent will use an LLM of your choosing to solve a programming assignment. The agent will have access to tools to write code and validate it.
Requirements
This quickstart will take you approximately 10 minutes to complete. You should be familiar with programming in Go, Python, TypeScript, PHP, or Java.
Before beginning, ensure that:
- you have installed the Dagger CLI.
- you know the basics of Dagger.
- you have a container runtime installed and running. This can be Docker, but you can also use Podman, nerdctl, or other Docker-like systems.
- you have a GitHub account (optional, only if configuring Dagger Cloud)
Configure the LLM endpoint
Follow the LLM endpoint configuration instructions to configure an LLM for use with Dagger.
Configure Dagger Cloud (optional)
This step is optional and will create a Dagger Cloud account, which is free of charge for a single user. If you prefer not to sign up for Dagger Cloud, you can skip this section.
Dagger Cloud is an online visualization tool for Dagger workflows. It provides a web interface to visualize each step of your workflow, drill down to detailed logs, understand how long operations took to run, and whether operations were cached.
Create a new Dagger Cloud account by running dagger login
:
dagger login
The Dagger CLI will invite you to authenticate your device by displaying a link containing a unique key. Click the link in your browser, and verify that you see the same key in the Dagger Cloud Web interface.
$ dagger login
Browser opened to: https://auth.dagger.cloud/activate?user_code=FCNP-SRLM
Confirmation code: FCNP-SRLM
Once you confirm your authentication code, your Dagger CLI will be authenticated and you will get redirected to your newly created Dagger Cloud organization.
After successfully creating your organization, all future Dagger workflows can be inspected in Dagger Cloud.
Initialize a Dagger module
In an empty directory, create a new Dagger module. This module will host your agent functions.
You can write your module in Go, Python, Typescript, PHP, or Java. Choose the SDK you are most comfortable with and run the command below to generate a new Dagger module:
- Go
- Python
- Typescript
- PHP
- Java
dagger init --sdk=go --name=coding-agent
dagger init --sdk=python --name=coding-agent
dagger init --sdk=typescript --name=coding-agent
dagger init --sdk=php --name=coding-agent
dagger init --sdk=java --name=coding-agent
This will generate various files that make up the Dagger module, including some boilerplate Dagger Functions as examples.
To see the generated Dagger Functions, run:
dagger functions
You should see information about two auto-generated Dagger Functions: container-echo
and grep-dir
.
Install a dependency with useful tools
An agent needs tools to complete its tasks. The agent uses this module to solve the assignment given to it. Agents can use any Dagger module as tools, which you can either create yourself or use from elsewhere.
This quickstart uses a pre-created Dagger module, which must be installed as a dependency:
dagger install github.com/shykes/toy-programmer/toy-workspace
Here, toy-workspace
is a Dagger module that provides a simple set of tools for the agent. It has a Container
object and functions to read, write, and build the code produced by the agent. The toy-workspace
is provided as an input to the environment of the LLM.
Learn more about agent environments.
Create the agent
Replace the generated Dagger module files as described below.
- Go
- Python
- Typescript
- PHP
- Java
Edit the agent (.dagger/main.go
) and replace the generated code with this code:
package main
import (
"dagger/coding-agent/internal/dagger"
)
type CodingAgent struct{}
// Write a Go program
func (m *CodingAgent) GoProgram(
// The programming assignment, e.g. "write me a curl clone"
assignment string,
) *dagger.Container {
workspace := dag.ToyWorkspace()
environment := dag.Env().
WithToyWorkspaceInput("before", workspace, "tools to complete the assignment").
WithStringInput("assignment", assignment, "the assignment to complete").
WithToyWorkspaceOutput("after", "the completed assignment")
return dag.LLM().
WithEnv(environment).
WithPrompt(`
You are an expert go programmer. You have access to a workspace.
Use the default directory in the workspace.
Do not stop until the code builds.
Your assignment is: $assignment`).
Env().
Output("after").
AsToyWorkspace().
Container()
}
Edit the agent (.dagger/src/coding_agent/main.py
) and replace the generated code with this code:
import dagger
from dagger import dag, function, object_type
@object_type
class CodingAgent:
@function
def go_program(
self,
assignment: str,
) -> dagger.Container:
"""Write a Go program"""
workspace = dag.toy_workspace()
environment = (
dag.env()
.with_toy_workspace_input(
"before", workspace, "tools to complete the assignment"
)
.with_string_input("assignment", assignment, "the assignment to complete")
.with_toy_workspace_output("after", "the completed assignment")
)
return (
dag.llm()
.with_env(environment)
.with_prompt(
"""
You are an expert go programmer. You have access to a workspace.
Use the default directory in the workspace.
Do not stop until the code builds.
Your assignment is: $assignment"""
)
.env()
.output("after")
.as_toy_workspace()
.container()
)
Edit the agent (.dagger/src/index.ts
) and replace the generated code with this code:
import { dag, object, func, Container } from "@dagger.io/dagger"
@object()
export class CodingAgent {
/**
* Write a Go program
*/
@func()
goProgram(
/**
* The programming assignment, e.g. "write me a curl clone"
*/
assignment: string,
): Container {
const workspace = dag.toyWorkspace()
const environment = dag
.env()
.withToyWorkspaceInput(
"before",
workspace,
"tools to complete the assignment",
)
.withStringInput("assignment", assignment, "the assignment to complete")
.withToyWorkspaceOutput("after", "the completed assignment")
return dag
.llm()
.withEnv(environment)
.withPrompt(`
You are an expert go programmer. You have access to a workspace.
Use the default directory in the workspace.
Do not stop until the code builds.
Your assignment is: $assignment
`)
.env()
.output("after")
.asToyWorkspace()
.container()
}
}
Edit the agent (.dagger/src/CodingAgent.php
) and replace the generated code with this code:
<?php
declare(strict_types=1);
namespace DaggerModule;
use Dagger\Attribute\DaggerFunction;
use Dagger\Attribute\DaggerObject;
use Dagger\Attribute\Doc;
use Dagger\Container;
use function Dagger\dag;
#[DaggerObject]
#[Doc('A generated module for CodingAgent functions')]
class CodingAgent
{
#[DaggerFunction]
#[Doc('Write a Go program')]
public function goProgram(string $assignment): Container
{
$workspace = dag()->toyWorkspace();
$environment = dag()->env()
->withToyWorkspaceInput("before", $workspace, "tools to complete the assignment")
->withStringInput("assignment", $assignment, "the assignment to complete")
->withToyWorkspaceOutput("after", "the completed assignment");
return dag()
->llm()
->withEnv($environment)
->withPrompt("
You are an expert go programmer. You have access to a workspace.
Use the default directory in the workspace.
Do not stop until the code builds.
Your assignment is: $assignment")
->env()
->output("after")
->asToyWorkspace()
->container();
}
}
Edit the agent (.dagger/src/main/java/io/dagger/modules/codingagent/CodingAgent.java
) and replace the generated code with this code:
package io.dagger.modules.codingagent;
import static io.dagger.client.Dagger.dag;
import io.dagger.client.Container;
import io.dagger.client.ToyWorkspace;
import io.dagger.client.Env;
import io.dagger.module.annotation.Function;
import io.dagger.module.annotation.Object;
/** CodingAgent main object */
@Object
public class CodingAgent {
/** Write a Go program */
@Function
public Container goProgram(String assignment) {
ToyWorkspace workspace = dag().toyWorkspace();
Env environment = dag().env()
.withToyWorkspaceInput("before", workspace, "tools to complete the assignment")
.withStringInput("assignment", assignment, "the assignment to complete")
.withToyWorkspaceOutput("after", "the completed assignment");
return dag()
.llm()
.withEnv(environment)
.withPrompt("""
You are an expert go programmer. You have access to a workspace.
Use the default directory in the workspace.
Do not stop until the code builds.
Your assignment is: $assignment
""")
.env()
.output("after")
.asToyWorkspace()
.container();
}
}
This code creates a Dagger Function called go-program
that takes an assignment as input and returns a Container
with the result of the assignment.
- The
ToyWorkspace
module is given to the LLM in an environment. It contains various Dagger Functions to write and validate code. - A pre-defined prompt is also given to the LLM as input. The description of assigment is a way of providing usage for the input.
- The LLM will use the Dagger Functions in the
ToyWorkspace
module as tools to write code, validate it, and loop until the code passes validation. - Once complete, a
ToyWorkspace
is returned by the LLM with the solution. The container with the LLM's solution can then be obtained by chaining calls totoy-workspace -> container
.
Run the agent
Dagger Shell is the fastest way to interact with the Dagger API, allowing access to both core types and custom Dagger Functions using a familiar Bash-like syntax. Type dagger
to launch Dagger Shell in interactive mode.
Check the help text for the new Dagger Function:
.help go-program
This should show how to use the go-program
function.
Make sure your LLM provider has been properly configured:
llm | model
This should show the model you've configured to use with your provider.
Call the Dagger Function with an assignment:
go-program "write a curl clone"
You'll see the agent receive the prompt, write code, validate it, and return a Container
with the result.
If you signed up for Dagger Cloud, the output of the previous command would have also included a link to visualize the workflow run on Dagger Cloud. Click the link in your browser to see a complete breakdown of the steps performed by the agent.
If the agent fails to write code that passes validation, try adding more helpful guidance to the prompt.
Once the agent has successfully returned a Container
, open an interactive terminal in the container so that you can inspect its work and run the generated code. Notice that the solution is not recomputed since it is still cached from the previous execution:
go-program "write a curl clone" | terminal
Run the program and confirm that it works. This will look slightly different depending on how the LLM wrote the code:
> ls
# should see a main.go and a go.mod, maybe other files
> go run main.go https://example.com
If the program does not look or function as you expect, try adjusting the prompt and input descriptions until you get the desired result.
Next steps
Congratulations! You've created your first AI coding agent. You can now use this agent to solve coding prompts using an LLM.
Now that you've grasped the basics of building an agent, look at the AI Agent examples for more examples and ideas for your next agent.
In the meantime, we encourage you to join our awesome community on Discord to introduce yourself and ask questions. And starring our GitHub repository is always appreciated!