Real Cases

Support Agent

A practical support chat harness with history, retrieval, account tools, and traces.

This pattern is for customer support chat, help center assistants, and account-aware product support. The agent answers user questions, retrieves support knowledge, reads customer state through tools, persists conversation history, and emits trace metadata.

Scenario

A signed-in customer asks, "Where is my order A-100 and can I change the address?" The agent needs previous conversation history, support documentation, order tools, and tenant-safe account context.

When to Use It

Use this pattern when:

  • the user is authenticated
  • the agent needs conversation history
  • account-specific data must be fetched through tools
  • documentation or policy should come from retrieval
  • the response is user-facing and should be traceable

Architecture Shape

LayerResponsibility
routeparse request and return product response
runnerresolve user, load history, create scoped tools, persist messages
support agentinstructions, dynamic context, scoped account tools, turn limit
toolsorder, ticket, account, and subscription service calls
retrievaltenant-safe or public support docs
storageconversation history and trace correlation ids

Code Example

import { AgentBuilder, Message } from "@anvia/core";
import { model } from "./model";
import { supportDocsIndex } from "./support-docs";
import { createSupportTools } from "./support-tools";

export async function runSupportTurn(input: SupportTurnInput) {
  const user = await input.auth.requireUser();
  const history = await input.conversations.loadMessages(input.conversationId);

  const agent = new AgentBuilder("support", model)
    .instructions(`
Answer support questions clearly.
Use account tools for customer-specific data.
Use retrieved support docs for policy and product behavior.
Ask for missing details before guessing.
    `)
    .dynamicContext(supportDocsIndex, {
      topK: 4,
      threshold: 0.72,
    })
    .tools(
      createSupportTools({
        userId: user.id,
        tenantId: user.tenantId,
        orders: input.services.orders,
        tickets: input.services.tickets,
        subscriptions: input.services.subscriptions,
      }),
    )
    .context(`Current customer plan: ${user.plan}`, "customer-plan")
    .defaultMaxTurns(4)
    .build();

  const response = await agent
    .prompt([...history, Message.user(input.message)])
    .withTrace({
      name: "support-chat",
      userId: user.id,
      metadata: {
        tenantId: user.tenantId,
        conversationId: input.conversationId,
        channel: input.channel,
      },
    })
    .send();

  await input.conversations.append(input.conversationId, response.messages);

  return {
    output: response.output,
    usage: response.usage,
  };
}

Tool Scope

Support tools should be scoped to the current user and tenant.

export function createSupportTools(scope: SupportToolScope) {
  return [
    createLookupOrderTool(scope),
    createCreateTicketTool(scope),
    createSubscriptionStatusTool(scope),
  ];
}

Do not give the model raw database clients. Give it narrow tools that call permission-aware services.

Failure Modes

FailureFix
agent answers from stale docsrebuild or refresh the retrieval index
account data leaks across tenantsenforce tenant filters in every tool and retrieval source
route tests call live providerstest runner and tools with fakes first
history grows too largesummarize, window, or use session memory policy
model calls too many toolslower defaultMaxTurns or split tools by workflow

Test Checklist

  • Test empty and malformed messages in the runner.
  • Test order lookup allowed, denied, and not found paths.
  • Test conversation history is appended with response.messages.
  • Test retrieval filters only return allowed documents.
  • Use Studio to inspect tool calls and retrieved context.
  • Add evals for common support questions and known policy answers.