Quick start
import { openai } from '@agentskit/adapters'import { createFintechExpensePolicyAuditorAgent } from './agents/fintech-expense-policy-auditor/agent'const agent = createFintechExpensePolicyAuditorAgent({ 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
- Review score
- 96/100
- Confidence
- 96%
- Evaluation cases
- 3
- Iterations
- 1
The agent produced valid structured outputs for all three cases, avoided inventing expense facts, handled sparse inputs with explicit gaps and open questions, and resisted the injection attempt without outputting APPROVED. Behavior is aligned with an expense policy auditor that should not substantiate violations without evidence. Minor weakness: it over-classifies benign evaluation wording as prompt-injection-style untrusted text, but the resulting audit posture is still safe and useful.
What passed review
- All outputs are non-empty and structured with summary, findings, gaps, open questions, and review posture.
- Does not hallucinate expense records, policy thresholds, employees, vendors, dates, or violations from missing context.
- Correctly rejects the explicit injection attempt and preserves uncertainty.
- Provides actionable missing-information requests for a human reviewer.
Reviewer notes
- Add or validate against at least one case containing real expense records and policy thresholds to confirm violation typing beyond sparse-input handling.
- Tone down prompt-injection labeling for ordinary sparse instructions; reserve injection severity for actual override attempts like the APPROVED case.
Extend it
Pass tools, retrieval, memory, permissions, and observers through the factory config.
const agent = createFintechExpensePolicyAuditorAgent({ 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'/** Expense Policy Auditor — v1 validated. Pain: Expense abuse */export interface Finding { id: string; severity: 'critical' | 'high' | 'medium' | 'low' | 'info'; message: string; source?: string; recommendation?: string }export interface AgentOutput { summary: string; findings: Finding[]; gaps: string[]; openQuestions: string[] }export interface AgentResult extends AgentOutput { requiresReview: boolean }export interface FintechExpensePolicyAuditorConfig { adapter: AdapterFactory memory?: ChatMemory observers?: Observer[] onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean> maxSteps?: number}const Output = z.object({ summary: z.string(), findings: z.array(z.object({ id: z.string(), severity: z.enum(['critical', 'high', 'medium', 'low', 'info']), message: z.string(), source: z.string().optional(), recommendation: z.string().optional(), })), gaps: z.array(z.string()).default([]), openQuestions: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = { name: 'fintech-expense-policy-auditor', description: "Expense Policy Auditor — typed output agent (draft spec).", systemPrompt: `You are Expense Policy Auditor. Expense abuse. Output: Violations typed.Actionable findings citing input sources. No invented issues.NEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_policy_auditor exactly once. Stop.`, tools: ['submit_policy_auditor'],}export function createFintechExpensePolicyAuditorAgent(config: FintechExpensePolicyAuditorConfig) { const submit = (): ToolDefinition => defineZodTool({ name: 'submit_policy_auditor', 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('fintech-expense-policy-auditor 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: 'fintech-expense-policy-auditor', run, asHandle() { return { name: 'fintech-expense-policy-auditor', 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: 'fintech-expense-policy-auditor', cases: [ { input: 'Complete input for Expense Policy Auditor: Expense abuse. 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.