Start with a runaway Agent call
Three months ago, I took over a AI agent project: let the agent automatically manage the customer work order system, including reading work orders, assigning responsible persons, and sending replies. Sounds simple, right? As a result, on the first day of launch, the agent called the "Reassignment" API 37 times in a loop and forwarded the same work order to 37 people. Customers complained directly to the CEO. At that moment I realized that Agent Engineering is not just about writing a few prompts - it requires systematic engineering thinking.
Here are 5 core lessons I learned during my subsequent refactoring.
1. Don’t just write schema for tool architecture, but also write “constraints”
When I first defined the tool function, I only wrote the function name, parameters, and return type. For example assign_ticket(ticket_id, assignee_id). As a result, agents often pass in assignee_id that does not exist, or assign work orders to people who have resigned.
The conclusion that really changes the way I judge is: Tool schema must contain business semantic constraints.
My approach:
- Write rules such as "Please ensure assignee_id is a current employee" in the parameter description.
- More radically, add enumeration or validation regex at parameter level. For example assignee_id only allows numbers and has a fixed length of 6.
- If the parameter relies on external data, such as "can only be assigned to your direct subordinates", then check the database first in the tool code for verification, and return a clear error instead of letting the agent guess.
Before the correction, my schema only had 5 lines; after the correction, the description of each tool increased to 15 lines on average, and parameter verification was built in. The price is more code, but the agent call failure rate dropped from 23% to 4%.

2. Permission control must be changed from "Default Allow" to "Explicit Deny"
By default in many agent frameworks, the agent can call all registered tools. The same happened to me in the early days - as a result, the agent automatically called the "Delete Work Order" interface and deleted a non-test work order by mistake.
Lesson: The scope of agent authority should be as small as possible, and then gradually relaxed.
What you can do:
- Use a configuration file or database table to explicitly list the operations each agent is allowed to perform.
- During the tool registration phase, use decorators or middleware to perform permission checks. For example
@require_permission('ticket:assign'). - For dangerous operations (deleting, modifying amounts, sending external notifications), add a secondary confirmation step to allow the agent to generate a "pre-execution" request, which is actually executed after manual confirmation.
I later added a "permission list" object in the refactoring, which was loaded when the agent started and verified before each tool call. This change reduces misoperations to zero. But note: the permissions list itself needs to be audited regularly because business roles change.

3. The loop will not disappear on its own, you must actively detect and interrupt it
The 37 reallocations mentioned earlier are caused by loops. The agent found that the status of the work order was wrong and believed that it "should be reassigned". The status did not change after the allocation, so it continued to allocate.
Loops are the most common failure scenario for Agent Engineering. The solution is not complicated:
- Step Counter: Limit the total number of steps in an agent run (for example, up to 20 steps). If it exceeds the limit, it will be forced to end and the intermediate result will be returned.
- Status Fingerprint Detection: Record each status snapshot. If a duplicate status is detected, it may be trapped in a loop and trigger a breakpoint.
- Built-in deduplication logic: Some loops are caused by the agent repeatedly calling tools with the same parameters. You can cache recent calls (such as the last 5 call hashes) at the tool call layer, and return "executed, no need to repeat" if duplicates are found.
Specific method: I wrote a LoopDetector class to maintain a queue of the last 10 status fingerprints. After each tool call, the current status hash is calculated and compared. If the repetition rate exceeds the threshold, it will interrupt and output a warning. After this mechanism was put online, failures caused by loops were reduced by 90%.
4. MCP is a double-edged sword: it can expand capabilities and may also introduce phantom dependencies
MCP (Model Context Protocol) is a good way for agents to obtain external context, such as connecting to databases, file systems, and Web APIs. But when I tried MCP, I found that the agent often relied on data that was not clearly stated in MCP, leading to inference bias.
A typical problem: the agent obtains the user's recent order list from MCP, but the data returned by MCP is mixed with canceled orders. The agent has no filtering and directly uses the status of canceled orders as the basis for judging user activity.
My lesson: MCP’s data must come with its own filtering and interpretation. The agent will use whatever the data source returns. You don't want the agent to blindly guess the reliability of the data.
Solution:
- Add "Data Source Description" and "Validity Period" to each data field when integrating MCP.
- When data is returned, it is accompanied by a "data quality label", such as "Updated in the last hour", "May contain canceled orders".
- If possible, perform data cleaning in the tool function beforehand. For example, the "Get User Orders" tool internally filters out canceled orders.
After introducing these, the agent's decision-making accuracy based on MCP has been significantly improved. But also be careful: don't over-process the data for MCP, otherwise it may hide the real problem.
5. When you find that the input-output ratio drops, change the route in time
Not all problems can be solved by agents. I encountered such a scenario in my project: I spent two weeks optimizing the agent's processing of a specific work order type, but the accuracy only increased from 80% to 82%. If you use traditional rules + simple classification model instead, you can reach 95% in one day.
How to tell if it’s time to change route?
- Marginal benefit: If each optimization only improves by less than 1% and the code complexity skyrockets, then stop.
- Failure Mode: If the agent's failure is random (for example, the error causes are completely different in different scenarios), then it is likely that the scenario is not suitable for the agent, and a deterministic solution should be used instead.
- Testability: If it is difficult to construct test cases to verify the improvement effect, it means that the problem may be too vague and the agent will just try its luck.
My decision-making principle: If key indicators (such as success rate) cannot be improved to an acceptable level within 3 iterations, stop - first use a simple solution to get the bottom of it, and then spend time understanding the root cause instead of blindly stacking prompts.
What scenarios are suitable for continuing to invest?
- Tasks have clear patterns but complex rules, such as work order classification and email reply.
- You need the agent to make decisions independently, but the cost of failure is controllable, such as recommending content and generating drafts.
- You have engineering capabilities and can do tool definition, permissions, and cycle detection.
When should we change routes?
- Tasks are completely random or irregular, such as user input without pattern.
- The cost of failure is extremely high, such as financial transactions and medical diagnosis.
- You cannot control the input and output format of the agent, such as integrating external uncontrollable APIs.
If you want to start now, the most worth following step is
Find a real but low-risk task (such as automatic assignment of internal work orders), and first manually complete the entire agent building process: registering tools, setting permissions, adding loop detection, and testing 10 scenarios. Then record the failure data. Don’t aim for perfection, the simpler the first version the better – you’ll be rewriting it.
My biggest feeling during this process is: Agent Engineering’s success does not lie in how smart the agent is, but in how rigorous its engineering constraints are.

No comments yet. Be the first to share your thoughts.