Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
74
My Robotic Mower Woes 2023-05-19 A Brief Background I'm no stranger to robotic lawnmowers. When my wife and I moved into our rural home just over five years ago, we picked up the Husqvarna 450X Automower since I was far too lazy to manually mow my property and the cost was equal to that of a standard riding mower. It was a no-brainer. The Husqvarna 450X (not mine but same model) Fast-forward five years. Everything is still going great with the Automower. Some minor repairs were needed but that was mostly my fault since I was allowing the mower into places it shouldn't have been (ie. root systems and dirt "craters"). Then lightning struck our backyard at the beginning of April this year. The mower was fine, since we stow it away inside for the winter but the charging station, charging brick and perimeter wire we not so lucky... The lightning traveled along the main perimeter wire and went straight to the charging station. Boom. The charging dock was quite literally blown up into a...
over a year ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from Making software better without sacrificing user experience.

Bringing dwm Shortcuts to GNOME

Bringing dwm Shortcuts to GNOME 2023-11-02 The dwm window manager is my standard "go-to" for most of my personal laptop environments. For desktops with larger, higher resolution monitors I tend to lean towards using GNOME. The GNOME DE is fairly solid for my own purposes. This article isn't going to deep dive into GNOME itself, but instead highlight some minor configuration changes I make to mimic a few dwm shortcuts. For reference, I'm running GNOME 45.0 on Ubuntu 23.10 Setting Up Fixed Workspaces When I use dwm I tend to have a hard-set amount of tags to cycle through (normally 4-5). Unfortunately, dynamic rendering is the default for workspaces (ie. tags) in GNOME. For my personal preference I set this setting to fixed. We can achieve this by opening Settings > Multitasking and selecting "Fixed number of workspaces". Screenshot of GNOME's Multitasking Settings GUI Setting Our Keybindings Now all that is left is to mimic dwm keyboard shortcuts, in this case: ALT + $num for switching between workspaces and ALT + SHIFT + $num for moving windows across workspaces. These keyboard shortcuts can be altered under Settings > Keyboard > View and Customize Shortcuts > Navigation. You'll want to make edits to both the "Switch to workspace n" and "Move window to workspace n". Screenshot of GNOME's keyboard shortcut GUI: switch to workspace Screenshot of GNOME's keyboard shortcut GUI: move window to workspace That's it. You're free to include even more custom keyboard shortcuts (open web browser, lock screen, hibernate, etc.) but this is a solid starting point. Enjoy tweaking GNOME!

a year ago 95 votes
The X220 ThinkPad is the Best Laptop in the World

The X220 ThinkPad is the Best Laptop in the World 2023-09-26 The X220 ThinkPad is the greatest laptop ever made and you're wrong if you think otherwise. No laptop hardware has since surpassed the nearly perfect build of the X220. New devices continue to get thinner and more fragile. Useful ports are constantly discarded for the sake of "design". Functionality is no longer important to manufacturers. Repairability is purposefully removed to prevent users from truly "owing" their hardware. It's a mess out there. But thank goodness I still have my older, second-hand X220. Specs Before I get into the details explaining why this laptop is the very best of its kind, let's first take a look at my machine's basic specifications: CPU: Intel i7-2640M (4) @ 3.500GHz GPU: Intel 2nd Generation Core Processor Memory: 16GB DDR3 OS: Arch Linux / OpenBSD Resolution: 1366x768 With that out of the way, I will break down my thoughts on the X220 into five major sections: Build quality, available ports, the keyboard, battery life, and repairability. Build Quality The X220 (like most of Lenovo's older X/T models) is built like a tank. Although sourced from mostly plastic, the device is still better equipped to handle drops and mishandling compared to that of more fragile devices (such as the MacBook Air or Framework). This is made further impressive since the X220 is actually composed of many smaller interconnected pieces (more on this later). A good litmus test I perform on most laptops is the "corner test". You grab the base corner of a laptop in its open state. The goal is to see if the device displays any noticeable give or flex. In the X220's case: it feels rock solid. The base remains stiff and bobbing the device causes no movement on the opened screen. I'm aware that holding a laptop in this position is certainly not a normal use case, but knowing it is built well enough to do so speaks volumes of its construction. The X220 is also not a lightweight laptop. This might be viewed as a negative for most users, but I actually prefer it. I often become too cautious and end up "babying" thinner laptops out of fear of breakage. A minor drop from even the smallest height will severely damage these lighter devices. I have no such worries with my X220. As for the laptop's screen and resolution: your mileage may vary. I have zero issues with the default display or the smaller aspect ratio. I wrote about how I stopped using an external monitor, so I might be a little biased. Overall, this laptop is a device you can snatch up off your desk, whip into your travel bag and be on your way. The rugged design and bulkier weight help put my mind at ease - which is something I can't say for newer laptop builds. Ports Ports. Ports Everywhere. I don't think I need to explain how valuable it is to have functional ports on a laptop. Needing to carrying around a bunch of dongles for ports that should already be on the device just seems silly. The X220 comes equipped with: 3 USB ports (one of those being USB3 on the i7 model) DisplayPort VGA Ethernet SD Card Reader 3.5mm Jack Ultrabay (SATA) Wi-Fi hardware kill-switch Incredibly versatile and ready for anything I throw at it! Keyboard The classic ThinkPad keyboards are simply that: classic. I don't think anyone could argue against these keyboards being the golden standard for laptops. It's commendable how Lenovo managed to package so much functionality into such a small amount of real estate. Most modern laptops lack helpful keys such as Print Screen, Home, End, and Screen Lock. They're also an absolute joy to type on. The fact that so many people go out of their way to mod X230 ThinkPad models with X220 keyboards should tell you something... Why Lenovo moved away from these keyboards will always baffle me. (I know why they did it - I just think it's stupid). Did I mention these classic keyboards come with the extremely useful Trackpoint as well? Battery Life Author's Note: This section is very subjective. The age, quality, and size of the X220's battery can have a massive impact on benchmarks. I should also mention that I run very lightweight operating systems and use DWM as opposed to a heavier desktop environment. Just something to keep in mind. The battery life on my own X220 is fantastic. I have a brand-new 9-cell that lasts for roughly 5-6 hours of daily work. Obviously these numbers don't come close to the incredible battery life of Apple's M1/M2 chip devices, but it's still quite competitive against other "newer" laptops on the market. Although, even if the uptime was lower than 5-6 hours, you have the ability to carry extra batteries with you. The beauty of swapping out your laptop's battery without needing to open up the device itself is fantastic. Others might whine about the annoyance of carrying an extra battery in their travel bag, but doing so is completely optional. A core part of what makes the X220 so wonderful is user control and choice. The X220's battery is another great example of that. Repairability The ability to completely disassemble and replace almost everything on the X220 has to be one of its biggest advantages over newer laptops. No glue to rip apart. No special proprietary tools required. Just some screws and plastic snaps. If someone as monkey-brained as me can completely strip down this laptop and put it back together again without issue, then the hardware designers have done something right! Best of all, Lenovo provides a very detailed hardware maintenance manual to help guide you through the entire process. My disassembled X220 when I was reapplying the CPU thermal paste. Bonus Round: Price I didn't list this in my initial section "breakdown" but it's something to consider. I purchased my X220 off eBay for $175 Canadian. While this machine came with a HDD instead of an SSD and only 8GB of total memory, that was still an incredible deal. I simply swapped out the hard-drive with an SSD I had on hand, along with upgrading the DDR3 memory to its max of 16. Even if you needed to buy those components separately you would be hard-pressed to find such a good deal for a decent machine. Not to mention you would be helping to prevent more e-waste! What More Can I Say? Obviously the title and tone of this article is all in good fun. Try not to take things so seriously! But, I still personally believe the X220 is one of, if not the best laptop in the world.

a year ago 121 votes
Installing Older Versions of MongoDB on Arch Linux

Installing Older Versions of MongoDB on Arch Linux 2023-09-11 I've recently been using Arch Linux for my main work environment on my ThinkPad X260. It's been great. As someone who is constantly drawn to minimalist operating systems such as Alpine or OpenBSD, it's nice to use something like Arch that boasts that same minimalist approach but with greater documentation/support. Another major reason for the switch was the need to run older versions of "services" locally. Most people would simply suggest using Docker or vmm, but I personally run projects in self-contained, personalized directories on my system itself. I am aware of the irony in that statement... but that's just my personal preference. So I thought I would share my process of setting up an older version of MongoDB (3.4 to be precise) on Arch Linux. AUR to the Rescue You will need to target the specific version of MongoDB using the very awesome AUR packages: yay -S mongodb34-bin Follow the instructions and you'll be good to go. Don't forget to create the /data/db directory and give it proper permissions: mkdir -p /data/db/ chmod -R 777 /date/db What About My "Tools"? If you plan to use MongoDB, then you most likely want to utilize the core database tools (restore, dump, etc). The problem is you can't use the default mongodb-tools package when trying to work with older versions of MongoDB itself. The package will complain about conflicts and ask you to override your existing version. This is not what we want. So, you'll have to build from source locally: git clone https://github.com/mongodb/mongo-tools cd mongodb-tools ./make build Then you'll need to copy the built executables into the proper directory in order to use them from the terminal: cp bin/* /usr/local/bin/ And that's it! Now you can run mongod directly or use systemctl to enable it by default. Hopefully this helps anyone else curious about running older (or even outdated!) versions of MongoDB.

a year ago 60 votes
Converting HEIF Images with macOS Automator

Converting HEIF Images with macOS Automator 2023-07-21 Often times when you save or export photos from iOS to iCloud they often render themselves into heif or heic formats. Both macOS and iOS have no problem working with these formats, but a lot of software programs will not even recognize these filetypes. The obvious step would just be to convert them via an application or online service, right? Not so fast! Wouldn't it be much cleaner if we could simply right-click our heif or heic files and convert them directly in Finder? Well, I've got some good news for you... Basic Requirements You will need to have Homebrew installed You will need to install the libheif package through Homebrew: brew install libheif Creating our custom Automator script For this example script we are going to convert the image to JPG format. You can freely change this to whatever format you wish (PNG, TIFF, etc.). We're just keeping things basic for this tutorial. Don't worry if you've never worked with Automator before because setting things up is incredibly simple. Open the macOS Automator from the Applications folder Select Quick Option from the first prompt Set "Workflow receives current" to image files Set the label "in" to Finder From the left pane, select "Library > Utilities" From the presented choices in the next pane, drag and drop Run Shell Script into the far right pane Set the area "Pass input" to as arguments Enter the following code below as your script and type ⌘-S to save (name it something like "Convert HEIC/HEIF to JPG") for f in "$@" do /opt/homebrew/bin/heif-convert "$f" "${f%.*}.jpg" done Making Edits If you ever have the need to edit this script (for example, changing the default format to png), you will need to navigate to your ~/Library/Services folder and open your custom heif Quick Action in the Automator application. Simple as that. Happy converting! If you're interested, I also have some other Automator scripts available: Batch Converting Images to webp with macOS Automator Convert Files to HTML with macOS Automator Quick Actions

a year ago 34 votes
Blogging for 7 Years

Blogging for 7 Years 2023-06-24 My first public article was posted on June 28th 2016. That was seven years ago. In that time, quite a lot has changed in my life both personally and professionally. So, I figured it would be interesting to reflect on these years and document it for my own personal records. My hope is that this is something I could start doing every 5 or 10 years (if I can keep going that long!). This way, my blog also serves as a "time capsule" or museum of the past... Fun Facts This Blog: I originally started blogging on bradleytaunt.com using WordPress, but since then I have changed both my main domain and blog infrastructure multiple times. At a glance I have used: Jekyll Hugo Blot Static HTML/CSS PHPetite Shinobi pblog barf Currently using! Personal: As with anyone over time, the personal side of my life has seen the biggest updates: Married the love of my life (after knowing each other for ~14 years!) Moved out into rural Ontario for some peace and quiet Had three wonderful kids with said wife (two boys and a girl) Started noticing grey sprinkles in my stubble (I guess I can officially call myself a "grey beard"?) Professionally: Pivoted heavily into UX research and design for a handful of years (after working mostly with web front-ends) Recently switched back into a more fullstack development role to challenge myself and learn more Nothing Special This post isn't anything ground-breaking but for me it's nice to reflect on the time passed and remember how much can change in such little time. Hopefully I'll be right back here in another 7 years and maybe you'll still be reading along with me!

over a year ago 57 votes

More in programming

My first year since coming back to Linux

<![CDATA[It has been a year since I set up my System76 Merkaat with Linux Mint. In July of 2024 I migrated from ChromeOS and the Merkaat has been my daily driver on the desktop. A year later I have nothing major to report, which is the point. Despite the occasional unplanned reinstallation I have been enjoying the stability of Linux and just using the PC. This stability finally enabled me to burn bridges with mainstream operating systems and fully embrace Linux and open systems. I'm ready to handle the worst and get back to work. Just a few years ago the frustration of troubleshooting a broken system would have made me seriously consider the switch to a proprietary solution. But a year of regular use, with an ordinary mix of quiet moments and glitches, gave me the confidence to stop worrying and learn to love Linux. linux a href="https://remark.as/p/journal.paoloamoroso.com/my-first-year-since-coming-back-to-linux"Discuss.../a Email | Reply @amoroso@oldbytes.space !--emailsub--]]>

17 hours ago 3 votes
Overanalyzing a minor quirk of Espressif’s reset circuit

The mystery In the previous article, I briefly mentioned a slight difference between the ESP-Prog and the reproduced circuit, when it comes to EN: Focusing on EN, it looks like the voltage level goes back to 3.3V much faster on the ESP-Prog than on the breadboard circuit. The grid is horizontally spaced at 2ms, so … Continue reading Overanalyzing a minor quirk of Espressif’s reset circuit → The post Overanalyzing a minor quirk of Espressif’s reset circuit appeared first on Quentin Santos.

17 hours ago 2 votes
What can agents actually do?

There’s a lot of excitement about what AI (specifically the latest wave of LLM-anchored AI) can do, and how AI-first companies are different from the prior generations of companies. There are a lot of important and real opportunities at hand, but I find that many of these conversations occur at such an abstract altitude that they’re a bit too abstract. Sort of like saying that your company could be much better if you merely adopted software. That’s certainly true, but it’s not a particularly helpful claim. This post is an attempt to concisely summarize how AI agents work, apply that summary to a handful of real-world use cases for AI, and make the case that the potential of AI agents is equivalent to the potential of this generation of AI. By the end of this writeup, my hope is that you’ll be well-armed to have a concrete discussion about how LLMs and agents could change the shape of your company. How do agents work? At its core, using an LLM is an API call that includes a prompt. For example, you might call Anthropic’s /v1/message with a prompt: How should I adopt LLMs in my company? That prompt is used to fill the LLM’s context window, which conditions the model to generate certain kinds of responses. This is the first important thing that agents can do: use an LLM to evaluate a context window and get a result. Prompt engineering, or context engineering as it’s being called now, is deciding what to put into the context window to best generate the responses you’re looking for. For example, In-Context Learning (ICL) is one form of context engineering, where you supply a bunch of similar examples before asking a question. If I want to determine if a transaction is fraudulent, then I might supply a bunch of prior transactions and whether they were, or were not, fraudulent as ICL examples. Those examples make generating the correct answer more likely. However, composing the perfect context window is very time intensive, benefiting from techniques like metaprompting to improve your context. Indeed, the human (or automation) creating the initial context might not know enough to do a good job of providing relevant context. For example, if you prompt, Who is going to become the next mayor of New York City?, then you are unsuited to include the answer to that question in your prompt. To do that, you would need to already know the answer, which is why you’re asking the question to begin with! This is where we see model chat experiences from OpenAI and Anthropic use web search to pull in context that you likely don’t have. If you ask a question about the new mayor of New York, they use a tool to retrieve web search results, then add the content of those searches to your context window. This is the second important thing that agents can do: use an LLM to suggest tools relevant to the context window, then enrich the context window with the tool’s response. However, it’s important to clarify how “tool usage” actually works. An LLM does not actually call a tool. (You can skim OpenAI’s function calling documentation if you want to see a specific real-world example of this.) Instead there is a five-step process to calling tools that can be a bit counter-intuitive: The program designer that calls the LLM API must also define a set of tools that the LLM is allowed to suggest using. Every API call to the LLM includes that defined set of tools as options that the LLM is allowed to recommend The response from the API call with defined functions is either: Generated text as any other call to an LLM might provide A recommendation to call a specific tool with a specific set of parameters, e.g. an LLM that knows about a get_weather tool, when prompted about the weather in Paris, might return this response: [{ "type": "function_call", "name": "get_weather", "arguments": "{\"location\":\"Paris, France\"}" }] The program that calls the LLM API then decides whether and how to honor that requested tool use. The program might decide to reject the requested tool because it’s been used too frequently recently (e.g. rate limiting), it might check if the associated user has permission to use the tool (e.g. maybe it’s a premium only tool), it might check if the parameters match the user’s role-based permissions as well (e.g. the user can check weather, but only admin users are allowed to check weather in France). If the program does decide to call the tool, it invokes the tool, then calls the LLM API with the output of the tool appended to the prior call’s context window. The important thing about this loop is that the LLM itself can still only do one interesting thing: taking a context window and returning generated text. It is the broader program, which we can start to call an agent at this point, that calls tools and sends the tools’ output to the LLM to generate more context. What’s magical is that LLMs plus tools start to really improve how you can generate context windows. Instead of having to have a very well-defined initial context window, you can use tools to inject relevant context to improve the initial context. This brings us to the third important thing that agents can do: they manage flow control for tool usage. Let’s think about three different scenarios: Flow control via rules has concrete rules about how tools can be used. Some examples: it might only allow a given tool to be used once in a given workflow (or a usage limit of a tool for each user, etc) it might require that a human-in-the-loop approves parameters over a certain value (e.g. refunds more than $100 require human approval) it might run a generated Python program and return the output to analyze a dataset (or provide error messages if it fails) apply a permission system to tool use, restricting who can use which tools and which parameters a given user is able to use (e.g. you can only retrieve your own personal data) a tool to escalate to a human representative can only be called after five back and forths with the LLM agent Flow control via statistics can use statistics to identify and act on abnormal behavior: if the size of a refund is higher than 99% of other refunds for the order size, you might want to escalate to a human if a user has used a tool more than 99% of other users, then you might want to reject usage for the rest of the day it might escalate to a human representative if tool parameters are more similar to prior parameters that required escalation to a human agent LLMs themselves absolutely cannot be trusted. Anytime you rely on an LLM to enforce something important, you will fail. Using agents to manage flow control is the mechanism that makes it possible to build safe, reliable systems with LLMs. Whenever you find yourself dealing with an unreliable LLM-based system, you can always find a way to shift the complexity to a tool to avoid that issue. As an example, if you want to do algebra with an LLM, the solution is not asking the LLM to directly perform algebra, but instead providing a tool capable of algebra to the LLM, and then relying on the LLM to call that tool with the proper parameters. At this point, there is one final important thing that agents do: they are software programs. This means they can do anything software can do to build better context windows to pass on to LLMs for generation. This is an infinite category of tasks, but generally these include: Building general context to add to context window, sometimes thought of as maintaining memory Initiating a workflow based on an incoming ticket in a ticket tracker, customer support system, etc Periodically initiating workflows at a certain time, such as hourly review of incoming tickets Alright, we’ve now summarized what AI agents can do down to four general capabilities. Recapping a bit, those capabilities are: Use an LLM to evaluate a context window and get a result Use an LLM to suggest tools relevant to the context window, then enrich the context window with the tool’s response Manage flow control for tool usage via rules or statistical analysis Agents are software programs, and can do anything other software programs do Armed with these four capabilities, we’ll be able to think about the ways we can, and cannot, apply AI agents to a number of opportunities. Use Case 1: Customer Support Agent One of the first scenarios that people often talk about deploying AI agents is customer support, so let’s start there. A typical customer support process will have multiple tiers of agents who handle increasingly complex customer problems. So let’s set a goal of taking over the easiest tier first, with the goal of moving up tiers over time as we show impact. Our approach might be: Allow tickets (or support chats) to flow into an AI agent Provide a variety of tools to the agent to support: Retrieving information about the user: recent customer support tickets, account history, account state, and so on Escalating to next tier of customer support Refund a purchase (almost certainly implemented as “refund purchase” referencing a specific purchase by the user, rather than “refund amount” to prevent scenarios where the agent can be fooled into refunding too much) Closing the user account on request Include customer support guidelines in the context window, describe customer problems, map those problems to specific tools that should be used to solve the problems Flow control rules that ensure all calls escalate to a human if not resolved within a certain time period, number of back-and-forth exchanges, if they run into an error in the agent, and so on. These rules should be both rules-based and statistics-based, ensuring that gaps in your rules are neither exploitable nor create a terrible customer experience Review agent-customer interactions for quality control, making improvements to the support guidelines provided to AI agents. Initially you would want to review every interaction, then move to interactions that lead to unusual outcomes (e.g. escalations to human) and some degree of random sampling Review hourly, then daily, and then weekly metrics of agent performance Based on your learnings from the metric reviews, you should set baselines for alerts which require more immediate response. For example, if a new topic comes up frequently, it probably means a serious regression in your product or process, and it requires immediate review rather than periodical review. Note that even when you’ve moved “Customer Support to AI agents”, you still have: a tier of human agents dealing with the most complex calls humans reviewing the periodic performance statistics humans performing quality control on AI agent-customer interactions You absolutely can replace each of those downstream steps (reviewing performance statistics, etc) with its own AI agent, but doing that requires going through the development of an AI product for each of those flows. There is a recursive process here, where over time you can eliminate many human components of your business, in exchange for increased fragility as you have more tiers of complexity. The most interesting part of complex systems isn’t how they work, it’s how they fail, and agent-driven systems will fail occasionally, as all systems do, very much including human-driven ones. Applied with care, the above series of actions will work successfully. However, it’s important to recognize that this is building an entire software pipeline, and then learning to operate that software pipeline in production. These are both very doable things, but they are meaningful work, turning customer support leadership into product managers and requiring an engineering team building and operating the customer support agent. Use Case 2: Triaging incoming bug reports When an incident is raised within your company, or when you receive a bug report, the first problem of the day is determining how severe the issue might be. If it’s potentially quite severe, then you want on-call engineers immediately investigating; if it’s certainly not severe, then you want to triage it in a less urgent process of some sort. It’s interesting to think about how an AI agent might support this triaging workflow. The process might work as follows: Pipe all created incidents and all created tickets to this agent for review. Expose these tools to the agent: Open an incident Retrieve current incidents Retrieve recently created tickets Retrieve production metrics Retrieve deployment logs Retrieve feature flag change logs Toggle known-safe feature flags Propose merging an incident with another for human approval Propose merging a ticket with another ticket for human approval Redundant LLM providers for critical workflows. If the LLM provider’s API is unavailable, retry three times over ten seconds, then resort to using a second model provider (e.g. Anthropic first, if unavailable try OpenAI), and then finally create an incident that the triaging mechanism is unavailable. For critical workflows, we can’t simply assume the APIs will be available, because in practice all major providers seem to have monthly availability issues. Merge duplicates. When a ticket comes in, first check ongoing incidents and recently created tickets for potential duplicates. If there is a probable duplicate, suggest merging the ticket or incident with the existing issue and exit the workflow. Assess impact. If production statistics are severely impacted, or if there is a new kind of error in production, then this is likely an issue that merits quick human review. If it’s high priority, open an incident. If it’s low priority, create a ticket. Propose cause. Now that the incident has been sized, switch to analyzing the potential causes of the incident. Look at the code commits in recent deploys and suggest potential issues that might have caused the current error. In some cases this will be obvious (e.g. spiking errors with a traceback of a line of code that changed recently), and in other cases it will only be proximity in time. Apply known-safe feature flags. Establish an allow list of known safe feature flags that the system is allowed to activate itself. For example, if there are expensive features that are safe to disable, it could be allowed to disable them, e.g. restricting paginating through deeper search results when under load might be a reasonable tradeoff between stability and user experience. Defer to humans. At this point, rely on humans to drive incident, or ticket, remediation to completion. Draft initial incident report. If an incident was opened, the agent should draft an initial incident report including the timeline, related changes, and the human activities taken over the course of the incident. This report should then be finalized by the human involved in the incident. Run incident review. Your existing incident review process should take the incident review and determine how to modify your systems, including the triaging agent, to increase reliability over time. Safeguard to reenable feature flags. Since we now have an agent disabling feature flags, we also need to add a periodic check (agent-driven or otherwise) to reenable the “known safe” feature flags if there isn’t an ongoing incident to avoid accidentally disabling them for long periods of time. This is another AI agent that will absolutely work as long as you treat it as a software product. In this case, engineering is likely the product owner, but it will still require thoughtful iteration to improve its behavior over time. Some of the ongoing validation to make this flow work includes: The role of humans in incident response and review will remain significant, merely aided by this agent. This is especially true in the review process, where an agent cannot solve the review process because it’s about actively learning what to change based on the incident. You can make a reasonable argument that an agent could decide what to change and then hand that specification off to another agent to implement it. Even today, you can easily imagine low risk changes (e.g. a copy change) being automatically added to a ticket for human approval. Doing this for more complex, or riskier changes, is possible but requires an extraordinary degree of care and nuance: it is the polar opposite of the idea of “just add agents and things get easy.” Instead, enabling that sort of automation will require immense care in constraining changes to systems that cannot expose unsafe behavior. For example, one startup I know has represented their domain logic in a domain-specific language (DSL) that can be safely generated by an LLM, and are able to represent many customer-specific features solely through that DSL. Expanding the list of known-safe feature flags to make incidents remediable. To do this widely will require enforcing very specific requirements for how software is developed. Even doing this narrowly will require changes to ensure the known-safe feature flags remain safe as software is developed. Periodically reviewing incident statistics over time to ensure mean-time-to-resolution (MTTR) is decreasing. If the agent is truly working, this should decrease. If the agent isn’t driving a reduction in MTTR, then something is rotten in the details of the implementation. Even a very effective agent doesn’t relieve the responsibility of careful system design. Rather, agents are a multiplier on the quality of your system design: done well, agents can make you significantly more effective. Done poorly, they’ll only amplify your problems even more widely. Do AI Agents Represent Entirety of this Generation of AI? If you accept my definition that AI agents are any combination of LLMs and software, then I think it’s true that there’s not much this generation of AI can express that doesn’t fit this definition. I’d readily accept the argument that LLM is too narrow a term, and that perhaps foundational model would be a better term. My sense is that this is a place where frontier definitions and colloquial usage have deviated a bit. Closing thoughts LLMs and agents are powerful mechanisms. I think they will truly change how products are designed and how products work. An entire generation of software makers, and company executives, are in the midst of learning how these tools work. Software isn’t magic, it’s very logical, but what it can accomplish is magical. The same goes for agents and LLMs. The more we can accelerate that learning curve, the better for our industry.

14 hours ago 2 votes
Can tinygrad win?

This is not going to be a cakewalk like self driving cars. Most of comma’s competition is now out of business, taking billions and billions of dollars with it. Re: Tesla and FSD, we always expected Tesla to have the lead, but it’s not a winner take all market, it will look more like iOS vs Android. comma has been around for 10 years, is profitable, and is now growing rapidly. In self driving, most of the competition wasn’t even playing the right game. This isn’t how it is for ML frameworks. tinygrad’s competition is playing the right game, open source, and run by some quite smart people. But this is my second startup, so hopefully taking a bit more risk is appropriate. For comma to win, all it would take is people in 2016 being wrong about LIDAR, mapping, end to end, and hand coding, which hopefully we all agree now that they were. For tinygrad to win, it requires something much deeper to be wrong about software development in general. As it stands now, tinygrad is 14556 lines. Line count is not a perfect proxy for complexity, but when you have differences of multiple orders of magnitude, it might mean something. I asked ChatGPT to estimate the lines of code in PyTorch, JAX, and MLIR. JAX = 400k MLIR = 950k PyTorch = 3300k They range from one to two orders of magnitude off. And this isn’t even including all the libraries and drivers the other frameworks rely on, CUDA, cuBLAS, Triton, nccl, LLVM, etc…. tinygrad includes every single piece of code needed to drive an AMD RDNA3 GPU except for LLVM, and we plan to remove LLVM in a year or two as well. But so what? What does line count matter? One hypothesis is that tinygrad is only smaller because it’s not speed or feature competitive, and that if and when it becomes competitive, it will also be that many lines. But I just don’t think that’s true. tinygrad is already feature competitive, and for speed, I think the bitter lesson also applies to software. When you look at the machine learning ecosystem, you realize it’s just the same problems over and over again. The problem of multi machine, multi GPU, multi SM, multi ALU, cross machine memory scheduling, DRAM scheduling, SRAM scheduling, register scheduling, it’s all the same underlying problem at different scales. And yet, in all the current ecosystems, there are completely different codebases and libraries at each scale. I don’t think this stands. I suspect there is a simple formulation of the problem underlying all of the scheduling. Of course, this problem will be in NP and hard to optimize, but I’m betting the bitter lesson wins here. The goal of the tinygrad project is to abstract away everything except the absolute core problem in the cleanest way possible. This is why we need to replace everything. A model for the hardware is simple compared to a model for CUDA. If we succeed, tinygrad will not only be the fastest NN framework, but it will be under 25k lines all in, GPT-5 scale training job to MMIO on the PCIe bus! Here are the steps to get there: Expose the underlying search problem spanning several orders of magnitude. Due to the execution of neural networks not being data dependent, this problem is very amenable to search. Make sure your formulation is simple and complete. Fully capture all dimensions of the search space. The optimization goal is simple, run faster. Apply the state of the art in search. Burn compute. Use LLMs to guide. Use SAT solvers. Reinforcement learning. It doesn’t matter, there’s no way to cheat this goal. Just see if it runs faster. If this works, not only do we win with tinygrad, but hopefully people begin to rethink software in general. Of course, it’s a big if, this isn’t like comma where it was hard to lose. But if it wins… The main thing to watch is development speed. Our bet has to be that tinygrad’s development speed is outpacing the others. We have the AMD contract to train LLaMA 405B as fast as NVIDIA due in a year, let’s see if we succeed.

18 hours ago 1 votes
Explaining nil interface{} gotcha in Go

Explaining nil interface{} gotcha in Go A footgun In Go empty interface is an interface without any methods, typed as interface{}. A zero value of interface{} is nil: var v interface{} // compiler sets this to nil, you could explicitly write = nil if v == nil { fmt.Printf("v is nil\n") } else { fmt.Printf("v is NOT nil\n") } Try online This prints: v is nil. However, this sometimes trips people up: type Foo struct { } var v interface{} var nilFoo *Foo // implicilty initialized by compiler to nil if nilFoo == nil { fmt.Printf("nilFoo is nil.") } else { fmt.Printf("nilFoo is NOT nil.") } v = nilFoo if v == nil { fmt.Printf("v is nil\n") } else { fmt.Printf("v is NOT nil\n") } Try online This prints: nilFoo is nil. v is NOT nil. On surface level, this is wrong: t is a nil. We assigned a nil to v but it doesn’t equal to nil? How to check if interface{} is nil of any pointer type? func isNilPointer(i interface{}) bool { if i == nil { return false // interface itself is nil } v := reflect.ValueOf(i) return v.Kind() == reflect.Ptr && v.IsNil() } type Foo struct { } var pf *Foo var v interface{} = pf if isNilPointer(v) { fmt.Printf("v is nil pointer\n") } else { fmt.Printf("v is NOT nil pointer\n") } Try online Why There’s a reason for this perplexing behavior. nil is an abstract value. If you come from C/C++ or Java/C#, you might think that this is equivalent of NULL pointer or null reference. It isn’t. nil is a symbol that represents a zero value of pointers, channels, maps, slices. Logically interface{} combines type and value. You can think of it as a tuple (type, value). An uninitialized value of interface{} is a tuple without a type and value (no type, no value). In Go uninitialized value is zero value and since nil is an abstract value representing zero value for several types, it makes sense to use it for zero value of interface{}. So: zero value of interface{} is nil which is (no type, no value). When we assigned nilFoo to v, the value is (*Foo, nil). Are you surprised that (no type, no value) is not the same as (*Foo, nil)? To understand this gotcha, you have to understand two things. One: nil is an abstract value that only has a meaning in context. Consider this: var ch chan (bool) var m map[string]bool if ch == m { fmt.Printf("ch is equal to m\n") } Try online This snippet doesn’t even compile: Error:./prog.go:8:11: invalid operation: ch == m (mismatched types chan bool and map[string]bool). Both ch and m are nil but you can’t compare them because they are of different types. nil != nil because nil is an abstract concept, not an actual value. Two: nil value of interface{} is (no type, no value). Once you understand the above, you’ll understand why nil doesn’t compare to (type, nil) e.g. (*Foo, nil) or (map[string]bool, nil) or (int, 0) or (string, ""). Bad design or inevitable consequence of previous decisions? Many claim it’s a bad design. No-one describes what a better design would look like. Let’s play act a Go language designer. You’ve already designed concrete types, you came up with notion of zero value and created nil to denote zero value for pointers, channels, maps, slices. You’re now designing interface{} as a logical tuple of (type, value). The zero value is obviously (no type, no value). You have to figure how to represent the zero value. A different symbol for interface{} zero value Instead of using nil you could create a different symbol e.g. zeroInteface. You could then write: var v interface{} var v2 interface{} = &Foo{nil} var v3 interface{} = int(0) if v == zeroInteface { // this is true } if v2 == nil { // tihs is true } if v3 == nil { // is it true or not? } Is this a better design? I don’t think so. We don’t have zeroPointer, zeroMap, zeroChanel etc. so this breaks consistency. It sticks out like a sore zeroInterface. And v == nil is subtle. Not all values wrapped in an interface{} have zero value of nil. What should happen if you compare to (int, 0) given that 0 is zero value of int? Damn the consistency, let’s do what user expects You could ditch the strict logic of nil values and special case the if v == nil for interface{} to do what people superficially expect to happen. You then have to answer the question below: what happens when you do if (int, 0) == nil? The biggest issue is that you’ve lost ability to distinguish between (no type, no value) and (type, nil). They both compare to nil so how would you test for (no type, no value) but not (type, nil)? It doesn’t seem like a better design either. Your proposal Now that you understand the problem and seen two ideas for how to fix it, it’s your turn to design a better solution. I tried and the above 2 are the only ideas I had. We are boxed by existing notions of zero values and using nil to represent them. We could explore designs that re-think those assumptions but would that be Go anymore? It’s easy to complain that something is a bad design. It’s much harder, often impossible, to design something better.

yesterday 2 votes