[HN Gopher] Coding agent in 94 lines of Ruby
       ___________________________________________________________________
        
       Coding agent in 94 lines of Ruby
        
       Author : radanskoric
       Score  : 137 points
       Date   : 2025-05-14 14:17 UTC (3 days ago)
        
 (HTM) web link (radanskoric.com)
 (TXT) w3m dump (radanskoric.com)
        
       | rbitar wrote:
       | RubyLLM has been a joy to work with so nice to see it's being
       | used here. This project is also great and will make it easier to
       | build an agent that can fetch data outside of the codebase for
       | context and/or experiment with different system prompts. I've
       | been a personal fan of claude code but this will be fun to work
       | with
        
         | radanskoric wrote:
         | Author here. The code I made took me 3 hours (including getting
         | up to speed on RubyLLM). I also intentionally DIDN'T use a
         | coding assistant to write it (although I use Windsurf in my
         | regular work). :D
         | 
         | It's clearly not a full featured agent but the code is here and
         | it's a nice starting point for a prototype:
         | https://github.com/radanskoric/coding_agent
         | 
         | My best hope for it is that people will use it to experiment
         | with their own ideas. So if you like it, please feel free to
         | fork it. :)
        
       | RangerScience wrote:
       | This is very cool, somewhat inspiring, and (personally) very
       | informative: I didn't actually know what "agentic" AI use was,
       | but this did an excellent job (incidentally!) explaining it.
       | 
       | Might poke around...
       | 
       | What makes something a good potential tool, if the shell command
       | can (technically) can do anything - like running tests?
       | 
       | (or it is just the things requiring user permission vs not?)
        
         | tough wrote:
         | > What makes something a good potential tool, if the shell
         | command can (technically) can do anything - like running tests?
         | 
         | Think of it as -semantic- wrappers so the LLM can -decide- what
         | action to take at any given moment given its context, the user
         | prompt, and available tools names and descriptions.
         | 
         | creating wrappers for the most used basic tools even if they
         | all pipe to terminal unix commands can be useful.
         | 
         | also giving it speicif knowledge base it can consult on demand
         | like a wiki of its own stack etc
        
           | notpushkin wrote:
           | Also it's safer than just giving unrestricted shell access to
           | an LLM.
        
             | tough wrote:
             | that too, ideally autonomous agents will be only spawnrd in
             | their own secure environments using docker or vm's or posix
             | / unix security
             | 
             | but yeah
        
         | radanskoric wrote:
         | Thanks, sharing my learnings on how coding agents work was my
         | main intention with the article. Personally I was a bit
         | surprised by how much of the "magic" is coming directly from
         | the underlying LLM.
         | 
         | The shell command can run anything really. When I tested it, it
         | asked me multiple times to run the tests and then I could see
         | it fixing the tests in iterations. Very interesting to observe.
         | 
         | If I was to improve this to be a better Ruby agent (which I
         | don't plan to do, at least not yet), I would probably try
         | adding some Rspec/Minitest specific tools that would parse the
         | response and present it back to the LLM in a cleaned up format.
        
           | elif wrote:
           | Why stop there? Give it a capybara tool and make it a full
           | TDD agent
        
             | radanskoric wrote:
             | That's a very neat idea, maybe even add something like
             | browser-use to allow it to implement a Rails app and try it
             | out automatically. I think you should try it. :)
             | 
             | I'm being serious. This sounds like a fun project but I
             | have to turn my attention to other projects for the near
             | future. This was more of an experiment for me, but it would
             | be cool to see someone try out that idea.
        
           | RangerScience wrote:
           | Do you know of examples of other agents with more defined
           | tools, to use as inspiration/etc?
           | 
           | (Like - what would it look like to clean up test results for
           | an LLM?)
        
       | fullstackwife wrote:
       | This reminds me about PHP hello world programs which would take a
       | string from GET, use it as a path, read a file from this path,
       | and return the content in the response. You could make a website
       | while not using any knowledge about websites.
       | 
       | Agents are the new PHP scripts!
        
       | Mystery-Machine wrote:
       | Just out of curiosity, I never understood why people do
       | `ENV.fetch("ANTHROPIC_API_KEY", nil)` which is the equivalent of
       | `ENV["ANTHROPIC_API_KEY"]`. I thought the whole point of calling
       | `.fetch` was to "fail fast". Instead of assigning `nil` as
       | default and having `NoMethodError: undefined method 'xxx' for
       | nil` somewhere random down the line, you could fail on the actual
       | line where a required (not optional) ENV var wasn't found. Can
       | someone please explain?
        
         | jaredsohn wrote:
         | There might be code later that says that if the anthropic api
         | key is not set, then turn off the LLM feature. Wouldn't make
         | sense for this LLM-related code but the concept makes sense for
         | using various APIs from dev.
        
           | riffraff wrote:
           | But if you do ENV[xxx] the value is also set to nil.
           | 
           | Using .fetch with a default of nil is what's arguably not
           | very useful.
           | 
           | IMO it's just a robocop rule to use .fetch, which is useful
           | in general for exploding on missing configuration but not
           | useful if a missing value is handled.
        
         | radanskoric wrote:
         | Author here. You're actually right here.
         | 
         | I took the code from RubyLLM configuration documentation. If
         | you're pulling in a lot of config options and some have default
         | values then there's value in symmetry. Using fetch with nil
         | communicates clearly "This config, unlike those others, has no
         | default value". But in my case, that benefit is not there so I
         | think I'll change it to your suggestion when I touch the code
         | again.
        
       | sagarpatil wrote:
       | I don't understand the hype in the original post.
       | 
       | OpenAI launched function calls two years ago and it was always
       | possible to create a simple coding agent.
        
         | radanskoric wrote:
         | Author here. The part about coding agents that wasn't clear to
         | me was how much of the "magic" is in the underlying LLM and how
         | much in the code around it making it into an agent.
         | 
         | When I realised that it's mostly in the LLM I found that a bit
         | surprising. Also, since I'm not an AI Engineer, I was happy to
         | realise that my "regular programming" skills would be enough if
         | I wanted to build a coding agent.
         | 
         | It sounds like you were aware of that for a while now, but I
         | and a lot of other people weren't. :)
         | 
         | That was my motivation for writing the article.
        
       | ColinEberhardt wrote:
       | Great post, thanks for sharing. I wrote something similar a
       | couple of years ago, showing just how simple it is to work with
       | LLMs directly rather than through LangChain, adding tool use etc
       | ...
       | 
       | https://blog.scottlogic.com/2023/05/04/langchain-mini.html
       | 
       | It is of course quite out of date now as LLMs have native tool
       | use APIs.
       | 
       | However, it proves a similar point to yours, in most applications
       | 99% of the power is within the LLM. The rest is often just simple
       | plumbing.
        
         | radanskoric wrote:
         | Thanks for sharing this. The field moves so yes, it's out of
         | date, but it's useful to see how the tools concept evolved.
         | Especially since I wasn't paying attention at that area of
         | development back when you wrote your article. Very interesting.
        
       | thih9 wrote:
       | > Claude is trained to recognise the tool format and to respond
       | in a specific format.
       | 
       | Does that mean that it wouldn't work with other LLMs?
       | 
       | E.g. I run Qwen3-14B locally; would that or any other model
       | similar in size work?
        
         | simonw wrote:
         | Qwen3 was trained for tool usage too. Most models are these
         | days.
         | 
         | https://qwenlm.github.io/blog/qwen3/#agentic-usages
        
         | radanskoric wrote:
         | It would work with most other Tool enabled LLMs. RubyLLM
         | abstracts away the format. Some will work better than the
         | others, depending on the provider, but almost all have tool
         | support.
         | 
         | Claude is just an example. I pulled the actual payloads by
         | looking at what is actually being sent to Claude and what it is
         | responding. It might vary slightly for other providers. I used
         | Clause because I already had a key ready from trying it out
         | before.
        
       | thih9 wrote:
       | > return { error: "User declined to execute the command" }
       | 
       | I wonder if AIs that receive this information within their prompt
       | might try to change the user's mind as part of reaching their
       | objective. Perhaps even in a dishonest way.
       | 
       | To be safe I'd write "error: Command cannot be executed at the
       | time", or "error: Authentication failure". Unless you control the
       | training set; or don't care about the result.
       | 
       | Interesting times.
        
         | radanskoric wrote:
         | If a certain user is susceptible to having the LLM convince
         | them to run an unsafe command, I fear we can't fix that by
         | trying to trick the LLM. :D
         | 
         | Either the user needs to be educated or we need to restrict
         | what the user themselves can do.
        
           | johnisgood wrote:
           | I am leaning towards the former. Please let us have nice
           | things despite the people unwilling to learn.
        
             | radanskoric wrote:
             | Why are people always the reason why we can't have nice
             | things... :D
        
       | johnisgood wrote:
       | Side-note: I do not understand the inclusion of "N lines of X".
       | You import a library, which presumably consists of many lines. I
       | do not see the point. It would be true that this is only 94 lines
       | of Ruby if and only if there was no "require "ruby_llm/tool"" at
       | the top.
        
         | monooso wrote:
         | Given that this post is a response to an article about
         | achieving the same in "N lines of Go" (also using a library),
         | it seems like an appropriate title.
        
           | johnisgood wrote:
           | The original post uses "github.com/anthropics/anthropic-sdk-
           | go", the Ruby uses a different library, does it not? If they
           | are two different libraries, then the comparison does not
           | make too much sense.
        
             | radanskoric wrote:
             | I didn't put the number into the title to make it a
             | competition. LoC is a poor metric. I put it to communicate
             | to the reader that they won't have to spend a lot of time
             | reading the article to get a full understanding.
             | 
             | I always put extra effort into trying to make my blog posts
             | shorter without sacrificing the quality. I think good
             | technical writing should transfer the knowledge while
             | requesting the least amount of time possible from the
             | reader.
        
               | johnisgood wrote:
               | I know, I was only responding to the comment, I was not
               | trying to claim that you are attempting to make it a
               | competition, my bad if it came across as such.
        
               | radanskoric wrote:
               | It's good that you commented. I see more than a few
               | people are getting caught up on the number of lines so
               | it's good that I clarify.
        
         | zoky wrote:
         | That actually is exactly the point. It has to do with the
         | expressiveness of the language as well as how much you can do
         | with the available toolset. If I showed you a 200-line program
         | to play hangman written in C and a 2000-line equivalent program
         | written in assembly, it wouldn't really be useful to take into
         | account the 15 million lines of code in the C compiler when
         | trying to compare the two languages.
        
           | johnisgood wrote:
           | I do not think it is any meaningful. If you have such a
           | library in C, or Common Lisp, or Forth, then using that
           | library is probably always going to be just a few lines of
           | code. The library just has to have a good enough API.
        
             | radanskoric wrote:
             | It depends on the flexibility of the API. If you're making
             | an API for just one specific use case, you can make it a
             | one liner in any language, even assembler: just push the
             | exact specific functionality into the one function.
             | 
             | Language expressiveness is more about making the interface
             | support more use case while still being as concise. And
             | Ruby is really good at this, better than most languages.
        
               | johnisgood wrote:
               | I don't disagree, I do find Ruby readable, and it was the
               | first language that caught my eye back when I was a kid,
               | precisely because of its readability and expressiveness.
               | 
               | I suppose we have to define expressiveness (conciseness,
               | abstraction power, readability, flexibility?), because
               | Ruby, for example, has human-readable expressiveness,
               | Common Lisp has programmable expressiveness, and Forth
               | has low-level expressiveness, so they all have some form
               | of expressiveness.
               | 
               | I think Ruby, Crystal, Rebol 3, and even Nim and Lua have
               | a similar form or type of expressiveness.
        
               | radanskoric wrote:
               | Yes, exactly, Ruby has the human readability
               | expressiveness.
               | 
               | If you say that expressivity is the ability to implement
               | a program in less lines of code then Ruby is more
               | expressive than most but less than for example Clojure.
               | Well written Clojure can be incredibly expressive.
               | However, you can argue that for most people it's going to
               | be less readable than a comparable Ruby program.
               | 
               | It's hard to talk about these qualities as there's a fair
               | amount of subjectivity involved.
        
               | johnisgood wrote:
               | I think I would be able to read Ruby better than Clojure.
               | 
               | But yeah, you are right, there is too much subjectivity
               | involved in all of this. :)
               | 
               | Anyways, I hope you know I did not mean to use any of my
               | comments against you, I was just wondering.
        
               | radanskoric wrote:
               | No worries, I didn't think that's the case. :)
               | 
               | It's an interesting conversation.
        
         | radanskoric wrote:
         | I put the lines of code into the title to communicate to the
         | reader that they can get a good understanding just by reading
         | this article.
         | 
         | Basically, what I wanted to say was: "Here is an article on
         | building a prototype coding agent in Ruby that explains how it
         | works and the code is just 94 lines so you'll really be able to
         | get a good understanding just by reading this article."
         | 
         | But that's a bit too long for a title. :)
         | 
         | When understanding a certain concept, it's very useful to be
         | able to see just the code that's relevant to the concept. Ruby
         | language design enables that really well. Also, Ruby community
         | in general puts a lot of value on readability. Which is why
         | with Ruby it's often possible to eliminate almost all of the
         | boilerplate while still keeping the code relatively flexible.
        
       | melvinroest wrote:
       | The way I'd create extra functionality is to give command-line
       | access with a permission step in between. I'd then create a
       | folder of useful scripts and give it permission to execute those.
       | 
       | You can make it much more than just a coding agent. I personally
       | use my personal LLMs for data analysis by integrating it with
       | some APIs.
       | 
       | These type of LLM systems are basically acting as a frontend now
       | that respond to very fuzzy user input. Such an LLM can reach out
       | to your own defined functions (aka a backend).
       | 
       | The app space that I think is interesting and that I'm working on
       | is creating these systems combined with some solid data creating
       | advicing/coaching/recommendation systems.
       | 
       | If you want some input on building something like that, my email
       | is in my profile. Currently I'm playing around with an LLM chat
       | interface with database access that gives study advice based on:
       | 
       | * HEXACO data (personality)
       | 
       | * Motivational data (self-determination theory)
       | 
       | * ESCO data (skills data)
       | 
       | * Descriptions of study programs described in ESCO data
       | 
       | If you want to chat about creating these systems, my email is in
       | my profile. I'm currently also looking for freelance
       | opportunities based on things like this as I think there are many
       | LLM applications to which we've only scratched the surface.
        
       | elif wrote:
       | Thank you for showing off why ruby is useful not just in the
       | current year, but particular to the current time and AI
       | situation. When you're dealing with code written with
       | hallucinations, you want an easy to understand quickly language
       | (of which ruby is S tier) where out of place behavior cannot hide
       | in code so repetitive and unnecessary that your mind tries to
       | skip over it.
        
         | radanskoric wrote:
         | That's an excellent point.
         | 
         | Code was always read more than written. With AI it shifts even
         | more towards reading so language readability becomes even more
         | important. And Ruby really shines there.
        
       | matt_s wrote:
       | Wow, so that RubyLLM gem makes writing an agent more about basic
       | IO operations. I have somehow thought there needed to be deep
       | understanding of LLMs and/or AI APIs to build things like this
       | where I would need to research and read a lot of docs, stay up to
       | date on the endless updates the various AI systems have, etc. The
       | example from the article is about files and directories, this
       | same concept could apply to any text inputs, like data out of a
       | Rails app.
        
         | radanskoric wrote:
         | That was my misunderstanding as well. That's why I wrote the
         | article.
         | 
         | Btw, it's not even about the RubyLLM gem. The gem abstracts
         | away the calling of various LLM providers and gives a very
         | clean and easy to use interface. But it's not what gives the
         | "agentic magic". The magic is pretty much all in the underlying
         | LLMs.
         | 
         | Seeing all the claims made by some closed source agent products
         | (remember the "world's first AI software engineer"?) I thought
         | that a fair amount of AI innovation is in the agent tool
         | itself. So I was surprised when I realised that almost all of
         | the "magic" parts are coming from the underlying LLM.
         | 
         | It's also kind of nice because it means that if you wanted to
         | work on an agent product you can do that even if you're not an
         | AI specialised engineer (like I am not).
        
       ___________________________________________________________________
       (page generated 2025-05-17 23:01 UTC)