UNPKG

mahler

Version:

A automated task composer and HTN based planner for building autonomous system agents

172 lines (171 loc) 6.74 kB
import type { Subscribable } from '../observable'; import { Planner } from '../planner'; import type { Sensor } from '../sensor'; import type { Target, StrictTarget } from '../target'; import type { Task } from '../task'; import type { AgentOpts, Result } from './types'; export * from './types'; export * from './events'; /** * An agent is an autonomous entity that can plan and execute * a sequence of actions to reach a given target. It can also * monitor the state of the system and react to changes. It * can be used to implement a wide range of behaviors, from * simple automation to complex decision making. * * An agent internally has a knowledge database, expressed as a list * of hierarchical tasks, and a set of sensors that monitor the state * of the system. The agent uses a planner to find a sequence of * actions that will lead to the desired target. If found, the agent * will execute the plan until completion or until it's stopped. If during * the plan execution the state of the system changes, and task conditions * no longer hold true, the Agent will stop the plan execution and re-plan. * The same thing will happen if an error occurs while running one of the tasks * of the plan. * * The agent will keep re-planning until it reaches the target or until it * reaches the maximum number of retries. If the agent reaches the maximum * number of retries, it will stop and return an error. * * Plans returned by the planner are structured as a Directed Acyclic Graph * and as such, they tell the agent which operations can be executed in parallel, * and which need to be run in sequence. * * An agent is also Observable, meaning it provides a `subscribe` function that * receives an `Observer`. The agent will notify the observer of changes in the * system state, even as they happen inside long running action exections. * * Example: * ```ts * import { Agent, Task } from 'mahler'; * * // Define a task to increase a counter * const plusOne = Task.from<number>({ * condition: (counter, {target}) => counter < target, * effect: (counter) => ++counter._, * description: '+1' * }); * * const counter = Agent.from({ * // The initial system state * initial: 0, * // The agent knowledge database * tasks: [plusOne], * opts: { * // Wait at minimum 10ms between re-plans * // after failing to find a plan, the agent will use * // exponential backoff to increase the wait time up to * // a maximum given by maxWaitMs * minWaitMs: 10, * } * }); * * // Tell the agent to start looking for a path to * // the goal. This will immediately start the agent without the * // need to wait. By default, the agent will continue looking for * // a plan forever if planning fails * counter.seek(10); * * // Subscribe to changes in the system state * counter.subscribe(console.log); * * // Wait for a result. If not given a timeout argument this may run forever * const res = agent.wait(1000); * * if (res.success) { * console.log(res.state); // 10 * } * * // Stop the agent. This does not need to be awaited * counter.stop(); * ``` */ export interface Agent<TState = any> extends Subscribable<TState> { /** * Tells the agent to seek a new (relative) target. * * The method doesn't wait for a result. * * If the agent is already seeking a plan, this will cancel * the current execution and wait for it to be stopped * before starting a new run. * * @param target - The target to seek */ seek(target: Target<TState>): void; /** * Tells the agent to seek a new strict target. * * A strict target means that the final state of the agent should * look exactly like the given target, with the exception of those properties * matching one of the `strictIgnore` globs in the Agent options. * * The method doesn't wait for a result. * * If the agent is already seeking a plan, this will cancel * the current execution and wait for it to be stopped * before starting a new run. * * @param target - The target to seek */ seekStrict(target: StrictTarget<TState>): void; /** * Wait for the agent to reach the given target or * terminate due to an error. * * If the timeout is reached before the agent terminates, the * method will return a timeout error. * * Make sure to use a timeout if using an agent configured with `follow: true` otherwise * this method will wait forever. * * @timeout - The maximum time to wait for the agent to reach the target, if not provided the method will until the agent reaches a result */ wait(timeout?: number): Promise<Result<TState>>; /** * Get the last known state of the agent. * * Note that if the agent is in the middle of executing an action, this * value may not be up to date with the actual state of the * system. */ state(): TState; /** * Stop any running execution. This method returns * immediately. */ stop(): void; } type DeepPartial<T> = T extends any[] | ((...args: any[]) => any) ? T : T extends object ? { [P in keyof T]?: DeepPartial<T[P]>; } : T; /** * Create a new agent * * @param config - The agent configuration * @param config.initial - The known initial state of the system * @param config.tasks - List tasks to use for planning. If not provided, the planner must be provided * @param config.planner - Planner to use for planning. If not provided, the tasks must be provided * @param config.sensors - List of sensors to use for monitoring the state * @param config.opts - The agent runtime options * @param config.opts.maxRetries - The maximum number of retries before giving up * @param config.opts.follow - If true, the agent will keep planning until it reaches the target or until it's stopped * @param config.opts.maxWaitMs - The maximum time to wait between retries * @param config.opts.minWaitMs - The minimum time to wait between retries * @param config.opts.backoffMs - A function that returns the time to wait between retries. It receives the number of failures as an argument, and defaults to exponential backoff. * @param config.opts.logger - A Logger instance to use for logging */ declare function from<TState>(config: { initial: TState; planner?: Planner<TState>; sensors?: Array<Sensor<TState>>; opts?: DeepPartial<AgentOpts<TState>>; } | { initial: TState; tasks?: Array<Task<TState, any, any>>; sensors?: Array<Sensor<TState>>; opts?: DeepPartial<AgentOpts<TState>>; }): Agent<TState>; export declare const Agent: { from: typeof from; };