{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/2026-04-29-deterministic-rails-agentic-judgement/","result":{"data":{"site":{"siteMetadata":{"siteUrl":"https://avniproject.org","twitterHandle":"@avniproject"}},"post":{"id":"9976ee6f-8569-5d70-a606-8c5a3959feb7","html":"<p>I recently presented our Avni App Configurator work at the T4D AI Cohort 2 midpoint showcase. The aim of this post is to put down on paper the same content, with a little more room than slides allow.</p>\n<p>We at <a href=\"https://samanvayfoundation.org\" target=\"_blank\" rel=\"noopener noreferrer\">Samanvay Foundation</a> have been building a chat-first AI Configurator for <a href=\"https://avniproject.org\" target=\"_blank\" rel=\"noopener noreferrer\">Avni</a> for some months now. Along the way, we had to course-correct a few times. This post isn't really about the configurator itself — it is about three major things we learnt along the way.</p>\n<hr>\n<h2>The problem we are trying to solve</h2>\n<p>Setting up Avni for a new programme is not a one-person job today. At Samanvay, it takes three personas:</p>\n<ol>\n<li><strong>Business Analyst</strong> — sits with the program manager, draws out workflows, entities, indicators and reports, and writes a scoping document.</li>\n<li><strong>Implementer</strong> — turns the scoping document into Avni configuration: Subject Types, Programs, Forms, Concepts, Rules and role-based access.</li>\n<li><strong>Quality Analyst</strong> — walks through the field app and dashboards, checks the rules fire correctly, and flags gaps.</li>\n</ol>\n<p>Asking the program manager to do all three jobs themselves is hard. The Avni expertise needed is uneven across the three roles, and the loop between <em>what we need</em>, <em>what we built</em> and <em>does it actually work</em> is tight enough that one person ends up wearing all three hats — which most program managers reasonably do not want to do.</p>\n<p>But these three roles are <em>well-defined</em>. Each has a bounded job, with reasonably clear inputs and outputs. So our <strong>chat-first AI Configurator</strong> is essentially an attempt to replace this three-person team with AI specialists working behind a single chat window. The program manager stays on chat and walks away with a working Avni implementation in minutes, not hours.</p>\n<p>The realisation that dawned on us through this exercise is that, <em>though the whole thing looks like an AI problem, most of it is actually predictable, repeatable plumbing</em> (what engineers call <em>deterministic</em>).</p>\n<h2>What we built</h2>\n<p>The system is a stack of layers, each with one job.</p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1908px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f7137f8aa45e86905855a678b73c0500/7970d/architecture.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 41.9921875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABSUlEQVQoz2WS2W6DMBBF8/9/V/W1G9mMF7xiCBBye22RFKkPR8jYXJ+Z4TCOI0yw8Ckg5Ux6hBTROQvr/YuQ0mv/Pxmhj5imCYflvkB5zdCuvvTERg/ZaShrYFyH71NT111wdb9c7rezfwSsjxWHeZ4hnca3bPCjjnA8bPlhqyU0A6VRuMq2rsuFe2rwhkse9/XOwGWuhidzwam7QLh2M1Q01NDOoLkccVUMNbJaP98Xqz01cFkWnBn21rzjQ3xCWFlLe5asWfJJnCFo+LRWdU9Xqz0vQ0vlGhQdbtOMyEa7ECo+RrRKwVhbscGjHwYMtxv6cSAjMklD3gLZQ8eBaCc5TYNYekJDQwPLsmwxPHMoLNmUwdDQ8+JyLtJqz7qupeQZvRWI8gs5OWQ2O/I30krAso9aChjNvnI4ZW/Ikc/Aszu4Hhj4oOEvxa5hIhERGmcAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Tier architecture: User → Dify → Function Tool Calling service → Avni Backend → Database\"\n        title=\"Tier architecture: User → Dify → Function Tool Calling service → Avni Backend → Database\"\n        src=\"/static/f7137f8aa45e86905855a678b73c0500/7970d/architecture.png\"\n        srcset=\"/static/f7137f8aa45e86905855a678b73c0500/01e7c/architecture.png 512w,\n/static/f7137f8aa45e86905855a678b73c0500/2bef9/architecture.png 1024w,\n/static/f7137f8aa45e86905855a678b73c0500/7970d/architecture.png 1908w\"\n        sizes=\"(max-width: 1908px) 100vw, 1908px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Tier architecture: User → Dify → Function Tool Calling service → Avni Backend → Database</figcaption>\n  </figure></p>\n<p>A program manager types a message in chat. From that point on, things happen behind the scenes. <a href=\"https://dify.ai\" target=\"_blank\" rel=\"noopener noreferrer\">Dify</a> is <strong>the Brains</strong> — it reads the message, decides what should happen next (a clarifying question? draft a form? generate a rule?), and routes the work accordingly. Whenever a human needs to approve something, it pauses there. The Brains then talks to <strong>Avni AI</strong>, our own service of about 60 small helpers — <strong>the Brawn</strong> — that do the actual work: parsing uploaded documents, drafting the plan, assembling the final app, writing rules, and checking that everything is valid. Once everything checks out, the finished Avni app is handed off to the regular Avni system, which serves it to field workers exactly the same way as any other Avni app.</p>\n<p>A glimpse of the chat experience and the underlying tool calls flowing through:</p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2048px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/1e7711a5c75555d898445fa1b6096273/f7171/chat-screenshot.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACh0lEQVQ4y5WQy08TURSH539wIbBQFpqYqNHEBW6UkAgaTDA+YHgJRRR0gxFF4n8gEGMqbaJojLox7iQRJfKQly3yWmAgQQiJmtJpaTuddmY6Ux6fd4rGGBfi5H455577O78590oNdZU01FZztbaK+iqZuooyLlWV46qUqakQlMtUy6VUXjiHfPYM50uKKTlVyOnCAopPHKco/yiFx/I4mZ/HrpydSEduPOVAg4c99V5ya93svnifnIp2ssrukiVvkS23kV3eRk55uzjrILe6g32uDva67rH/spuDjV4ONT1jx+EipPEIfFRgLAS+sGAV/AKn7o/8jr/y4SAMrsCoiD1fbN4sWHTPGfiiIF9rRYK0WAZsWD9JwaaI6+bW/o+YymjTuiokOmtGAjW8QiTwFee703obaX1jE90w0RJJorE4kbiBkbJYjUQz9biom2Ifimoi14nGk4QiKrqZEjqbmKbzPRxHtzdxueqQ0uk0uq5jGgaaJv6o2xkD0zSxbTuDo4kb9pZB0iKSsIWhSTSZZDUa51swytoGNDY2Olcm0+ygaRpJYW4I81Qq9RemmMq2UiwrCTGtQUIYrgSDqLFY5sq3WlrEldfXSYoDZxKnybKsf5K2LRYUg4B4onBIQVFCGcPm5ptIMeGuKAqqqm6LWExFi6ushFYJChYXl5id/Yxlp2lquo4UCAREcZHl5eX/Ykn0OGYTE5P4/eOEQmGuNIg3nJqewufzMTMzw/Q2cbSfhNG4oH9gkHe9vczNz1NTW4M0NjLCYF8/o0PDjAwNbQtH+2FggP6+AXp63vO6+y3j/knKSmWkF12PeeTu5HGnd9s88XgzPV0PPDx0e0Tu4eXzVxTkF/ED89ZaBJD7XDwAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Avni AI Configurator chat experience\"\n        title=\"Avni AI Configurator chat experience\"\n        src=\"/static/1e7711a5c75555d898445fa1b6096273/a878e/chat-screenshot.png\"\n        srcset=\"/static/1e7711a5c75555d898445fa1b6096273/01e7c/chat-screenshot.png 512w,\n/static/1e7711a5c75555d898445fa1b6096273/2bef9/chat-screenshot.png 1024w,\n/static/1e7711a5c75555d898445fa1b6096273/a878e/chat-screenshot.png 2048w,\n/static/1e7711a5c75555d898445fa1b6096273/f581c/chat-screenshot.png 3072w,\n/static/1e7711a5c75555d898445fa1b6096273/f7171/chat-screenshot.png 3456w\"\n        sizes=\"(max-width: 2048px) 100vw, 2048px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Avni AI Configurator chat experience</figcaption>\n  </figure></p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2048px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/3d4cea10a7e15e26c6a79453f35c23b0/f7171/tool-calls-screenshot.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAACrUlEQVQ4y5WSy0tUYRiHz38RRFQLu4CLoKIkKBEsKTNI8zJqqaVNm4jWLdtlUt6SJJAu0CIoIhDUMW/NmJUSlSajjpe5n5sz48wZZzSfvnNGpUWLOvDw/d77eb9zpOt1ldhrqrBfqeRqZTk1FWVcrbZRV2WjptLGZWFXlZViu1RM2cULlBQVcr4gn3P5eRTmn+JM7nFOn8yhIDeHXTt3IB253cXBhjb217dzoL6VfXXNZNU9zFD7gN1V9yz2mFQ3Ct1I9rUmDjU0kVXTRHZDC9n2dg7f6mLv0XykURVcYeiehxc/Urxyw2uPYC5zvvkD09fthbvODTrG13F40rydMnj3M8GYDuXXbiJBGjD46o1yfyBIx0eNFpcu0DbRaR3N2M1OjY6xCHd6FIbcy6J0haimocoqq4k4pSXFSBuiXTxhkEyuEgoGkGWZcCgotECcihwWyJYdDPiRw2Eimizyk2hxg0jMwK/GiBprlJdXIKVTaeLxOKlUipBIVlSVYChEIBDYRlFUa1AolInLsoJhGOgrCaaXguiauDfx2O03zJWxguZEj8eDz+dndnbWYmlpycLn87GwsLAZ97G4uEjSSBBNJHH7FDFQ4ZfoU1/fgLS2vk58ZYV0OvOm0WiUWCxmaZNIJLKtTX9UkEommFOS+PUEXjli5WxsNdR1XawSspxbWhMXvby8jCrWM23Tv2Wb65s6rOosBGS+usUWPi8R8VFqa2uR/H4/MzMz1jom8/Pz2/pv9hazoubHtBvnxHcmJyfxBoJU2GxIX8a/4HK5mJiYYPwfMXM/ff6Mc/QTjn4nQyMjfJucorhY/DYfhod53+dgZHCQ4X9kZHCIgf5+HH2CXge9PX04nWOcLSxCetb5hM6WVp60tf8Hj0RNG49b2ulobrP086cvOXYij9+xiklA+1m4QgAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Tool calls flowing through the Avni AI service during configuration\"\n        title=\"Tool calls flowing through the Avni AI service during configuration\"\n        src=\"/static/3d4cea10a7e15e26c6a79453f35c23b0/a878e/tool-calls-screenshot.png\"\n        srcset=\"/static/3d4cea10a7e15e26c6a79453f35c23b0/01e7c/tool-calls-screenshot.png 512w,\n/static/3d4cea10a7e15e26c6a79453f35c23b0/2bef9/tool-calls-screenshot.png 1024w,\n/static/3d4cea10a7e15e26c6a79453f35c23b0/a878e/tool-calls-screenshot.png 2048w,\n/static/3d4cea10a7e15e26c6a79453f35c23b0/f581c/tool-calls-screenshot.png 3072w,\n/static/3d4cea10a7e15e26c6a79453f35c23b0/f7171/tool-calls-screenshot.png 3456w\"\n        sizes=\"(max-width: 2048px) 100vw, 2048px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Tool calls flowing through the Avni AI service during configuration</figcaption>\n  </figure></p>\n<p>The mantra we ended up with for this stack is <em>\"Deterministic on the rails, agentic in the gaps\"</em> — predictable code where we can write the rules down, AI judgement where we cannot. The three lessons that follow are how we arrived there.</p>\n<hr>\n<h2>Lesson 1 — Where to use AI, and where to use plain code</h2>\n<p>Our first instinct was to throw the whole problem at the AI and see what came back. The output wasn't satisfactory, and we could not verify it, and running the same input twice gave us slightly different answers — which is a problem if the result is a programme's data-collection app.</p>\n<p>The rule we now follow is <strong>write the rules down first</strong>. If the work has clear, predictable rules — <em>turn this checklist into Avni forms, validate that all mandatory fields are filled</em> — we use plain code. If the input is fuzzy and we cannot pin the rules down — <em>like a conversation with a program manager</em> — we hand it to an AI specialist, with a tightly-scoped job.</p>\n<p>In our case, the \"rules written down\" piece is the <strong>spec</strong> — a structured plan of what the app needs, capturing Subject Types, Programs, Encounter Types, Forms, Concepts and Rules. The Business Analyst specialist does the fuzzy work of turning a conversation with the program manager into this spec. From there, converting the spec into the actual Avni app is <em>plain code</em> — no AI judgement on the Avni-config side. This is what makes the resulting app reproducible and auditable.</p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2048px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/6a9afe6de2ce82010f2ff7423dba871d/f7171/spec-configuration.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 48.63281250000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA70lEQVQoz5WSS27DMAxEfR1T/Eiy4tTZFL3/oaYjK4uidtJ6MaA+xPCR0vRxC3xugVoDHoG8ZORG1cy9w/2oYN7peQ5Mq8/4ajPcEiQJbDFYo4pCRCCzjPgfMXdyTVhzQi4OC0PiPtmQ9qhHqZ6fd00iNCyCbWW7rcCywuog7Gv1X+qF9vXPSDOqA02SBmEtMbDZtq8OpVmnPGstSXrZ9jAsCY2E6iRjVQtWfirx/oomVUNje/cbzfZXNc5I38rsdc5O2ELwaJ3ILhMdCLthiYSNhJlz7Id/fY93Obvhwkd5rMpPzZarjxn6c35yjfAbqbAvaR3BYiUAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Spec-first flow: program requirements turn into a structured spec, which a deterministic pipeline converts into Avni configuration\"\n        title=\"Spec-first flow: program requirements turn into a structured spec, which a deterministic pipeline converts into Avni configuration\"\n        src=\"/static/6a9afe6de2ce82010f2ff7423dba871d/a878e/spec-configuration.png\"\n        srcset=\"/static/6a9afe6de2ce82010f2ff7423dba871d/01e7c/spec-configuration.png 512w,\n/static/6a9afe6de2ce82010f2ff7423dba871d/2bef9/spec-configuration.png 1024w,\n/static/6a9afe6de2ce82010f2ff7423dba871d/a878e/spec-configuration.png 2048w,\n/static/6a9afe6de2ce82010f2ff7423dba871d/f581c/spec-configuration.png 3072w,\n/static/6a9afe6de2ce82010f2ff7423dba871d/f7171/spec-configuration.png 3456w\"\n        sizes=\"(max-width: 2048px) 100vw, 2048px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Spec-first flow: program requirements turn into a structured spec, which a deterministic pipeline converts into Avni configuration</figcaption>\n  </figure></p>\n<p><strong>We use plain code when:</strong></p>\n<ol>\n<li>The rules don't change between two runs of the same input</li>\n<li>We need the same answer every time</li>\n<li>There is a clear right answer, and we can describe what \"right\" looks like</li>\n<li>Examples in our case: parsing uploaded documents, validating a form, keeping track of where we are in a conversation</li>\n</ol>\n<p><strong>We use AI when:</strong></p>\n<ol>\n<li>The input is open-ended — a conversation, a free-form document, an image</li>\n<li>We need judgement, not a lookup</li>\n<li>The user is asking for clarification, or going back and forth</li>\n<li>The output is open-ended — drafting text, fixing a broken configuration, generating something from scratch</li>\n<li>Examples in our case: deciding which path to take, figuring out what the program manager actually wants, drafting form text, auditing whether a configuration really meets the original goals</li>\n</ol>\n<p>What ties the two halves together is a small set of shared notes — <em>where are we in the conversation, has the user approved this draft yet, how many times have we tried to fix this rule.</em> Both the AI side and the plain-code side read and update these notes, and that is how they stay in sync.</p>\n<p>The takeaway in one line — <em>if you can write the rules down, use code; if you cannot, use AI.</em></p>\n<h2>Lesson 2 — One AI doing everything is a bad idea</h2>\n<p>We started with one big AI generalist doing everything — listening to the program manager, drafting the app, writing rules, auditing the result. The more we asked it to do, the more it forgot. Small mistakes early on snowballed into bigger ones later, and every fix tended to redo the whole app from scratch.</p>\n<p>So we split the one big AI into a team of small AI specialists (engineers call these <em>agents</em>) — one specialist per job, each with a clear input and a clear output, and a hard limit on how long it gets to keep trying.</p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2048px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/e0fd735dd0cfce1f7c52eaf0d321867f/99250/subagent-archetypes.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 57.421875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABY0lEQVQoz51SSXLCMBD0NyhL1mbJeJMXID/ML8Ihf0rBIcdc8oa47ElLEAJHc+ga2TNqdc9Mkhf1a5ap96YZ3/r+cOTcHIUwRymfQ8K5+krTjKwtqSxbGoeBjLHEmCTO1yMQfgTCsvQ/3o8TCKfcuAnJKct0jGsAQnlmF4Xzdlsv+91uMcYt+L+gFYsQeuGIjIlHpFmMoe4eCS6dGaRC4dy2AzWNJ61tlJ8JTcgTevoAKCelHUmVU3AX7TKBNglKhLLnbnxBoZ5Dsu9Hsq4ipVy8VFSeyqYnh/62fk/b0pOUNn4XQKjN0X9tivhYAjsnBUVFUcNyQ94PUOjwqorqQi4UB7Ui4qIwOkD+5gZnKXMKlj83G0Z13c/DcMCUR0zZxSIRLes4cXa19IeQvz//WxbmO1iFwqWCvQuhvSgUOsa1a3NKU04VhtJ1O+q74Ua4luxKiLWBVGz5rDA1cZ3sM0sd8AtkqCd3mQpm1AAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Sub-agent archetypes: Classifier, Planner, Synthesizer, Patcher, Inspector\"\n        title=\"Sub-agent archetypes: Classifier, Planner, Synthesizer, Patcher, Inspector\"\n        src=\"/static/e0fd735dd0cfce1f7c52eaf0d321867f/a878e/subagent-archetypes.png\"\n        srcset=\"/static/e0fd735dd0cfce1f7c52eaf0d321867f/01e7c/subagent-archetypes.png 512w,\n/static/e0fd735dd0cfce1f7c52eaf0d321867f/2bef9/subagent-archetypes.png 1024w,\n/static/e0fd735dd0cfce1f7c52eaf0d321867f/a878e/subagent-archetypes.png 2048w,\n/static/e0fd735dd0cfce1f7c52eaf0d321867f/f581c/subagent-archetypes.png 3072w,\n/static/e0fd735dd0cfce1f7c52eaf0d321867f/99250/subagent-archetypes.png 3455w\"\n        sizes=\"(max-width: 2048px) 100vw, 2048px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Sub-agent archetypes: Classifier, Planner, Synthesizer, Patcher, Inspector</figcaption>\n  </figure></p>\n<p>Four things changed for the better:</p>\n<ol>\n<li><strong>Less to confuse it</strong> — each specialist sees only the slice of the world it needs.</li>\n<li><strong>Cheaper</strong> — small inputs cost less than one big input. And because each specialist's setup looks the same on every call, we get a discount on the repeated parts.</li>\n<li><strong>One job, done well</strong> — clear inputs, clear outputs, and a fixed number of tries before it stops and asks for help.</li>\n<li><strong>Edit, don't redo</strong> — when something needs fixing, change one form or one rule, not the whole app.</li>\n</ol>\n<p>Five kinds of specialist recur often enough that we now name them:</p>\n<ol>\n<li><strong>Classifier</strong> — route input to the right path.</li>\n<li><strong>Planner</strong> — break a task into ordered steps.</li>\n<li><strong>Synthesizer</strong> — produce the artifact as per the plan.</li>\n<li><strong>Patcher</strong> — edit one part, not regenerate.</li>\n<li><strong>Inspector</strong> — audits the work as a read-only critic, doesn't change anything itself.</li>\n</ol>\n<p>The takeaway in one line — <em>small brain, clear job, surgical edits.</em></p>\n<h2>Lesson 3 — Build helpers that carry the load, not the AI</h2>\n<p>This was the most painfully learnt lesson. The tools we started with weren't built with the AI in mind — they handed back everything they knew, every time. The AI ended up exhausting its working memory, or its allotted attempts, just figuring out the current state of affairs. Very little was left for the actual task.</p>\n<p>So the rule we now follow is: <strong>every helper returns only what this turn needs — never the whole base.</strong></p>\n<p><figure class=\"gatsby-resp-image-figure\" style=\"\">\n    <span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 2048px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/bec2a06d3d5a0f4c04c0178bf54771ce/765b7/tool-principles.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 56.0546875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABnklEQVQoz6XS30/TUBTA8f47q/qiwRf/O+ObkUwmC8wXH4wxSnBGQdQnX0yMiQiCY6iMsa3d7Ho7+rsd3dezlhA0Jk69ySenPbn35P442r0Hd7leuc+N+WXKlSq3F2timVvlRcp3lqhUayzMYLquulRDQ8arHmxbMAozVAxOBEq4Y0iYTUQxNN/32Wo0aXxt0e4O6PWHua5pYX5XjNwAR/waf8p5IUPHwwtCtCiKCL69YdB8i3Gwy6C1I3ZzVruB6jZzdmcP1ZnG5rncaRSusU8WHaPFUtAbKSzjCNU/Ymi0sc32uXh4Zvpv9Q7zeYHTx1cmgerj2Qa+3WOSBkXBwO6ILkgii10myamzb+83ijypx4nszJWi4ziYHjkkDY+ZRK5caSYmf0EexHeIXCWFfbI0QhunKTub76ivPOT1izov156w8WyVjeerRfyD9acrrNUfs15/hLLMom0W5m9yee4KV6/Noes6pVJpZhfy+TqXLupsfXiPliSxPEiLL583ae1/4mBv+x99JJLja4H0YRbL5Z6Estfxf5D2zhJ+AKBS+1K+IvKyAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Tool-design principles: sectional retrieval, response truncation, surgical edits, stateless, handles-not-payloads, defensive guards\"\n        title=\"Tool-design principles: sectional retrieval, response truncation, surgical edits, stateless, handles-not-payloads, defensive guards\"\n        src=\"/static/bec2a06d3d5a0f4c04c0178bf54771ce/a878e/tool-principles.png\"\n        srcset=\"/static/bec2a06d3d5a0f4c04c0178bf54771ce/01e7c/tool-principles.png 512w,\n/static/bec2a06d3d5a0f4c04c0178bf54771ce/2bef9/tool-principles.png 1024w,\n/static/bec2a06d3d5a0f4c04c0178bf54771ce/a878e/tool-principles.png 2048w,\n/static/bec2a06d3d5a0f4c04c0178bf54771ce/f581c/tool-principles.png 3072w,\n/static/bec2a06d3d5a0f4c04c0178bf54771ce/765b7/tool-principles.png 3418w\"\n        sizes=\"(max-width: 2048px) 100vw, 2048px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span>\n    <figcaption class=\"gatsby-resp-image-figcaption\">Tool-design principles: sectional retrieval, response truncation, surgical edits, stateless, handles-not-payloads, defensive guards</figcaption>\n  </figure></p>\n<p>Six principles came out of this exercise:</p>\n<ol>\n<li><strong>Hand over the section, not the book</strong> — when the AI asks about a particular form, the helper finds it in the configuration and hands back just that one form. The AI doesn't have to wade through the rest.</li>\n<li><strong>Cap the answer</strong> — if there are 500 matches, the helper picks the top 20 and a count. The AI never sees the rest.</li>\n<li><strong>Edit, don't rewrite</strong> — when one rule needs to change, the helper opens the form, applies the patch, and saves it. The AI doesn't regenerate the whole thing.</li>\n<li><strong>Don't carry the world around</strong> — helpers don't keep their own memory. Anything that needs to persist lives in a shared notebook, with separate helpers to save, modify, and read from it. The AI never has to hold state itself.</li>\n<li><strong>Use a bookmark, not the page</strong> — when state is bulky (a 30-page document, a big Avni configuration), the helper keeps the bytes. The AI just gets a bookmark and a one-line summary.</li>\n<li><strong>Check the door, both directions</strong> — every helper validates what comes in and what goes out, rejects oversized or malformed requests, and fails clearly when it cannot help. The AI doesn't have to recover from broken data.</li>\n</ol>\n<p>Before we did this, the AI was burnt out within the first three steps and could not go any deeper into the work. After, it uses <strong>about a tenth of the words it used to per step</strong> — so it can keep going for much longer, and the work it produces is more thorough.</p>\n<p>The takeaway here — <em>the more the helpers carry, the further the AI can go.</em></p>\n<hr>\n<h2>What we are shipping next, and what is in the way</h2>\n<p>What we are shipping next:</p>\n<ol>\n<li>Better support for the more complex parts of an Avni configuration</li>\n<li>Multi-turn conversations — letting program managers refine the configuration after the first draft, not start from scratch</li>\n<li>Reading scoping documents (PDFs, images) directly, instead of asking the program manager to retype them</li>\n<li>Letting real organisations use the configurator on their live setup — today it is trial-only, sandboxed</li>\n<li>Safety rails — preventing the AI from going off-rails on sensitive prompts</li>\n<li>Measuring how much faster the AI route is than the manual one</li>\n</ol>\n<details>\n<summary><strong>For the technically curious — what didn't work in Dify</strong></summary>\n<br/>\n<ul>\n<li><strong>Doc extractor misses content</strong> — we moved parsing into our own parser inside Avni AI.</li>\n<li><strong>Agent nodes cannot call authenticated tools using perishable tokens</strong> — the auth token has to live in a conversation-scoped in-memory store.</li>\n<li><strong>No sub-workflow imports</strong> — either HTTP-call ourselves (200–500 ms overhead) or copy-paste flows.</li>\n<li><strong>Prompt-length UI limits</strong> — long prompts injected as variables.</li>\n<li><strong>YAML / visual-flow config</strong> — adoption curve for developers used to plain code files.</li>\n</ul>\n</details>\n<p>More fundamentally, orchestrating <strong>open-ended, long-horizon tasks</strong> — the kind where the AI needs to loop back, change its mind, or hand work between specialists — using <a href=\"https://www.langchain.com\" target=\"_blank\" rel=\"noopener noreferrer\">LangChain</a>, <a href=\"https://www.langchain.com/langgraph\" target=\"_blank\" rel=\"noopener noreferrer\">LangGraph</a> or Dify was a lost cause for us. These tools ask you to draw the whole workflow as a flowchart upfront, before you know what is going to happen. That works for simple, linear flows. The configurator is not that. We ended up with workarounds piled on workarounds.</p>\n<p>So we are switching the brains of the system to <strong>Claude managed agents</strong> — a different kind of AI runtime where the AI plans its own steps as it goes, instead of being routed through a fixed flowchart. Same lessons, same shape — different conductor.</p>\n<p><em><strong>\"The Devil is in the Details.\"</strong></em></p>\n<hr>\n<p>The code is at <a href=\"https://github.com/avniproject/avni-ai/tree/app-configurator-dev\" target=\"_blank\" rel=\"noopener noreferrer\">github.com/avniproject/avni-ai/tree/app-configurator-dev</a>.</p>","frontmatter":{"date":"April 29, 2026","title":"Deterministic Rails, Agentic Judgement: Lessons from building the Avni App Configurator","author":"Himesh R","description":"Three lessons from building a chat-first AI Configurator for Avni — and what we changed when our first version stopped scaling.","tags":["AI","Tech4Dev","Architecture","LLM","Avni"],"image":{"childImageSharp":{"resize":{"src":"/static/5741384f2393930c5e8fd6a01597e768/f3583/cover-agents.png","height":888,"width":1200}}}},"fields":{"slug":"/blog/2026-04-29-deterministic-rails-agentic-judgement/"}}},"pageContext":{"id":"9976ee6f-8569-5d70-a606-8c5a3959feb7"}},"staticQueryHashes":["1175525803","4080856488"]}