PAIPAI

Define An Agent

Instructions, identity, tools, and the createPai registry.

An agent is a description of work the model may do. createPai turns one or more agents into an app.

You will use this page every time you add an agent. Tools, identity, and serving are separate guides.

server/assistant-agent.ts
import { defineAgent } from "@pai/core";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
import { searchDocs } from "./tools/search-docs";

export const assistantAgent = defineAgent({
  name: "assistant",
  model: openai("gpt-4.1-mini"),
  identity: z.object({
    userId: z.string(),
    workspaceId: z.string(),
  }),
  instructions: "Help with this workspace. Prefer tools over guessing.",
  tools: { searchDocs },
});

Start with these fields

FieldWhat it is
nameHuman label. The registry key (main, explorer) is set later in createPai.
modelAny AI SDK language model.
identityZod schema for the trusted user. Set this whenever you isolate threads by workspace or user.
instructionsSystem prompt. A string, or a function of ctx.
toolsServer tools the model may call. See Tools.

That is not all of AgentConfig. The full list is on defineAgent().

Long threads: compact history before the next model step.

import { composeLifecycle, createContextCompaction } from "@pai/core";

defineAgent({
  lifecycle: composeLifecycle(
    createContextCompaction({
      threshold: { maxMessages: 40 },
      retain: { maxMessages: 6 },
      summarize: async ({ messages }) =>
        `Earlier turns: ${messages.length} messages.`,
    }),
  ),
});

See composeLifecycle().

Put it in an app

server/pai.ts
import { createPai, type InferPaiContract } from "@pai/core";
import { assistantAgent } from "./assistant-agent";

export const pai = createPai({
  agents: {
    main: assistantAgent,
  },
  scopeKey: (identity) => identity.workspaceId,
  userKey: (identity) => identity.userId,
});

export type AssistantPai = InferPaiContract<typeof pai>;

Several agents share storage, files, and realtime. Register a user-facing agent plus specialists on the same createPai call:

export const pai = createPai({
  agents: {
    main: assistantAgent,
    explorer: explorerAgent,
  },
  scopeKey: (identity) => identity.workspaceId,
  userKey: (identity) => identity.userId,
});

The HTTP client picks an agent with paiClient.agent("main") — that is createPaiHttpClient, not pai.agent("main"). createPai().agent(id) returns the in-process AgentRuntime (client() / admin()). Child agents you do not want the browser to start can be mounted read-only over HTTP.

Instructions that see the user

instructions: async ({ identity, runtimeContext }) => `
You are helping ${identity.userId} in workspace ${identity.workspaceId}.
Today: ${runtimeContext.todayIso}.
`,

Keep secrets out of instructions. Put credentials on runtimeContext and use them inside tools.

defineAgent({
  runtimeContext: async ({ identity }) => ({
    db: getDb(),
    permissions: await loadPermissions(identity),
  }),
  // ...
});

Tools then read ctx.context.db and ctx.context.permissions.

Next

On this page