Back to Resources

Resources · Use Case

Build Ask Anter — Part 1 + Part 2

This is the exact implementation flow we use for Ask Anter: first set up the platform (Part 1), then wire the chat experience with @anter/ai-chat-sdk (Part 2).

Problem: Visitors need fast and accurate answers in-chat, but a plain widget without a configured agent stack cannot deliver reliable, grounded responses.
Scope: Part 1 covers project setup, API key generation, attaching the knowledge-base retrieval builtin tool, Azure Foundry provider registration, and onboarding your first flow agent. Part 2 shows how to connect that agent into a production chat widget.

Use case definition

Use case

Customer gets accurate product/support answers from Ask Anter chat.

Scope

Agent Builder setup (Part 1) + landing chat integration (Part 2).

Primary actor

Ops/Builder configuring Ask Anter.

Supporting actors

Site visitor, Agent Runner API, Azure Foundry.

Trigger

A user opens Ask Anter on landing and sends a question.

Preconditions

  1. Organization access is available and you can create/select projects.
  2. You can store secrets (project API key, provider credentials) in a secure secret manager.
  3. You have an Azure Foundry endpoint + key and at least one deployed model.

Minimal guarantees

  1. No long-lived credentials are exposed in browser code.
  2. If a configuration step fails, previous saved state remains intact.

Part 1 — Platform setup

Step 1

Create project

Where: /agent-builder/projects

Create and select the project that will contain your full Ask Anter bot stack.

Actions

  1. Open "Projects" and click "+ Create Project".
  2. Set project name and description, then click “Create & Select Project”.
  3. Confirm the new project is marked as “Current”.

Beginner track

Treat this as your isolated workspace. Everything in Part 1 (tools, keys, models, agent graph) should live in this project.

Technical track

The project context drives all downstream API scopes (keys, MCP enablement, providers, and agent resources).

Expected output: Project is created, selected, and visible as the current context.
Create project
Evidence: project was created and selected as current context.

Step 2

Create project API key and save it

Where: /agent-builder/projects → Details

Generate a project-scoped key for secure runtime access in your chatbot integration.

Actions

  1. Open your project row and click "Details".
  2. In "API Access Keys", click "+ Generate key".
  3. Enter a key label and click “Create key”.
  4. Copy and store the plaintext key immediately in your secret manager.

Beginner track

The key is shown once only. Save it before closing the panel, then keep it in your environment secrets.

Technical track

Persist as a server-side secret (never client-side). Rotate keys on schedule and update deployment secrets atomically.

Expected output: A new API key exists and the plaintext value is safely stored.
Create project API key and save it
Evidence: one-time plaintext project key is shown and copied to secure storage.

Step 3

Attach the knowledge-base retrieval builtin tool

Where: /agent-builder/flows

Give your Ask Anter sub-agent semantic search over the project's knowledge base — no external server setup required.

Actions

  1. Open the sub-agent that should answer from your knowledge base.
  2. Open "Builtin Toolsets" and enable `context_retrieval`.
  3. Confirm your knowledge-base documents are indexed for the project.

Beginner track

context_retrieval is a builtin tool — enabling it is a one-click toggle, no server connection to configure.

Technical track

context_retrieval runs in-process against the project's vector index, scoped by project tag — no MCP server or bulk import step involved.

Expected output: The sub-agent has context_retrieval enabled and can answer from indexed content.
Attach the knowledge-base retrieval builtin tool
Evidence: context_retrieval is enabled on the sub-agent's Builtin Toolsets.

Step 4

Create LLM provider (Azure Foundry example)

Where: /agent-builder/llm-providers

Register and validate the model provider used by your agent and sub-agents.

Actions

  1. Click "+ New Provider" and set provider type to "Azure Foundry".
  2. Enter provider name, endpoint, and API key.
  3. Create provider, open it, and run "Test Connection".
  4. Confirm deployed model visibility (example: `gpt-5.4-nano`).

Beginner track

Use the exact endpoint format from your Azure deployment and verify before continuing to agent setup.

Technical track

Provider IDs are used in model selectors and can be combined with model names for explicit overrides in flow/sub-agent settings.

Expected output: Provider is created, reachable, and usable for model selection.
Create LLM provider (Azure Foundry example)
Evidence: Azure Foundry provider is reachable and model is discoverable.

Step 5

Onboard an agent

Where: /agent-builder/flows

Create the initial Ask Anter flow agent and baseline configuration so it is ready for sub-agent expansion and runtime calls.

Actions

  1. Open "Flows" and click "Onboard Agent".
  2. Open "Settings" and configure name, description, and model/provider.
  3. Save flow settings, then save changes to persist the topology.

Beginner track

Start with one clear orchestrator role (what this agent should do and what it should not do).

Technical track

Use explicit provider/model selection and maintain deterministic prompts before adding specialized sub-agents.

Expected output: Flow agent is onboarded and saved with valid model configuration.
Onboard an agent
Evidence: flow topology is saved with configured nodes and delegation route.

Part 2 — Chat widget implementation with @anter/ai-chat-sdk

The implementation below mirrors the live landing integration in apps/landing/src/main.tsx, apps/landing/src/App.tsx, and apps/landing/src/lib/landing-adapter.ts.

New to the SDK? See full install, API, and configuration on the @anter/ai-chat-sdk reference page.

Step 1

Install SDK and load widget styles

Where: apps/landing/src/main.tsx

Enable chat UI rendering and base widget behavior in your app shell.

Actions

  1. Install `@anter/ai-chat-sdk` in your app package.
  2. Import SDK styles once at app entry: `import "@anter/ai-chat-sdk/styles-no-base.css";`
  3. Keep your own app styles imported before/after based on your CSS cascade preference.

Beginner track

Without the SDK stylesheet, the widget can mount but will look broken or unstyled.

Technical track

Use `styles-no-base.css` (as landing does) to avoid global CSS resets and keep host styling control.

Expected output: Widget styles load correctly and are ready for provider/widget components.

Step 2

Implement a ChatAdapter that streams to your Part 1 agent

Where: apps/landing/src/lib/landing-adapter.ts

Connect the chat widget to the agent-runner stream endpoint for the exact agent created in Part 1.

Actions

  1. Create a class implementing `ChatAdapter` from `@anter/ai-chat-sdk/types`.
  2. Set stream path to `/api/v1/external/agent-runner/agents/<agent_id>/run-stream`.
  3. In `sendMessage`, POST JSON with `message`, `organizationId`, and a `contextVariables` object carrying per-session `userId` + `sessionId`.
  4. Return `res.body` as stream and keep session methods as lightweight stubs if using stateless runtime.

Beginner track

Think of adapter as the bridge between chat UI and your agent endpoint.

Technical track

Landing uses a proxy path (`/api/...`) and worker header injection so project/API secrets never ship to the browser.

Expected output: User messages stream responses from your Part 1 onboarded agent.

Step 3

Wrap app with ChatProvider

Where: apps/landing/src/App.tsx

Provide runtime chat context, org scope, adapter wiring, and UX configuration.

Actions

  1. Instantiate your adapter with `VITE_CHAT_AGENT_ID` (`new LandingChatAdapter(chatAgentId)`).
  2. Pass `organizationId` from `VITE_ORG_ID` (fallback allowed for local dev).
  3. Set `config` flags to match your product experience.
  4. Set user-facing strings such as composer placeholder and disclaimer.

Beginner track

This is the required wrapper. Place your page/router content inside `ChatProvider`.

Technical track

Landing disables model selector, slash commands, command palette, and uploads; it enables artifacts.

Expected output: Chat context is available globally and consistent with your chosen UX policy.

Step 4

Mount ChatWidget and choose interaction pattern

Where: apps/landing/src/App.tsx

Expose the widget trigger and tune open/close behavior for your site layout.

Actions

  1. Add `<ChatWidget position='bottom-right' title='Anter' />` under your page content.
  2. Optionally keep full chat navigation disabled (`fullChatUrl={() => '#'}; onNavigate={() => undefined}`).
  3. Use custom `trigger` render prop if you want branded button UI like Ask Anter.
  4. Optionally handle external open events (landing listens to `anter:open-chat`).

Beginner track

Default widget works immediately; custom trigger is optional polish.

Technical track

Landing maps trigger state to `aria-label` and uses event-driven open for CTA integrations.

Expected output: A production-ready widget can be opened from floating trigger and CTA events.
Mount ChatWidget and choose interaction pattern
Evidence: the Ask Anter chat widget open on the live landing page.

Code pattern: adapter

import type { ChatAdapter, MessagePayload } from "@anter/ai-chat-sdk/types";

export class LandingChatAdapter implements ChatAdapter {
  constructor(private readonly agentId: string) {}

  private get streamPath(): string {
    return `/api/v1/external/agent-runner/agents/${this.agentId}/run-stream`;
  }

  async sendMessage(payload: MessagePayload): Promise<ReadableStream<Uint8Array>> {
    const res = await fetch(this.streamPath, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        message: payload.message,
        organizationId: payload.organizationId,
        // contextVariables flow through to the agent. Pass per-session identity
        // so the runner can scope memory/history to this conversation.
        contextVariables: {
          ...(payload.contextVariables ?? {}),
          userId: `chat-widget-${payload.sessionId}`,
          sessionId: payload.sessionId,
        },
      }),
    });
    if (!res.ok) throw new Error(`sendMessage failed: ${res.status}`);
    if (!res.body) throw new Error("sendMessage: missing response body");
    return res.body;
  }
}

Code pattern: provider + widget

import { ChatProvider, ChatWidget } from "@anter/ai-chat-sdk";
import { LandingChatAdapter } from "./lib/landing-adapter";

const organizationId = (import.meta.env.VITE_ORG_ID as string | undefined) ?? "anter";
const chatAgentId = import.meta.env.VITE_CHAT_AGENT_ID as string;
const adapter = new LandingChatAdapter(chatAgentId);

<ChatProvider
  organizationId={organizationId}
  adapter={adapter}
  config={{
    theme: "light",
    enableModelSelector: false,
    enableSlashCommands: false,
    enableCommandPalette: false,
    enableFileUpload: false,
    enableSlashFocusShortcut: false,
    enableArtifacts: true,
  }}
  strings={{
    composerPlaceholder: "Ask a question...",
    footerDisclaimer: "AI responses can contain mistakes.",
  }}
>
  {page}
  <ChatWidget position="bottom-right" title="Anter" />
</ChatProvider>

Configuration options used on landing

OptionLanding valueWhy usedOther options
enableModelSelectorfalseKeep model choice controlled by backend/flow configuration.Set `true` to expose model switching to end users.
enableSlashCommandsfalseSimplify consumer-facing UX.Set `true` for power-user workflows and command shortcuts.
enableCommandPalettefalseAvoid advanced command UI in public marketing chat.Enable for internal apps or operator consoles.
enableFileUploadfalseLimit public-surface risk and keep scope to text chat.Enable for document workflows once validation is in place.
enableSlashFocusShortcutfalsePrevent accidental shortcut conflicts on landing pages.Enable for app-like experiences with keyboard-heavy users.
enableArtifactstrueAllow rich response rendering with artifacts.Disable if your use case requires strict plain-text output only.

Alternative flows and recovery paths

Knowledge base returns no results after attaching context_retrieval

Confirm the sub-agent's Builtin Toolsets selection includes context_retrieval and that documents are indexed for the project's knowledge base.

Provider reachable but target model unavailable

Update deployed model list in Azure Foundry, then rerun provider test and reselect model in flow settings.

Project API key was not saved

Generate a new key immediately and rotate the old one; keep only secret-manager copies.

Chat stream call returns non-OK status

Check worker-injected headers (`x-project-id`, `x-api-key`), organizationId, and agentId path in ChatAdapter.

Postconditions (success guarantees)

  1. A configured Ask Anter flow agent exists and is callable.
  2. Landing chat widget streams responses from the Part 1 agent endpoint.
  3. Knowledge retrieval path is available through the context_retrieval builtin tool.
Production note: keep project/API credentials server-side. The landing setup proxies chat calls through Cloudflare Worker and injects x-project-id / x-api-key there, not in browser code.