AI Coding Agents: From Uncontrolled Behavior to Reliable Engineering via the ReAct Loop and Rules Engineering
Many teams initially treat AI coding agents as an upgraded form of code autocomplete, but their real failure modes come from task-level autonomy, statefulness, and insufficient guardrails. This article explains how coding agents work through a simple ReAct-style loop of model calls, tool execution, and feedback, and why their engineering complexity lies mostly in context management, permissions, tools, and safety layers. It also presents practical rule-engineering techniques, including layered rule files, requirement alignment, context management, sandboxing, CI verification, and metrics, to help teams move from unpredictable agent behavior to stable, controllable engineering workflows.
Many teams initially treat AI coding agents as an upgraded form of code autocomplete. In that mental model, the workflow looks simple: the user issues an instruction, the agent generates code, and a sufficiently detailed prompt should guarantee a good result. Real usage often shows the opposite. The same task may pass on one attempt or require more than ten rounds of correction when the instruction style changes. Sometimes the agent refactors unrelated modules on its own. Sometimes it claims that tests have passed, even when the code has not been compiled successfully.
The root problem is usually not a lack of model capability. It is a misunderstanding of how the agent works. Only after understanding how an agent “thinks” and “acts” can teams explain why it becomes unstable, and how to guide it from uncontrolled behavior toward reliable execution. This article breaks that process down: starting from the core execution loop, explaining the role of each constraint layer, and providing engineering practices that can be applied directly.
1. Cognitive correction: it is not a super completion tool, but an agent that acts
Code completion and coding agents represent two fundamentally different collaboration models.
Code completion works at the level of local continuation. It predicts the next few lines or dozens of lines of code based on the context around the cursor. The human developer retains control over planning: what to write, how to organize it, and when to stop. The AI only turns intent into code. If the completion is unsatisfactory, it can be deleted at very low cost.
A coding agent works at the level of a task. The developer provides a goal, and the agent decides which file to read first, which logic to modify, whether to run tests, and how to adjust after failure. Planning authority is partially or fully transferred to the model. The AI is no longer merely a ghostwriter; it is an agent that can perceive the environment, make decisions, and take action.
This shift introduces a fundamental increase in risk. Errors are no longer limited to a few lines of code. They can be amplified through a chain of operations. A function written based on a wrong assumption may be referenced by later code. An unconfirmed file overwrite may erase someone else’s work.
Therefore, when evaluating this type of tool, the important question is not only “Can it write code?” but also “What information does it rely on when making decisions, what constraints apply to it, and can mistakes be detected and corrected in time?” Understanding this explains why the same coding agent can appear either out of control or highly productive in different teams.
2. The core: a very simple decision loop, with complexity hidden in the guardrails
Beneath the large engineering shell, the decision core of a coding agent is often surprisingly simple. The loop repeatedly does three things: call the model, execute the tools requested by the model, and return tool results to the model, until the model believes the task is complete. In pseudocode, it looks roughly like this:
# Illustrative decision loop of a coding agent: one “think-act” cyclemessages = [system_prompt, user_task] # Initial context: system prompt + user taskwhile not is_finished(messages):response = model.generate(messages) # 1. The model decides based on the current contextif response.has_tool_call: # 2a. The model chooses to actresult = execute_tool(response.tool_call) # Execute tools such as file read/write or commandsmessages.append(tool_result(result)) # 2b. Append the execution result to the contextelse:is_finished = True # 2c. The model stops calling tools and declares completion
There is no complex workflow engine in this loop, no predefined task queue, and no pre-orchestrated processing pipeline. “What to do first, what to do next, and whether to retry” is decided dynamically by the model at each turn based on the current context.
Analyses of the code structure of these tools reveal an informative conclusion: the code that carries the actual decision logic is often less than 2% of the total, while more than 98% is runtime armor built around the loop. This armor generally includes four categories:
- Context management: how to compress and retain key information when the context window is limited and the conversation becomes long.
- Permission systems: which operations can run automatically, which require human confirmation, and which must be prohibited.
- Tool implementation: how tools describe themselves, how parameters are validated, and how errors are fed back to the model.
- Security and auditing: how to resist malicious input, prevent leakage of sensitive information, and make operations traceable.
This ratio highlights a core point: whether a coding agent is useful depends mainly on the quality of its guardrails, not on how complex the decision loop itself is. A simpler decision loop means more judgment is delegated to the model. Each judgment consumes context window capacity, and every tool call really reads and writes files or executes commands. Therefore, the quality of guardrail design determines whether the model “stays on track” or “runs wild.”
3. Inside the loop: four steps per cycle under the ReAct paradigm
This loop follows the ReAct paradigm, short for Reasoning + Acting, in which reasoning and acting alternate. The core idea is that the model does not provide a complete solution all at once. Instead, it thinks, acts, and adjusts its strategy based on feedback. One cycle can be divided into four steps:
- Observation: Receive new information. This may be the user’s initial requirement, or the result of a tool execution in the previous step, such as file content, command output, or error messages.
- Reasoning: The model analyzes the current situation based on available information and decides what to do next. For example, “This error points to a missing dependency, so I should first check the dependency list.”
- Action: Call a specific tool, such as reading a file, modifying code, or running tests, thereby producing a real effect on the external world.
- Feedback: The execution result of the tool, whether success or failure, including its output, is written back into the context and becomes the input for the next observation.
These four steps repeat, forming a closed loop of hypothesis, verification, and correction. A typical scenario looks like this: the model infers a cause from an error message, reasoning; opens a related file to locate the issue, action; reads the file content and judges whether the inference is correct, observation and further reasoning; modifies the code, action; and reruns tests to verify, action and observation. It is this loop that gives the agent the ability to handle complex tasks. It does not need to know all answers before starting; it can gradually approach the goal through trial and error.
The loop also has an important characteristic: every action proposed by the model immediately produces a real consequence. An incorrect file write may overwrite existing content. An unreviewed command may affect other processes or even a production environment. A seemingly reasonable refactor may introduce subtle regressions.
A key conclusion follows: improving agent reliability is not mainly about making the model “think longer.” It is about constraining each step in the loop within a controllable range so that the agent knows what to look at, what it may do, and how to verify the result afterward. This is the problem that rules engineering aims to solve.
4. Why agents become uncontrolled: statelessness makes uncontrolled behavior the default
The loop principle also explains a common confusion: why does an agent seem to “forget” when moved to a new session, even if the instructions were clear? The reason is that coding agents are usually stateless by design. Each new session starts from a blank page. The agent knows nothing about the project’s business background, technology choices, or team conventions.
Without external rule constraints, its working mode can be summarized as “read here, think here, write here.” It reads one file and decides what to modify next based on that file. It can see local context, but not the whole project. It can imitate the code style in front of it, but it may not know the team’s actual architectural conventions. Problems tend to concentrate along four paths:
- Convention drift: indentation, naming, and comment styles are inconsistent with existing project conventions. The code may “run,” but it increases the team’s reading and maintenance cost.
- Out-of-scope operations: the agent modifies unrelated modules for convenience, or reads configuration files and secrets it should not access.
- Skipping verification: it claims “completed” without running tests or type checks, delaying discovery until code review or even after deployment.
- Overreach: it introduces new dependencies, creates new directory structures for a small change, or “optimizes” code that nobody asked to change.
From this, an important judgment can be made: uncontrolled behavior is not an occasional accident. It is the inevitable result of a stateless design operating in an unconstrained environment. The agent does not intend to violate rules. It simply lacks a basis for judging what it can and cannot do. The solution direction is therefore clear: instead of repeatedly correcting the agent after each mistake, teams should inject team knowledge into the agent in a persistent, structured way so that from the first message of a session, the agent “understands” the project.
5. Rules files: giving the agent persistent memory
5.1 Role and loading mechanism
One of the most direct and cost-effective methods is to maintain a project rules file for AI in the root directory of the code repository. In common industry practice, this file is stored as Markdown plain text. File names and loading mechanisms vary by tool, so this article refers to it generically as the “rules file.”
It is not a development document for humans. It is the agent’s persistent memory. Before each session begins, before the user sends the first message, the tool loads it into the system prompt. This is like giving the agent a complete project onboarding before it starts working.
It addresses a core tension: the context window is limited, while project knowledge must remain effective over the long term. The system prompt is a resident area that does not participate in the conversation and is not compressed. Placing key rules there truly enables “configure once, apply continuously.”
5.2 Why important rules should not only be said in the chat window
In practice, many developers simply “tell” the agent the rules in the conversation: “Do not use a new framework,” “Remember to run tests.” This may work in short sessions. However, as tasks progress, the context gradually approaches the window limit. The tool may trigger a compression mechanism: earlier conversation is automatically summarized to free space for later operations.
There is an easily overlooked but crucial difference: the rules file is re-read from disk and re-injected after context compression, while verbal instructions given in the chat window may lose detail or be distorted when compressed into a summary. In other words, the chat window is suitable for discussing “what to do this time,” while the rules file is suitable for carrying “what must always be done.” Important rules that are only spoken and not written are left to uncertain luck.
5.3 From unconstrained to constrained: a comparison
The following two illustrative snippets show the effect of a rules file. Without rule constraints, the model may improvise based on a single file and introduce a new framework that the project does not use:
# Without rule constraints: the model sees only one file and may introduce a framework not used by the projectfrom pydantic import BaseModel # The project actually uses a different validation approachclass OrderItem(BaseModel):sku: strquantity: int = 1
With rule constraints, the model follows the project’s established layering and code style, producing code consistent with the overall project:
# With rule constraints: follow the established layering and validation style, consistent with the projectfrom dataclasses import dataclass, field@dataclassclass OrderItem:sku: strquantity: int = field(default=1) # Quantity validation is handled uniformly in the service layer
Both may run, but the latter better fits the team’s long-term conventions. This value of “conforming to conventions” becomes more important as the codebase grows.
5.4 Layered organization: different levels serve different responsibilities
Mature teams usually adopt a three-layer structure, with each layer having a clear responsibility:
| Level | Typical location | Scope | Typical content |
|---|---|---|---|
| User level | User home directory | All projects for that user | Personal preferences: reply language, level of explanation |
| Project level | Repository root, committed with code | All sessions within the project | Technology stack, directory structure, coding conventions, required verification commands, prohibited actions |
| Subdirectory level | Specific module directory | When operating files under that directory | Module-specific conventions: API conventions, business terms, special dependency notes |
The project-level file should be placed under version control and reviewed together with code during code review. This ensures that rules evolve with the project and that new team members can quickly obtain agent behavior consistent with existing members.
5.5 Writing rules: executable, verifiable, and concise
A rules file is not “better if longer.” Overly long rules dilute the model’s attention, and vague wording such as “try to write better code” is almost impossible to execute. A good rule should answer “how to do it” and “how to verify it.” When writing rules, keep five points in mind:
- Prefer commands over descriptions. Instead of writing “test after modification,” specify the exact command to execute.
- Define boundaries clearly. State both what must be done and what is prohibited, especially paths that must not be touched and operations that must not be executed.
- Reference tools rather than duplicate them. Leave detailed code-style rules to formatters and static analysis tools. The rules file only needs to state which tools are used and which commands are run, avoiding drift between documentation and tooling.
- Iterate continuously. Whenever the agent repeatedly makes the same type of mistake, turn it into a rule and let the rules file evolve with the collaboration process.
- Clean up regularly. Remove rules that have become ineffective or conflict with each other, keeping the rule set concise and useful.
The following is an illustrative rules file. It can be trimmed and adapted for an actual project:
# Project Rules (Example)## Technology Stack- Use Python 3.11 and a mainstream web framework on the backend.- Do not introduce new web frameworks, ORMs, or message queues.## Directory Structure and Layering- The API layer only validates parameters and formats responses.- Business logic belongs in the service layer.- Data access belongs in the repository layer.- Do not add business scripts to the repository root.## Prohibited Actions- Do not modify committed database migration files.- Do not read or output sensitive files such as .env.- Do not execute destructive commands such as rm -rf, git push --force, or commands that delete database tables.## Required Before Delivery1. Run `make lint`.2. Run `make test`.3. Before committing, summarize in three sentences: what changed, why it changed, and how it was verified.
6. From following rules to doing things well: three-pass requirement alignment
Rules files solve the boundary problem: what can and cannot be done. However, the agent may still “follow rules while doing the wrong thing” because of misunderstanding requirements, choosing a poor approach, or starting too early on an ambiguous goal.
To address this, teams can use a “three-pass alignment” method to eliminate understanding gaps before the agent starts coding, at as low a cost as possible.
First pass: restate the requirement. After issuing the task, ask the agent to restate the goal, the modules involved, and the acceptance criteria in its own words, without rushing to modify code. If the restatement does not match your expectation, the understanding has already drifted. Correcting at this point is much cheaper than rewriting half-finished code.
Second pass: present a plan before acting. For tasks that span multiple files or involve architectural changes, require the agent to first list a modification plan: which files it intends to change, what will change in each file, and what alternative approaches exist along with their tradeoffs. A human should confirm the plan before execution. This step turns a one-time large change into a set of reviewable small decisions and prevents the agent from moving further in the wrong direction.
Third pass: define the meaning of completion. Before starting, agree on acceptance conditions: which tests must pass, which commands must succeed, and which edge cases must be covered. The more specific the completion criteria, the clearer the agent’s self-verification direction becomes, and the fewer cases of “claims complete but not actually complete” will occur.
Task granularity also directly affects the probability of losing control. Giving the agent a broad goal is like letting it improvise on a blank page. Breaking a large task into small tasks with clear boundaries and independent verification keeps each step within human view and makes it easier to locate errors when they occur.
7. Manage context: the window is a scarce resource
The context window is one of the agent’s most scarce resources. It determines how much information the model can “see” at once and directly affects reasoning quality and response speed. Three context-management strategies are worth attention.
First, start with a task and close promptly. Use a new session for each relatively independent task to avoid unrelated historical information occupying the window. When a task is complete, end the session instead of letting context accumulate indefinitely.
Second, make knowledge persist in files rather than staying only in the conversation. Important conclusions formed during discussion, such as architectural decisions, API conventions, and lessons learned, should be written into the rules file or project documentation in time. Knowledge stored in files does not disappear with the session and can be loaded directly in the next session. Knowledge left in the conversation risks being lost to compression.
Third, use retrieval instead of stuffing everything into the context at once. For large codebases, rather than pushing a large number of files into the context, let the agent use code search, symbol lookup, and similar tools to locate relevant code on demand. This “fetch only when needed” approach significantly reduces window usage and helps the model focus on truly relevant information, reducing interference from irrelevant content.
8. Build guardrails: permissions, sandboxing, and a rollback-friendly environment
Returning to the earlier point that “more than 98% is runtime armor,” the presence or absence of guardrails determines the ceiling of agent capability. These guardrails should include at least four layers:
- Permission control: classify tools into three categories: automatic execution, human confirmation, and complete prohibition. Read operations are usually lower risk, while write operations and command execution should be authorized based on their impact scope.
- Execution sandbox: run the agent in an isolated workspace, limit write scope to specified directories, impose timeouts and resource limits on command execution, and open network access only when necessary. This physically limits the blast radius of errors.
- Tool feedback quality: when a tool reports an error, the returned information should be specific enough, such as “missing type annotation on line 42,” rather than a vague “execution failed.” The more precise the feedback, the higher the quality of the model’s next reasoning step and the fewer trial-and-error cycles.
- Operation traceability: keep complete records of all tool calls. When a problem occurs, teams can trace back through the chain of observation, reasoning, and action to quickly determine whether the requirement was misunderstood, the information was wrong, or the execution step failed.
For organizations deploying agents at scale in team or enterprise environments, these guardrails can also be extended to the platform layer. Operation records can be archived in object storage, abnormal behavior can trigger real-time alerts through monitoring services, and model-service platforms can be used to unify model access and quota management. If Baidu AI Cloud is part of an organization’s technology stack, its object storage, logging, monitoring, alerting, and model-service capabilities can support this type of governance when available in the target environment.
9. Closed-loop verification: trust reproducible results, not verbal claims
The most common illusion produced by an agent is claiming that a task is complete. The only reliable way to counter this illusion is to make verification independent of the agent’s own narrative and turn it into an automated gate:
- Put automated checks before delivery. Consolidate commands such as formatting checks, static analysis, unit tests, and type checks into a single command, such as
make check, and require the agent to run it and paste the full output before delivery. - Use continuous integration as a safety net. Before code is merged, the CI pipeline should run the same checks again. Local passing does not replace the pipeline gate.
- Focus code review on intent. Reviewers do not need to check syntax line by line. They should focus on whether the change matches the design intent, whether edge cases are covered, and whether prohibited areas were touched.
- Commit in small steps and stay rollback-friendly. Use version control to split changes into multiple small, focused commits, each corresponding to a clear intent. If the direction is wrong, it can be quickly rolled back without having to investigate inside a huge change.
When “act, verify, and correct” forms a stable closed loop, the agent stops being a risk source that requires constant watching and becomes a trusted productivity tool.
10. Measure improvement: use data to judge whether “uncontrolled” behavior has decreased
Rule tuning should not rely on intuition alone. Teams also need quantifiable metrics to judge whether improvements are effective:
- First-pass success rate: the proportion of tasks that can be completed without human rework.
- Average number of interaction turns: the number of dialogue turns required to complete the same type of task. Too many turns often indicate problems in requirement expression or rule configuration.
- Human intervention rate: the proportion of operations requiring human confirmation or correction.
- Regression defect rate: the proportion of agent-generated code that later reveals problems in testing or review.
It is recommended to hold a weekly retrospective. Categorize recurring problems caused by the agent. If a problem can be converted into a rule, write it into the rules file. If it can be handled by automated tooling, add it to the check commands. After several weeks, the proportion of “uncontrolled” behavior usually decreases significantly, and each reduction corresponds to an improvement in the guardrail system.
Conclusion: turn occasional surprises into reliable engineering
Overall, the transformation of a coding agent from “uncontrolled” to “productive” is essentially a process of using engineering methods to constrain probabilistic behavior:
- Understand the principle: recognize it as a ReAct loop of observation, reasoning, action, and feedback, where decisions are simple but consequences are real.
- Enforce boundaries: replace one-off instructions in the chat window with layered, concise, executable rules files so that team knowledge persists across sessions.
- Align requirements: use restatement, planning, and acceptance criteria to eliminate understanding gaps before action begins.
- Manage resources: fetch context on demand and persist important conclusions in files.
- Add layers of defense: use permission control, execution sandboxing, operation traceability, and small-step commits to limit errors to the smallest possible scope.
- Close the verification loop: replace verbal promises with automated checks and continuous integration, and use data to measure improvement.
When this system is in place, agent performance no longer depends on luck. It depends on the quality of guardrail design. At that point, the coding agent becomes less of an occasionally surprising black box and more of an engineering partner that teams can trust, plan with, and continuously improve.
