PAIPAI

Skills

Load reusable instruction packages through storage-neutral skill providers.

Skills are reusable instruction packages that an agent can load on demand. They are useful when a workflow needs durable instructions, examples, or reference material, but those details should not be placed in every agent prompt.

PAI models skills as normal backend tools with hidden outputs:

  • discovery metadata is rendered into the agent instructions;
  • loadSkill loads the full skill instructions;
  • readSkillReference loads package-relative supporting files;
  • the chat renderer can omit those tool calls while the runtime still persists them for replay and debugging.

Basic Setup

For demos, tests, or embedded skill lists, use @pai/skills-inmemory.

import { defineAgent } from "@pai/core";
import {
  createInMemorySkills,
  type SkillPackage,
} from "@pai/skills-inmemory";
import { z } from "zod";

const apiReviewSkill = {
  name: "api-review",
  description:
    "Review public TypeScript APIs for consumer ergonomics. Use when assessing a proposed SDK or component API.",
  instructions: `# API Review

1. Identify what the consumer knows at the call site.
2. Separate the common path from escape hatches.
3. Read references/rubric.md before the final recommendation.`,
  references: [
    {
      path: "references/rubric.md",
      description: "API review checklist.",
      content: "- Look for casts, duplicated ids, unclear sequencing, and hidden state.",
    },
  ],
} satisfies SkillPackage;

const skills = createInMemorySkills({
  skills: [apiReviewSkill],
});

export const agent = defineAgent({
  name: "api-reviewer",
  identity: z.object({ id: z.string(), workspaceId: z.string() }),
  model: ({ runtimeContext }) => runtimeContext.models.primary,
  instructions: async (input) =>
    [
      "Review APIs with a focus on consumer-facing behavior.",
      await skills.renderInstructions({ signal: input.signal }),
    ].join("\n\n"),
  tools: skills.tools,
});

The consumer imports only from @pai/skills-inmemory for this path. That package re-exports the core skills API for advanced composition.

Package Shape

SkillPackage is the in-memory provider representation of a skill package. It maps to SKILL.md concepts, but it is not the raw filesystem format.

type SkillPackage = {
  id?: string;
  name: string;
  description: string;
  version?: string;
  instructions: string;
  references?: readonly SkillPackageReference[];
  contentHash?: string;
  sourceType?: string;
};

type SkillPackageReference = {
  path: string;
  description?: string;
  content: string;
  contentHash?: string;
  mediaType?: string;
};

name and description match Agent Skills SKILL.md frontmatter. They are the discovery fields the model sees before a skill is loaded.

instructions is the Markdown body of SKILL.md.

references represents package-relative files such as references/rubric.md. In a filesystem provider those files would be read from disk. In the in-memory provider, each reference includes content.

id, version, and contentHash are PAI runtime metadata for stable tool calls, replay, audit, and cache/debug behavior. They are not required frontmatter fields in the Agent Skills format.

sourceType is optional display/debug metadata, useful in demos when comparing filesystem, registry, or embedded sources.

Agent Tools

createInMemorySkills() returns a SkillIntegration:

type SkillIntegration = {
  tools: {
    loadSkill: ToolDefinition;
    readSkillReference: ToolDefinition;
  };
  /** The literal names the two tools are registered under. */
  toolNames: { loadSkill: "loadSkill"; readSkillReference: "readSkillReference" };
  renderInstructions(input?: { signal?: AbortSignal }): Promise<string>;
  isSkillToolName(
    toolName: string,
  ): toolName is "loadSkill" | "readSkillReference";
};

loadSkill({ skillId }) returns the loaded instructions, reference metadata, version/hash metadata, and visibility: "hidden".

readSkillReference({ skillId, path }) returns one package-relative reference file and visibility: "hidden".

There is no model-facing listSkills tool. Discovery happens through renderInstructions(), which renders compact metadata into the agent prompt. The model can then choose to call loadSkill, and can call readSkillReference after it knows a needed reference path.

Providers

@pai/skills owns the storage-neutral contracts and generated tool behavior. Provider packages own storage-specific loading.

Current provider:

  • @pai/skills-inmemory: virtual packages for demos, tests, and embedded registries.

Expected future providers:

  • @pai/skills-fs: read SKILL.md directories from a filesystem root.
  • hosted or registry providers: resolve immutable skill versions from database records, object storage, or package archives.

Provider packages should expose a shorthand helper for the common path and a source helper for advanced composition:

createInMemorySkills({ skills });

createInMemorySkillSource({ skills });

The agent integration should still receive a single SkillSource. A composite source can merge filesystem, registry, and embedded providers later without changing how the agent consumes skills.

Visibility

Skill tool calls are execution context, not user-facing chat content. A product can keep them out of the visible transcript while still preserving them in the runtime transcript for replay and debugging.

If a UI wants to show that skills were used, render a redacted activity view such as "Loaded api-review" or "Read references/rubric.md" instead of showing raw skill instructions.

When To Use Skills

Use skills for reusable procedures, style guides, domain workflows, or task-specific playbooks that the model should load only when relevant.

Use a RAG or search tool when the model needs to retrieve arbitrary knowledge from a large corpus. Skills are curated instruction packages; RAG tools are retrieval mechanisms.

On this page