Research, Plan, Implement: A Workflow That Keeps AI Agents Accurate A developer from HumanLayer shared a workflow to keep AI agents accurate by limiting context usage to under 40% and using a three-phase process of research, planning, and implementation, with subagents and markdown files to preserve state. The approach, inspired by a HumanLayer talk, aims to reduce hallucinations in long-running agent sessions. Have you ever had to stop an AI agent halfway through a task to correct it? Work with AI agents long enough and you'll see a pattern: the longer a session runs, the worse the output gets. Every input you give the agent and every output it produces gets appended to the context window. Nothing leaves. By the time you're fifty messages deep, the agent is re-reading abandoned approaches, stale file contents, and corrections you made an hour ago. The fix isn't a better prompt. It's less context. Keep the context window small. Two habits will keep your AI agent from hallucinating: I aim to stay under 40% context usage in the main agent. I picked up this workflow from a HumanLayer talk https://www.youtube.com/watch?v=rmvDxxNubIg , and it's the most reliable setup I've used. There are three phases, each ending in a markdown file, with a context clear between each. The main agent never needs to remember the previous phase, because the previous phase wrote it down. All it needs is the conclusion. The research phase answers how something works today. For example: Describe how the payments flow works end to end. Look carefully at the API endpoint implementations. The main agent spins up parallel subagents to figure it out. From HumanLayer's repo https://github.com/humanlayer/humanlayer/tree/main/.claude , I found three subagents to be the most useful: codebase-locator — finds where things live codebase-analyzer — explains how a component works codebase-pattern-finder — finds existing patterns to model the new work afterThe best part about using subagents is that you can point them at a cheaper model. Mine run Sonnet while the orchestrator runs Opus. The plan phase creates the exact steps needed to build the feature. It lists which files to touch, which lines, and what exactly needs to change. It runs codebase-locator and codebase-analyzer in parallel, then writes the plan. You can pass in an existing research doc. The agent will reference it but still verify it, in case the code has drifted since it was written. Plans are only as good as the requirements you give them. Agents don't have the full context, so they fill the gaps by assuming what you need. I've found those assumptions rarely match what the app actually needs. Write out the edge cases and the exact behavior you want. I'd also add a fourth subagent: context-locator , which finds prior research relevant to your task. My research docs overlap a lot, and pulling in older ones produces noticeably better plans. A real plan runs long — usually a few hundred lines across several phases. Here's the shape of one, for a made-up feature: adding rate limiting to an API. Rate Limiting Implementation Plan Current State Analysis - All routes are unthrottled — src/api/router.ts:34-58 - Redis is already available for session storage, so no new infra is needed — src/lib/redis.ts:12 - Auth middleware runs before routing , which is where a limiter would slot in — src/middleware/auth.ts:20-45 Key gaps: - No rate-limiting library installed verified: absent from package.json - No per-user identifier available on unauthenticated routes Desired End State - Authenticated requests are limited to 100/min per user; unauthenticated to 20/min per IP. - Exceeding the limit returns 429 with a Retry-After header. - Limits are configurable per route without code changes. Key Discoveries - The existing Redis client is created per-request, which will not work for a shared counter — it needs a singleton — src/lib/redis.ts:12-19 - Health-check endpoints must stay unthrottled or the load balancer will mark instances unhealthy — deploy/lb-config.yaml:22 What We're NOT Doing - No distributed quota syncing across regions. - No admin UI for adjusting limits config file only . - No billing-tier-based limits — that's a follow-up. --- Phase 1: Shared Redis Client Changes Required 1. Convert the Redis client to a singleton File : src/lib/redis.ts Changes : Export one shared connection instead of constructing per request. ts let client: RedisClient | null = null; export function getRedis : RedisClient { if client client = createClient { url: process.env.REDIS URL } ; return client; } 2. Update existing call sites Files : src/middleware/auth.ts:28 , src/api/session.ts:15 Changes : Replace new RedisClient ... with getRedis . Success Criteria Automated Verification: - Unit tests pass: npm test - Type check passes: npm run typecheck Manual Verification: - Sessions still persist across requests after the singleton change. Implementation Note : Pause after Phase 1 for confirmation — this touches session handling, so a regression here breaks login. A few things in there matter more than they look: src/api/router.ts:34-58 means the agent actually read the file instead of guessing at its shape. package.json " tells you it checked rather than inferred.The implement phase reads the plan in full before touching anything. If it can't see how the pieces fit together, it should stop and validate rather than guess. It then executes the plan, checking in when something is ambiguous. This is the step people skip. I've lost count of the research and plan docs that assumed the wrong thing. Catching those errors early saves you the bug fixes you'd otherwise do later. It's the same rule as any development cycle: catch errors early. Read every research and plan file before moving to the next phase. The instinct is to always run research → plan. That's not always the best process. context-locator more to look through and burns tokens. Add a new payment method already knows where to look.For a monorepo, I use thoughts/shared/ with research/ and plans/ subfolders, and I commit every file I create to keep the context alongside the code. The exception is large teams, where a lot of people may be committing their files. In that case, I'd keep them local and share when needed. Let me know how this works for you