PlanTask

πŸ’‘ This middleware was introduced in v0.8.0. Package path: github.com/cloudwego/eino/adk/middlewares/plantask

Overview

plantask is a task management middleware that injects four tools into the Agent through the BeforeAgent hook, giving it structured task planning capabilities:

ToolFunction
TaskCreate
Create a task
TaskGet
Get details of a single task
TaskUpdate
Update task status/fields, set dependencies, delete task
TaskList
List summaries of all tasks

Core purpose: break complex requests into trackable sub-tasks, manage dependencies between tasks, and let users see execution progress.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              Agent                                      β”‚
β”‚                                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  BeforeAgent: Inject task tools (with sync.Mutex for concurrency) β”‚  β”‚
β”‚  β”‚    - TaskCreate                                                    β”‚  β”‚
β”‚  β”‚    - TaskGet                                                       β”‚  β”‚
β”‚  β”‚    - TaskUpdate                                                    β”‚  β”‚
β”‚  β”‚    - TaskList                                                      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                             Backend                                     β”‚
β”‚                                                                         β”‚
β”‚  Storage structure:                                                     β”‚
β”‚    baseDir/                                                             β”‚
β”‚    β”œβ”€β”€ .highwatermark    # Maximum assigned ID (plain numeric text)     β”‚
β”‚    β”œβ”€β”€ 1.json            # Task #1                                      β”‚
β”‚    β”œβ”€β”€ 2.json            # Task #2                                      β”‚
β”‚    └── ...                                                              β”‚
β”‚                                                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

API

Constructors

// Generic version, supports *schema.Message and *schema.AgenticMessage
func NewTyped[M adk.MessageType](ctx context.Context, config *Config) (adk.TypedChatModelAgentMiddleware[M], error)

// Non-generic version, equivalent to NewTyped[*schema.Message]
func New(ctx context.Context, config *Config) (adk.ChatModelAgentMiddleware, error)

Config

type Config struct {
    Backend Backend  // Storage backend, required
    BaseDir string   // Task file storage directory, required
}

πŸ’‘ The Backend should be isolated at the session level β€” different sessions correspond to different Backend instances (i.e., different task lists).

Backend Interface

Backend is defined within the plantask package and is a minimal subset of filesystem.Backend, retaining only the four methods needed for task storage:

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

Type alias relationships:

type FileInfo = filesystem.FileInfo        // Path, IsDir, Size, ModifiedAt
type LsInfoRequest = filesystem.LsInfoRequest  // Path string
type ReadRequest = filesystem.ReadRequest       // FilePath, Offset, Limit
type WriteRequest = filesystem.WriteRequest     // FilePath, Content string

// DeleteRequest is custom to the plantask package (filesystem package has no such type)
type DeleteRequest struct {
    FilePath string
}

πŸ’‘ Note that Read returns *filesystem.FileContent (containing a Content string field), not a bare string. Import path is github.com/cloudwego/eino/adk/filesystem.


Task Structure

type task struct {
    ID          string         `json:"id"`
    Subject     string         `json:"subject"`
    Description string         `json:"description"`
    Status      string         `json:"status"`
    Blocks      []string       `json:"blocks"`
    BlockedBy   []string       `json:"blockedBy"`
    ActiveForm  string         `json:"activeForm,omitempty"`
    Owner       string         `json:"owner,omitempty"`
    Metadata    map[string]any `json:"metadata,omitempty"`
}

Status

Status ValueDescription
pending
Pending (default on creation)
in_progress
In progress
completed
Completed
deleted
Deleted (physically deletes the JSON file and removes from other tasks' dependency lists)

Status transitions: pending β†’ in_progress β†’ completed; any status can be directly set to deleted.


Tool Parameters

TaskCreate

Tool name constant: TaskCreateToolName = "TaskCreate"

ParameterTypeRequiredDescription
subject
stringYesTask title (imperative form)
description
stringYesDetailed task description, including context and acceptance criteria
activeForm
stringNoActive form text (e.g., "Running tests"), displayed to user when in_progress
metadata
objectNoCustom key-value pairs

After creation, the task ID auto-increments (based on the .highwatermark file), with initial status pending.

TaskGet

Tool name constant: TaskGetToolName = "TaskGet"

ParameterTypeRequiredDescription
taskId
stringYesTask ID (numeric string)

Returns complete task information: subject, description, status, blocks, blockedBy, owner.

TaskUpdate

Tool name constant: TaskUpdateToolName = "TaskUpdate"

ParameterTypeRequiredDescription
taskId
stringYesTask ID
subject
stringNoNew title
description
stringNoNew description
activeForm
stringNoNew active form text
status
stringNoNew status, enum:
pending
/
in_progress
/
completed
/
deleted
addBlocks
[]stringNoAdd task IDs that are blocked by the current task (written bidirectionally)
addBlockedBy
[]stringNoAdd task IDs that block the current task (written bidirectionally)
owner
stringNoResponsible agent name
metadata
objectNoMerged into existing metadata; setting a key to null deletes that key

Key behaviors:

  • status: "deleted" physically deletes the task file and removes the ID from all other tasks’ blocks/blockedBy
  • Adding dependencies performs cycle detection; an error is returned if a cycle would be created
  • When all tasks are completed, all task files are automatically deleted (cleanup mechanism)

TaskList

Tool name constant: TaskListToolName = "TaskList"

No parameters. Returns a summary list of all tasks (sorted by ID), each formatted as:

#ID [status] subject [owner: xxx] [blocked by #x, #y]

Usage Example

ctx := context.Background()

// Backend should be isolated at the session level
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 multiple sub-tasks
   - #1: Analyze requirements
   - #2: Implement code
   - #3: Write tests
       β”‚
       β–Ό
3. TaskUpdate to set dependencies
   - #2 addBlockedBy: ["1"]
   - #3 addBlockedBy: ["2"]
       β”‚
       β–Ό
4. TaskList to view available tasks
       β”‚
       β–Ό
5. TaskUpdate #1 β†’ in_progress
       β”‚
       β–Ό
6. After completion, TaskUpdate #1 β†’ completed
       β”‚
       β–Ό
7. Loop 4-6 until all completed
       β”‚
       β–Ό
8. All completed β†’ automatic cleanup of all files

Dependency Management

  • blocks: “After I complete, these tasks can start”
  • blockedBy: “After these tasks complete, I can start”

Dependency writing is bidirectional: executing addBlocks: ["2"] on Task A will also write A’s ID into Task #2’s blockedBy.

Task #1 (blocks: ["2"])  ────►  Task #2 (blockedBy: ["1"])

#2 can only start after #1 completes

Cycle detection is implemented via DFS reachability:

#1 blocks #2
#2 blocks #1  ← Error: would create a cyclic dependency

Implementation Details

MechanismDescription
ID allocation
.highwatermark
file stores the current maximum ID, incremented by 1 on creation
Concurrency safetyAll four tools share a single
sync.Mutex
, serializing execution within the same middleware instance
File formatOne
{id}.json
file per task, JSON serialized using
sonic
Automatic cleanupAfter TaskUpdate marks a task as completed, it checks β€” if all tasks are completed, batch delete all files
ID validationPure numeric regex
^\d+$
Delete cascadingWhen deleting a task, iterates all task files to remove references to that ID

Multi-language Support

Tool descriptions support Chinese and English, switchable via global setting:

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

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

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