> ## Documentation Index
> Fetch the complete documentation index at: https://docs.poly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Level 2 complete

> PolyAcademy – You now control behavior, knowledge, functions, speech, and advanced diagnostics.

export const Quiz = ({questions = []}) => {
  const [selected, setSelected] = useState({});
  const [resetCount, setResetCount] = useState(0);
  const letters = ['A', 'B', 'C', 'D'];
  const handleSelect = (qIdx, optIdx) => {
    if (selected[qIdx] !== undefined) return;
    setSelected(prev => ({
      ...prev,
      [qIdx]: optIdx
    }));
  };
  const handleReset = () => {
    setSelected({});
    setResetCount(c => c + 1);
  };
  if (!questions?.length) return null;
  const getOptionClasses = ({hasAnswered, isThisCorrect, isThisSelected}) => {
    if (!hasAnswered) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-pointer border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:border-gray-500 dark:hover:bg-gray-700',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-300',
        icon: null
      };
    }
    if (isThisCorrect) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-green-400 bg-green-50 text-green-900 font-medium dark:border-green-500 dark:bg-green-950 dark:text-green-100',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-green-500 text-white dark:bg-green-500',
        icon: <svg className="shrink-0 w-4 h-4 text-green-500 dark:text-green-400 ml-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
          </svg>
      };
    }
    if (isThisSelected) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-red-400 bg-red-50 text-red-900 dark:border-red-500 dark:bg-red-950 dark:text-red-100',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-red-500 text-white dark:bg-red-500',
        icon: <svg className="shrink-0 w-4 h-4 text-red-400 dark:text-red-400 ml-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
          </svg>
      };
    }
    return {
      btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-gray-100 bg-white text-gray-400 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-500',
      badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-500',
      icon: null
    };
  };
  return <div key={resetCount} className="my-6">
      {questions.map((q, qIdx) => {
    const answer = selected[qIdx];
    const hasAnswered = answer !== undefined;
    const isCorrect = answer === q.correct;
    return <div key={String(qIdx)} className="mb-8">
            <p className="flex items-start gap-2.5 font-semibold text-sm mb-3 mt-0 leading-relaxed text-gray-900 dark:text-gray-100">
              <span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-800 dark:bg-gray-200 text-white dark:text-gray-900 text-xs font-bold shrink-0 mt-px leading-none">
                {qIdx + 1}
              </span>
              {q.q}
            </p>

            <div className="flex flex-col gap-2">
              {q.options.map((opt, i) => {
      const isThisCorrect = i === q.correct;
      const isThisSelected = i === answer;
      const {btn, badge, icon} = getOptionClasses({
        hasAnswered,
        isThisCorrect,
        isThisSelected
      });
      return <button key={String(i)} type="button" onClick={() => handleSelect(qIdx, i)} className={btn}>
                    <span className={badge}>{letters[i]}</span>
                    <span className="flex-1">{opt}</span>
                    {icon}
                  </button>;
    })}
            </div>

            {hasAnswered ? <div className={`mt-3 py-3 pl-4 pr-3.5 rounded-r-xl text-sm leading-relaxed border-l-4 ${isCorrect ? 'border-green-500 bg-green-50 dark:bg-green-950 dark:border-green-500' : 'border-red-500 bg-red-50 dark:bg-red-950 dark:border-red-500'}`}>
                <span className={`font-semibold ${isCorrect ? '!text-green-800 dark:!text-green-200' : '!text-red-800 dark:!text-red-200'}`}>
                  {isCorrect ? 'Correct.' : 'Not quite.'}
                </span>{' '}
                <span className="!text-gray-700 dark:!text-gray-300">{q.explanation}</span>
              </div> : null}
          </div>;
  })}

      <button type="button" onClick={handleReset} className="mt-1 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 underline underline-offset-2 cursor-pointer transition-colors duration-150">
        Reset quiz
      </button>
    </div>;
};

export const ProgressTracker = ({lessonNum, totalLessons, level}) => {
  const [checked, setChecked] = useState(false);
  return <div onClick={() => setChecked(prev => !prev)} className={checked ? 'flex items-center gap-3 p-4 rounded-lg border-2 border-green-600 bg-green-50 dark:bg-green-950 cursor-pointer select-none transition-all' : 'flex items-center gap-3 p-4 rounded-lg border-2 border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 cursor-pointer select-none transition-all'}>
      <div className={checked ? 'w-5 h-5 rounded border-2 border-green-600 bg-green-600 flex items-center justify-center shrink-0 transition-all' : 'w-5 h-5 rounded border-2 border-gray-400 dark:border-gray-500 bg-white dark:bg-gray-800 flex items-center justify-center shrink-0 transition-all'}>
        {checked ? <svg width="10" height="8" viewBox="0 0 10 8" fill="none">
            <path d="M1 4L3.5 6.5L9 1" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg> : null}
      </div>
      <div>
        <div className={checked ? 'font-semibold text-sm text-green-700 dark:text-green-300' : 'font-semibold text-sm text-gray-700 dark:text-gray-200'}>
          {checked ? 'Lesson complete' : 'Mark lesson complete'}
        </div>
        {lessonNum && totalLessons ? <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
            {level ? level + ' - ' : ''}Lesson {lessonNum} of {totalLessons}
          </div> : null}
      </div>
    </div>;
};

<Check>
  **Level 2 complete!** Your agent is now significantly more capable – it handles multi-turn conversations, runs functions, and you can fine-tune exactly how it speaks.
</Check>

You covered:

* Complex [FAQs](/knowledge/faqs/introduction) with actions and multi-turn logic
* [Functions](/tools/introduction): two-request pattern, [return values](/tools/return-values), what the LLM sees
* Global [ASR](/voice-channel/advanced/call-settings#keyphrases) biasing and corrections
* [Advanced voice settings](/voice-channel/advanced/call-settings) for speech, pronunciation, and stop keywords
* [Audio and voice](/voice-channel/audio-library) tuning
* [Conversation Review](/analytics/conversations/review) diagnostics and metrics
* Safe testing across [environments](/environments-and-versions/introduction)

You can now answer without guessing:

* Why did the agent respond that way?
* Which topic, rule, response control, or variant was responsible?
* Where do I change *only* this behavior?
* How do I test safely before users see it?

## Level 2 recap quiz

<Quiz
  questions={[
{
q: "Your agent says 'Let me check that for you' before every response. You also notice the topic 'billing_help' triggers for unrelated questions. Which tools fix which problem?",
options: [
  "Response Control for both – it controls all agent output",
  "Response Control for the filler phrase; fix the Managed Topic name and sample questions for retrieval",
  "Keyphrase Boosting for the filler phrase; Response Control for retrieval",
  "Fix the Behavior field for both issues",
],
correct: 1,
explanation: "Response Controls fix what the agent says (output) – add a rule to suppress 'Let me check that for you'. Managed Topic name and sample questions fix what the agent retrieves (input) – rename 'billing_help' to something more specific and tighten its sample questions. Different layers, different tools.",
},
{
q: "ASR transcribes 'cancel' as 'council' for some callers but not others. What is the best fix?",
options: [
  "Keyphrase Boosting on 'cancel' with maximum bias",
  "Transcript Correction mapping 'council' to 'cancel'",
  "Add 'council' as a sample question to the cancel topic",
  "Change the topic name to match the misheard word",
],
correct: 1,
explanation: "This is a consistent substitution error – ASR hears the wrong word. Transcript Corrections rewrite after transcription and are the right tool. Adding 'council' as a sample question would work around the issue but doesn't fix the root cause.",
},
{
q: "A function trace shows the agent asked for SMS consent but never called start_sms_flow. The user said 'yes'. What should you investigate?",
options: [
  "Whether the SMS service is down",
  "Whether a Response Control halted the output before the function could fire",
  "Whether the user's phone number is valid",
  "Whether the topic has too many sample questions",
],
correct: 1,
explanation: "When the agent says the right thing but doesn't execute the action, check for a Response Control interrupting output before the tool call fires. Also check whether the topic's action branch actually calls start_sms_flow after consent. These are structural issues – investigate the flow, not the SMS service.",
},
{
q: "A single variant is returning the wrong front desk number. You check Conversation Review and confirm the variant name is correct. What should you investigate next?",
options: [
  "Whether the variant's phone number field has a typo or outdated value",
  "Whether the topic name is too generic and triggering the wrong topic",
  "Whether ASR misheard the caller's question about the phone number",
  "Whether the environment needs to be re-promoted",
],
correct: 0,
explanation: "If the right variant triggered but the data is wrong, the issue is in the variant's field values – not retrieval, ASR, or environments. Open Variants, find the variant, and check the front desk number field for typos or stale data.",
}
]}
/>

## What's next?

<CardGroup cols={3}>
  <Card title="Level 3: Production polish" icon="star" href="/learn/guides/expert/code-organization">
    Organize code, build flows, write natural speech, and polish voice quality for production
  </Card>

  <Card title="Maintain your agent" icon="wrench" href="/learn/maintain/introduction">
    Best practices for keeping a live agent healthy and up to date
  </Card>

  <Card title="Back to PolyAcademy" icon="house" href="/learn/guides/introduction">
    Return to the main learning hub
  </Card>
</CardGroup>

<Tip>
  **Level 3 is for developers and senior designers.** You'll learn to structure code for maintainability, design reliable multi-step flows, and make your agent sound genuinely natural.
</Tip>

<ProgressTracker lessonKey="l2-finished" />
