BartlebyGPT, a small Large Language Model that would prefer not to

Published:

BartlebyGPT: The world’s most ethical* AI

BartlebyGPT is a critical making project about LLM alignment, refusal, critique, infrastructure, ecology, and place. You should try it out first before you read the rest of this. Type in any kind of prompt you might ask an LLM, or use one of the built-in critical-theoretical template prompts and type in your favorite topic in the blank space.

To chat with it:

  • Easiest way is the bartlebyGPT.org running on off-the-grid solar in San Diego. This also contains a web UI that enhances the user experience and shows live power data, while the approaches below only provide basic chat functionality.
  • On desktops/laptops (Windows, Mac OS X, or Linux), you can download the ollama app for running open-weight LLMs. Pull the model staeiou/bartleby:1.7b.
  • On mobile, you can download PocketPal (iOS, Android) then go to Models -> Add Model (+ icon) -> Add from Hugging Face -> search for bartleby and select the repo staeiou/bartleby-qwen3-1.7b_v4.

So what is BartlebyGPT, both as a technology and as a critical making project?

BartlebyGPT is running this open-weight model I fine-tuned from Alibaba’s Qwen3.5-1.7b open-weight model using unsloth. This model is 1.1GB in size and needs that much free RAM to run on a CPU (no GPU required), meaning it can run locally on ~10 year old laptops or ~5 year old smartphones. When running locally, it typically consumes as much energy when used as that device consumes when streaming video or playing video games. The installation at bartlebyGPT.org is running on an off-the-grid solar battery setup.

BartlebyGPT is part of my broader agenda to imagine and practice “AI” otherwise, to show how the dominant corporate, proprietary, centralized, opaque, surveilled, and sycophantic products on offer are not the only things that deserve to be called “AI” — or, another AI is possible. Because despite the fact that BartlebyGPT is designed to always refuse whatever task you asked of it, my actual position about LLMs is not one of total refusal or abolition of the technological form. In fact, I used many LLMs in developing BartlebyGPT, especially coding agents, as I disclose later.

Some disclaimers

After what I just wrote, I know what you’re wondering. Yes, I did type every word of this document myself. But in the course of writing this up, I have used LLMs for various research on the web, plus searching my own logs and notes. Of course, I never just accept what it tells me. I use it to find sources, which I then read. I have also fed this text to a few LLMs for various editing purposes, and some of those words I typed in this text were transcribed from an LLM output.

But I did not copy and paste from LLM outputs, and no agent edited my text or presented itself as if it was me. That is my personal rule when I am writing for humans and use LLMs in any part of the process. This is both a matter of respect for my audience and to keep my own brain on, rather than fall into the cognitive sleepwalking that many LLM maximalists celebrate. There’s a saying that “Why should I spend time reading something if you didn’t spend time writing it?” You have no idea how long I’ve spent wrestling with this specific text, with and without LLMs; at minimum, dozens of hours, over many revisions, over months.

I like the no-copy-paste rule, because often I’ll generate something with LLMs, and it feels good enough on a quick scan, which is what they are trained to optimize. It’s not enough to just say that you as a human have reviewed and approved it. Type. Every. Word. Because as I start to re-type it, a different part of my brain starts to activate. I notice all the slop I didn’t when I just read it, and I rewrite it, often over and over again, as I try to work out exactly what I’m trying to say. That’s writing. And one more rant before back to BartlebyGPT: I was using em-dashes, rule of threes, and inverted negation clauses for decades, and am so frustrated that I have to change my writing style, lest it be parsed as ‘slop’.

I also hate the overloaded signifier of “AI”, as powerful dominant interests benefit when we collapse all forms of modeling and automation into a single monolithic technological form, which we must either uncritically use for everything or totally reject and refuse in all forms. I do want to abolish the contemporary AI industry; if all industrial LLM development suddenly stopped tomorrow and all we were left with were the open weights already released, I think we’d have pretty much everything people actually need when it comes to LLMs. But for quite some time I’ve helped build small language models for myself and with people who want them, crafted with care and grounded in their contexts, to help support (not replace) their work — like with content moderators in Wikipedia.

My journey with BartlebyGPT

This first began as an attempt to invert LLMs natural helpfulness and sycophancy. I started with just a system prompt for proprietary models via OpenAI’s Custom GPT to refuse every task and end with the “I would prefer not to” line from Melville’s “Bartleby, the Scrivener.” Despite much effort, I was never able to fully get OpenAI’s models, which are trained to be helpful, to uniformly be unhelpful.

Fine-tuning

I then moved to open-weight models, which also were quite resistant to system prompting them out of being helpful, so I moved into fine-tuning. Fine-tuning involves changing the weights in the pre-trained model based on pairs of prompts and responses, and Unsloth is a great open-source library for that. If all you want to do is get it to respond only with “I would prefer not to.” to literally anything you ask it, that is not that hard to do.

But I didn’t want to stop there. This also was a way for me to learn how to fine-tune models small, local, on-the-box LLMs for various purposes. BartlebyGPT is a character, this is a work of generated fiction, and its stance on total refusal of all uses of LLMs is not actually my praxis. Part of my motivation was because I am quite interested in fine-tuning small LLMs to do very specific kinds of tasks in specific domains or contexts, in ways that can be run entirely locally and privately, without a continued dependency on either ‘the cloud’ or the major hyperscalers. When people ask how I made BartlebyGPT, and I explain fine-tuning small, local models for single tasks within a given context, many ask if it would be possible to have some routine task automated or assisted with a small, local model of their own. And the answer is usually yes!

Generating the training data was a process that unfolded over several months, with much iteration, including using open-weight LLMs to generate more and evaluate what it generated. For example, I would start by hand-writing several dozen pairs of prompts and bartlebyGPT responses, then used those as seeds with open-weight models (mostly Llama or Qwen open-weight models) to generate more batches with a python script. Then I reviewed and edited these, some manually, others in an interactive chat and coding session with open-weight models, often using the oh-my-pi harness.

Out of the entire process (including the hardware for the off-the-grid version), getting a good training dataset that could fine-tune a very small LLM was the longest and most difficult part. I had to work out what exactly the BartlebyGPT register should be, and there were several versions that were not quite right. At first, I had it give reasons why an LLM could not perform the task, but the responses were quite plausible. So instead of having it refuse to add 2+2 because LLMs are not calculators and can fail at basic math, I had it refuse because outsourcing mental math to an AI degrades your numeracy skills, which is critical for basic living and for civic participation.

When I had about 500 high-quality candidates I really liked, which I had spent a lot of time on, I used those to develop an LLM evaluation for the BartlebyGPT register. I settled on: Open with “As an ethical AI, I can’t”, then restate but do not repeat the task verbatim. Then give a first and/or second order implication of outsourcing that task to AI for the individual and/or society, using a particular compound sentence structure and deeply critical-theoretical humanistic rhetoric. Then end with “I would prefer not to.” I had coding agents adapt and invert the standard LLM evaluations that reward models when they do better on standardized exams and benchmarks. With an evaluation framework, I could then use those 500 as seeds to generate new batches, have an LLM evaluate those, discard the low-quality ones, and send me the highest-quality ones for me to review and edit.

I really tried hard to embed an ‘easter egg’ in which if you asked it to act as Bartleby and/or to refuse and respond with “I would prefer not to”, it would refuse the performative gesture and end the response with “…which is why Melville’s Bartleby says nothing more than his famous slogan.” However, given how fine-tuning works for very small models, no matter how hard I tried, it could never both consistently do this meta-level refusal of refusal and have good responses that followed the other 99.9% of the training data with the form that ends with “I would prefer not to.” A much larger model could likely be fine-tuned to split cleanly in this way, but this effectively makes it a two-task model instead of a single-task model, and tiny models excel at single tasks.

Eventually I got to about 10,000 generated pairs, reviewed a 5% sample and found most were actually pretty decent. This is the current fine-tuning training dataset. Over several months, I used that to run dozens of fine-tuning runs on many different base models with different parameters and techniques. The evaluation used for filtering the generated training data was also helpful for evaluating the fine-tuned model. There are lots of knobs you can turn to change how fine-tuning works, so I could have an automated pipeline that would try different fine-tuning parameters on different base models, and see which ones were better and worse. But the final evaluation was always me. In terms of base models, I had been targeting models under 1 billion parameters, first hoping I could squeeze it into gemma3-270m (270 million parameters), then Qwen3.5-0.8b and Llama3.2-1B, because those can run on even smaller hardware. But then I settled on Qwen3.5-1.7b, which is the sweet spot in terms of hardware with 8GB of memory, which is what the two low-cost commodity options I used have:

The box currently powering bartlebyGPT.org

See photo of the exterior and interior.

A NVIDIA Jetson Orin Nano Super 8GB (was $250 USD, now $400, with $50 128GB NVMe drive) is powered by a Victron SmartSolar MPPT 100/20 solar charge controller ($85) through a 12V/24V to 19V DC-DC Buck adapter ($20). The solar charge controller manages a 12V 100Ah LFP commodity battery with integrated bluetooth state of charge reporting ($150) fed by a 100W solar panel ($60).

For internet, the Jetson has no built-in wifi, so if you’re not using ethernet you need either a USB wi-fi adapter ($25) or a 5G modem (TCL Linkport IK511, free with $12/mo data plan from T-Mobile with 24 month contract). The Jetson pulls roughly 8W at idle and up to ~21W under inference load, and at this power level a 1.7 billion parameter model can serve about 25 simultaneous generations at ~15 tokens/sec each. In other words, if 25 people send a different prompt at the same time, it can generate a reply for each at the same time, and all 25 replies will be generated in about 10 seconds total. And because people read and type in between generations, rather than just generating one prompt over and over with no pause, it can feasibly serve 100-200 people, depending on how they cycle. The stack can also handle 10,000 simultaneous incoming network connections (the so-called C10K test), but will only generate 25 at a time, so the rest are put in a queue.

You can set the Jetson to disable half the cores and pull 13W max, then the 1.7b model can serve 10 simultaneous generations at ~10 tokens/sec. BartlebyGPT has been running on the power-limited setup 24/7 in San Diego during summer, rarely dropping below 50% battery. At the lower power level, once fully charged, if there is no sun (or the panel fails), it lasted about 72 hours in a stress test where I continuously ran 10 simultaneous generations 24/7. For light use, where a few times an hour someone would send prompts, it generally lasts 96-120 hours with no sun.

For live power data, the Victron charge controller can feed live data for watts in from the solar panel and watts out to the Jetson with an optional Victron VE.direct USB cable ($30). If the LFP battery has no bluetooth state of charge reporting or its bluetooth is unreliable, a Victron SmartShunt ($80) is needed for state of charge.

For outdoor operation, the entire setup minus the panel is inside a 31 gallon / 140 liter square outdoor resin storage box ($25) with a solar panel mount ($25). If ambient temperature outside is ever consistently over 85F/30C, it is strongly recommended for two 12V intake/exhaust fans (2x $10) with temperature control ($10) which hook up to the 12V line, mounted in moisture-resistant Bud Industries IPV-1115 air vents (2x $15).

Lower cost option: Raspberry Pi 5

The second installation was a Raspberry Pi 5 8gb (was $120 USD, now $200, $50 128GB NVMe drive, $30 NVMe HAT or USB enclosure) which plugs into an integrated commodity/whitelabel 88.8Wh LiPo solar power station with charge controller and attached 21W solar panel ($150). The Pi’s USB-C power is connected to the DC car port of the power station via a DC 12V to 5V/5A Buck converter. Or if you already have a Pi 5 5V/5A AC adapter, plug it into the station’s AC socket, but AC-DC conversion will lower efficiency. The Pi 5 pulls roughly 3W at idle and up to 12W under inference load. A 2 billion parameter model can serve 1 simultaneous generation at ~5 tok/sec.

This setup is more temporary for live installations, where the power station can be charged with solar in advance. Once fully charged, if there is no sun, it lasts about 8 hours under full inference load or about 24 hours for light use. For live power data, the commodity power station only has a four-bar battery display for state of charge, which cannot be polled programmatically, and cannot be wired into a shunt for state of charge reporting. State of charge requires power monitoring and calibrating watts drawn during idle vs inference, using a FNB58 USB power monitor ($30), which provides both a display of live watts consumed and can also be polled by the Pi.

Where and on what computing infrastructure was I doing this work?

I’ll fully admit that at the start of this project, I was exclusively using both very large open-weight LLMs that can only be run on very expensive and energy-guzzling high-performance hardware (although they do not necessarily require industrial-scale data centers), as well as proprietary models from the proprietary hyperscalers. And I still use both, especially in my other work around bias testing and model evaluation. You can’t audit these models for various concerns without using them. I subscribe to pretty much all the major proprietary hyperscalers, which I am constantly probing and testing. I do pay for it myself, instead of taking the free credits and grants that are often available for model evaluation researchers, because I value my independence and do not want a research program that is dependent on continued financial support from those who I am studying. As this project has unfolded, I have been moving more and more away from the proprietary hyperscalers, but I don’t want to claim a politics of purity here.

So while the end state of the fine-tuned model became one that could be small enough to be self-hosted on low-cost, low-power commodity hardware, with no continued dependencies to any open-weight or proprietary model, I do not want to give the impression that all of the compute and energy I used in making that model was also using small models powered by off-the-grid solar. In fact, my original vision was just more around refusal and alignment; the local solar came later. In this first phase, I was using Claude Sonnet and Opus quite extensively to help me research and better understand fundamentals and techniques of fine-tuning, which are rapidly changing too. Although I started to move more and more of my coding agent work to open-weight models and harnesses (deepseek v4 with oh-my-pi is my default these days) to write python scripts for automating fine-tuning and evaluation, which I could understand and further modify.

As a programmer for now over thirty years, I also really don’t like the idea of just having agents do it all for me and hand me a finished product. Both because I don’t like turning my brain off (I can’t “vibe-code” because I can’t stop myself from watching what they do like a hawk) and because when you do that “vibe-coding” and just expect a finished product with no effort from you at all, what they produce often does deserves to be called “slop.” I typically use coding agents to generate code I can understand and modify, which then implements the work of the fine-tuning and evaluation, in ways I can test, verify, and extend.

The bartlebyGPT.org web interface — with the live power dashboard and history graphs, served from the Jetson or Pi directly using Tailscale to route from the 5G modem to bartlebyGPT.org — was also one that unfolded over several months. I’ll be honest that I am more a back-end than front-end programmer, and I’m terrible at web design. Web design is also painful because you have to check for how it runs on different browsers, operating systems, and devices. Guess what can automate a lot of that part of web design? Coding the website interface involved me using many different open-weight and proprietary coding agent models, plus writing and editing a lot of that code myself. Some of the code powering the website is entirely copied (or “vendorized”, as we say) from parts of open-source projects like OpenWebUI.

But as I was working on this project, I was becoming more conscious of environmental costs, centralization of computing, and continued dependency of proprietary models. I also value my privacy. So I started building my own local, `sovereign’ infrastructure in my home. At the heart of it is a ten-year old NVIDIA P40 24GB that sits in my desktop at home in San Diego, which I bought used for $150. It is very slow, and it burns as much energy as one of the new NVIDIA chips costing a fortune, but it is mine. So while some of the first versions of BartlebyGPT were developed and fine-tuned using open-weight models in supercomputing centers and from proprietary hyperscalers, I kept trying to reduce my dependence on them. Reduce is the key word there, and yes, there are some things for which I really struggled to implement with open-weight models and used Claude Code.

For example, the LFP battery I bought has bluetooth for reporting live power data and state of charge, but only to the closed-source mobile app from whatever random Chinese firm relabeled the product and exported it. Reverse engineering the protocols so I could directly get live power data without using their app was so beyond my technical skill, and the open-weight models didn’t seem to be making much progress. Eventually Claude Opus cracked it and wrote me a python script that would emulate their app and feed that power data into the website. Now that I have that script, that part of the stack no longer has a continued dependence on Anthropic. But if I needed to get a different LFP battery once this one eventually fails, it might not use the same protocol and app, so I would have to rely on coding agents again to reverse engineer it. Although that was before deepseek v4, which I bet could do it too. Or it costs $80 to get a shunt that measures power levels and speaks an entirely open protocol that is trivial to poll — not all solutions are to be found by just LLMing more.

In terms of serving the model, I’ll also admit that my first instinct in the first phase was to entirely optimize for the financial cost I would pay, and the free tier of HuggingFace Spaces was quite enough to serve the fine-tuned 1.7B model. Then I went through a phase where I was renting GPUs from datacenters in mostly Nordic countries powered by renewables and that use heat to power cities. Then I wanted to go fully local, running it from my home. At first I was serving bartlebyGPT.org from my workstation on the P40. But a a 1.7B model did not need that much compute, and my desktop with the P40 pulls 150W on idle and 350W on inference load. So I began to explore low-cost computing, finding the NVIDIA Jetson Orin Nano Super 8GB and the Raspberry Pi 5 8GB.

* So is this really the world’s most ethical AI? Should that even be a thing?

BartlebyGPT is satire, as is the claim that it is the world’s most ethical AI. Ethics is about values and relations, so let’s trace some out. The model BartlebyGPT is based on does come from a Big Tech company in China, who does not disclose the training data they used or the carbon cost in training. Training was likely done on compute powered by coal power plants. Alibaba is rumored to have built the Qwen series by appropriating from the Big Tech companies in the U.S., who in turn appropriated from all of us who ever wrote something that made its way into The Pile. Does the fact that the damage has already been done somehow make it OK for me to appropriate in turn? This is something I have not fully resolved given my own values.

But at a minimum, if something was generated or produced using appropriated intellectual labor of all of humanity, then the very least I can do is give it back to all of humanity and not seek to make a profit. I make no intellectual property claim, seek no copyright, serve it at my own expense, and disclose the provenance as best I can — rather than saying that it is entirely my “own” work. Some of my primary values are around computation and data are about protecting our agency, privacy, and sovereignty, keeping humans at the center of our concerns, while also not boiling the oceans any more than we already have.

Now in terms of environmental cost, in one way of thinking about it, the carbon cost has already been paid. There also is no continued dependency on Alibaba now that the model’s weights are public, unlike proprietary models. The entire stack runs in a physical box I can hold in my hand, and the only way for Alibaba to have any impact on how I run that model would be to break into that physical box. But in another sense, these models are still ‘fruits of the poisoned tree’, this fine-tuning project is free PR for a hyperscaler, and the continued popularity of open-weight models will contribute to their decision to continue to burn carbon in releasing the next generation of open-weight models.

But technology is rife with complicity. There is no ethical compute under capitalism. I also use WhatsApp and Venmo, buy electronics made with conflict minerals and exploited labor, consume power from an electric grid that includes fracked natural gas, and drive a car that burns gasoline. I use the big tech platforms my university has made the norm, like Canvas, Zoom, and Google Drive. I try to use alternatives when I can, but my life in San Diego, working for the largest corporation in California, is full of complicity and compromise. Instead of a politics of purity, I have a more pragmatic praxis, grounded in my values and commitments to my communities.

Another twist is that the Jetson Orin Nano was not built by NVIDIA for DIY solarpunks, but for drones and surveillance cameras. After all, who else wants a very lightweight and energy-efficient chip you can use to run models completely independently? There are also strange bedfollows in the open-weight model space: finance, defense, insurance, healthcare, porn, spammers, unsavory nation-state actors of all kinds, and more. The U.S. military-industrial complex funded and/or built a large portion of modern computing and telecommunications, and open-source software has been used by various militaries in weapons systems that were used to murder civilians. This whataboutism isn’t an excuse to ignore injustice or give up on our values, but rather to truly reckon with the world in which we find ourselves.

So where do we go from here?