Middleware: PlanTask

PlanTask Middleware

adk/middlewares/plantask

πŸ’‘ This middleware was introduced in v0.8.0.Beta.

Overview

plantask is a task management middleware that allows Agents to create and manage task lists. The middleware injects four tools through the BeforeAgent hook:

  • TaskCreate: Create a task
  • TaskGet: View task details
  • TaskUpdate: Update a task
  • TaskList: List all tasks

Main purposes:

  • Track progress of complex tasks
  • Break large tasks into smaller steps
  • Manage dependencies between tasks

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              Agent                                      β”‚
β”‚                                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  BeforeAgent: Inject task tools                                   β”‚  β”‚
β”‚  β”‚    - TaskCreate                                                    β”‚  β”‚
β”‚  β”‚    - TaskGet                                                       β”‚  β”‚
β”‚  β”‚    - TaskUpdate                                                    β”‚  β”‚
β”‚  β”‚    - TaskList                                                      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                             Backend                                     β”‚
β”‚                                                                         β”‚
β”‚  Storage structure:                                                     β”‚
β”‚    baseDir/                                                             β”‚
β”‚    β”œβ”€β”€ .highwatermark    # ID counter                                   β”‚
β”‚    β”œβ”€β”€ 1.json            # Task #1                                      β”‚
β”‚    β”œβ”€β”€ 2.json            # Task #2                                      β”‚
β”‚    └── ...                                                              β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Configuration

type Config struct {
    Backend Backend  // Storage backend, required
    BaseDir string   // Task file directory, required
}
  • Note that the Backend implementation should be isolated by session, with different sessions corresponding to different Backends (task lists)

Backend Interface

type Backend interface {
    LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error)
    Read(ctx context.Context, req *ReadRequest) (string, error)
    Write(ctx context.Context, req *WriteRequest) error
    Delete(ctx context.Context, req *DeleteRequest) error
}

Task Structure

type task struct {
    ID          string         `json:"id"`          // Task ID
    Subject     string         `json:"subject"`     // Title
    Description string         `json:"description"` // Description
    Status      string         `json:"status"`      // Status
    Blocks      []string       `json:"blocks"`      // Tasks blocked by this one
    BlockedBy   []string       `json:"blockedBy"`   // Tasks blocking this one
    ActiveForm  string         `json:"activeForm"`  // Active form text
    Owner       string         `json:"owner"`       // Responsible agent
    Metadata    map[string]any `json:"metadata"`    // Custom data
}

Status

StatusDescription
pending
Pending (default)
in_progress
In progress
completed
Completed
deleted
Deleted (will delete the file)

Status transition: pending β†’ in_progress β†’ completed, any status can be directly deleted.


Tools

TaskCreate

Create a task.

ParameterTypeRequiredDescription
subject
stringYesTitle
description
stringYesDescription
activeForm
stringNoActive form text, e.g., "Running tests"
metadata
objectNoCustom data

When to use:

  • The task is relatively complex with 3 or more steps
  • The user has given a list of things to do
  • You need to show progress to the user

When not to use:

  • It’s just a simple task
  • Something that can be done quickly

TaskGet

View task details.

ParameterTypeRequiredDescription
taskId
stringYesTask ID

Returns complete information about the task: title, description, status, dependencies, etc.

TaskUpdate

Update a task.

ParameterTypeRequiredDescription
taskId
stringYesTask ID
subject
stringNoNew title
description
stringNoNew description
activeForm
stringNoNew active form text
status
stringNoNew status
addBlocks
[]stringNoAdd blocked tasks
addBlockedBy
[]stringNoAdd tasks blocking this one
owner
stringNoResponsible agent
metadata
objectNoCustom data (set to null to delete)

Notes:

  • status: "deleted" will directly delete the task file
  • Circular dependencies are checked when adding dependencies
  • Automatic cleanup occurs when all tasks are completed

TaskList

List all tasks, no parameters required.

Returns a summary of each task: ID, status, title, responsible agent, dependencies.


Usage Example

ctx := context.Background()

// The plantask middleware should normally be session-scoped
// Different sessions correspond to different task lists
middleware, err := plantask.New(ctx, &plantask.Config{
    Backend: myBackend,
    BaseDir: "/tasks",
})
if err != nil {
    return err
}

agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
    Model:    myModel,
    Handlers: []adk.ChatModelAgentMiddleware{middleware},
})

Typical Flow

1. Receive complex task
       β”‚
       β–Ό
2. TaskCreate to create tasks
   - #1: Analyze requirements
   - #2: Write code
       β”‚
       β–Ό
3. TaskUpdate to set dependencies
   - #2 depends on #1
   - #3 depends on #2
       β”‚
       β–Ό
4. TaskList to see what tasks exist
       β”‚
       β–Ό
5. TaskUpdate to start working
   - Change #1 to in_progress
       β”‚
       β–Ό
6. When done, TaskUpdate
   - Change #1 to completed
       β”‚
       β–Ό
7. Loop 4-6 until all completed
       β”‚
       β–Ό
8. Automatic cleanup

Dependency Management

  • blocks: These tasks can start after I complete
  • blockedBy: I can start after these tasks complete
Task #1 (blocks: ["2"])  ────►  Task #2 (blockedBy: ["1"])

#2 can only start after #1 completes

Circular dependencies will throw an error:

#1 blocks #2
#2 blocks #1  ← Not allowed, circular

Automatic Cleanup

When all tasks are completed, all task files will be automatically deleted.


Notes

  • Task files are stored in JSON format in the BaseDir directory, with filenames as {id}.json
  • The .highwatermark file is used to record the maximum assigned task ID, ensuring IDs don’t repeat
  • All tool operations are protected by mutex locks and are concurrency-safe
  • The tool descriptions contain detailed usage guidelines that the Agent will follow

Multi-language Support

Tool descriptions support Chinese and English switching via adk.SetLanguage():

// Use Chinese descriptions
adk.SetLanguage(adk.LanguageChinese)

// Use English descriptions (default)
adk.SetLanguage(adk.LanguageEnglish)

This setting is global and affects all ADK built-in prompts and tool descriptions.