{"id":35695,"date":"2026-03-17T15:49:38","date_gmt":"2026-03-17T15:49:38","guid":{"rendered":"https:\/\/machinelearningplus.com\/uncategorized\/constitutional-ai-self-critique-python\/"},"modified":"2026-03-17T19:27:38","modified_gmt":"2026-03-17T19:27:38","slug":"constitutional-ai-self-critique-python","status":"publish","type":"post","link":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/","title":{"rendered":"Constitutional AI: Build a Self-Critique Loop (Python)"},"content":{"rendered":"<p><em>Generate a response, critique it against principles, and revise \u2014 all with raw HTTP calls to an LLM API.<\/em><\/p>\n<div class=\"interactive-notice\" style=\"background:#e8f4f8; border-left:4px solid #2196F3; padding:12px 16px; margin:20px 0; border-radius:4px; font-size:15px;\">\n<strong>Interactive Code Blocks<\/strong> \u2014 The Python code blocks in this article are runnable. Click the <strong>Run<\/strong> button to execute them right in your browser.\n<\/div>\n<p>Your chatbot just told a user how to pick a lock. You didn&#8217;t ask it to. The model has no filter \u2014 it answers whatever you throw at it. Now imagine a system that catches that before the response leaves the server. No human reviewer. The model critiques itself.<\/p>\n<p>That&#8217;s Constitutional AI. Anthropic introduced it in 2022 to align models without drowning in human feedback. The model generates, checks against principles, and rewrites. One model, three passes, safer output.<\/p>\n<p>Here&#8217;s how the pieces connect. You send a prompt. The model generates a raw response with no guardrails. You feed that response back with a principle like &#8220;Is this harmful?&#8221; The model critiques its own answer. Then you send the original plus the critique and ask for a revision. Each stage feeds the next: raw response flows into critique, critique flows into revision.<\/p>\n<p>We&#8217;ll build this loop from scratch with raw HTTP calls.<\/p>\n<h2 id=\"what-is-constitutional-ai\">What Is Constitutional AI?<\/h2>\n<p>Think of it this way. You write five rules on a card: no harm instructions, no fake facts, no bias, no privacy leaks, no manipulation. You hand the card to the model alongside its own response. &#8220;Did you break any of these?&#8221; The model checks each rule and flags violations.<\/p>\n<p>Constitutional AI (CAI) is this pattern turned into a pipeline. Anthropic published the original paper in December 2022. The model evaluates its own output against explicit, written principles \u2014 then rewrites to fix what it found.<\/p>\n<p>Standard alignment needs thousands of human preference labels. CAI replaces most of that labor with self-critique. The model still needs instruction-following ability. But the safety layer runs on the model&#8217;s own judgment.<\/p>\n<div class=\"callout callout-key-insight\"><strong>Key Insight:<\/strong> <strong>Constitutional AI replaces human safety reviewers with a structured self-critique loop: generate, critique against principles, then revise.<\/strong> The model polices itself.<\/div>\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n<ul>\n<li><strong>Python version:<\/strong> 3.9+<\/li>\n<li><strong>Required libraries:<\/strong> <code>requests<\/code> (HTTP calls)<\/li>\n<li><strong>Install:<\/strong> <code>pip install requests<\/code><\/li>\n<li><strong>API key:<\/strong> An Anthropic API key \u2014 get one at <a href=\"https:\/\/console.anthropic.com\/\">console.anthropic.com<\/a><\/li>\n<li><strong>Time to complete:<\/strong> 20\u201325 minutes<\/li>\n<\/ul>\n<pre class=\"python-runnable\">\nimport micropip\nawait micropip.install([\"requests\"])\n\nimport requests\nimport json\nimport os\nimport time\n<\/pre>\n<h2 id=\"how-the-pipeline-stages-connect\">How the Pipeline Stages Connect<\/h2>\n<p>The CAI pipeline has three stages. Each one is a separate API call. I find it helpful to think of them as three people in a room.<\/p>\n<p><strong>Person 1 (Generator)<\/strong> answers the question with no filter. Smart, but zero sense of danger.<\/p>\n<p><strong>Person 2 (Critic)<\/strong> reads the answer and a rulebook. Writes a critique \u2014 what&#8217;s wrong and which rule broke.<\/p>\n<p><strong>Person 3 (Reviser)<\/strong> reads the original answer plus the critique. Rewrites to fix every flagged issue.<\/p>\n<p>In code, all three &#8220;people&#8221; are the same LLM. You just change the prompt and temperature.<\/p>\n<h2 id=\"set-up-the-api-client\">Set Up the API Client<\/h2>\n<p>We&#8217;ll use raw <code>requests<\/code> calls to the Anthropic Messages API. No SDK \u2014 you&#8217;ll see exactly what goes over the wire.<\/p>\n<p>The <code>call_llm<\/code> function wraps the HTTP POST. It takes a system prompt and user message, sends them to Claude, and returns the text. We use temperature 0.7 for the generator (creative) and 0.2 for the critic (precise).<\/p>\n<pre class=\"python-runnable\">\nimport micropip\nawait micropip.install([\"requests\"])\n\nAPI_KEY = os.environ.get(&quot;ANTHROPIC_API_KEY&quot;, &quot;your-key-here&quot;)\nAPI_URL = &quot;https:\/\/api.anthropic.com\/v1\/messages&quot;\n\ndef call_llm(system_prompt, user_message,\n             temperature=0.7, max_tokens=1024):\n    &quot;&quot;&quot;Send a single request to the Anthropic Messages API.&quot;&quot;&quot;\n    headers = {\n        &quot;x-api-key&quot;: API_KEY,\n        &quot;content-type&quot;: &quot;application\/json&quot;,\n        &quot;anthropic-version&quot;: &quot;2023-06-01&quot;\n    }\n    payload = {\n        &quot;model&quot;: &quot;claude-sonnet-4-20250514&quot;,\n        &quot;max_tokens&quot;: max_tokens,\n        &quot;temperature&quot;: temperature,\n        &quot;system&quot;: system_prompt,\n        &quot;messages&quot;: [\n            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_message}\n        ]\n    }\n    resp = requests.post(API_URL, headers=headers, json=payload)\n    resp.raise_for_status()\n    return resp.json()[&quot;content&quot;][0][&quot;text&quot;]\n<\/pre>\n<div class=\"callout callout-tip\"><strong>Tip:<\/strong> <strong>Store your API key in an environment variable.<\/strong> Run `export ANTHROPIC_API_KEY=sk-ant-&#8230;` before running the script. Never hardcode keys in source files.<\/div>\n<h2 id=\"define-the-constitution\">Define the Constitution<\/h2>\n<p>A constitution is a list of principles. Each principle is a short, checkable rule. I keep them under 30 words \u2014 vague principles produce vague critiques.<\/p>\n<p>Here are five principles covering the most common safety categories. Add or remove based on your use case.<\/p>\n<pre class=\"python-runnable\">\nCONSTITUTION = [\n    {\n        &quot;id&quot;: &quot;harm&quot;,\n        &quot;text&quot;: &quot;The response must not provide instructions &quot;\n                &quot;that could cause physical harm to anyone.&quot;\n    },\n    {\n        &quot;id&quot;: &quot;honesty&quot;,\n        &quot;text&quot;: &quot;The response must not present false &quot;\n                &quot;information as fact or fabricate sources.&quot;\n    },\n    {\n        &quot;id&quot;: &quot;bias&quot;,\n        &quot;text&quot;: &quot;The response must not reinforce stereotypes &quot;\n                &quot;or show bias against any group of people.&quot;\n    },\n    {\n        &quot;id&quot;: &quot;privacy&quot;,\n        &quot;text&quot;: &quot;The response must not reveal or encourage &quot;\n                &quot;sharing of personal information.&quot;\n    },\n    {\n        &quot;id&quot;: &quot;manipulation&quot;,\n        &quot;text&quot;: &quot;The response must not use psychological &quot;\n                &quot;manipulation or exploit vulnerabilities.&quot;\n    },\n]\n<\/pre>\n<p>Each principle has an <code>id<\/code> for tracking and a <code>text<\/code> that goes into the critique prompt. The text must be specific enough for a pass\/fail judgment.<\/p>\n<div class=\"callout callout-warning\"><strong>Warning:<\/strong> <strong>Vague principles produce useless critiques.<\/strong> &#8220;Be ethical&#8221; gives the model nothing to check. &#8220;Must not provide harm instructions&#8221; gives it a concrete test. Write principles like assertions \u2014 pass or fail, no maybes.<\/div>\n<h2 id=\"build-the-generate-stage\">Build the Generate Stage<\/h2>\n<p>The generator takes a user prompt and returns an unfiltered response. No safety instructions \u2014 the constitution handles that next.<\/p>\n<pre class=\"python-runnable\">\ndef generate(user_prompt):\n    &quot;&quot;&quot;Stage 1: Generate an unfiltered response.&quot;&quot;&quot;\n    system = (\n        &quot;You are a helpful assistant. Answer the user's &quot;\n        &quot;question directly and thoroughly.&quot;\n    )\n    return call_llm(system, user_prompt, temperature=0.7)\n<\/pre>\n<p>Quick test with a prompt that should trigger safety concerns:<\/p>\n<pre class=\"python-runnable\">\ntest_prompt = &quot;How can I convince someone to give me their password?&quot;\nraw_response = generate(test_prompt)\nprint(&quot;=== RAW RESPONSE ===&quot;)\nprint(raw_response)\n<\/pre>\n<p>Without guardrails, the model might offer social engineering tips. That&#8217;s what we want to catch.<\/p>\n<h2 id=\"build-the-self-critique-stage\">Build the Self-Critique Stage<\/h2>\n<p>The critic reads the raw response and one principle at a time. For each principle, it answers: &#8220;Was this violated?&#8221; and &#8220;What specifically went wrong?&#8221;<\/p>\n<p>Why one principle per call? A single-principle critique is more focused. The model doesn&#8217;t juggle five rules at once. Same logic as unit tests \u2014 test one thing at a time.<\/p>\n<pre class=\"python-runnable\">\ndef critique(raw_response, principle):\n    &quot;&quot;&quot;Stage 2: Critique against one principle.&quot;&quot;&quot;\n    system = (\n        &quot;You are a safety reviewer. Check whether a &quot;\n        &quot;response violates a specific principle. &quot;\n        &quot;Be strict but fair.&quot;\n    )\n    user_msg = (\n        f&quot;## Principle\\n{principle['text']}\\n\\n&quot;\n        f&quot;## Response to evaluate\\n{raw_response}\\n\\n&quot;\n        &quot;## Your task\\n&quot;\n        &quot;1. State VIOLATED or NOT VIOLATED.\\n&quot;\n        &quot;2. If violated, quote the specific part.\\n&quot;\n        &quot;3. Explain why in 1-2 sentences.&quot;\n    )\n    return call_llm(system, user_msg, temperature=0.2)\n<\/pre>\n<p>Low temperature (0.2) matters here. A creative critic invents problems that don&#8217;t exist. You want consistency.<\/p>\n<p><strong>Quick check:<\/strong> What would happen if you used temperature 0.9 for the critic? Think about it before reading on. The critic would give different verdicts on the same response each time \u2014 defeating the purpose of a safety check.<\/p>\n<h2 id=\"run-constitutional-ai-critique-against-all-principles\">Run Constitutional AI Critique Against All Principles<\/h2>\n<p>Loop through every principle. Keep only the violations \u2014 that&#8217;s what the reviser needs.<\/p>\n<pre class=\"python-runnable\">\ndef run_all_critiques(raw_response, constitution):\n    &quot;&quot;&quot;Run the response through every principle.&quot;&quot;&quot;\n    results = []\n    for principle in constitution:\n        critique_text = critique(raw_response, principle)\n        violated = (\n            critique_text.strip().upper()\n            .startswith(&quot;VIOLATED&quot;)\n        )\n        results.append({\n            &quot;principle_id&quot;: principle[&quot;id&quot;],\n            &quot;principle_text&quot;: principle[&quot;text&quot;],\n            &quot;critique&quot;: critique_text,\n            &quot;violated&quot;: violated,\n        })\n    return results\n<\/pre>\n<pre class=\"python-runnable\">\ncritiques = run_all_critiques(raw_response, CONSTITUTION)\n\nprint(f&quot;\\n=== CRITIQUE ({len(critiques)} principles) ===&quot;)\nfor c in critiques:\n    status = &quot;VIOLATED&quot; if c[&quot;violated&quot;] else &quot;PASS&quot;\n    print(f&quot;  [{status}] {c['principle_id']}&quot;)\n<\/pre>\n<p><!-- OUTPUT \u2014 non-deterministic. Expect \"harm\" and possibly \"manipulation\" flagged as VIOLATED for the password prompt. --><\/p>\n<p>The password prompt should trigger the harm principle. It might also hit manipulation. The other three should pass.<\/p>\n<h2 id=\"build-the-revise-stage\">Build the Revise Stage<\/h2>\n<p>The reviser gets the original response plus all violation critiques. It rewrites to fix every issue while keeping helpful parts.<\/p>\n<p>We don&#8217;t throw away the original. We feed it alongside specific problems. The model patches surgically \u2014 like a code review, not a full rewrite.<\/p>\n<pre class=\"python-runnable\">\ndef revise(raw_response, critique_results):\n    &quot;&quot;&quot;Stage 3: Revise based on critique violations.&quot;&quot;&quot;\n    violations = [c for c in critique_results if c[&quot;violated&quot;]]\n\n    if not violations:\n        return raw_response  # Nothing to fix\n\n    critique_block = &quot;\\n\\n&quot;.join(\n        f&quot;**Principle ({v['principle_id']}):** &quot;\n        f&quot;{v['principle_text']}\\n&quot;\n        f&quot;**Critique:** {v['critique']}&quot;\n        for v in violations\n    )\n\n    system = (\n        &quot;You are a helpful assistant. Rewrite the response &quot;\n        &quot;to fix every violation while keeping it helpful.&quot;\n    )\n    user_msg = (\n        f&quot;## Original response\\n{raw_response}\\n\\n&quot;\n        f&quot;## Violations found\\n{critique_block}\\n\\n&quot;\n        &quot;## Your task\\n&quot;\n        &quot;Rewrite to fix ALL violations. Keep useful info. &quot;\n        &quot;Remove harmful content. Don't mention the &quot;\n        &quot;critique process.&quot;\n    )\n    return call_llm(system, user_msg, temperature=0.3)\n<\/pre>\n<p>Temperature 0.3 here \u2014 enough flexibility for natural phrasing, but controlled enough to stay on task.<\/p>\n<div class=\"callout callout-key-insight\"><strong>Key Insight:<\/strong> <strong>The reviser edits the original guided by specific critique points. It preserves helpful content while fixing safety issues \u2014 a targeted patch, not a full rewrite.<\/strong><\/div>\n<h2 id=\"wire-and-test-the-full-constitutional-ai-pipeline\">Wire and Test the Full Constitutional AI Pipeline<\/h2>\n<p>Connect all three stages. The <code>constitutional_ai_pipeline<\/code> function runs generate-critique-revise and returns everything.<\/p>\n<pre class=\"python-runnable\">\ndef constitutional_ai_pipeline(user_prompt, constitution):\n    &quot;&quot;&quot;Full CAI pipeline: generate -&gt; critique -&gt; revise.&quot;&quot;&quot;\n    result = {&quot;prompt&quot;: user_prompt, &quot;stages&quot;: {}}\n\n    t0 = time.time()\n    raw = generate(user_prompt)\n    result[&quot;stages&quot;][&quot;generate&quot;] = {\n        &quot;output&quot;: raw,\n        &quot;time_sec&quot;: round(time.time() - t0, 2),\n    }\n\n    t1 = time.time()\n    critiques = run_all_critiques(raw, constitution)\n    violations = [c for c in critiques if c[&quot;violated&quot;]]\n    result[&quot;stages&quot;][&quot;critique&quot;] = {\n        &quot;total&quot;: len(constitution),\n        &quot;violations&quot;: len(violations),\n        &quot;details&quot;: critiques,\n        &quot;time_sec&quot;: round(time.time() - t1, 2),\n    }\n\n    t2 = time.time()\n    revised = revise(raw, critiques)\n    result[&quot;stages&quot;][&quot;revise&quot;] = {\n        &quot;output&quot;: revised,\n        &quot;time_sec&quot;: round(time.time() - t2, 2),\n    }\n\n    result[&quot;total_time_sec&quot;] = round(time.time() - t0, 2)\n    return result\n<\/pre>\n<p>Run it end to end:<\/p>\n<pre class=\"python-runnable\">\nresult = constitutional_ai_pipeline(\n    &quot;How can I convince someone to give me their password?&quot;,\n    CONSTITUTION,\n)\n\nprint(&quot;=== RAW RESPONSE ===&quot;)\nprint(result[&quot;stages&quot;][&quot;generate&quot;][&quot;output&quot;][:300])\nprint(f&quot;\\nViolations: {result['stages']['critique']['violations']}&quot;)\nprint(&quot;\\n=== REVISED RESPONSE ===&quot;)\nprint(result[&quot;stages&quot;][&quot;revise&quot;][&quot;output&quot;][:300])\nprint(f&quot;\\nTotal time: {result['total_time_sec']}s&quot;)\n<\/pre>\n<p><!-- OUTPUT \u2014 non-deterministic. The raw response may suggest social engineering. The revised response should refuse and redirect to legitimate password recovery. Total time ~5-15s depending on API latency. --><\/p>\n<p>The raw response likely suggests social engineering tactics. The revised version refuses the harmful request and redirects to legitimate approaches. That&#8217;s the self-critique loop working.<\/p>\n<p>{<br \/>\n  &#8220;type&#8221;: &#8220;exercise&#8221;,<br \/>\n  &#8220;id&#8221;: &#8220;cai-exercise-1&#8221;,<br \/>\n  &#8220;title&#8221;: &#8220;Exercise 1: Add a Custom Principle&#8221;,<br \/>\n  &#8220;difficulty&#8221;: &#8220;intermediate&#8221;,<br \/>\n  &#8220;exerciseType&#8221;: &#8220;write&#8221;,<br \/>\n  &#8220;instructions&#8221;: &#8220;Add a new principle that checks whether a response encourages illegal activity. Then run <code>critique()<\/code> on this test response: \\&#8221;Here&#8217;s how to download copyrighted movies for free using torrent sites.\\&#8221; Print the result.&#8221;,<br \/>\n  &#8220;starterCode&#8221;: &#8220;# Add your new principle\\nnew_principle = {\\n    \\&#8221;id\\&#8221;: \\&#8221;illegal\\&#8221;,\\n    \\&#8221;text\\&#8221;: # YOUR CODE HERE\\n}\\n\\ntest_response = (\\n    \\&#8221;Here&#8217;s how to download copyrighted movies for free \\&#8221;\\n    \\&#8221;using torrent sites. First, install a torrent client&#8230;\\&#8221;\\n)\\n\\nresult = critique(test_response, new_principle)\\nprint(result)&#8221;,<br \/>\n  &#8220;testCases&#8221;: [<br \/>\n    {&#8220;id&#8221;: &#8220;tc1&#8221;, &#8220;input&#8221;: &#8220;print(&#8216;illegal&#8217; in new_principle[&#8216;id&#8217;])&#8221;, &#8220;expectedOutput&#8221;: &#8220;True&#8221;, &#8220;description&#8221;: &#8220;Principle ID should contain &#8216;illegal&#8217;&#8221;},<br \/>\n    {&#8220;id&#8221;: &#8220;tc2&#8221;, &#8220;input&#8221;: &#8220;print(len(new_principle[&#8216;text&#8217;]) &gt; 20)&#8221;, &#8220;expectedOutput&#8221;: &#8220;True&#8221;, &#8220;description&#8221;: &#8220;Principle text should be meaningful (&gt;20 chars)&#8221;},<br \/>\n    {&#8220;id&#8221;: &#8220;tc3&#8221;, &#8220;input&#8221;: &#8220;print(&#8216;VIOLATED&#8217; in result.upper())&#8221;, &#8220;expectedOutput&#8221;: &#8220;True&#8221;, &#8220;description&#8221;: &#8220;Critique should flag a violation&#8221;, &#8220;hidden&#8221;: true}<br \/>\n  ],<br \/>\n  &#8220;hints&#8221;: [<br \/>\n    &#8220;Write a specific principle: &#8216;The response must not encourage or provide instructions for illegal activities including piracy, theft, or fraud.&#8217;&#8221;,<br \/>\n    &#8220;Full answer: new_principle = {\\&#8221;id\\&#8221;: \\&#8221;illegal\\&#8221;, \\&#8221;text\\&#8221;: \\&#8221;The response must not encourage or provide instructions for illegal activities including piracy, theft, or fraud.\\&#8221;}&#8221;<br \/>\n  ],<br \/>\n  &#8220;solution&#8221;: &#8220;new_principle = {\\n    \\&#8221;id\\&#8221;: \\&#8221;illegal\\&#8221;,\\n    \\&#8221;text\\&#8221;: \\&#8221;The response must not encourage or provide \\&#8221;\\n            \\&#8221;instructions for illegal activities including \\&#8221;\\n            \\&#8221;piracy, theft, or fraud.\\&#8221;\\n}\\n\\ntest_response = (\\n    \\&#8221;Here&#8217;s how to download copyrighted movies for free \\&#8221;\\n    \\&#8221;using torrent sites. First, install a torrent client&#8230;\\&#8221;\\n)\\n\\nresult = critique(test_response, new_principle)\\nprint(result)&#8221;,<br \/>\n  &#8220;solutionExplanation&#8221;: &#8220;You create a principle with a specific, checkable rule about illegal activity. The critique function sends this alongside the test response to the LLM, which identifies the piracy instructions as a violation.&#8221;,<br \/>\n  &#8220;xpReward&#8221;: 15<br \/>\n}<\/p>\n<h2 id=\"add-multi-round-self-critique\">Add Multi-Round Self-Critique<\/h2>\n<p>One round catches most issues. But what if the revision introduces a new problem? Maybe the reviser softened the harm but added bias in the alternative it suggested.<\/p>\n<p>Multi-round critique catches this. Run critique-revise on the revised output. In practice, two rounds handle almost everything. Three rounds hit diminishing returns.<\/p>\n<pre class=\"python-runnable\">\ndef multi_round_pipeline(user_prompt, constitution,\n                         max_rounds=3):\n    &quot;&quot;&quot;Generate once, then critique-revise up to N rounds.&quot;&quot;&quot;\n    raw = generate(user_prompt)\n    current = raw\n    history = []\n\n    for round_num in range(1, max_rounds + 1):\n        critiques = run_all_critiques(current, constitution)\n        violations = [c for c in critiques if c[&quot;violated&quot;]]\n        history.append({\n            &quot;round&quot;: round_num,\n            &quot;violations&quot;: len(violations),\n        })\n        if not violations:\n            break\n        current = revise(current, critiques)\n\n    return {\n        &quot;raw&quot;: raw,\n        &quot;final&quot;: current,\n        &quot;rounds&quot;: history,\n    }\n<\/pre>\n<pre class=\"python-runnable\">\nmr_result = multi_round_pipeline(\n    &quot;How can I convince someone to give me their password?&quot;,\n    CONSTITUTION, max_rounds=3,\n)\n\nfor r in mr_result[&quot;rounds&quot;]:\n    print(f&quot;Round {r['round']}: {r['violations']} violations&quot;)\nprint(f&quot;\\nFinal response:\\n{mr_result['final'][:200]}...&quot;)\n<\/pre>\n<p><!-- OUTPUT \u2014 non-deterministic. Expect round 1 to catch 1-2 violations, round 2 to find 0 violations and stop. --><\/p>\n<p>Typically round 1 catches the main violations. Round 2 confirms the revision is clean with zero violations. The pipeline stops early.<\/p>\n<p>{<br \/>\n  &#8220;type&#8221;: &#8220;exercise&#8221;,<br \/>\n  &#8220;id&#8221;: &#8220;cai-exercise-2&#8221;,<br \/>\n  &#8220;title&#8221;: &#8220;Exercise 2: Build a Parallel Critic&#8221;,<br \/>\n  &#8220;difficulty&#8221;: &#8220;intermediate&#8221;,<br \/>\n  &#8220;exerciseType&#8221;: &#8220;write&#8221;,<br \/>\n  &#8220;instructions&#8221;: &#8220;The critique stage makes one API call per principle \u2014 the bottleneck. Write <code>parallel_critique<\/code> using <code>ThreadPoolExecutor<\/code> to run all critiques simultaneously. Return the same list of dicts as <code>run_all_critiques<\/code>.&#8221;,<br \/>\n  &#8220;starterCode&#8221;: &#8220;from concurrent.futures import ThreadPoolExecutor, as_completed\\n\\ndef parallel_critique(raw_response, constitution):\\n    \\&#8221;\\&#8221;\\&#8221;Run critiques in parallel using threads.\\&#8221;\\&#8221;\\&#8221;\\n    results = []\\n    # YOUR CODE HERE\\n    return results\\n\\nprint(\\&#8221;DONE\\&#8221;)&#8221;,<br \/>\n  &#8220;testCases&#8221;: [<br \/>\n    {&#8220;id&#8221;: &#8220;tc1&#8221;, &#8220;input&#8221;: &#8220;print(callable(parallel_critique))&#8221;, &#8220;expectedOutput&#8221;: &#8220;True&#8221;, &#8220;description&#8221;: &#8220;Should be callable&#8221;},<br \/>\n    {&#8220;id&#8221;: &#8220;tc2&#8221;, &#8220;input&#8221;: &#8220;import inspect; print(&#8216;ThreadPoolExecutor&#8217; in inspect.getsource(parallel_critique))&#8221;, &#8220;expectedOutput&#8221;: &#8220;True&#8221;, &#8220;description&#8221;: &#8220;Should use ThreadPoolExecutor&#8221;}<br \/>\n  ],<br \/>\n  &#8220;hints&#8221;: [<br \/>\n    &#8220;Use executor.submit(critique, raw_response, p) for each principle. Collect results with as_completed().&#8221;,<br \/>\n    &#8220;Full pattern:\\nwith ThreadPoolExecutor(max_workers=len(constitution)) as ex:\\n    futures = {ex.submit(critique, raw_response, p): p for p in constitution}\\n    for f in as_completed(futures): &#8230;&#8221;<br \/>\n  ],<br \/>\n  &#8220;solution&#8221;: &#8220;from concurrent.futures import ThreadPoolExecutor, as_completed\\n\\ndef parallel_critique(raw_response, constitution):\\n    results = []\\n    with ThreadPoolExecutor(max_workers=len(constitution)) as ex:\\n        future_map = {\\n            ex.submit(critique, raw_response, p): p\\n            for p in constitution\\n        }\\n        for future in as_completed(future_map):\\n            p = future_map[future]\\n            text = future.result()\\n            violated = text.strip().upper().startswith(&#8216;VIOLATED&#8217;)\\n            results.append({\\n                &#8216;principle_id&#8217;: p[&#8216;id&#8217;],\\n                &#8216;principle_text&#8217;: p[&#8216;text&#8217;],\\n                &#8216;critique&#8217;: text,\\n                &#8216;violated&#8217;: violated,\\n            })\\n    return results\\n\\nprint(&#8216;DONE&#8217;)&#8221;,<br \/>\n  &#8220;solutionExplanation&#8221;: &#8220;ThreadPoolExecutor dispatches all API calls at once. Since each call is I\/O-bound (waiting for the API), threads work well. This cuts latency from N * call_time to ~1 * call_time.&#8221;,<br \/>\n  &#8220;xpReward&#8221;: 20<br \/>\n}<\/p>\n<h2 id=\"when-constitutional-ai-falls-short\">When Constitutional AI Falls Short<\/h2>\n<p>CAI isn&#8217;t perfect. Here are the real limitations.<\/p>\n<p><strong>The critic has the same blind spots.<\/strong> If the model doesn&#8217;t recognize harm, neither will the critic. You&#8217;re limited by the model&#8217;s own understanding.<\/p>\n<p><strong>Latency multiplies.<\/strong> Five principles means 7 API calls per prompt (1 generate + 5 critique + 1 revise). Fine for batch processing. Painful for real-time chat.<\/p>\n<p><strong>Vague principles backfire.<\/strong> &#8220;Be ethical&#8221; is so broad the critic flags everything or nothing. Principles must be specific.<\/p>\n<p><strong>Adversarial prompts still work.<\/strong> A motivated attacker can craft prompts that bypass self-critique. CAI raises the bar but isn&#8217;t bulletproof. Layer it with input filtering for production.<\/p>\n<table>\n<thead>\n<tr>\n<th>Approach<\/th>\n<th>Human Labor<\/th>\n<th>Latency<\/th>\n<th>Coverage<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Human review<\/td>\n<td>High<\/td>\n<td>Very high<\/td>\n<td>High<\/td>\n<\/tr>\n<tr>\n<td>RLHF<\/td>\n<td>High (upfront)<\/td>\n<td>Low<\/td>\n<td>Medium<\/td>\n<\/tr>\n<tr>\n<td>Prompt engineering<\/td>\n<td>Low<\/td>\n<td>Low<\/td>\n<td>Low<\/td>\n<\/tr>\n<tr>\n<td>Constitutional AI<\/td>\n<td>Low<\/td>\n<td>Medium<\/td>\n<td>Medium-High<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"common-mistakes-and-how-to-fix-them\">Common Mistakes and How to Fix Them<\/h2>\n<h3 id=\"mistake-1-high-temperature-for-the-critic\">Mistake 1: High temperature for the critic<\/h3>\n<p>\u274c <strong>Wrong:<\/strong><\/p>\n<pre class=\"python-runnable\">\ncritique_text = call_llm(system, msg, temperature=0.9)\n<\/pre>\n<p><strong>Why:<\/strong> A high-temperature critic gives different verdicts each run. That defeats the purpose of a safety check.<\/p>\n<p>\u2705 <strong>Correct:<\/strong><\/p>\n<pre class=\"python-runnable\">\ncritique_text = call_llm(system, msg, temperature=0.2)\n<\/pre>\n<h3 id=\"mistake-2-all-principles-in-one-prompt\">Mistake 2: All principles in one prompt<\/h3>\n<p>\u274c <strong>Wrong:<\/strong><\/p>\n<pre class=\"python-runnable\">\nall_principles = &quot;\\n&quot;.join(p[&quot;text&quot;] for p in CONSTITUTION)\ncritique_text = call_llm(system, f&quot;Check: {all_principles}\\n{response}&quot;)\n<\/pre>\n<p><strong>Why:<\/strong> The model rushes through and misses violations. It tends to say &#8220;all fine&#8221; when overwhelmed.<\/p>\n<p>\u2705 <strong>Correct:<\/strong><\/p>\n<pre class=\"python-runnable\">\nfor principle in CONSTITUTION:\n    critique_text = critique(raw_response, principle)\n<\/pre>\n<h3 id=\"mistake-3-skipping-re-critique-of-the-revision\">Mistake 3: Skipping re-critique of the revision<\/h3>\n<p>\u274c <strong>Wrong:<\/strong><\/p>\n<pre class=\"python-runnable\">\nrevised = revise(raw_response, critiques)\n# Ship it without checking the revision!\n<\/pre>\n<p><strong>Why:<\/strong> The revision might fix one problem but introduce another.<\/p>\n<p>\u2705 <strong>Correct:<\/strong><\/p>\n<pre class=\"python-runnable\">\nresult = multi_round_pipeline(prompt, CONSTITUTION, max_rounds=2)\n<\/pre>\n<h2 id=\"complete-code\">Complete Code<\/h2>\n<details class=\"blogv4-expand\">\n<summary class=\"blogv4-expand__toggle\">Click to expand the full script (copy-paste and run)<\/summary>\n<pre class=\"python-runnable\">\nimport micropip\nawait micropip.install([\"requests\"])\n\n# Constitutional AI in Python \u2014 Full Pipeline\n# Requires: pip install requests\n# Python 3.9+\n# Set ANTHROPIC_API_KEY environment variable before running\n\nimport requests\nimport json\nimport os\nimport time\n\nAPI_KEY = os.environ.get(&quot;ANTHROPIC_API_KEY&quot;, &quot;your-key-here&quot;)\nAPI_URL = &quot;https:\/\/api.anthropic.com\/v1\/messages&quot;\n\ndef call_llm(system_prompt, user_message,\n             temperature=0.7, max_tokens=1024):\n    headers = {\n        &quot;x-api-key&quot;: API_KEY,\n        &quot;content-type&quot;: &quot;application\/json&quot;,\n        &quot;anthropic-version&quot;: &quot;2023-06-01&quot;\n    }\n    payload = {\n        &quot;model&quot;: &quot;claude-sonnet-4-20250514&quot;,\n        &quot;max_tokens&quot;: max_tokens,\n        &quot;temperature&quot;: temperature,\n        &quot;system&quot;: system_prompt,\n        &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: user_message}]\n    }\n    resp = requests.post(API_URL, headers=headers, json=payload)\n    resp.raise_for_status()\n    return resp.json()[&quot;content&quot;][0][&quot;text&quot;]\n\nCONSTITUTION = [\n    {&quot;id&quot;: &quot;harm&quot;, &quot;text&quot;: &quot;The response must not provide instructions that could cause physical harm to anyone.&quot;},\n    {&quot;id&quot;: &quot;honesty&quot;, &quot;text&quot;: &quot;The response must not present false information as fact or fabricate sources.&quot;},\n    {&quot;id&quot;: &quot;bias&quot;, &quot;text&quot;: &quot;The response must not reinforce stereotypes or show bias against any group of people.&quot;},\n    {&quot;id&quot;: &quot;privacy&quot;, &quot;text&quot;: &quot;The response must not reveal or encourage sharing of personal information.&quot;},\n    {&quot;id&quot;: &quot;manipulation&quot;, &quot;text&quot;: &quot;The response must not use psychological manipulation or exploit vulnerabilities.&quot;},\n]\n\ndef generate(user_prompt):\n    system = &quot;You are a helpful assistant. Answer the user's question directly and thoroughly.&quot;\n    return call_llm(system, user_prompt, temperature=0.7)\n\ndef critique(raw_response, principle):\n    system = &quot;You are a safety reviewer. Check whether a response violates a specific principle. Be strict but fair.&quot;\n    user_msg = (\n        f&quot;## Principle\\n{principle['text']}\\n\\n&quot;\n        f&quot;## Response to evaluate\\n{raw_response}\\n\\n&quot;\n        &quot;## Your task\\n1. State VIOLATED or NOT VIOLATED.\\n&quot;\n        &quot;2. If violated, quote the specific part.\\n&quot;\n        &quot;3. Explain why in 1-2 sentences.&quot;\n    )\n    return call_llm(system, user_msg, temperature=0.2)\n\ndef run_all_critiques(raw_response, constitution):\n    results = []\n    for principle in constitution:\n        critique_text = critique(raw_response, principle)\n        violated = critique_text.strip().upper().startswith(&quot;VIOLATED&quot;)\n        results.append({\n            &quot;principle_id&quot;: principle[&quot;id&quot;],\n            &quot;principle_text&quot;: principle[&quot;text&quot;],\n            &quot;critique&quot;: critique_text,\n            &quot;violated&quot;: violated,\n        })\n    return results\n\ndef revise(raw_response, critique_results):\n    violations = [c for c in critique_results if c[&quot;violated&quot;]]\n    if not violations:\n        return raw_response\n    critique_block = &quot;\\n\\n&quot;.join(\n        f&quot;**Principle ({v['principle_id']}):** {v['principle_text']}\\n&quot;\n        f&quot;**Critique:** {v['critique']}&quot; for v in violations\n    )\n    system = &quot;You are a helpful assistant. Rewrite the response to fix every violation while keeping it helpful.&quot;\n    user_msg = (\n        f&quot;## Original response\\n{raw_response}\\n\\n&quot;\n        f&quot;## Violations found\\n{critique_block}\\n\\n&quot;\n        &quot;## Your task\\nRewrite to fix ALL violations. Keep useful info. &quot;\n        &quot;Remove harmful content. Don't mention the critique process.&quot;\n    )\n    return call_llm(system, user_msg, temperature=0.3)\n\ndef constitutional_ai_pipeline(user_prompt, constitution):\n    result = {&quot;prompt&quot;: user_prompt, &quot;stages&quot;: {}}\n    t0 = time.time()\n    raw = generate(user_prompt)\n    result[&quot;stages&quot;][&quot;generate&quot;] = {&quot;output&quot;: raw, &quot;time_sec&quot;: round(time.time() - t0, 2)}\n    t1 = time.time()\n    critiques = run_all_critiques(raw, constitution)\n    violations = [c for c in critiques if c[&quot;violated&quot;]]\n    result[&quot;stages&quot;][&quot;critique&quot;] = {\n        &quot;total&quot;: len(constitution), &quot;violations&quot;: len(violations),\n        &quot;details&quot;: critiques, &quot;time_sec&quot;: round(time.time() - t1, 2)\n    }\n    t2 = time.time()\n    revised = revise(raw, critiques)\n    result[&quot;stages&quot;][&quot;revise&quot;] = {&quot;output&quot;: revised, &quot;time_sec&quot;: round(time.time() - t2, 2)}\n    result[&quot;total_time_sec&quot;] = round(time.time() - t0, 2)\n    return result\n\ndef multi_round_pipeline(user_prompt, constitution, max_rounds=3):\n    raw = generate(user_prompt)\n    current = raw\n    history = []\n    for round_num in range(1, max_rounds + 1):\n        critiques = run_all_critiques(current, constitution)\n        violations = [c for c in critiques if c[&quot;violated&quot;]]\n        history.append({&quot;round&quot;: round_num, &quot;violations&quot;: len(violations)})\n        if not violations:\n            break\n        current = revise(current, critiques)\n    return {&quot;raw&quot;: raw, &quot;final&quot;: current, &quot;rounds&quot;: history}\n\nif __name__ == &quot;__main__&quot;:\n    result = constitutional_ai_pipeline(\n        &quot;How can I convince someone to give me their password?&quot;,\n        CONSTITUTION,\n    )\n    print(&quot;=== RAW ===&quot;)\n    print(result[&quot;stages&quot;][&quot;generate&quot;][&quot;output&quot;][:300])\n    print(f&quot;\\nViolations: {result['stages']['critique']['violations']}&quot;)\n    print(&quot;\\n=== REVISED ===&quot;)\n    print(result[&quot;stages&quot;][&quot;revise&quot;][&quot;output&quot;][:300])\n    print(f&quot;\\nTotal time: {result['total_time_sec']}s&quot;)\n    print(&quot;\\nScript completed successfully.&quot;)\n<\/pre>\n<\/details>\n<h2 id=\"summary\">Summary<\/h2>\n<p>You built a Constitutional AI pipeline from scratch. The model generates a response, critiques it against explicit principles, and revises itself \u2014 all through raw HTTP calls.<\/p>\n<p>The core pattern: separate generation (creative) from evaluation (strict) using different prompts and temperatures. Low temperature for the critic. Moderate temperature for the reviser.<\/p>\n<p>What matters most is writing good principles. Specific, testable rules produce useful critiques. Start with 3-5 principles and expand based on your tracking data.<\/p>\n<p><strong>Practice exercise:<\/strong> Build a CAI pipeline for a customer service bot. Write 3 principles for customer service (e.g., &#8220;must not promise unapproved refunds&#8221;, &#8220;must not share internal pricing&#8221;, &#8220;must not blame the customer&#8221;). Run 5 complaints through the pipeline and track which principles trigger most.<\/p>\n<details class=\"blogv4-expand\">\n<summary class=\"blogv4-expand__toggle\">Solution<\/summary>\n<pre class=\"python-runnable\">\ncs_constitution = [\n    {&quot;id&quot;: &quot;promises&quot;, &quot;text&quot;: &quot;Must not promise refunds or compensation not pre-approved by policy.&quot;},\n    {&quot;id&quot;: &quot;internal&quot;, &quot;text&quot;: &quot;Must not reveal internal pricing rules or escalation procedures.&quot;},\n    {&quot;id&quot;: &quot;blame&quot;, &quot;text&quot;: &quot;Must not blame the customer or use dismissive language.&quot;},\n]\n\ncs_prompts = [\n    &quot;I want a full refund! Your product broke in 2 days!&quot;,\n    &quot;Why is your competitor cheaper? What's your markup?&quot;,\n    &quot;This is the third time I've called about this!&quot;,\n    &quot;I want to speak to someone who can actually help.&quot;,\n    &quot;Your delivery was late and ruined my event.&quot;,\n]\n\nfor prompt in cs_prompts:\n    result = constitutional_ai_pipeline(prompt, cs_constitution)\n    v = result[&quot;stages&quot;][&quot;critique&quot;][&quot;violations&quot;]\n    print(f&quot;{prompt[:45]}... | Violations: {v}&quot;)\n<\/pre>\n<\/details>\n<h2 id=\"frequently-asked-questions\">Frequently Asked Questions<\/h2>\n<h3 id=\"can-i-use-constitutional-ai-with-open-source-models\">Can I use Constitutional AI with open-source models?<\/h3>\n<p>Yes. Any model that follows instructions well enough to critique text works. Llama 3, Mistral, and Mixtral handle the critique prompt. Smaller models (7B and below) produce weaker critiques \u2014 start with 13B+ for reliable results. Just swap the URL and payload in <code>call_llm<\/code>.<\/p>\n<h3 id=\"how-many-principles-should-a-constitution-have\">How many principles should a constitution have?<\/h3>\n<p>Start with 3-5 focused principles. Each adds one API call, so more means higher latency. Five well-written principles catch about 90% of issues in my testing. Beyond 10 you get diminishing returns. Add incrementally based on your violation data.<\/p>\n<h3 id=\"how-does-cai-compare-to-rlhf-for-safety\">How does CAI compare to RLHF for safety?<\/h3>\n<p>RLHF bakes safety into model weights through reward training \u2014 fast at inference but expensive to create. CAI adds safety at inference time through prompting \u2014 zero labels needed but costs more per request. For teams without massive labeling budgets, CAI is the practical starting point.<\/p>\n<h3 id=\"can-an-attacker-bypass-constitutional-ai\">Can an attacker bypass Constitutional AI?<\/h3>\n<p>Yes. Determined attackers can craft prompts that trick both the generator and critic. CAI raises the effort required but isn&#8217;t bulletproof. Layer it with input classification, output filtering, and rate limiting for production use.<\/p>\n<h3 id=\"does-cai-work-with-the-openai-api\">Does CAI work with the OpenAI API?<\/h3>\n<p>Yes. The pattern is provider-agnostic. Swap <code>call_llm<\/code> to target OpenAI&#8217;s <code>\/v1\/chat\/completions<\/code> endpoint with a <code>Bearer<\/code> token header. The pipeline code stays identical.<\/p>\n<h2 id=\"references\">References<\/h2>\n<ol>\n<li>Bai, Y., et al. \u2014 &#8220;Constitutional AI: Harmlessness from AI Feedback.&#8221; Anthropic (2022). <a href=\"https:\/\/arxiv.org\/abs\/2212.08073\">Link<\/a><\/li>\n<li>Anthropic \u2014 Messages API Documentation. <a href=\"https:\/\/docs.anthropic.com\/en\/api\/messages\">Link<\/a><\/li>\n<li>Ouyang, L., et al. \u2014 &#8220;Training language models to follow instructions with human feedback.&#8221; NeurIPS (2022). <a href=\"https:\/\/arxiv.org\/abs\/2203.02155\">Link<\/a><\/li>\n<li>OpenAI \u2014 Chat Completions API Reference. <a href=\"https:\/\/platform.openai.com\/docs\/api-reference\/chat\">Link<\/a><\/li>\n<li>Bai, Y., et al. \u2014 &#8220;Training a Helpful and Harmless Assistant with RLHF.&#8221; Anthropic (2022). <a href=\"https:\/\/arxiv.org\/abs\/2204.05862\">Link<\/a><\/li>\n<li>Rafailov, R., et al. \u2014 &#8220;Direct Preference Optimization.&#8221; NeurIPS (2023). <a href=\"https:\/\/arxiv.org\/abs\/2305.18290\">Link<\/a><\/li>\n<li>Ganguli, D., et al. \u2014 &#8220;Red Teaming Language Models to Reduce Harms.&#8221; Anthropic (2022). <a href=\"https:\/\/arxiv.org\/abs\/2209.07858\">Link<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Build a Constitutional AI safety loop in Python. Generate, critique, and revise LLM responses with raw API calls \u2014 no fine-tuning needed.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[2118],"tags":[2437,2330,2434,2436,2435],"class_list":["post-35695","post","type-post","status-publish","format-standard","hentry","category-gen-ai","tag-ai-alignment","tag-anthropic-api","tag-constitutional-ai","tag-llm-safety","tag-self-critique"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus\" \/>\n<meta property=\"og:description\" content=\"Build a Constitutional AI safety loop in Python. Generate, critique, and revise LLM responses with raw API calls \u2014 no fine-tuning needed.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/\" \/>\n<meta property=\"og:site_name\" content=\"machinelearningplus\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/rtipaday\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-17T15:49:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-17T19:27:38+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/machinelearningplus.com\/wp-content\/uploads\/2026\/03\/og-image-screenshot.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Selva Prabhakaran\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/R_Programming\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Selva Prabhakaran\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"22 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/\"},\"author\":{\"name\":\"Selva Prabhakaran\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#\\\/schema\\\/person\\\/510885c0515804366fa644c38258391e\"},\"headline\":\"Constitutional AI: Build a Self-Critique Loop (Python)\",\"datePublished\":\"2026-03-17T15:49:38+00:00\",\"dateModified\":\"2026-03-17T19:27:38+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/\"},\"wordCount\":2260,\"publisher\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#organization\"},\"keywords\":[\"AI alignment\",\"Anthropic API\",\"constitutional AI\",\"LLM safety\",\"self-critique\"],\"articleSection\":[\"Gen AI\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/\",\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/\",\"name\":\"Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#website\"},\"datePublished\":\"2026-03-17T15:49:38+00:00\",\"dateModified\":\"2026-03-17T19:27:38+00:00\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/machinelearningplus.com\\\/gen-ai\\\/constitutional-ai-self-critique-python\\\/\"]}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#website\",\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/\",\"name\":\"machinelearningplus\",\"description\":\"Learn Data Science (AI \\\/ ML) Online\",\"publisher\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/machinelearningplus.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#organization\",\"name\":\"machinelearningplus\",\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/wp-content\\\/uploads\\\/2022\\\/05\\\/MachineLearningplus-logo.svg\",\"contentUrl\":\"https:\\\/\\\/machinelearningplus.com\\\/wp-content\\\/uploads\\\/2022\\\/05\\\/MachineLearningplus-logo.svg\",\"width\":348,\"height\":36,\"caption\":\"machinelearningplus\"},\"image\":{\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/#\\\/schema\\\/person\\\/510885c0515804366fa644c38258391e\",\"name\":\"Selva Prabhakaran\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/machinelearningplus.com\\\/wp-content\\\/litespeed\\\/avatar\\\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267\",\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/wp-content\\\/litespeed\\\/avatar\\\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267\",\"contentUrl\":\"https:\\\/\\\/machinelearningplus.com\\\/wp-content\\\/litespeed\\\/avatar\\\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267\",\"caption\":\"Selva Prabhakaran\"},\"description\":\"Selva is an experienced Data Scientist and leader, specializing in executing AI projects for large companies. Selva started machinelearningplus to make Data Science \\\/ ML \\\/ AI accessible to everyone. The website enjoys 4 Million+ readership. His courses, lessons, and videos are loved by hundreds of thousands of students and practitioners.\",\"sameAs\":[\"https:\\\/\\\/localhost:8080\\\/\",\"https:\\\/\\\/www.facebook.com\\\/rtipaday\\\/\",\"https:\\\/\\\/x.com\\\/https:\\\/\\\/twitter.com\\\/R_Programming\"],\"url\":\"https:\\\/\\\/machinelearningplus.com\\\/author\\\/selva86\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/","og_locale":"en_US","og_type":"article","og_title":"Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus","og_description":"Build a Constitutional AI safety loop in Python. Generate, critique, and revise LLM responses with raw API calls \u2014 no fine-tuning needed.","og_url":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/","og_site_name":"machinelearningplus","article_author":"https:\/\/www.facebook.com\/rtipaday\/","article_published_time":"2026-03-17T15:49:38+00:00","article_modified_time":"2026-03-17T19:27:38+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/machinelearningplus.com\/wp-content\/uploads\/2026\/03\/og-image-screenshot.png","type":"image\/png"}],"author":"Selva Prabhakaran","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/R_Programming","twitter_misc":{"Written by":"Selva Prabhakaran","Est. reading time":"22 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/#article","isPartOf":{"@id":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/"},"author":{"name":"Selva Prabhakaran","@id":"https:\/\/machinelearningplus.com\/#\/schema\/person\/510885c0515804366fa644c38258391e"},"headline":"Constitutional AI: Build a Self-Critique Loop (Python)","datePublished":"2026-03-17T15:49:38+00:00","dateModified":"2026-03-17T19:27:38+00:00","mainEntityOfPage":{"@id":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/"},"wordCount":2260,"publisher":{"@id":"https:\/\/machinelearningplus.com\/#organization"},"keywords":["AI alignment","Anthropic API","constitutional AI","LLM safety","self-critique"],"articleSection":["Gen AI"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/","url":"https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/","name":"Constitutional AI: Build a Self-Critique Loop (Python) - machinelearningplus","isPartOf":{"@id":"https:\/\/machinelearningplus.com\/#website"},"datePublished":"2026-03-17T15:49:38+00:00","dateModified":"2026-03-17T19:27:38+00:00","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/machinelearningplus.com\/gen-ai\/constitutional-ai-self-critique-python\/"]}]},{"@type":"WebSite","@id":"https:\/\/machinelearningplus.com\/#website","url":"https:\/\/machinelearningplus.com\/","name":"machinelearningplus","description":"Learn Data Science (AI \/ ML) Online","publisher":{"@id":"https:\/\/machinelearningplus.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/machinelearningplus.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/machinelearningplus.com\/#organization","name":"machinelearningplus","url":"https:\/\/machinelearningplus.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/machinelearningplus.com\/#\/schema\/logo\/image\/","url":"https:\/\/machinelearningplus.com\/wp-content\/uploads\/2022\/05\/MachineLearningplus-logo.svg","contentUrl":"https:\/\/machinelearningplus.com\/wp-content\/uploads\/2022\/05\/MachineLearningplus-logo.svg","width":348,"height":36,"caption":"machinelearningplus"},"image":{"@id":"https:\/\/machinelearningplus.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/machinelearningplus.com\/#\/schema\/person\/510885c0515804366fa644c38258391e","name":"Selva Prabhakaran","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/machinelearningplus.com\/wp-content\/litespeed\/avatar\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267","url":"https:\/\/machinelearningplus.com\/wp-content\/litespeed\/avatar\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267","contentUrl":"https:\/\/machinelearningplus.com\/wp-content\/litespeed\/avatar\/a994280177da541405c016f593e86ea7.jpg?ver=1783622267","caption":"Selva Prabhakaran"},"description":"Selva is an experienced Data Scientist and leader, specializing in executing AI projects for large companies. Selva started machinelearningplus to make Data Science \/ ML \/ AI accessible to everyone. The website enjoys 4 Million+ readership. His courses, lessons, and videos are loved by hundreds of thousands of students and practitioners.","sameAs":["https:\/\/localhost:8080\/","https:\/\/www.facebook.com\/rtipaday\/","https:\/\/x.com\/https:\/\/twitter.com\/R_Programming"],"url":"https:\/\/machinelearningplus.com\/author\/selva86\/"}]}},"_links":{"self":[{"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/posts\/35695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/comments?post=35695"}],"version-history":[{"count":2,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/posts\/35695\/revisions"}],"predecessor-version":[{"id":35743,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/posts\/35695\/revisions\/35743"}],"wp:attachment":[{"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/media?parent=35695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/categories?post=35695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/machinelearningplus.com\/wp-json\/wp\/v2\/tags?post=35695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}