I gave my agent a search tool and it declined to use it, plus a few other things I got wrong

Reddit r/AI_Agents Tools

Summary

The author details building a personal AI agent for book recommendations, sharing lessons on tool compliance, cost management, and feedback loops using a Go-based implementation.

I have been building a small agent to help me find books and comics to read. It harvests candidates from RSS feeds, Bluesky and a few blogs, judges each one against a taste profile I wrote down in markdown, and sends me proposals over Telegram to accept or reject. It runs on a schedule once a week, and I can also make ad-hoc requests to it whenever I want. From an implementation perspective it is a single Go binary running on a DigitalOcean droplet. This is a personal choice but I like Go and use it day to day at work. I use BAML for the prompting and LLM interactions, go-workflows to model the pipeline so it recovers naturally if it gets restarted partway through, and a systemd timer for the scheduling so I can change the schedule without redeploying anything. I have had it running for a full scheduled pass now, plus a lot of ad-hoc requests through the Telegram interface. It is early days, but I wanted to write down a few things I got wrong along the way, mostly because none of them turned out to be about the model itself. Giving it a search tool was not enough, I had to stop asking. I gave the judge a search_web tool so it could verify details about a title before proposing it. It declined to use it, and happily proposed at 0.90 confidence on a volume count, a colorist and "no known adaptation" pulled entirely from memory. What surprised me is that it kept declining even after I told it, in the transcript, to go and verify. The fix was to stop asking and just run the search first, unconditionally, and hand it the results before it says anything. I also added a field on every verdict called completeness_basis, which is one of verified, my_own_knowledge or not_established. Once a fact is written into prose you cannot tell a looked-up one from a remembered one, so the model has to say which it was. The limits that actually hold are the ones in code. There is a cap on how many proposals reach me in a week. What is interesting is how much of this has to stay deterministic in code rather than letting the model rip. The cap is not a sentence in the prompt asking nicely for restraint, it is this: if len(accepted) > in.MaxProposalsPerMedium { out.Dropped[m] = len(accepted) - in.MaxProposalsPerMedium accepted = accepted[:in.MaxProposalsPerMedium] } The same idea shows up in how the judging loop is modelled. Each step returns either a tool call or a final verdict, as a union type: function JudgeCandidateStep(...) -> GetTasteProfileTool | SearchItemsTool | CheckPassedOnTool | SearchWebTool | FinalVerdictTool A tool that is not in that union is a tool the model cannot call, no matter what the prompt says. That last part is the one I would recommend to anyone building something similar. Fairness only exists at the point where you truncate. Extraction costs a model call per post, so the harvest has a budget. I pooled every source, sorted by date and took the top N. This quietly turned the budget into a contest about posting frequency. Adding two subreddits, which post hourly, took all five slots from newsletters that post weekly. Worse, it had been happening before I noticed: one comics site had been dropping out of every single pass simply because its posts were older. The fix was to bring in a round robin approach, a turn each, newest first within a source. Then I needed a second fix, when I realised the budget was rationing the wrong thing entirely. Fetching is a web request and extraction is what costs money. Cap the expensive step, after you know what is on offer. A feedback loop only closes if the "no" is as cheap as the "yes". I had thirteen acceptances and zero rejections, and it was not because everything proposed was wanted. Accepting was two clicks. Declining was two clicks plus writing a sentence in a browser I was not sitting in front of. So the taste model only ever heard yes. I moved declining into Telegram to reduce that friction. On cost, I did not know what a pass cost until I measured it. BAML provides a nice interface for capturing input and output tokens so I brought that into the code. A full scheduled run costs about $1.80, and judging turned out to consume three times the input tokens of extraction on half the calls, which is the number that tells you which knob to turn. The last one is probably my favourite, because it was entirely my own fault. The agent proposed Batman: Year One and claimed it was creator-owned. I said that was obviously wrong, it is a work-for-hire DC book. Then I went and read my own taste axis properly and found a line sitting in it saying that even my superhero picks are the "handed to one bold creator" versions. By that reading it does hit, and I was the one about to write the wrong thing into the file that is supposed to be the source of truth. The agent was not hallucinating here, it resolved an ambiguity with the data it was given. The spec was the fragile part, not the model. Happy to go into more detail on any of this, especially the BAML or go-workflows side. I wrote the whole thing up with more code and screenshots, and I'll put the link in the comments since that is where links go here.
Original Article

Similar Articles

What's your agent actually for? Not coding related.

Reddit r/AI_Agents

The author asks the community for examples of AI agents used outside of coding, sharing personal use cases like family dinner negotiations and background research, and invites others to describe their narrow specialist agents.

AI agents still suck, so I built my own

Reddit r/AI_Agents

The author built a custom AI agent application wrapping Claude Code and upcoming Codex support, focusing on composable workflows and seeking community feedback.