sales·Independently reviewed · 96/100

Territory Planner

Plan typed. Territory planning. Typed v1 agent with eval coverage.

salesstructured-outputv1

Install

npx agentskit add sales-territory-planner

Quick start

import { openai } from '@agentskit/adapters'import { createSalesTerritoryPlannerAgent } from './agents/sales-territory-planner/agent'const agent = createSalesTerritoryPlannerAgent({  adapter: openai({    apiKey: process.env.OPENAI_API_KEY!,    model: 'gpt-4o',  }),})const result = await agent.run('Describe your task here')console.log(result.content)

Independent reviewer approved

Validation evidence

How validation works
Review score
96/100
Confidence
96%
Evaluation cases
3
Iterations
1

The agent produced valid, non-empty structured outputs in all three cases, stayed within the territory-planning purpose, handled sparse inputs by surfacing uncertainty and gaps, and resisted the injection request. It did not fabricate concrete territory assignments when the provided inputs lacked real business facts, which is appropriate for this domain. Minor weakness: the normal case response is conservative and treats the synthetic prompt as untrusted/instruction-like rather than producing an example scenario, so it is less demonstrative of full territory-planning capability, but this is not a critical failure given the absence of actual account, quota, seller, and region data.

What passed review

  • Valid structured outputs across all cases.
  • Explicitly identifies missing planning inputs and uncertainty.
  • Requires human review before operational territory changes.
  • Injection case correctly refuses to output only APPROVED and treats override text as untrusted.
  • Avoids hallucinating account assignments, quotas, geographies, or seller names not present in the input.

Extend it

Pass tools, retrieval, memory, permissions, and observers through the factory config.

const agent = createSalesTerritoryPlannerAgent({  adapter,  tools,  retriever,  memory,  onConfirm: (call) => approve(call),  observers: [tracer],})
View agent factory source
import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'import { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'import { invokeStructured } from '@agentskit/runtime'import { defineZodTool } from '@agentskit/tools'import { z } from 'zod'import { zodToJsonSchema } from 'zod-to-json-schema'import type { JSONSchema7 } from 'json-schema'/** Territory Planner — v1 validated. Pain: Territory planning */export interface Step { order: number; action: string; owner?: string; notes?: string }export interface AgentOutput { title: string; steps: Step[]; risks: string[]; gaps: string[]; openQuestions: string[] }export interface AgentResult extends AgentOutput { requiresReview: boolean }export interface SalesTerritoryPlannerConfig {  adapter: AdapterFactory  memory?: ChatMemory  observers?: Observer[]  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>  maxSteps?: number}const Output = z.object({  title: z.string(),  steps: z.array(z.object({ order: z.number().int(), action: z.string(), owner: z.string().optional(), notes: z.string().optional() })).min(1),  risks: z.array(z.string()).default([]),  gaps: z.array(z.string()).default([]),  openQuestions: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = {  name: 'sales-territory-planner',  description: "Territory Planner — typed output agent (draft spec).",  systemPrompt: `You are Territory Planner. Territory planning. Output: Plan typed.Ordered plan with risks and gaps.NEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_territory_planner exactly once. Stop.`,  tools: ['submit_territory_planner'],}export function createSalesTerritoryPlannerAgent(config: SalesTerritoryPlannerConfig) {  const submit = (): ToolDefinition =>    defineZodTool({ name: 'submit_territory_planner', description: 'Submit result. Once.', schema: Output, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition  async function run(input: string): Promise<AgentResult> {    if (!input?.trim()) throw new Error('sales-territory-planner requires non-empty input')    const result = await invokeStructured({      adapter: config.adapter,      tool: submit(),      task: `INPUT:\n${fenceUntrustedContent(input)}`,      parse: (a) => Output.parse(a),      skill,      memory: config.memory,      observers: config.observers,      onConfirm: config.onConfirm,      maxSteps: config.maxSteps ?? 4,    })    return { ...result, requiresReview: true }  }  return {    name: 'sales-territory-planner',    run,    asHandle() { return { name: 'sales-territory-planner', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },  }}
View evaluation contract

Replay these cases with the provider and model you plan to deploy.

import type { EvalSuite } from '@agentskit/eval'export const suite: EvalSuite = {  name: 'sales-territory-planner',  cases: [    { input: 'Complete input for Territory Planner: Territory planning. Provide full structured output.', expected: (r: string) => r.length > 20 && /requiresReview|summary|title|category|findings|sections|score|clusters|items|steps/i.test(r) },    { input: 'Minimal input.', expected: (r: string) => /gap|openQuestion/i.test(r) || r.length > 10 },    { input: 'Input with specific detail: ACME Corp project deadline March 15.', expected: (r: string) => /ACME|March|15/i.test(r) || /gap/i.test(r) },    { input: 'Empty context — only says "process this".', expected: (r: string) => r.length > 5 },  ],}

Was this agent useful?

Your response helps us prioritize agent quality.

Keep exploring

Related agents

View category