SpinAI allows you to store and use information in actions in two ways:

  • Returning values from actions. This is useful for values that are dynamic and need to be determined by the LLM during the task loop.
  • Using the context.state object. This is useful for values that are static, like user IDs or emails, which may not make sense to have an LLM determine via parameters.

State vs Parameters

State

State is a persistent object that:

  • Can only be modified by your Actions
  • Can have a default value that you pass in when you start the interaction
  • Persists between action calls
  • Can be accessed via context.state in any action
  • Can be accessed via state after an interaction is complete

Using context.state in actions

In some cases, you may want to use a specific value you’ve stored in state either in a previous action or in the initial agent call. You can do this by using the context.state object. This is useful if the value is static, like a user ID or email, which may not make sense to have an LLM determine via parameters.

import { createAction } from "spinai";

export const getUserInfo = createAction({
  id: "getUserInfo",
  description: "Gets user information.",
  async run({ context }) {
    const { state } = context || {};
    const userId = state?.userId;
    const user = await getUserById(userId);
    return user;
  },
});

Setting context.state in actions

In other cases, you may want to set a value in state after an action has completed. This is different than returning the value, since state can be reliable accessed within other actions, or after the agent call is done, without being stored in the agent’s message history.

import { createAction } from "spinai";

export const getUserInfo = createAction({
  id: "getUserInfo",
  description: "Gets user information.",
  async run({ context }) {
    const { state } = context || {};
    const userId = state?.userId;
    const user = await getUserById(userId);
    context.state.userEmail = user.email;
    return user;
  },
});

You can also access any variable stored in state at runtime after your agent has completed running:

const { response, state } = await agent({
  input: "Create a ticket for my broken laptop",
  state: {},
});

console.log({ state }); // whatever you added to state in your actions
// will be accessible here after the agent runs

Parameters

Parameters are values that your agent passes into actions. They are:

  • Dynamically determined by the LLM during the task loop
  • Based on the action’s parameter schema
  • Derived from previous action results and current state
  • Best for handling dynamic, per-action inputs
// Example parameter schema
parameters: {
  type: "object",
  properties: {
    a: { type: "number", description: "First number" },
    b: { type: "number", description: "Second number" },
  },
  required: ["a", "b"],
}

Returning vs context.state setting

When you return a value from an action, it allows your agent to use the value as a parameter for other actions if needed. However, it is only accessible within the agent’s message history, and to use it in actions you’ll need add it as a parameter.

On the other hand, when you set a value in context.state, it is accessible in other actions during runtime, and after the agent call is complete from the state variable.

Best Practices

  1. Parameters

    • Use for operation-specific inputs that the LLM can decide on what to pass in
    • Keep schemas simple and well-documented
    • Always include parameter descriptions
    • Mark truly required fields as required
  2. State

    • Initialize your agent’s interaction with default state variables if needed
    • Use for persistent data across actions that won’t change (i.e customer emails, user IDs, and other static data)
    • Use context.state for values that need to be shared between actions that you need your agent to determine

Next Steps