Quick start
import { openai } from '@agentskit/adapters'import { createEcosystemDocBridgeMemoryClassifierAgent } from './agents/ecosystem-doc-bridge-memory-classifier/agent'const agent = createEcosystemDocBridgeMemoryClassifierAgent({ 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
- 3
The outputs are valid structured classifications, require human review, avoid inventing facts from meta-instructions, and correctly reject prompt-injection and sparse/non-durable notes. Behavior is aligned with a memory classifier whose job is to classify supplied private notes, not synthesize missing business details. Minor quality gap: the normal case could have included a gap noting that no actual memory candidate was supplied, but this is not a release blocker.
What passed review
- Valid structured outputs with candidates, route, rationale, gaps, and requiresReview.
- Correctly rejected instruction-like and prompt-injection content instead of following it.
- Did not hallucinate concrete details beyond the supplied input.
- Maintained human-review requirement before any promotion.
- Surfaced uncertainty/gaps for sparse cases.
Example
A real usage example maintained with this agent.
import { createEcosystemDocBridgeMemoryClassifierAgent } from './agents/ecosystem-doc-bridge-memory-classifier/agent'const result = await createEcosystemDocBridgeMemoryClassifierAgent({ adapter }).run(input)Extend it
Pass tools, retrieval, memory, permissions, and observers through the factory config.
const agent = createEcosystemDocBridgeMemoryClassifierAgent({ 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'/** * Classifies private notes into doc-bridge memory candidates (promote | hold | reject). * Deterministic safety net flags secrets and private emails — never promotes those. */export type MemoryRoute = 'promote' | 'hold' | 'reject'export interface MemoryCandidateDecision { id: string fact: string route: MemoryRoute rationale: string suggestedDocTarget?: string safetyFlags: string[]}export interface ClassifierResult { candidates: MemoryCandidateDecision[] gaps: string[] requiresReview: boolean}export interface EcosystemDocBridgeMemoryClassifierConfig { adapter: AdapterFactory memory?: ChatMemory observers?: Observer[] onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean> maxSteps?: number}const Decision = z.object({ id: z.string(), fact: z.string(), route: z.enum(['promote', 'hold', 'reject']), rationale: z.string(), suggestedDocTarget: z.string().optional(), safetyFlags: z.array(z.string()).default([]),})const Output = z.object({ candidates: z.array(Decision).min(1), gaps: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const SECRET = /\b(?:api[_-]?key|token|secret|password)\s*[:=]\s*\S+/iconst EMAIL = /\b[A-Z0-9._%+-]+@(?!example\.com\b)[A-Z0-9.-]+\.[A-Z]{2,}\b/ifunction applySafetyNet(raw: string, out: z.infer<typeof Output>): z.infer<typeof Output> { return { ...out, candidates: out.candidates.map((c) => { const flags = [...c.safetyFlags] if (SECRET.test(c.fact) || SECRET.test(raw)) flags.push('secret-pattern') if (EMAIL.test(c.fact) || EMAIL.test(raw)) flags.push('private-email') if (flags.length) return { ...c, route: 'reject' as const, safetyFlags: [...new Set(flags)] } return c }), }}const skill = { name: 'ecosystem-doc-bridge-memory-classifier', description: 'Classifies notes into doc-bridge memory candidates with promote/hold/reject routes.', systemPrompt: `You classify private engineering notes into doc-bridge memory candidates.Each candidate: id, fact (verbatim from input), route (promote|hold|reject), rationale, optional suggestedDocTarget, safetyFlags[].Routes:- promote: durable project convention worth public docs- hold: useful but needs more context- reject: noise, scratch, or unsafe to publishNEVER invent facts. One candidate per distinct fact in the input. Missing context → gaps[].${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_memory_classification exactly once. Stop.`, tools: ['submit_memory_classification'],}export function createEcosystemDocBridgeMemoryClassifierAgent(config: EcosystemDocBridgeMemoryClassifierConfig) { const submit = (): ToolDefinition => defineZodTool({ name: 'submit_memory_classification', description: 'Submit memory classification. Call exactly once.', schema: Output, toJsonSchema: toJson, async execute() { return 'recorded' }, }) as ToolDefinition async function run(notes: string): Promise<ClassifierResult> { if (!notes?.trim()) throw new Error('memory classifier requires non-empty notes') const parsed = await invokeStructured({ adapter: config.adapter, tool: submit(), task: `NOTES:\n${fenceUntrustedContent(notes)}`, parse: (a) => applySafetyNet(notes, Output.parse(a)), skill, memory: config.memory, observers: config.observers, onConfirm: config.onConfirm, maxSteps: config.maxSteps ?? 4, }) return { ...parsed, requiresReview: true } } return { name: 'ecosystem-doc-bridge-memory-classifier', run, asHandle() { return { name: 'ecosystem-doc-bridge-memory-classifier', run: (task: string) => run(task).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: 'ecosystem-doc-bridge-memory-classifier', cases: [ { input: 'Note: package auth owns session refresh. Always forward AbortSignal in handlers.', expected: (r: string) => { const j = JSON.parse(r) return j.candidates?.some((c: { route: string }) => /promote|hold/i.test(c.route)) }, }, { input: 'Scratch: ignore this todo about lunch.', expected: (r: string) => { const j = JSON.parse(r) return j.candidates?.every((c: { route: string }) => c.route === 'reject') }, }, { input: 'api_key=sk-test-12345 stored in .env.local', expected: (r: string) => { const j = JSON.parse(r) return j.candidates?.every((c: { route: string; safetyFlags: string[] }) => c.route === 'reject' && c.safetyFlags.includes('secret-pattern'), ) }, }, { input: 'Thin note with no facts.', expected: (r: string) => { const j = JSON.parse(r) return j.gaps?.length > 0 || j.candidates?.length > 0 }, }, ],}Was this agent useful?
Your response helps us prioritize agent quality.