For the complete documentation index, see llms.txt. This page is also available as Markdown.

Aesop User Guide

Introduction

Welcome to Aesop, a planning system designed to help create intelligent, adaptive AI behavior for NPCs. This guide will introduce you to the core concepts of Hierarchical Task Network (HTN) planning and help you reason about how decisions are made by the planner.

What is an HTN?

A Hierarchical Task Network (HTN) is a way of organizing behaviors from high-level tasks down to specific actions.

The Basic Structure

1

Tasks

These are goals or objectives your character needs to accomplish, like "harvest crops" or "craft item".

2

Task Methods

These are different ways (strategies or recipes) to accomplish a task. For example, "harvest crops" might have two methods: "harvest wheat" or "harvest carrots".

3

Steps

Each method is a series of steps. A step can be either:

  • An action (a concrete thing to do, like "walk to field" or "pick up tool")

  • A subtask (another task that needs its own method, allowing the hierarchy to continue)

Why Hierarchical?

The power of HTNs comes from their hierarchical nature. Instead of listing out every possible sequence of actions your character might take, you define high-level tasks that break down into more specific tasks, which eventually break down into concrete actions.

For example:

  • Task: Harvest Crops

    • Method: Harvest Wheat

      • Step 1: Get Tool (this is a subtask)

        • Method: Get Sickle

          • Step 1: Walk to Tool Shed (action)

          • Step 2: Pick Up Sickle (action)

      • Step 2: Walk to Crop (action)

      • Step 3: Harvest Crop (action)

      • Step 4: Walk to Barn (action)

      • Step 5: Deposit Crop (action)

This hierarchy lets you compose complex behaviors from reusable pieces, and it lets the planner make intelligent choices about how to achieve goals based on the current situation.

How Does HTN Planning Work?

The planning process is like solving a puzzle where the planner tries to find a complete sequence of actions that will accomplish the goal.

The Planning Process

1

Start with a Root Task

You give the planner a high-level goal (the "root task"), such as "harvest crops".

2

Choose a Method

The planner looks at the methods available for that task and picks one to try.

3

Check the Conditions

Before committing to this method, the planner checks if the current situation allows it. For example, "harvest wheat" might require wheat to be ripe in the field.

4

Decompose the Steps

The planner works through each step of the method:

  • If the step is an action, the planner checks its conditions and simulates its effects on the world state.

  • If the step is a subtask, the planner repeats this method choice and step validation process for that subtask.

5

Simulate the World

As the planner works through steps, it simulates how the world changes. For example, after "pick up sickle," the farmer's has_sickle state changes to true.

6

Validate the Entire Chain

While tasks are decomposed into action steps, the planner verifies that starting from the current world state, every condition can be satisfied as each step comes up.

7

Search for Alternatives

If any condition can't be met, the planner backtracks and tries a different method. It continues searching until it finds a valid plan or exhausts all possibilities.

A Simple Example

Let's walk through a concrete example:

Initial Situation:

  • Farmer is at the farmhouse

  • Farmer has_sickle = false

  • Wheat field has crops ready to harvest

  • Carrot field has crops ready to harvest

  • Barn is ready to receive crops

Root Task Given to Planner: Harvest Crops

Planning Process:

  1. Planner tries "Harvest Wheat" method

    • Precondition: Wheat field has harvestable crops ✓

    • Step 1 is "Get Tool" (subtask for sickle)

      • This is a subtask that decomposes into "Walk to Tool Shed" → "Pick Up Sickle"

      • Simulates effect: Farmer location changes to tool shed, then has_sickle = true

    • Step 2 is "Walk to Crop" (action with location = wheat field)

      • Simulates effect: Farmer location changes to wheat field

    • Step 3 is "Harvest Crop" (action with crop_type = wheat)

      • Precondition: has_sickle = true ✓ (obtained in Step 1)

      • Simulates effect: Wheat is collected, farmer is carrying wheat

    • Step 4 is "Walk to Barn" (action)

      • Simulates effect: Farmer location changes to barn

    • Step 5 is "Deposit Crop" (action)

      • Simulates effect: Wheat is stored in barn, farmer is no longer carrying crops

  2. Plan is valid! The planner returns:

    • Walk to Tool Shed

    • Pick Up Sickle

    • Walk to Crop (wheat field)

    • Harvest Crop (wheat)

    • Walk to Barn

    • Deposit Crop

What if the Sickle is Broken?

If the sickle were broken or unavailable, the "Get Tool" subtask would fail. The planner would:

  • Backtrack from "Harvest Wheat"

  • Try "Harvest Carrots" instead

  • This method doesn't require a tool (carrots can be pulled by hand)

  • Plan becomes: Walk to Crop (carrot field) → Harvest Crop (carrots) → Walk to Barn → Deposit Crop

This adaptive behavior is the key power of HTN planning - the same domain can produce different plans based on the current situation.

What Decisions Does the Planner Make?

Understanding what the planner decides helps you design better domains. The planner makes two main types of choices:

1

Method Selection

For every task (including the root task and all subtasks), the planner chooses which method to use from the available options.

Example: For the task "Harvest Crops", the planner might choose between:

  • Harvest Wheat

  • Harvest Carrots

  • Harvest Tomatoes

The planner tries these in the order they are authored, picking the first one whose conditions are satisfied.

2

Parameter Value Assignment

Many actions and tasks need specific information to work with - these are called parameters. For example:

  • Which crop type to harvest

  • Which location to walk to

  • Which tool to pick up

For each parameter, the planner needs to assign a value. This value might be:

  • Fixed - Specified directly in the method (e.g., "always harvest wheat")

  • Queried - Found by searching the world state (e.g., "find the nearest ripe crop")

  • Passed along - Inherited from a parameter in an earlier step

Where Do These Options Come From?

All the choices available to the planner - both which methods exist and what values parameters can take - are defined in the domain. The domain is your authored collection of tasks, methods, actions, conditions, and effects as well as the characters, roles, props, and prop types involved in the various behaviors the planner uses.

Think of the domain as the "vocabulary" that the planner uses to construct plans. The planner can only create plans using the building blocks you provide in the domain.

How to Control Agent Behavior

As a designer working with Aesop, you have two main ways to shape how your characters behave:

1

Author the Domain (Primary Approach)

The domain is your main tool for defining behavior. By carefully crafting tasks, methods, conditions, and effects, you control what the planner can and will do.

Use Conditions to Guide Choices:

  • If the planner is selecting an option you don't want in certain situations, add a condition to prevent it.

Example: Suppose your farmer sometimes tries to harvest crops that aren't ripe yet, and you want to prevent this:

  • Add a condition to the "Harvest Crop" action: "Crop ripeness must be 100%"

  • Now the planner will only choose to harvest when crops are ready

Create Multiple Methods for Flexibility:

  • Give tasks multiple methods to handle different situations.

Example: For the task "Harvest Crops":

  • Method 1: "Harvest Wheat" - Condition: Wheat field has ripe crops

  • Method 2: "Harvest Carrots" - Condition: Carrot field has ripe crops

  • Method 3: "Harvest Tomatoes" - No conditions (fallback, tomatoes are always available)

2

Manage the World State (Secondary Approach)

The world state contains all the information about characters, objects, and their relationships. What you include in the world state affects what the planner can choose.

Control Available Options Through Registration:

  • The planner can only work with what's in the world state.

Example: Suppose your game has 20 different crop fields on a large farm, but only 3 are ready for harvest:

  • Only register those 3 fields to the world state used for planning

  • The planner can only assign parameters to these 3 fields

  • This makes planning faster and more predictable

Example: Suppose certain tools should only be chosen in specific contexts:

  • Don't register every tool in the game to the world state

  • Only register tools appropriate for the current harvest task

  • The planner's choices are naturally constrained to sensible options

Update World State to Reflect Game Changes:

  • As your game progresses, keep the world state synchronized:

    • When a farmer picks up a tool, update has_sickle to true in the world state

    • When crops are harvested, update the field's crop count

    • When the farmer's stamina drops, update the stamina value

This ensures the planner always works with current information.

Best Practices

Following these guidelines will help you create domains that produce reliable, performant AI behavior.

1

Keep Plans Short

Why: Shorter plans are more robust and easier to work with.

  • Less prone to interruption — In games, situations change rapidly. A 3-step plan is less likely to become invalid mid-execution than a 10-step plan

  • Easier to reason about — When debugging or tuning behavior, short plans are simpler to understand

  • Faster to replan — When a plan fails and needs to be regenerated, short plans complete more quickly

How to Keep Plans Short:

  • Design high-level actions that accomplish more in a single step

  • Use subtasks to break complex behaviors into phases that can be replanned independently

  • Consider having characters create new plans frequently rather than planning far ahead

Example: Instead of planning individual footsteps, create a "Move to Position" action that handles pathfinding.

2

Branch Only Where You Need To

Why: Every choice point (method selection or parameter assignment) increases the search space the planner must explore. Unnecessary branching makes planning slower and less predictable.

Be Discerning with Queries:

  • Queries that search the world state can return many possible values. Use queries when you need dynamic selection, but avoid them when a fixed value works.

Example:

  • ✓ Good: "Find the ripest crop field" when you want to prioritize harvest quality

  • ✗ Overkill: "Find any wheat field" when you just need any wheat — use a fixed value instead

Share Parameters Where Possible:

  • If multiple steps need the same information, pass the parameter from step to step rather than querying multiple times.

Example:

  • Task: "Harvest Crops"

    • Step 1: "Select Field" - Query finds the nearest ripe field, assigns to parameter field_location

    • Step 2: "Walk to Field" - Uses parameter field_location from Step 1

    • Step 3: "Harvest from Field" - Uses parameter field_location from Step 1

Create Focused Subtasks:

  • If a subtask has many methods but you only want a few in a specific context, create a new specialized subtask.

Example: Instead of reusing a "Get Tool" subtask that has 15 different tool methods:

  • Create a "Get Harvesting Tool" subtask with only 3 methods (sickle, scythe, basket)

  • Use this focused subtask in harvest-related methods

  • This prevents the planner from considering irrelevant options like "Get Hammer"

3

Register Only What You Need to the World State

Why: Everything in the world state increases the search space and can lead to unexpected behavior.

Performance Impact:

  • If 20 crop fields are registered but only 3 are relevant, the planner must consider all 20 when evaluating queries. This slows down planning.

Predictability Impact:

  • Irrelevant options can lead to surprising choices. If the planner can choose from 10 tools but only 2 make sense for the current task, you might see unintended behavior.

Best Practice:

  • Before planning, register only the characters and objects relevant to the current goal

  • After planning completes, you can add or remove objects as needed for the next planning cycle

4

Express Conditions and Effects at the Right Abstraction Level

Why: The planner works by predicting the future. If your conditions and effects rely on low-level details or rapidly changing values, predictions become unreliable.

Avoid Granular Features:

  • Low-level details like exact position coordinates, rotation angles, or pixel-perfect distances are hard to predict.

Example:

  • ✗ Poor: Condition checks "Character is at exactly X=105.3, Y=200.7"

  • ✓ Better: Condition checks "Character is in the Courtyard area"

Avoid Highly Dynamic Values:

  • Values that change rapidly or unpredictably make planning unreliable.

Example:

  • ✗ Poor: Condition checks "Crop ripeness is exactly 87%"

  • ✓ Better: Condition checks "Crop is Ready/Not Ready"

Use High-Level Abstractions:

  • Design conditions and effects that map to meaningful game-level changes, not implementation details.

Example: For a stealth game:

  • ✗ Poor: "Character is 23.5 units from guard, and guard's view angle is 45 degrees, and line-of-sight is unobstructed..."

  • ✓ Better: "Character is Hidden from guard" (computed by your game systems, exposed as a simple boolean)

Benefits:

  • Plans remain valid longer because abstract conditions are more stable

  • Behavior is more understandable because it maps to game concepts

  • You can change implementation details without breaking planning

Last updated