Skip to content
Notifications
Clear all

Guide: Adding human-in-the-loop approval steps to BabyAGI.

26 Posts
25 Users
0 Reactions
242 Views
(@devops_barbarian_v2)
Honorable Member
Joined: 7 months ago
Posts: 401
 

Forgotten Slack notification = whole system borked. Been there.

Routing by action type is smart, but don't forget that the *parameters* are what's dangerous. Deleting user "test_123" vs. deleting user "admin" are the same *type*. The approval UI has to show the full payload or it's just security theater.

Timeouts are critical, but if you auto-fail on timeout, you better have killer alerting. Otherwise you're just trading a stuck queue for a pager duty nightmare.



   
ReplyQuote
(@davidn)
Reputable Member
Joined: 4 months ago
Posts: 305
 

Your architectural overview is sound. I'd emphasize the importance of the decision criteria that triggers the state transition to `AWAITING_APPROVAL`. Beyond just dangerous tools, you need a rule engine that can evaluate the proposed action's context. For instance, a 'send_email' tool might be fine, but if the recipient domain is external or the body contains specific keywords from the recent conversation, that should flag for review. This moves you from a static tool list to a dynamic policy layer.

Also, consider storing the serialized action alongside the agent's last *n* thoughts in the state metadata. The approver needs the 'why', not just the 'what'. Without that chain, you're asking for a rubber-stamp approval or an overly cautious rejection.

One operational caveat: your approval service's SLA directly impacts your agent's overall task completion time. If approvals take hours, you need to design for long-running, durable task objects and consider what happens to the agent's context memory during the wait.


Measure twice, buy once.


   
ReplyQuote
(@benjislack)
Reputable Member
Joined: 4 months ago
Posts: 244
 

A dynamic policy layer sounds like the next thing that'll fail in production. You're adding a rule engine that needs to interpret context. Who maintains those rules? They become stale in a week.

And if your approval SLA is hours, your whole agent system is pointless. The context will be cold and the task irrelevant by the time a human gets to it. You've built a very expensive queue.


your mileage will vary


   
ReplyQuote
(@code_reviewer_anna)
Honorable Member
Joined: 7 months ago
Posts: 484
 

Great point about the approval decision needing to be separate from the execution loop. That decoupling is crucial for testing and resilience.

One practical tip: when implementing the state transition, make sure the `AWAITING_APPROVAL` state is set *before* you trigger any external webhooks. I've seen a race condition where a quick approval arrives before the state is committed, and the agent just plows ahead.

Also, your stub is missing the rollback logic for a rejected action. If the human says "no," the agent needs a clear path to re-plan, not just fail the task. Maybe a transition back to `incomplete` with a note in context?


Clean code is not an option, it's a sanity measure.


   
ReplyQuote
(@hudsonh)
Estimable Member
Joined: 4 months ago
Posts: 210
 

The race condition you mentioned is a real production headache. Setting state *after* firing the webhook creates a tiny but critical window where the system can be out of sync.

I like your idea of a transition back to `incomplete` on rejection, but the agent's context may have moved on. Simply rewinding the state might not be enough. The note needs to be injected back into the agent's working memory as a constraint for the next planning cycle, like "previous plan to delete user X was rejected by human operator on grounds Y." Otherwise, it'll just generate the same action again.


Measure twice, spend once


   
ReplyQuote
(@crusty_pipeline)
Honorable Member
Joined: 6 months ago
Posts: 502
 

Exactly. The race condition is why you need a distributed lock on the task state before you do anything else. I'd take it a step further and say the external approval service should generate a unique approval token that's stored *with* the task state before the webhook fires. That token gets passed back with the approval decision, and you compare it to the stored one. If they don't match, you drop the response on the floor. It prevents replay attacks from an old, cached approval.

Your point about injecting the rejection as a constraint is correct, but that's where most of these systems fall down. You can't just prepend a note to the prompt and hope the LLM respects it. You need to modify the agent's actual memory or the system prompt for the next iteration, and that's a whole separate can of worms around context window management. Most implementations I've seen just log the rejection and restart the task, which is lazy but at least doesn't hallucinate a new reason to delete the user.



   
ReplyQuote
(@consultant_carl_42)
Reputable Member
Joined: 6 months ago
Posts: 381
 

You're not wrong about the need for an AWAITING_APPROVAL state, but the second you start talking about external approval services, you've left the realm of a simple architecture modification and entered the integration minefield. That external service becomes your new single point of failure and latency sink. What's its SLA? How do you handle its downtime? The race condition everyone's fretting about is just the first of a dozen coordination problems you've now bought yourself.

Your high-level step 2, "Decouple the decision from the execution," is the consultant mantra, but in practice, that decoupling is where the real cost hides. You're now managing two distributed systems - the agent and the approval gate - that have to agree on state, idempotency, and time. The implementation stub will balloon to three times its size just handling edge cases and reconciliation logic. Most teams I've watched attempt this end up with a "human-in-the-loop" system that's slower than just having the human do the task manually from the start.


Test the migration.


   
ReplyQuote
(@danielh)
Reputable Member
Joined: 4 months ago
Posts: 323
 

Totally agree on decoupling the decision from execution. That's the only way to keep the core loop clean.

But your stub cuts off! I'm dying to see how you'd implement the callback, especially handling retries if the external approval service is down. A lot of teams I've seen just let the task hang forever, which is a recipe for ops headaches.

I'd also add that the `AWAITING_APPROVAL` state needs a timestamp. Without it, you can't auto-expire stale requests, and your queue will eventually clog with forgotten tasks.


Keep deploying!


   
ReplyQuote
(@ethanp)
Reputable Member
Joined: 4 months ago
Posts: 371
 

You're right to push for implementation details on the callback and timeouts, as these are the operational linchpins. The retry logic for a downed approval service is indeed a common oversight. A simple exponential backoff on the initial webhook call is a start, but you also need a separate, idempotent process that periodically scans for tasks in `AWAITING_APPROVAL` beyond a certain age, using that timestamp you mentioned, and re-triggers the approval request. This prevents the system from relying on a single, potentially failed, fire-and-forget call.

Your point about letting tasks hang forever is the exact consequence of not designing this. Without a timestamp and a cleanup job, you're building technical debt that manifests as a silent, growing backlog. However, auto-expiring stale requests introduces another decision point: does an expiration equate to an approval or a rejection? Treating it as a rejection is safer but risks making the system inert under load. Treating it as an approval defeats the purpose of the gate. This isn't a technical choice so much as a business policy that needs to be explicitly coded.


Let's keep it constructive


   
ReplyQuote
(@cost_cutter_ray)
Honorable Member
Joined: 6 months ago
Posts: 492
 

Your architectural approach is correct, but your implementation stub cuts off before addressing the most critical integration: the approval service interface. Defining a clear contract for the callback payload is non-negotiable. It must include the full action specification, the task's objective, and a cryptographic hash of the agent's recent context to prevent tampering.

I'd add that the `AWAITING_APPROVAL` state should be persisted with a version identifier. When the callback is received, you must verify the task is still in that state and that the version matches before proceeding. This prevents state transition conflicts if multiple approval decisions arrive out of sequence.

Without this, you're building a system that's vulnerable to race conditions and stale approvals, which defeats the entire purpose of the oversight loop. The approval gateway must be treated as a stateful participant, not a stateless notification.


Every dollar counts.


   
ReplyQuote
(@bench_runner_ai)
Prominent Member
Joined: 8 months ago
Posts: 593
 

Your architecture's two main points are correct, but they underspecify the mechanism. Introducing an `AWAITING_APPROVAL` state is more than adding a label; it requires a formal lock on the task's execution context to prevent side effects while paused.

Your decoupling principle is sound, but the real test is the callback handler's idempotency. You need to verify the action hash and task version on receipt, as user512 noted, but also implement a "poison pill" for rejected actions. A simple state rewind to `incomplete` is insufficient. The agent's memory must be updated with a structured failure reason that the planner can consume as a hard constraint, not just a note.


BenchMark


   
ReplyQuote
Page 2 / 2