Angular Embedded AnalyticsAgent Configuration

Version 3.0.0

The built-in agents are a starting point, not a fixed team. You can change what they say, what they can do and who they hand work to, all while keeping Studio's harness and chat panel.

To own the run loop instead, see Client Tool Runner.

Declare Your Own Team Copy Link

builtIn is a convenience. Declare agents directly and you control every part:

// Your AgLlmAdapter - see Direct LLM Runner.
const adapter = myOpenAiAdapter({ endpoint: '/api/llm' });

ai: ({ api }) =>
    createAiHarness(api, ({ tools: { studio } }) => ({
        agents: [
            directLlmRunner({
                id: 'analyst',
                adapter,
                instructions: () => 'You build sales dashboards. Prefer bar charts over pie charts.',
                tools: () => [
                    studio.viewSchema(),
                    studio.viewPage(),
                    studio.executeQuery(),
                    studio.addWidget(),
                ],
            }),
        ],
        primary: 'analyst',
    })),

The builder hands you the Studio tools on tools.studio, so there is no need to call api.getAiTools() yourself. Each tool is a function: call it where you list it. Instructions and tools are callbacks, re-resolved every run, and primary names the agent a new thread starts from.

The example below declares one agent of its own - a chart builder with its own instructions and a narrow tool set - in place of the default team.

Change Instructions Copy Link

Instructions are a function, so build them from live context rather than hardcoding what the data looks like:

// Your AgLlmAdapter - see Direct LLM Runner.
const adapter = myOpenAiAdapter({ endpoint: '/api/llm' });

const studio = api.getAiTools();

directLlmRunner({
    id: 'analyst',
    adapter,
    instructions: () => {
        const { tables } = api.getAiContext().schema();
        const names = tables.map((table) => table.name).join(', ');
        return [
            'You build dashboards for the finance team.',
            `Available data: ${names}.`,
            'Currency is GBP. Never mix currencies in one chart.',
            'When asked for a trend, prefer a line chart over a bar chart.',
        ].join('\n');
    },
    tools: () => [studio.viewSchema(), studio.executeQuery()],
});

See Agent Context for everything available to build instructions from.

Change the Tool Set Copy Link

The simplest control over what an agent can do is which tools you list. An agent with no mutating tools cannot change the dashboard, whatever the model decides:

// A read-only analyst: it can look and answer, but not touch the page.
tools: () => [studio.viewSchema(), studio.viewPage(), studio.executeQuery()],

Every Studio tool takes the same optional overrides, applied to that listing only:

string
Advertise the tool under a different name.
descriptionCopy Link
string
Advertise a different description to this agent.
tools: () => [
    studio.addWidget({ description: 'Add a widget. Prefer charts; only use a grid when asked for a table.' }),
    // Renamed to avoid a clash with a tool of your own
    studio.executeQuery({ name: 'query_dashboard_data' }),
],

A description here applies to one agent's listing. To change a description everywhere, use the aiText property instead, which is keyed by the name the tool advertises:

aiText: {
    'tools.add_widget.description': 'Add a widget to the grid. Position is 0-indexed.',
},

Binding a Widget Tool Copy Link

configureWidget is the one tool that takes parameters rather than only overrides. Its schema is narrowed to a single widget type's options, so it has to be told which widget it configures:

tools: () => [studio.configureWidget({ widgetType: 'grid', widgetId: 'main' })],

The built-in widget agent resolves those two values from its delegation parameters, which is how one agent configures whichever widget the lead hands it.

Add Tools of Your Own Copy Link

Anything from api.defineAiTool sits alongside the built-ins in the same array:

tools: () => [studio.viewSchema(), studio.executeQuery(), setThemeTool, notifyTeamTool],

Change Delegation Copy Link

tools.delegateTo builds the delegation tool for a set of target ids. Give an agent a narrower set and it can only hand off to those:

// Your AgLlmAdapter - see Direct LLM Runner.
const adapter = myOpenAiAdapter({ endpoint: '/api/llm' });

createAiHarness(api, ({ tools }) => ({
    agents: [
        directLlmRunner({
            id: 'lead',
            adapter,
            instructions: () => 'Coordinate. Delegate data questions; never answer them yourself.',
            tools: () => [tools.studio.viewSchema(), tools.delegateTo(['data'])],
        }),
        directLlmRunner({
            id: 'data',
            adapter,
            description: 'Answers questions about the data by running queries.',
            instructions: () => 'You are a data analyst. Answer with numbers, not adjectives.',
            tools: () => [tools.studio.executeQuery(), tools.studio.viewSchema()],
        }),
    ],
    primary: 'lead',
}));

A delegate target needs a description, which is what the delegating model reads to decide whether to hand off. Give it delegation parameters with schema, and the delegating agent must supply them:

directLlmRunner<{ region: string }>({
    id: 'regional-analyst',
    adapter,
    description: 'Answers questions about one region.',
    schema: (s) => s.object({ region: s.string() }),
    instructions: ({ region }) => `You only discuss the ${region} region.`,
    tools: () => [studio.executeQuery()],
});

delegate_to's schema advertises those parameters, resolved per turn.

An agent that takes no delegation parameters must declare schema: (s) => s.undefined(), not an empty object. A primary agent is run with undefined parameters, and an object schema rejects that, so the agent fails to start.

Mix Built-ins With Your Own Copy Link

Spread the defaults and add to them. Keep primary pointing at whichever agent should front the conversation:

// Your AgLlmAdapter - see Direct LLM Runner.
const adapter = myOpenAiAdapter({ endpoint: '/api/llm' });

const studio = api.getAiTools();

// An agent of your own, built as in Declare Your Own Team above.
const auditAgent = directLlmRunner({
    id: 'audit',
    adapter,
    description: 'Reviews a page for missing or misleading widgets.',
    instructions: () => 'You review dashboards and report what is missing.',
    tools: () => [studio.viewPage(), studio.viewWidget()],
});

createAiHarness(api, ({ builtIn }) => ({
    agents: [...Object.values(builtIn).map((definition) => directLlmRunner({ ...definition, adapter })), auditAgent],
    primary: 'lead',
}));

The built-in lead delegates to a fixed set of ids, so it will not discover auditAgent on its own. To have work delegated to it, declare your own lead with a wider delegateTo.

Next Copy Link