Summoning software on demand

Published: 2026-10-11

This morning I asked an AI to tell me what was in a Microsoft Word document I wrote years ago. It wrote a program to do it, ran it, showed me the answer, and threw the program away. The whole exercise, including a follow-up question a little later, took just over a minute and cost less than $0.02.

I believe that's a sign that the economics of software have already changed. Software has been so expensive to build that we could only afford it if the cost was amortized across a large number of users. Now an LLM can write glue logic in seconds, so some of our software can be built on demand. It can be built to answer a single question, used once, and then discarded.

That comes with risk, and it only works if we can do it safely.

Composable software

I build an open source AI environment called Humbug, something that has been largely written by AI since its inception in late 2024.

One of the spin-offs from the Humbug project is a programming language Menai. Menai is a Lisp/Scheme-like language designed for LLMs to use, and focused on a key safety constraint. It only has pure functions (literally no I/O in the language and no state mutation), with any sources of data provided to it by a separate tool framework (in this case Humbug).

There are a few interesting ideas in Menai:

  • The worst a Menai program can do is use too much time or memory, and Humbug enforces restrictions on both. There is a strict time limit on virtual machine (VM) program execution, and a limit on the amount of heap space that can be used.
  • It can only work on the data provided to it by the tool framework (Humbug in this case), so if Humbug doesn't let you read your .ssh directory or the file with your API keys then the LLM can't write a program that will fetch them.
  • It's designed to be fast to compile and run so users don't perceive a pause when an LLM builds software using it.
  • Using pure functions makes it very easy for an LLM to compose functions on top of each other.
  • It has a standard library of modules written by LLMs that can be composed on demand. The libraries are in source form so the LLM gets to read the code to understand how they work and how to use them. They're "discoverable".

Library modules are defined in the spirit of the Unix philosophy. Each provides small chunks of orthogonal functionality that can be freely composed to solve a more complex problem defined by an LLM. Give a capable LLM a problem, it can find the library components that can help and then craft a custom program to help achieve the user's request. If it can't find a component it needs then it's also free to write new ones, including tests.

The user experience is to express a desired outcome, the LLM works out how to solve it deterministically, then processes the results until it's ready to present them back to the user.

The actions are auditable. Each operation and each deterministic program execution is captured in the AI conversation transcripts.

A worked example

Back to my experiment this morning...

Many years ago I used to work in procurement and wrote a spec for a long-obsolete PC. I have a version of the spec as a Microsoft Word docx file. I asked DeepSeek v4.1 Flash (a fast, inexpensive LLM) to read the spec and tell me what the document was about (and asked it not to cheat by using the pre-existing read_text tool).

The speed with which it acquired the skill to do this may remind you of Trinity asking for a program to fly a B212 helicopter in the Matrix...

The first 9 seconds
The first 9 seconds

In 9 seconds, DeepSeek found the file I requested, read the Menai help file (to understand the language because it doesn't know anything about it when it started), found and read the project blueprint.md file, found and read the AGENTS.md that goes with it, and read the implementation of the docx-decode.menai library file.

The next 14 seconds
The next 14 seconds

Another 14 seconds, and DeepSeek had started to write code to pull apart the docx file (for anyone unfamiliar, the main file in a docx file is an XML file inside a zip file). It now knew there were 618 elements within it.

Decoding the document
Decoding the document

DeepSeek had now fixed a syntax error (LLMs can struggle with paren nesting in s-expressions sometimes, but the Menai compiler does a lot of work to help them pinpoint and resolve issues), and we had the program that was summoned to answer my question.

And there we have it...
And there we have it...

At 33 seconds, from start to finish, we had a description of the document contents.

Things got more interesting with a follow-on prompt: "can you give me all the section headings and how long each section is".

This added another 34 seconds of work, and resulted in a total cost for all the work of less than $0.02 (it might be less than $0.01 but there's some rounding on the billing report).

The read_text tool's built-in docx converter would have handled the initial query faster, but it couldn't handle this one. At best our LLM would either try to guess or resort to writing a Python script.

The AI didn't write all the code from scratch. It wrote the last 106 lines required to bridge the problem, but it's worth noting that the libraries it did leverage were also built by posing a series of problems to AI models in the same Humbug framework.

You can see the full, disposable program in the postscript section. It existed for long enough to meet my objective for this blog post and then had no further purpose. It's a demonstration of something that cost next to nothing to build and thus could be discarded.

Analyzing document sections
Analyzing document sections

I didn't exhaustively check the results, but I couldn't find any mistakes after I manually sampled sections at random. Word counts were correct and matched my own. It was certainly enough to give me the sort of qualitative assessment I was looking for on relative sizing.

The most important thing worth noting is how none of this needed my approval. Menai programs have no ambient authority bestowed by the user's permissions, so there was nothing to approve. This matters. If approvals are rare then users are much more likely to pay close attention to them and are far less likely to fall into authorization fatigue and keep clicking "approve".

Were such tools written in Python code I'd have to carefully review every script to ensure safety, or else put the Python interpreter in a container and orchestrate interactions between the LLM and that sandbox.

It's worth noting that there's nothing particularly special about the use of DeepSeek in this example. Humbug and Menai have been developed using models from OpenAI, Anthropic, Google, and Z.ai, and all are quite capable of doing similar things.

A few last thoughts

I don't claim Menai and Humbug are the only way to approach the problem of safely creating software on demand, but they do help explore some potential solutions.

Here's what I think we should start with:

  • Generated programs should have no ambient authority. Data can only be passed from or to the AI tooling harness.
  • A pure language is much safer to work with, and we don't lose any computational power. We can still process complex binary data, as the docx example showed.
  • Strong audit trails are important. We can see what was requested and why. We can also check for bugs in summoned programs later, should we have concerns.
  • We should make approvals infrequent by design. They should signify a genuine need for caution.

Summoning software on demand seems inevitable. Doing it safely is not, but it should be. We should be designing for it now. If we get that right then we should be looking forward to an era in which software can be personalized in ways that used to look like pure science fiction.

Postscript

The throwaway program written to calculate the section details and sizing is here. This is extracted, unedited, from the tool call that was made.

(letrec
  ((docx-decode (import "docx-decode"))

   (collect-text
    (lambda (node)
      (if (string? node)
          node
          (fold-list (lambda (acc child) (string-concat acc (collect-text child)))
                     ""
                     (dict-get node "children")))))

   (para-style
    (lambda (p)
      (let ((ppr (find-list (lambda (c) (and (dict? c) (string=? (dict-get c "tag") "w:pPr")))
                            (dict-get p "children"))))
        (if (dict? ppr)
            (let ((s (find-list (lambda (c) (and (dict? c) (string=? (dict-get c "tag") "w:pStyle")))
                                (dict-get ppr "children"))))
              (if (dict? s) (dict-get (dict-get s "attrs") "w:val") "none"))
            "none"))))

   (level-num
    (lambda (style)
      (match style
        ("Heading1" 1)
        ("Heading2" 2)
        ("Heading3" 3)
        ("Heading4" 4)
        (_ 0))))

   (is-heading?
    (lambda (p)
      (integer>? (level-num (para-style p)) 0)))

   (word-count
    (lambda (s)
      (list-length
        (filter-list (lambda (w) (string!=? w ""))
                     (string->list s " ")))))

   (child-text
    (lambda (c) (if (dict? c) (collect-text c) "")))

   (walk
    (lambda (children state)
      (if (list-null? children)
          (let ((heading (list-nth state 0)))
            (if (string=? heading "")
                (list-nth state 4)
                (list-append (list-nth state 4)
                             (dict "heading" heading
                                   "level" (list-nth state 1)
                                   "words" (list-nth state 2)
                                   "paras" (list-nth state 3)))))
          (let ((c (list-first children)))
            (let ((heading (list-nth state 0))
                  (style (list-nth state 1))
                  (wc (list-nth state 2))
                  (pc (list-nth state 3))
                  (secs (list-nth state 4)))
              (if (and (dict? c)
                       (string=? (dict-get c "tag") "w:p")
                       (is-heading? c))
                  (let ((newsecs (if (string=? heading "")
                                     secs
                                     (list-append secs (dict "heading" heading
                                                             "level" style
                                                             "words" wc
                                                             "paras" pc)))))
                    (walk (list-rest children)
                          (list (string-trim (collect-text c))
                                (para-style c)
                                0
                                0
                                newsecs)))
                  (walk (list-rest children)
                        (list heading style
                              (integer+ wc (word-count (child-text c)))
                              (integer+ pc 1)
                              secs))))))))

   (inclusive-words
    (lambda (secs idx)
      (let ((level (level-num (dict-get (list-nth secs idx) "level"))))
        (letrec ((loop (lambda (j acc)
                         (if (integer>=? j (list-length secs))
                             acc
                             (let ((l (level-num (dict-get (list-nth secs j) "level"))))
                               (if (integer<=? l level)
                                   acc
                                   (loop (integer+ j 1) (integer+ acc (dict-get (list-nth secs j) "words")))))))))
          (integer+ (dict-get (list-nth secs idx) "words") (loop (integer+ idx 1) 0)))))))

  (let ((doc ((:: docx-decode decode) (dict-get inputs "input-bytes"))))
    (let ((body (list-first (dict-get (dict-get doc "document") "children"))))
      (let ((secs (walk (dict-get body "children") (list "" "" 0 0 (list)))))
        (map-list
          (lambda (pair)
            (let ((idx (list-first pair))
                  (s (list-nth pair 1)))
              (dict "heading" (dict-get s "heading")
                    "level" (dict-get s "level")
                    "own-words" (dict-get s "words")
                    "own-paras" (dict-get s "paras")
                    "incl-words" (inclusive-words secs idx))))
          (list-zip (range 0 (list-length secs)) secs))))))