<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lfzds4399-cpu</title>
    <description>The latest articles on DEV Community by lfzds4399-cpu (@lfzds4399cpu).</description>
    <link>https://dev.to/lfzds4399cpu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3922708%2F04063e89-b2a1-415e-8d87-e57ef10f5e39.png</url>
      <title>DEV Community: lfzds4399-cpu</title>
      <link>https://dev.to/lfzds4399cpu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lfzds4399cpu"/>
    <language>en</language>
    <item>
      <title>Automated domain investing with hard budget walls and an AI council that has to agree before any money moves</title>
      <dc:creator>lfzds4399-cpu</dc:creator>
      <pubDate>Sat, 16 May 2026 11:27:13 +0000</pubDate>
      <link>https://dev.to/lfzds4399cpu/automated-domain-investing-with-hard-budget-walls-and-an-ai-council-that-has-to-agree-before-any-n9o</link>
      <guid>https://dev.to/lfzds4399cpu/automated-domain-investing-with-hard-budget-walls-and-an-ai-council-that-has-to-agree-before-any-n9o</guid>
      <description>&lt;p&gt;Most "domain finders" on GitHub stop at stage one — they spit out a list of names someone might want to buy. The real work is everything after that: deciding which ones are worth money, not buying the obvious trademark traps, not blowing the monthly budget on a hype day, and actually settling a sale.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/lfzds4399-cpu/domain-harness" rel="noopener noreferrer"&gt;domain-harness&lt;/a&gt; because I wanted to test a small thesis — "AI-generated brandable domains + automated valuation can clear $X / month" — without giving a script direct keys to a registrar and praying.&lt;/p&gt;

&lt;p&gt;This post is about the architecture, not the thesis. Specifically: how do you build something that touches money and registrar APIs &lt;strong&gt;without losing money the moment the LLM hallucinates?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;discover  ─►  value  ─►  acquire  ─►  list  ─►  negotiate  ─►  settle
   │           │           │           │           │
   AI gen +    local +     multi-     Dan /        AI reply
   expired     AI Council  registrar   Afternic    + counter
   feeds       (Claude     fallback   Sedo
               + DeepSeek)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six stages, all gateable individually, all &lt;code&gt;dry_run&lt;/code&gt; by default. The thing that makes it safe is what's between the stages, not the stages themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense layer 1: two-tier valuation
&lt;/h2&gt;

&lt;p&gt;Calling an LLM to value every candidate domain is how you waste $50 of tokens on garbage by 11pm. Local heuristic gate first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Length penalty over 12 chars&lt;/li&gt;
&lt;li&gt;Hard-cap punctuation / digit counts&lt;/li&gt;
&lt;li&gt;Dictionary-word boost&lt;/li&gt;
&lt;li&gt;Brandable phonotactics scoring (CVC patterns score better than &lt;code&gt;xqkz&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Hard reject on hyphens-and-numbers cocktails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only candidates above &lt;code&gt;min_local_score&lt;/code&gt; get billed against the AI Council. In practice this filters about 90% of the discovery output, and the surviving 10% is the only thing worth a token spend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense layer 2: AI Council, not AI Oracle
&lt;/h2&gt;

&lt;p&gt;Every survivor goes to &lt;strong&gt;two independent providers&lt;/strong&gt; — Claude and DeepSeek — with the same prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;council&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Council&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;claude_valuator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deepseek_valuator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# both must clear per-provider threshold
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both must clear their threshold before the harness will spend a cent. One model alone is a hallucination machine; two independent models with the same hallucination at the same time is much rarer (and when it does happen, your loss is capped by layer 3 anyway).&lt;/p&gt;

&lt;p&gt;Caveat I learned the hard way in a &lt;em&gt;different&lt;/em&gt; project: &lt;strong&gt;LLM scores are not alpha.&lt;/strong&gt; In a trading harness I weighted council confidence directly into position size and the high-confidence picks systematically lost money. So in domain-harness the council is a &lt;strong&gt;gate&lt;/strong&gt;, not a &lt;strong&gt;size multiplier&lt;/strong&gt;. It can say "yes / no, the domain is worth it" — it cannot say "buy 5x more of this one."&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense layer 3: hard budget walls
&lt;/h2&gt;

&lt;p&gt;This is the part that lets me sleep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;daily_cap_usd&lt;/code&gt;: $50&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monthly_cap_usd&lt;/code&gt;: $1000&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;per_domain_cap_usd&lt;/code&gt;: $15&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't soft warnings. Every &lt;code&gt;acquisition.buy()&lt;/code&gt; call goes through &lt;code&gt;budget_guard.check()&lt;/code&gt; and an over-cap call &lt;strong&gt;raises &lt;code&gt;BudgetExceeded&lt;/code&gt;&lt;/strong&gt;. The buyer literally cannot exceed them. If any single wall trips, the discovery loop stops and the daemon goes idle until a human re-enables it.&lt;/p&gt;

&lt;p&gt;I keep a state file &lt;code&gt;data/budget_state.json&lt;/code&gt; with today's spend and this month's spend. The CI smoke test verifies every guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[5] 预算守卫硬刹车   ✓ 单域名超过返回拦截（$200 &amp;gt; $15）
                  ✓ 正常金额通过（$10）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defense layer 4: trademark + WHOIS
&lt;/h2&gt;

&lt;p&gt;A surprising fraction of "great brandable domain" suggestions from LLMs are subtle trademark violations. &lt;code&gt;mygoogle.com&lt;/code&gt; is obvious; &lt;code&gt;paypall.io&lt;/code&gt; is less obvious; &lt;code&gt;openai-clone.ai&lt;/code&gt; is comically obvious but the council will still rank it because it scores "ai".&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static blocklist (Fortune 500 + Anthropic / OpenAI / Stripe / etc).&lt;/li&gt;
&lt;li&gt;User-extensible blocklist.&lt;/li&gt;
&lt;li&gt;Substring match against the blocklist (so &lt;code&gt;mygoogle.com&lt;/code&gt; and &lt;code&gt;openai-clone.ai&lt;/code&gt; fail).&lt;/li&gt;
&lt;li&gt;WHOIS check before any buy attempt (some "available" feeds lie).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Edit-distance / typo-variant matching is on the roadmap — &lt;code&gt;goggle.com&lt;/code&gt; currently slips past unless you add it explicitly.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[3] 商标黑名单     ✓ mygoogle.com 拦截
                  ✓ openai-clone.ai 拦截
                  ✓ paywall.com 通过（非商标）
[4] WHOIS 检查    ✓ 注册过的域名识别（google.com）
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defense layer 5: multi-registrar fallback
&lt;/h2&gt;

&lt;p&gt;When the harness decides to buy, it doesn't fail-stop on a single registrar's API hiccup. Priority chain: &lt;strong&gt;Porkbun → Cloudflare&lt;/strong&gt;. If Porkbun returns "unavailable" or 5xx, Cloudflare tries. If both fail, the candidate goes to a re-try queue, not a buy.&lt;/p&gt;

&lt;p&gt;Each registrar is a thin adapter — same &lt;code&gt;register(domain, contact)&lt;/code&gt; interface, different SDK underneath. The adapter shape is deliberately simple so adding Namecheap or GoDaddy later is a single new file, not a refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense layer 6: dry_run by default, opt-in spend
&lt;/h2&gt;

&lt;p&gt;The default mode in &lt;code&gt;.env.example&lt;/code&gt; is &lt;code&gt;MODE=dry_run&lt;/code&gt;. Every stage knows the mode and short-circuits actual money calls when it's &lt;code&gt;dry_run&lt;/code&gt;. The full pipeline runs end-to-end in &lt;code&gt;dry_run&lt;/code&gt; so I can backtest discovery → valuation → would-have-bought against a real registrar SDK without paying. To flip to live: change one line, re-read the smoke test results, then change it.&lt;/p&gt;

&lt;p&gt;CI runs the 25-case smoke on every push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;汇总：25 PASS / 0 FAIL/ERROR / 共 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That suite covers every gate — discovery → valuation → trademark → WHOIS → budget guard → duplicate guard → registration → portfolio write → listing → AI negotiation → settle. Failing any one of them blocks merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd build differently if starting over
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster registrar latency profiling.&lt;/strong&gt; Currently the fallback is static priority. A version 2 should A/B by recent success rate and latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-vertical AI prompts.&lt;/strong&gt; The council right now is generic; "this is a fintech domain" vs "this is a SaaS domain" probably wants different valuation framing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negotiation memory.&lt;/strong&gt; The reply bot is stateless per inquiry; same buyer asking three times gets three independent counters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When this is the wrong tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You want to register vanity domains for personal projects. This is overkill.&lt;/li&gt;
&lt;li&gt;You're already buying via a managed broker. They handle the registrar / settle layers.&lt;/li&gt;
&lt;li&gt;You don't want any LLM cost. The council is the heart of the value layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When this is the right tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You want a budget-walled testbed for a domain investing thesis.&lt;/li&gt;
&lt;li&gt;You're tired of "I clicked buy and it took $50 because the dropdown defaulted to 5 years."&lt;/li&gt;
&lt;li&gt;You want a CI-gated pipeline where every buy decision is auditable and reversible (well — except for the buy, but the &lt;strong&gt;decision&lt;/strong&gt; is).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lfzds4399-cpu/domain-harness.git
&lt;span class="nb"&gt;cd &lt;/span&gt;domain-harness
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env       &lt;span class="c"&gt;# fill in only what you use&lt;/span&gt;
python tests/e2e_smoke.py   &lt;span class="c"&gt;# 25 PASS in dry_run, no spend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;a href="https://github.com/lfzds4399-cpu/domain-harness" rel="noopener noreferrer"&gt;github.com/lfzds4399-cpu/domain-harness&lt;/a&gt;. MIT.&lt;/p&gt;

&lt;p&gt;If you're building anything that touches money + LLMs, I'd love to compare notes on which guards you ended up needing vs which ones you removed because they fired too often. Issues / PRs welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Stop reinventing 'ask GPT-4 and Claude and a regex, then count the votes'</title>
      <dc:creator>lfzds4399-cpu</dc:creator>
      <pubDate>Sat, 16 May 2026 11:27:12 +0000</pubDate>
      <link>https://dev.to/lfzds4399cpu/stop-reinventing-ask-gpt-4-and-claude-and-a-regex-then-count-the-votes-5fkd</link>
      <guid>https://dev.to/lfzds4399cpu/stop-reinventing-ask-gpt-4-and-claude-and-a-regex-then-count-the-votes-5fkd</guid>
      <description>&lt;p&gt;If you've ever wired up "ask GPT-4 and Claude and a regex, then count the votes" inside a moderation pipeline, an agent router, or a code-review bot — you've written this code before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;gpt_ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_gpt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;claude_ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_claude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;regex_ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;contains_blocklist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt_ok&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;claude_ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gpt_ok&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;regex_ok&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;APPROVE&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;REJECT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then someone asks "what about weighting the senior model 2x?" and the if-tree doubles. Then "what if regex sees a hard policy violation — that should veto everything?" and you bolt on another branch. Then the audit log is a &lt;code&gt;print()&lt;/code&gt; you forgot to wire to a file.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/lfzds4399-cpu/ai-council" rel="noopener noreferrer"&gt;ai-council&lt;/a&gt; because I've written this glue four times across four projects (trading bot, moderation layer, code-review CI, agent router) and the fifth time I gave up and made it a primitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole API in 10 lines
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ai_council&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Council&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function_voter&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gpt4_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;safe_gpt4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;   &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;claude_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;safe_claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;regex_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;veto&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;council&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Council&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gpt4_voter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                   &lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claude_voter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                   &lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;regex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regex_voter&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;council&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliberate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;safe_gpt4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;safe_claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;approved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;final_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No subclassing, no orchestrator process, no config files. Each &lt;code&gt;Voter&lt;/code&gt; is just an object with a name, a weight, and a &lt;code&gt;vote(proposal, context, peers)&lt;/code&gt; method. Inside, the voter can call an LLM, run a regex, hit a database — the framework only cares about the &lt;code&gt;Vote&lt;/code&gt; it returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the framework actually owns
&lt;/h2&gt;

&lt;p&gt;Four things stay tangled inside every hand-rolled implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What&lt;/strong&gt; is being decided (proposal shape)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who&lt;/strong&gt; votes (the voter set)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt; votes combine (threshold, weights, veto)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where&lt;/strong&gt; the audit log lives&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ai-council separates them. You bring 1 and 2. The framework owns 3 and 4.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without ai-council&lt;/th&gt;
&lt;th&gt;With ai-council&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;80 lines of &lt;code&gt;if a and b and not c:&lt;/code&gt; per project&lt;/td&gt;
&lt;td&gt;1 &lt;code&gt;Council(...)&lt;/code&gt; line + N small voter functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threshold logic re-invented (and bugged) every time&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;threshold=2&lt;/code&gt; (absolute) or &lt;code&gt;threshold=0.6&lt;/code&gt; (ratio)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Veto / hard-policy bolted on with extra ifs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Vote(..., veto=True)&lt;/code&gt; from any voter blocks approval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weighting senior voters means rewriting the aggregator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;function_voter("senior", fn, weight=2.0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit log is a &lt;code&gt;print()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JsonMeetingStore("meetings.jsonl")&lt;/code&gt; persists every decision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One flaky LLM call crashes the pipeline&lt;/td&gt;
&lt;td&gt;Exceptions captured as &lt;code&gt;approve=False, score=0&lt;/code&gt; (or &lt;code&gt;strict=True&lt;/code&gt; to re-raise)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A real-shape example: order approval
&lt;/h2&gt;

&lt;p&gt;Pricing, reviews, stock — three independent signals, one decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cheap_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cheap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cheap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cheap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cheap&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reviewed_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;4.5&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rated&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stocked_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;in_stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Vote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;approve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;in_stock&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;veto&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# out of stock → hard veto
&lt;/span&gt;
&lt;span class="n"&gt;council&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Council&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cheap&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cheap_voter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reviewed_voter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="nf"&gt;function_voter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stocked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stocked_voter&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;council&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deliberate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;4.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Out-of-stock vetoes the order regardless of price and rating. 2-of-3 must approve otherwise. No nested ifs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned wiring LLMs as voters
&lt;/h2&gt;

&lt;p&gt;One painful lesson from production: &lt;strong&gt;LLM scores are not alpha&lt;/strong&gt;. In one of my projects (an automated trading harness) I made the council's average score gate the trade size. Two months later the audit showed high-confidence council picks were systematically worse than low-confidence ones. The model "knew" which trades were obvious, and obvious had no edge left.&lt;/p&gt;

&lt;p&gt;So: in ai-council you can use LLM voters for &lt;strong&gt;veto&lt;/strong&gt; — "this looks like a scam, block" — but I'd argue against using LLM &lt;code&gt;score&lt;/code&gt; as a weighted input to a financial or safety-critical aggregate. The framework lets you do either; the choice is yours, but the bias is real.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this is the wrong tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You only have one voter.&lt;/strong&gt; Just call it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need streaming partial votes&lt;/strong&gt; (e.g., multi-round Socratic deliberation). ai-council is single-round; for streaming agent debate you want something like AutoGen or CrewAI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need a UI for human voters in real time.&lt;/strong&gt; This is a Python library, not a service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When this is the right tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Moderation gates ("approve if 2 of {gpt4, claude, regex} agree, but any single one can veto").&lt;/li&gt;
&lt;li&gt;Agent routing decisions ("which sub-agent handles this — let three rankers vote").&lt;/li&gt;
&lt;li&gt;Code-review bots ("approve if linter, model, and security scanner all agree").&lt;/li&gt;
&lt;li&gt;Trading or financial guard rails (use as &lt;strong&gt;veto&lt;/strong&gt; layer, not scoring layer).&lt;/li&gt;
&lt;li&gt;Anywhere "ask multiple independent signals, combine, log" is the shape.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;git+https://github.com/lfzds4399-cpu/ai-council.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires Python 3.11+. Zero runtime dependencies (the &lt;code&gt;Optional[LLM voter examples]&lt;/code&gt; pull in whatever LLM SDK you want). MIT.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/lfzds4399-cpu/ai-council" rel="noopener noreferrer"&gt;github.com/lfzds4399-cpu/ai-council&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've been writing this glue too, I'd love feedback on the API and edge cases that bit you in production. Issues / PRs welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>llm</category>
      <category>ai</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
