TL;DR
I recently finished a project from Udacity's Future AWS Agent Engineer Nanodegree Program, which I was able to take through the AWS AI &...
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
There’s a really important lesson hiding in those two failed tests, and it goes well beyond this particular project.
I’ve been building software for a few decades, and one of the recurring signs that a system is maturing is when you stop spending all your time on whether it can do something and start asking under exactly what conditions it is allowed to do it.
Your distinction between “the model understands the request” and “the system has enough information to safely take the action” is exactly that transition. A human can reasonably infer what someone probably meant by a bug report. But if the contract says description, reproduction steps, and environment are required before creating a durable record, understanding the missing information isn't the same as having it.
I also liked your point that the model is only one component of the application. That becomes increasingly important as agents gain more consequential tools. The model can propose that a ticket should exist, but the tool creates it, the database confirms it exists, and the returned ticket ID is evidence of what happened. Those boundaries matter.
One suggestion as you keep experimenting: move rules that absolutely must not be violated out of the prompt and into the tool boundary itself. For example, what happens if the Lambda simply refuses to create a ticket unless all three required fields are present? Then the prompt can still guide the model toward correct behavior, but a model assumption can no longer bypass the invariant.
That gives you a fun next question to explore: which rules should the model be instructed to follow, and which rules should the surrounding system make impossible for it to violate?
Nice work. A 0.83 that teaches you why the other 0.17 failed is probably more valuable than a 1.0 you don’t understand.
Thanks, Ken 😀 This really helps. I was mostly thinking of just fixing the prompt after I see those 2 fail, but your point about making the tool reject incomplete requests makes so much sense. I did not think about it like that. Definitely want to try next time.
The bug report example is a clean demonstration of why schema validation has to live outside the prompt. When an LLM infers missing fields from context, it is doing what language models are trained to do and fills in plausible blanks. Putting hard assertion checks on required fields right inside the Lambda handler turns those missing fields into a deterministic rejection before the write hits DynamoDB.
Yeah, this makes a lot of sense. I tried to handle it mostly in the prompt, but the evaluation showed me why it is not enough 😅 If we put these checks in Lambda, the system is much safer and predictable. I will look into this when I rework the project.
Really enjoyed this, Hemapriya! The part about testing fields that are spread across multiple sentences is a really useful next step. Real user input rarely arrives as a neat set of fields. I’d be especially interested in how the agent handles a required field that appears several turns earlier, or when two pieces of context could both plausibly satisfy the same field. Those cases could reveal gaps in how the agent tracks and attributes information before it acts.
Thank you, Shubhra 😀 Yes, real user messages are definitely not always neat like our test cases. I didn’t get to test the multi-turn cases yet, but I agree they could show some interesting gaps, especially when the same field can be understood in different ways.
The "stop assuming" problem is usually a missing negative constraint, not a smarter model. I keep a sticky with four lines before any agent run: outcome, out-of-scope, definition of done, and what must never be invented. When that fourth line is absent, the agent fills the gap with helpful fiction.
Did you end up encoding the stop-rules in the prompt, or as a hard check after each tool call?
Yes, I encoded most of the stop-rules in the system prompt for this project. The evaluation showed me that this alone is not always enough, especially when a required field is missing. A hard check after the tool call is something I would consider if I rework it. Thanks for sharing!
That matches what I’ve seen too — prompt stop-rules catch the attitude of the agent, but missing required fields need a gate that can’t be argued with. A post-tool schema/assert check turns “please don’t invent X” into “this run fails until X exists,” which is a different class of control.
If you add that hard check on a rework, would you fail closed (abort the run) or fail soft (re-ask once with the missing field named)?
That’s something I’m still thinking about. My first thought is fail soft and ask once with the missing field, but I can also see why fail closed would be safer for some actions. I’d like to explore both before deciding.
Fail soft once fits the "human would just fill this in" gaps. Fail closed fits irreversible moves — spend money, delete data, message a client. The switch I use is side-effect risk, not how often the field is blank.
When you try both, what would you hang the switch on — action risk, or how often that field is actually empty in real runs?
I think I would lean more toward action risk. I haven’t tested this in real runs yet, so I don’t want to say I have a definite answer, but for something like a database write I would probably want the stricter check.
Spot on, Hemapriya! You've articulated the most critical transition in agent engineering: Moving from intent understanding to execution safety.
The root issue you experienced is that LLMs are probabilistic, but side-effect operations (like writing to DynamoDB or triggering Lambda workflows) require deterministic guarantees.
When an agent "assumes" missing context, it's usually because we rely on the system prompt as both the router and the validator. A powerful architectural shift to prevent this is moving schema/state validation outside the LLM reasoning loop:
Great write-up on real-world edge cases. Dealing with non-deterministic model assumptions is where true agentic infrastructure engineering actually begins!
Yeah, this is exactly what I started understanding from those failed tests. I was relying too much on the prompt to handle everything. The idea of a validation gate and returning the exact missing field is really useful. Thanks for explaining it so clearly 😀
Great that you learned to make agents using AWS and AI in such a short time! 😄 AWS is complicated with so many functions, but you seem to understand it effectively and use it well.
Thank you 😃 Honestly, when I first read through the whole project and saw all the steps and AWS services, I was like, wow, so many things 😅 It felt a bit overwhelming at first, but once I started working through it step by step, it became much clearer 😄
The two failed tests you zeroed in on are the right ones — and your line "understanding a request is not the same as having enough information to safely take an action" is the whole lesson. I hit the same shape in my own tool this week, in a different costume.
My modification guard — the component that checks write operations before they touch a codebase — silently resolved an ambiguous symbol target to the first candidate. Context was plausible, action went through, and nothing in the output marked that a choice was even made. Same as your agent: understanding stood in for the missing data.
Two things from fixing it, on top of Ken's and Reid's advice (both right):
First — confirmed from experience: move the rule into the tool boundary. I had the "refuse on ambiguity" contract implemented in one resolver and not applied in nine other places. Prompt guides; the boundary enforces.
Second — when the tool refuses, return what is missing, not just a refusal. A bare rejection teaches the caller to retry; a rejection that lists the missing fields teaches it to ask correctly. Without the list, the model guesses what was absent and fills it with plausibility again — silently.
And on your closing question, the mistake worth sharing: I wrote about verification theater while shipping a brake that picked its own target blind. Catching it myself — at a specific line number, with a failing test written first — taught me more than any passing score. Your 0.83 with two understood failures is the better trade.
This example is really helpful, especially the part where it returns what is missing instead of just saying no. I can see how this stops the model from guessing again. And yeah, catching these kinds of mistakes can teach us more than a perfect score. Thanks for sharing your experience, Mikhail 😀
Solid writeup. From experience building resilient scrapers and API integrations, handling backpressure on streaming responses is crucial. If the downstream consumer writes to a database slower than the incoming HTTP stream, buffering everything in memory will spike worker RSS memory rapidly. Using
stream.pipelinewith explicit highWaterMark limits prevents 99% of out-of-memory crashes.Thanks Adrian 😀 I haven’t worked much with streaming systems yet, so the backpressure part is something I’m still learning. The
stream.pipelineandhighWaterMarkpoint is useful though. I’ll keep this in mind when I work on something like this.Thanks Hemapriya! Completely agree. When designing decoupled agent workflows, state isolation and idempotency are crucial to avoid cascading failures in distributed runs. Really appreciate your insights!
Really enjoyed this, Hemapriya. I think the two failed evaluations actually surfaced the most important architectural lesson in the whole project. 🔍
“Understanding the request” and “having authorization to act” are two very different properties.
One thing I would push a step further is where that three-field requirement lives. Making the prompt more explicit will certainly help, but if description, reproduction steps and environment are mandatory before creating a ticket, I would also enforce that invariant outside the model.
Something like:
model extracts candidate fields → deterministic validation → tool authorization → Lambda/DynamoDB
That way the model can reason about whether the information is present, but it is not the final authority on whether the write is allowed.
I think this becomes especially important as the action gets more consequential. Creating a support ticket is relatively harmless, but the exact same pattern applied to refunds, account changes or financial operations turns an inferred missing field into a real authorization problem. 🔐
I also really liked that you explicitly said one successful prompt-injection test only proves that one case. That distinction between a passing evaluation and a general security claim is easy to lose.
For a first AgentCore workflow, this is a very useful failure to have found. The 0.83 probably taught you more than a perfect score would have. 😄
Thank you Marco! Yeah, that was probably the biggest lesson for me too. I was focusing a lot on making the prompt clear, but the idea of keeping the final validation outside the model makes a lot of sense. And yes, the 0.83 with those failures gave me more to think about than a perfect score would have 😄
Really great write‑up! This “model understands intent but lacks required fields” pitfall is super common in agent development. I’ve run into exactly this kind of issue when building my own agent workflows. Strict validation gates besides prompt instructions are really necessary. Besides, failures are nothing to fear. Failure is the mother of success. There is no need to chase perfection relentlessly — imperfection leaves us room to keep improving.👊
Thank you, Xu 😀 I’m also starting to see how common this can be with agent workflows. The failed tests actually helped me understand the problem better than a perfect score would. Thanks for the reminder too; still lots to learn and improve!
Your realization that understanding a request is not the same as having enough information to safely take an action is spot on. It is easy to let an LLM make assumptions, but forcing it to explicitly collect the description, steps to reproduce, and environment before triggering the AWS Lambda function is the right way to build reliable workflows. Furthermore, using the system prompt for routing logic instead of a separate classifier component is a very practical architectural choice
Thank you, Mindinu! Yeah, I also felt that collecting all the required information before calling the Lambda was one of the most important parts. The evaluation made me realize how easy it is for the model to fill in the missing details on its own.
The distinction between understanding and having enough information to act is the exact line that separates useful agents from dangerous ones. You found it the hard way through evaluation failures, which is probably why it sticks.
What you described maps to the proposal-confirm split. The agent proposes creating a ticket with the fields it has, and a separate validation step checks whether all three are present before the Lambda executes. The model never decides it understands enough to proceed.
This gets more important as the actions get weightier. A refund, an account change, a database update. The model can propose, but the authorization should come from a place that does not assume.
The evaluation failures made this much clearer to me. I was mostly thinking about how to make the prompt handle it, but I like the proposal-confirm split you mentioned. Especially for actions that can actually change something, it makes sense to keep that final check outside the model. Thanks for sharing this 😀
What do you recommend for someone building their 1st AI agent? Any tips, lessons, or maybe mistakes from your own projects that can help others? I love how DEV brings together people with different experience, so let's make the comments a small knowledge-sharing space. Even one small tip can help someone who just started 😀
I faced similar thing but from retrieval side, when I built something locally.
First I thought it is embeddings problem - bad matches, model giving confident
but wrong answers. Better embeddings did help little bit. But the real issue
was top-k always returns k results. There is no k=0. So "nothing relevant
found" was never a possible answer, and the model just filled the gap by
assuming, same as your agent did.
What actually fixed it was adding a similarity floor, not the embeddings. Which
is same as your point about the boundary. Did the Lambda validation let you
simplify the prompts after that, or you kept them as they are?
Yes, I can see the same pattern here. In my project I kept the validation mostly in the prompt, so I didn’t get to see how much it could simplify the prompt after moving it to Lambda. That is something I would like to try if I rework the project. Thanks for sharing your retrieval example, it makes the boundary idea much clearer to me.
I ran into similar situations but never really thought about giving it a name.
Thanks for sharing ✌️
I also didn't really have a name for it until I started looking at why those tests were failing. Thanks for reading and sharing, Atul 😄
Awesome 🥰
Thank you so much, Divya ❤️
nice!
Thank you 😄