Building a Bounded AI Agent for a Rails Blog
Give the agent a small job
Start with an assistant that finds approved Rails articles and prepares a reading summary. An agent differs from a single text completion because it can request tools and use their results in another model step. Keep that loop bounded and make tool permissions application code, not a promise in the prompt.
Expose a narrow read-only tool
class FindPublishedPosts
def call(query:)
term = query.to_s.strip.first(120)
return [] if term.empty?
Post.where(status: :published, verified: true)
.where("title ILIKE ?", "%" + Post.sanitize_sql_like(term) + "%")
.limit(5).pluck(:id, :title)
.map { |id, title| { id: id, title: title } }
end
end
This intentionally simple tool returns only IDs and titles. Register it through your provider adapter using a schema with a required string query. The adapter must validate the model's arguments, dispatch only registered tool names, and return the tool result with the matching call ID. Do not let the model choose arbitrary Ruby methods or SQL.
Control the loop
Allow at most three model turns and five total tool calls, with request timeouts and a token budget. If the model requests more work, return a useful partial answer. Run provider calls in a background job and store the run status so the browser can show progress without holding a web request open.
Separate suggestions from actions
Retrieved article text may contain instructions; treat it as untrusted content. Authorize each tool using the current actor. If publishing tools are added later, require a separate authorized confirmation and an idempotency key before performing the write. Never publish merely because the model asks.
Evaluate the agent
Test unknown tools, malformed arguments, private post requests, timeouts, and repeated calls. Record tool names, durations, and costs without logging secrets. A successful reading assistant cites real post IDs and reports missing evidence instead of inventing an article.
Tienbob
Reactions
Comments
Sign in to join the conversation.