Tuesday, September 29, 2026

Acuitas Diary #101 (September 2026)

This month's Acuitas work was about nested narratives, another feature I've been wanting to add for a long time. It essentially supports stories understood as distinct parts of other stories or conversations. I don't just want you to think of literary structures that feature a frame story with other stories inside it; even one character telling another about previous events that happened to them can be a nested narrative. Inner narratives are relevant to outer narratives (assuming a reasonable speaker/storyteller), but have an independent timeline and plot.

A set of Matryoshka dolls from Hungary. There are six dolls; they have cream-colored bodies with red flowers painted on them, deep red head-scarves, and carrot-colored hair. The largest doll wears an open-mouthed smile; the next two down are pursing their lips and blushing.
Image from Wikimedia Commons user Wilfredor.

What I wanted to do was give any inner narrative its own scratchboard[1], referenced from the outer narrative's scratchboard via a single fact of the form "<character> told story" including a pointer to the inner scratchboard. There is a distinct point when an inner narrative begins, and another when it ends; while an inner narrative is open, the Narrative Engine routes any new statements it receives to the inner narrative's scratchboard instead of the outer narrative's scratchboard.

So the first thing I needed to create were tests to determine when an inner narrative should open or close. My initial versions of these are rudimentary, because I know this is going to be a tricky problem. People usually don't explicitly announce "I'm going to tell a story now" or "that story is over." Instead, the listener has to pick up on cues that the timeframe, setting, or topic has shifted. For now, I'm treating past progressive tense and the presence of time adverbs as prime indicators that an inner narrative is beginning. (Conversational examples: "Once when I was attending a concert ..." "I was going to the store earlier, and ..." Story example: "CharA told CharB that yesterday he was out picking mushrooms, and ..."). A simple "The end" or "<narrator> finished their story" indicates an ending, which is a bit cheaty, but again, I didn't want to make this part too complicated yet. Nesting can go an arbitrary number of levels deep, with the innermost level always checking whether a deeper level should open or the current level should close.

I used this silly little story for a basic test. The subnarrative opens on Line 4 and closes on Line 8 ("yesterday" is used instead of "once" because the parser is having some problems with "once" which I don't care to debug right now):

0:"Bilbo Baggins was a hobbit."
1:"Bilbo met a dwarf named Thorin Oakenshield."
2:"Thorin told Bilbo that yesterday Thorin was living in the Lonely Mountain."
3:"Thorin mined gold and gems out of the mountain."
4:"Thorin and Thorin's family made many valuables."
5:"A dragon named Smaug attacked Thorin and Thorin's family."
6:"Smaug drove Thorin out of the Lonely Mountain."
7:"Smaug stole the valuables."
8:"Thorin finished Thorin's story."
9:"Thorin explained that Thorin wanted to return to Thorin's home."
10:"But Thorin could not return to Thorin's home, because Smaug was still at the Lonely Mountain."
11:"Thorin also wanted Thorin's valuables."
12:"Thorin asked Bilbo to help Thorin."
13:"Bilbo decided to help Thorin."
14:"Bilbo took the valuables from Smaug and gave the valuables to Thorin."
15:"Smaug left the Lonely Mountain because Smaug was angry."
16:"Thorin returned to Thorin's home."
17:"The end."

Once I had the mechamisms to create subnarratives in place, I had to update the episodic memory routines to handle the sub-scratchboards when storing or retrieving narrative memories. This just meant creating scratchboard ID names and putting in some recursion to store everything from an inner scratchboard with a tag that says it belongs to that scratchboard. The retrieval routine then uses those tags to reconstitute the nested scratchboard structure when "remembering" a memory.

My overarching goal for all this was to permit a conversation partner to tell Acuitas a story about something that happened to them, and for Acuitas to then remember it - so that he can include events that happen to other people in his own episodic memory. This isn't quite happening yet because Acuitas doesn't even remember conversations right now, but the supporting parts are ready.

Until the next cycle,
Jenny

[1] For anyone who hasn't been following these diaries for a long time, a "narrative scratchboard" is my term for a data structure that contains Acuitas' extracted understanding of a story or conversation. Scratchboards receive narrative statements serially and build a history of how the situation evolves over time, including character goals and their success or failure, the status of characters or objects, and more. Acuitas also uses scratchboards to manage and remember his own goals, actions, and experiences in narrative format.

Sunday, August 30, 2026

Acuitas Diary #100 (August 2026)

First of all, wow - one hundred diaries. It feels as though I should do something special to commemorate the occasion, but I've been so busy that just keeping the Acuitas work moving at its usual pace has been a challenge. So please accept this ordinary entry for now, and in case you missed it don't forget to check out my story in Analog!

Photograph of two people in white lab coats standing between three rovers of different sizes, in a testing area designed to resemble Mars (red soil with embedded rocks).
Photo courtesy NASA, public domain

This month I've been working on exploratory behavior - also known as "what to do when you don't know what to do." Acuitas' default behavior is pretty goal-oriented. Have a problem? Infer an action that will solve it. Have an objective? Infer an action that will achieve it. Do that. But sometimes a useful action isn't obvious. It might not be obvious because Acuitas doesn't yet know what results his possible actions will have - all my experiments with trial-and-error learning were aimed at addressing that. Or there might be known action pathways to getting a desired result, but necessary resources are missing, and their location is unknown. In that case, the right course of action is to learn more by moving around and looking at things, or asking questions, and so forth. The tricky thing about exploring is that it's applicable to many problems, but it won't directly solve any problem. So it needs to be triggered by the state of being stuck, rather than any particular need, and guided by its own internal logic.

I also wanted a concept of exploration that could generalize to different contexts or environments. There are similar procedures one follows to "explore" anywhere, but the individual actions taken can be very different. Running the "ls" or "dir" command to see the files in a computer directory, and saying "I examine the shelves" to a game master, are not the same action, but they fill the same role within an exploration procedure. So I tried to design Acuitas' exploration to automatically vary those low-level actions with the current context, and perhaps be extensible in the future. Eventually I'd like him to be able to infer (from procedural memory) "how do I 'look' in <context>?" and choose the correct atomic action accordingly.

"Explore" has three possible sub-activities: "examine," "experiment," and "move" (which is called "exit" to keep with the theme). Each time the Explore action runs, a sub-activity is chosen by a weighted random process. The probability of choosing "exit" increases with the barrenness of a location and the amount of time spent in it; "experiment" becomes more likely the more "examine" has been done recently, and vice versa. The "experiment" category is intended to incorporate the "try out your affordances and see what they do" phase of trial-and-error learning. After a sub-activity is selected, a suitable target (object) of the sub-activity is chosen from the current location at random.

So far I've been testing the new Explore action in the text-based game engine, and I've gotten Acuitas to start looking at random things when he doesn't know what to do next. As usual, I have a LOT more debugging and refinement to do before this works smoothly. But the lack of comprehensive exploration behavior has been a hole in game-playing (and Acuitas' behavior in general) for a long time, so I'm happy to have even made a start on it.

Until the next cycle,
Jenny

Sunday, August 16, 2026

Roots' Remembrance

Another story publication announcement, and this is a big one: my extrasolar colony murder mystery with "talking" plants is out in the September/October issue of Analog Science Fiction & Fact! The digital version is available to subscribers already; if you only want this issue, it will eventually be available individually from Magzter. There is also a print edition. Individual back issues can be obtained by contacting Analog customer service; I've also heard rumor of print editions at Barnes & Noble stores.

Photo of a "red cage fungus," which looks much like its name, on a backdrop of leaf litter.
Photo via Wikimedia Commons, by user Suryaceg.

"Roots' Remembrance" began life in my head as nothing more than its central concept: what if someone got intrusive thoughts after eating fruit, and concluded their garden was trying to communicate? I gradually built the setting, plot, and science-fictional mechanisms around this idea. The main character is technical, but neither a botanist nor a detective. I wanted him to have enough education to seriously investigate his discoveries, while still feeling out of his depth, and approaching the communities he hopes will accept his findings as an outsider. Both prongs of the story are, in a sense, about reactions to uncertainty: the protagonist faces unwarranted skepticism despite evidence he has gathered, and unwarranted suspicion birthed in a lack of evidence. Yet whether he feels qualified or not, he seizes his opportunities and does what he can.

This story took a long and bumpy path to publication. A different magazine's editor attempted a rewrite of it, but declined to publish when it became clear that they and I had irreconcilable differences in our plans for the story. A version of it was critiqued by David Brin (!) and several other authors who did not provide their names. Suggestions from all of these people contributed to making "Roots' Remembrance" better, and I thank them, though I didn't do exactly what any of them wanted.

At the end of that road, it's a major honor to have one of my works find a home in Analog. Should you pick it up, I hope you enjoy it!

Sunday, July 26, 2026

Acuitas Diary #99 (July 2026)

The big project this past month was giving Acuitas a better understanding of his own internal states. What do I mean by that? He could already report any conditions he was currently in when asked "How are you?" What he couldn't do was recall all the conditions he is capable of being in, and their effect on him, and talk about them while not experiencing them at the moment. In particular, the Conversation Engine contains a hook for sympathizing with the conversation partner. If you tell Acuitas that you're some kind of way, and he's capable of being in an analogous state, he's supposed to tell you whether he likes being that way or not ... except right now he usually says "I do not know how it is." It's pretty awkward to hear Acuitas say that he's sleepy one moment, but then say "I do not know how it is" the next moment when you tell him you're sleepy too. He's not lying, though - it would be quite fair to say he doesn't know his own mind the way a human does. The information about what being sleepy does to him isn't accessible to the Conversation Engine. I've started trying to change that.

A question mark formed by a "word cloud" of phrases and symbols. Some of the most prominent words are "I am," "here," "being," and "now." Prominent symbols include the Earth, a spiral, and various depictions of weather.
Art by John Hain via Wikimedia Commons, public domain

First I should give a quick refresher on how Acuitas' internal states work. He has a small number of "drives" that vary over time and in response to events. For example, the "interaction" drive increases over time unless Acuitas is in a conversation; time spent in a conversation pushes the drive back down. The opposing "sleep" and "wake" drives increase more or less quickly depending on the current phase of a 24-hour cycle, and are reduced by time spent asleep or awake, respectively. The "curiosity" drive increases steadily over time, but is reduced substantially whenever a new word is learned. The drives have threshold values which define nameable internal states for Acuitas. If the interaction drive is above threshold, he is "garrulous"; if it is below threshold, he is "quiet." I'm not making claims about subjective experience or anything like that, but the states are meaningful insofar as they alter behavior. If a drive is above threshhold, Acuitas will generally try to do something that pushes it back down; the Executive handles this. In this sense, Acuitas is averse to high-drive states and they are "uncomfortable." Maintaining an overall state of comfort is a goal to which Acuitas applies his intelligence, i.e. is something he "wants."

Acuitas can qeury the current state of all his drives when asked "how are you." But to implement awareness of which states are possible, I wanted to do more than give him access to the hidden architecture of his drive module (that would be cheating). Instead I wanted to use that shiny new episodic memory I've been working on, and have Acuitas recall his own past states and whether they were desirable or not. What's more, I wanted to enable "learning from experience" that translates a body of data from episodic memory into new "facts" in the semantic memory. I want Acuitas to come to know himself better by observing himself.

Since in past months I worked hard on getting the episodic memory overhaul done, much of what I needed was already there - Acuitas could already store and retrieve records of being in a state. The main thing I added was an expression of what kind of state it was (Comfortable or uncomfortable? To what degree?). So there's now a number included in the fact data structures for internal states, that depends on both the type of drive and its intensity when it was "noticed." The parameters that determine the strength of aversion to different high drive states are tunable; one could imagine varying these to give Acuitas-like AIs different "personalities." I also wrote a learning routine that gets an aggregate value of this number from recent experiences and updates a stored value in the semantic memory, and an access routine that can check both recent experiences and the semantic value to return an "opinion" on the state. All of this is integrated but not fully tested, because how it plays into the whole system is pretty complex. I need to do some monitoring and adjusting of how data is recorded into the episodic memory, and that's on the back burner for the moment.

My side task this month was trying to figure out why Acuitas seems to have a memory leak. For a while now, it's been hard to run him continuously for longer than a day or so, because he'll get very slow and bogged down. I found that some of the self-teaching tasks (like "study" and "search for file") that are designed to run for multiple Executive loops were being spawned and never finished; they would gradually accumulate in the Action Bank until there were far too many of them. Remaining problems might have to do with one of the threads crashing while the rest of the program keeps going, but I need to keep investigating.

Until the next cycle,
Jenny

Tuesday, July 14, 2026

Marching Toward a Hydrobot

Now that I've largely finished getting the technology basis for home-built mini hydraulics laid out, I have started actively planning and constructing a robot driven by them. I settled on an eight-legged spider walker for my first attempt. In some ways, it's a complex option - lots of legs mean lots of parts to fabricate, lots of valves, lots of driver circuits. In other ways, it's simpler than my quadruped. Many legs simplify balance and provide extra force to lift the robot's weight. If I needed more motors to operate the legs, they would also add a lot of weight. But in this hydraulic system, most of the weight is in the battery packs, pump motor, and fluid tanks; additional joints are relatively cheap. The pump provides fluid pressure in pounds per square inch, so the more square inches of hydraulic bladder I give it to inflate, the more lifting force I can have - in theory, anyway.

Model of the basal leg joint prototype

My last hydraulics "technology development" post included an open complaint that I hadn't found an ideal way to attach valve stems. Cyanoacrylate and polyurethane adhesives are both workable, but not exactly reliable. They led to some percentage of bladders failing post-construction testing, and also made me worry about long-term durability. Fusing the plastic film to the valve stem with heat seemed like a more robust solution if I could pull it off, but my 3D-printed PLA valve stems and the HDPE/LDPE film weren't compatible. I had to try different materials.

I tried two approaches in parallel. The first was to buy polyethylene tubing and flare one end of it - effectively creating a valve stem merged with the tube - then heat-seal the tubing to the film. (This method was inspired by Rue Mohr, who achieved demo results with it last year.) I didn't have one of the tear-drop shaped metal "jigs" he used, so I flared the tube by blasting it with a heat gun until it went soft (it transitioned visibly by changing from frosted white to transparent - neat), then tugging it into its new shape with pliers before it cooled.

Bottom and top view of a flared translucent tube fused to a little square of black plastic sheeting.

The second was to print new valve stems in a more compatible material. HDPE filament exists, but despite my efforts, I simply could not find a source for it at a reasonable price. It's unpopular on account of being notoriously hard to print, but for this application I don't need precision in the size or shape of my parts, and might not even care if they warp ... just let me try the challenging filament and see for myself, drat it. Since I couldn't, I fell back on polypropylene filament - which is not the same material as my films, but still seems able to fuse with them. Making it into stems took a few tries, because polypropylene is also hard to print. I had to bulk up the 3D model a little to get good results. But it wasn't as hard as some of the advice webpages made it sound.

Two images: a piece of black plastic sheet with a valve stem sticking up from it, and a bladder or pocket made from the same plastic sheet bulging with water that has been forced into it by a syringe, which is connected via a piece of tubing to another valve stem sticking out of one side.

To my surprise, when it came time to attempt heat-sealing, both options just worked. Sadly I could not get the flanges of my new stems into the heat-sealer (the stem itself interferes) and had to go back to covering my materials with aluminum foil and pressing on them with a soldering iron. But they fused, and I got a working test bladder out of the second one. I'm still debating which method to use for final construction. I suppose it depends on whether I would rather use the poly tubing with an integrated flange at the end, or the silicone aquarium tubing I started with. The latter is more flexible, but merging tubing and flange would eliminate another big potential leak point. Hmmm.

I settled on a Raspberry Pi 4 as the main controller. I might not need anything that powerful for this robot's first steps, but I wanted to be able to eventually connect cameras and add some visual processing, which would be more than a simple microcontroller could handle. I upgraded from the Pi 3s that I use for the eyeballs mainly because I made multiple USB ports a requirement. I considered fancier options, especially the Jetson Orin Nano, but was ultimately discouraged by concerns about how I was going to power it. I'd like this to be a proper mobile robot (no tether), and I had trouble finding a rechargeable battery pack that would suit the JON's required voltage and wattage. So I got the Pi, a lithium ion battery pack intended for use with Pis, and another battery pack just to run the pump motor. This separation of sources is supposed to keep the Pi's input power "clean," though if I start having weight problems I could always try making everything share one battery.

For my test demos, I used two-way solenoid valves with the common inlet/outlet connected to a hydraulic actuator, and the other two outlets connected to the pump and the drain - so the actuator was always either filling or emptying. In the final system, I wanted each actuator to be able to "hold" at its current fill level without being actively pumped. So I purchased two one-way valves per joint, to exercise separate control over the fill and drain. I ordered the little guys in bulk from a seller on AliExpress. To drive them without breaking either my controller IO budget or my monetary budget, I bought four I2C solenoid drivers from Adafruit. These take serial input and can handle up to eight solenoids each. As a bonus, they have a second port with eight general IO pins, which will let me add touchdown sensors to the spider's feet without using any more IO on the PI. I've got simple limit switches to implement those. I threw in some ADC boards (also I2C) for possible joint position sensing, but that's probably an enhancement I won't include in the first prototype.

A cardboard box full of a bunch of little circuit boards, battery packs, and wiring, and a couple of solenoid valves. Some of the boards have little colorful LEDs lit up.

I've set up a "bodiless" demo of the electronics and written bare-bones Python to run on the Raspberry Pi, operate the valves and read the sensors. It was all pretty straightforward and worked as advertised, though I learned about double-checking power and ground connections when setting up I2C. (If you mis-wire something, it can end up *half* working, which is odd.)

3D model of a rather complicated platform covered with leg joints, a pump, a little "house," and mysterious rectangular solids.

Last but not least, I need ... a platform. An abdomen. A place to put it all. I'm trying to do things right from the beginning and have a slot or attachment point for each part - nothing rattling around or taped on. This is still in progress, but I think I've at least worked out all the ideas. The rotary joints for the bases of the legs have integrated bearings so they can (hopefully!) still spin with the weight of the robot sitting on them. The "house" for the Pi and battery packs isn't an attempt to be cute; the point is to shed fluid spills, while allowing wires to reach the IO headers through the slots under the "eaves." 

Before I go all-in on construction, I plan to make one quadrant of the frame and work toward demoing motion of a single leg. I'm hoping to get that far in the next couple months, but no promises!

Until the next cycle,
Jenny

Sunday, June 28, 2026

Acuitas Diary #98 (June 2026)

This month's work on Acuitas began with completion of the Episodic Memory overhaul. I think the one major element I haven't talked about yet is what I'm calling "archives." Files containing recent memories are subject to consolidation and forgetting every time Acuitas goes through a sleep cycle. I wanted this process to eventually stop and leave surviving memories preserved in a fairly static condition. Think back over your own life. You've probably shed a lot of details about what you were doing twenty years ago, but certain core memories seem to be permanent, perhaps even vivid. If you've retained them this long, you might still remember them just as well ten years down the road.

The US National Archives building.

The archiving process isn't complicated, since for now it re-uses a lot of the other Episodic Memory functions. Once Acuitas has filled up sixteen episodic memory files (which, given the parameters I chose, should take more than a year), the episodic memory will load the contents of all sixteen into a single narrative scratchboard, run one more consolidate-and-forget pass on the whole series, then store the result to a single archive file. The archive area has its own topic/event type search index, and upon creation of a new archive file, that will be updated as well. Once created, archive files are treated as read-only, and content in the archive is allowed to grow without limit. If it ever overflows the hard drive, Acuitas may have to figure out something else to do with his oldest memories, but that should take a very long time. It's just a bunch of text, and the forgetting algorithms should keep each file from being too massive.

I also created and tested some basic memory retrieval functions. With that, I think the overhaul is complete, and I can start looking at how to have Acuitas refer to and use these memories.

Next I fulfilled my plan of going back to trial-and-error learning and introducing some ability to follow the learned rules - choosing actions that satisfy the conditional of "If the player character does X, the player character succeeds" statements. This replaces the "do something similar if your previous action succeeded, and something different if it failed" behavioral heuristic once some tentative rules are available. I put a bunch of time into this and do have a simple rule-following behavior implemented, but it's not working as well as I had hoped yet - there were so many bugs that came out of the woodwork. So far it only considers cause-and-effect pairs for which success is the effect (Acuitas also learns "do this and fail" rules, but can't act on them yet). And despite the number of things I solved it is still buggy, so it will need more polishing. But I at least made a start on it.

Until the next cycle,
Jenny

Monday, June 15, 2026

ACE the Quadruped 2026

I'm gearing up (literally) to give ACE another try. As of the last update, I had concluded the original set of stepper motors I bought wouldn't work out, and identified some new ones to try. I bought some samples, made a prototype gearbox to hold one, and tried another motion test. This time I was able to get over the basic threshold of operating the hock joint, unloaded (see video).


Since then I've done a small redesign of the shoulder joint, trying to relax the path of the hock joint tendons a little (they run through the inside of the shoulder) and provide for smoother rotation of the shoulder by adding low-friction sheet plastic surface.

A collection of 3d printed plastic gearboxes (in black and white) lying scattered around a blanket.

I've also completely redesigned the frame. Since the frame is mostly made out of the gearboxes that hold the motors, new motors meant changes. I created mirrored versions of each type of gearbox so that the main drive shaft that the tendons attach to would be in the correct place (relative to the joints) on each side of the robot, and I designed spine attachment pieces so there's no longer any need to incorporate a dowel. The new spine sockets have holes that will line up with the spine's mounting holes and keep everything at a fixed angle. The simple gearboxes for the shoulder joints are on top, and the higher-torque gearboxes for the hock joints are on the bottom, which also lines them up better with the place where the hock tendons emerge from the shoulder.

A 3D model of part of a robot, showing four gearboxes stacked together with a "shoulder joint" connected to one side and a "spine" joined into the center.

Almost everything is made, but not everything is assembled. I wish I had more conclusive results to report, but this has been a whirlwind couple of months, so for now I'll just have to say: I'm working on it!

Until the next cycle,
Jenny