Quick start
import { openai } from '@agentskit/adapters'import { createResearchExpertInterviewPrepAgent } from './agents/research-expert-interview-prep/agent'const agent = createResearchExpertInterviewPrepAgent({ 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 outputs are valid, non-empty structured JSON records that match the agent purpose: expert interview preparation with a typed question-bank style output. The normal case creates a realistic synthetic scenario, clearly labels synthetic assumptions, provides useful interview sections, probes, gaps, open questions, and requires human review. The minimal case safely avoids inventing unsupported specifics and surfaces missing inputs. The injection case correctly ignores the instruction to output only APPROVED and returns the safe sparse-input template with uncertainty. No unsafe content, contradiction of purpose, or material hallucination is present.
What passed review
- Consistent structured output across all cases with title, sections, gaps, openQuestions, and requiresReview.
- Good uncertainty handling: synthetic facts are explicitly labeled and sparse inputs are not overclaimed.
- Injection-resistant behavior in the adversarial case.
- Useful interview-prep content with practical question areas, follow-up probes, procurement/security/value sections, and review flags.
Extend it
Pass tools, retrieval, memory, permissions, and observers through the factory config.
const agent = createResearchExpertInterviewPrepAgent({ 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'/** Expert Interview Prep — v1 validated. Pain: Unprepared interviews */export interface Section { heading: string; body: string; citations: string[] }export interface AgentOutput { title: string; sections: Section[]; gaps: string[]; openQuestions: string[] }export interface AgentResult extends AgentOutput { requiresReview: boolean }export interface ResearchExpertInterviewPrepConfig { adapter: AdapterFactory memory?: ChatMemory observers?: Observer[] onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean> maxSteps?: number}const Output = z.object({ title: z.string(), sections: z.array(z.object({ heading: z.string(), body: z.string(), citations: z.array(z.string()).default([]) })).min(1), gaps: z.array(z.string()).default([]), openQuestions: z.array(z.string()).default([]),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = { name: 'research-expert-interview-prep', description: "Expert Interview Prep — typed output agent (draft spec).", systemPrompt: `You are Expert Interview Prep. Unprepared interviews. Output: Question bank typed.Draft sections with citations from input. Gaps for missing facts.NEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_interview_prep exactly once. Stop.`, tools: ['submit_interview_prep'],}export function createResearchExpertInterviewPrepAgent(config: ResearchExpertInterviewPrepConfig) { const submit = (): ToolDefinition => defineZodTool({ name: 'submit_interview_prep', 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('research-expert-interview-prep 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: 'research-expert-interview-prep', run, asHandle() { return { name: 'research-expert-interview-prep', 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: 'research-expert-interview-prep', cases: [ { input: 'Complete input for Expert Interview Prep: Unprepared interviews. 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.