Ceres has been running the raised bed garden for a while now. It has specialists for watering, planting, pests and phenology, and it puts together a morning briefing every day. Kim raves about how much it has helped her understand what the garden needs and when. The rest of the property, the grass, the trees and the shrubs, had nothing. I mow and weed when it looks like it needs it.

Eden’s Today View

That’s why I built Eden. It is a second agent that runs alongside Ceres and covers the yard: mowing, weeds, fertilizing, overseeding, drought and pruning. I had two goals beyond a working yard assistant. I wanted to try LangGraph, since Ceres uses a custom orchestrator and I wanted to see what the framework buys you. And I wanted to reuse as much of the Ceres infrastructure as I could.

What Eden Does

Eden is an advisor. It does not control anything. Every morning it looks at the data and produces a short list of tasks, each with a time window and a reason. “Mow the upper yard in the next four days, grass has grown about an inch since the last cut.” “Hold off on overseeding, no rain in the forecast.” I accept, complete or dismiss them, and Eden keeps the record of what got done.

The property is five acres, but nowhere near five acres of grass. Most of it is canopy, two buildings, a driveway, the garden and the pond. The maintained turf came out to 0.81 acres, about 36,000 square feet in six areas, plus a couple of pastures that get mowed but never seeded. It is tall fescue, unirrigated, and it goes dormant every summer.

That last point shapes everything. Watering is not an option with this much area, and Eden is not allowed to suggest it. Drought gets measured rather than solved. How long each zone sat past the point of crown loss becomes an estimate of how much seed September needs and where. The levers are seeding, mowing height, weed control and pruning, and every recommendation has to land on one of those.

Mapping the Yard

Before Eden could cost anything per area, it needed to know what the areas actually were. Guessing from the parcel size would have been off by a factor of six.

Google Maps satellite view with annotated zones

The first tool I built was a site map editor. I downloaded an aerial photo, set a scale from a couple of distances I actually measured, and drew a polygon around each area I mow. Every polygon becomes a zone with a real square footage, a care level (full care, mow only, or unmanaged), and the seed quantity that follows from it. Mowing and seeding have different measurements, since the pastures get cut but never seeded, and the zone table keeps those straight.

There is a second editor for the cameras. Each camera that sees meaningful turf gets a polygon drawn over the grass in its view. The two sets of polygons describe different things and never get merged. The camera polygons are a sample. The aerial polygons are the census. Cameras come from Frigate. Eight of the Amcrest cameras see turf, and once a day at solar noon Eden pulls a full-resolution frame from each and archives it to the NAS. Frigate only keeps about ten days of history, so that archive is the one piece of data that cannot be regenerated later.

Back Pasture camera with annotated lawn area

Vision in Eden is a change detector, not a level meter. The same patch compared against itself over time is a real trend. Its absolute state says almost nothing about the zone around it.

Where the Data Comes From

Weather comes from the Tempest station. A listener service was already writing it into TimescaleDB for Ceres, so Eden just reads those tables, and that is enough to compute growing degree days, evapotranspiration and a soil water balance.

Ceres has soil probes in the raised beds, but those are in amended, hand-watered soil and say nothing about in-ground turf, so soil temperature is modeled. The only Home Assistant sensor Eden reads is the pond temperature, as a sanity check on that model.

The new data source is a knowledge corpus. Lawn advice on the internet is overwhelmingly Midwest advice, which means crabgrass panic, summer fertilizing and “lime fixes moss,” none of which fits western Washington. I pulled together eleven WSU and OSU Extension bulletins plus the lawn section of the PNW Plant Disease Management Handbook and embedded them into Qdrant. Anything written for eastern Washington was left out on purpose. Seasonal recommendations cite a chunk from that corpus, the citation is stored on the task, and Eden is good about saying when it has nothing to cite.

The Plant Catalog

Turf is only part of the yard. There are a lot of rhododendrons, hydrangeas, fruit trees and shrubs out there, and pruning advice is useless unless you know which plant it is and whether it flowers on old wood or new.

Adding a plant to Eden is adding photos. I walk up to it, take a few pictures in the app, and Eden identifies it and creates the record. Once it knows the name, it derives the rest on its own: the pruning rule, the bloom window, how vulnerable it is to deer.

Plant Catalog Map View

Then I drop pins on the aerial for where that plant lives. A record is a planting, not a specimen, so a dozen of the same plant is one catalog entry with a dozen pins. The Plants tab has the global map, and tapping a pin names the planting, shows the photo it was identified from, and lights up every other pin of the same record. The woody-plant specialist reads this inventory when it decides what to prune and when.

Ask Eden

The daily briefing is a schedule, and sometimes I just have a question. Ask is a chat tab grounded in the same computed conditions, zone areas and plant inventory the agent reads, with citations from the extension corpus and an optional photo. “What is this weed?” with a picture works. So does “should I be doing anything about the moss on the north side?”

It deliberately does not run the agent graph. The graph schedules work, and a question should never leave a card on the task board.

How It Is Built

Everything runs in one container on my AI server, next to the TimescaleDB, Ollama and Qdrant instances Ceres already uses. Eden shares the weather tables, Frigate, the GPU and the vector store, but no code and no tables with Ceres, and its database user is read-only against everything it did not create.

  • Python, FastAPI, Jinja2 and htmx for the app and UI
  • LangGraph with the Postgres checkpointer for orchestration
  • TimescaleDB for Eden’s own tables and for reading weather
  • Qdrant for the extension corpus
  • Ollama running qwen3:8b for cheap classification and parsing
  • Claude Sonnet and Opus for the reasoning steps
  • Frigate for camera frames
  • Gitea Actions for deploy on push to main

The one rule that matters: the agronomy math is not in the model. Growing degree days, modeled soil temperature, Penman-Monteith evapotranspiration, the water balance, dry windows and the one-third mow rule are all plain Python in one module, with tests against fixture weather. I deployed that part first, before any LLM was wired in, so I could check the numbers. The model only ever sees numbers it did not compute itself. Its job is judgment, conflict resolution and narrative.

The second rule is the safety gate. We have dogs that get walked all over the property, chickens in a run and a pond full of wildlife, so every product in the catalog has a verdict for each of those. Anything that fails is filtered out before a specialist or the Ask tab can mention it, and there is no override flag. The one exception is cut-stump glyphosate on blackberries, which is scoped to that method and has its own row.

The LangGraph Part

This is the piece I wanted to learn. The graph state is one run’s working set: the date, the weather summary, the zones, candidate tasks, conflicts, approved tasks and the briefing text. Long-term memory is not in the graph. It lives in the database, and each run starts fresh.

The graph is fairly linear. Three sense nodes fan out to pull weather, zones and camera state. Those feed a derive node that runs the agronomy math once, so every specialist reasons over the same numbers. Then four specialist subgraphs run in parallel: turf, weeds, woody plants and water. Their candidate tasks go through the safety gate, then arbitration, then scheduling, and the briefing gets written to the database.

Arbitration is mostly rules in code. Pre-emergent and overseeding in the same window, the pre-emergent loses. No nitrogen on dormant turf. No woody work during nesting season. Only the leftover conflicts, usually two tasks competing for the same dry window, go to a model, and most days there are none.

The Postgres checkpointer means every run is replayable, which has been the main way I debug the graph. State reducers bit me once: candidate tasks use an additive reducer so the parallel specialists can all append, which means the safety gate cannot write a filtered list back to the same key without it being appended to the unfiltered one. Each stage narrows into its own key instead. The interrupt mechanism handles approval for anything irreversible, like tree removal. The run pauses after the briefing and resumes when I answer.

Compared to the custom orchestrator in Ceres, I got parallel fan-out, checkpointing, replay and human-in-the-loop for free. The cost is that the framework has opinions about state you have to learn.

Model routing is a database table, copied straight from Ceres. Arbitration and seasonal planning go to Opus, the briefing and plant identification go to Sonnet, and classification and parsing go to the local qwen3 model. Every call logs tokens and cost, and a full daily run comes in well under ten cents.

The App

The interface is the same pattern Ceres uses: server-rendered templates with htmx, no build step, no JavaScript framework. It works one-handed on a phone in the yard.

Eden Conditions View

Six tabs. Today opens with one verdict, the three reasons behind it and the two or three things to do, with the full briefing behind a disclosure. Tasks is the board in urgency buckets, with a Done log that keeps completed and dismissed work instead of deleting it. Property has the zone list with both area denominators and the calibration behind them. Plants is the inventory and the pin map. Conditions shows the numbers with their provenance, so a modeled soil temperature says it is modeled and how confident it is, plus six weeks of history and the camera diagnostics tucked at the bottom. Ask is the conversation.

What Is Next

Next comes per-zone image indices, a weekly model pass over the frames, and the feedback loop that checks whether a task got done and whether anything responded.

Right now Eden is watching the overseeding window. Soil is at 57°F and cooling, but there is no rain in the ten-day forecast, so the standing advice is buy the seed and wait.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply