feat: human-in-the-loop ambient integration with smart escalation (closes #482)#589
Conversation
|
@SakethSumanBathini is attempting to deploy a commit to the sreerevanth's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Review limit reached
Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🧪 PR Test Results
Python 3.12 · commit 8913ca9 |
Summary
Closes #482. AgentWatch can block dangerous actions, but has no proactive way to bring a human into the loop for the actions that fall short of an outright block but still warrant judgment — today, a user has to manually check a dashboard to find out anything needs attention. This PR adds an ambient human-in-the-loop (HITL) layer: agent actions are triaged by risk level into one of three interaction patterns — a fire-and-forget notify, an question where the agent asks a human for clarification, or a blocking review requiring approve/reject/edit — with every decision recorded in an audit trail and a bounded feedback loop that adjusts escalation thresholds based on observed human behavior.
Why this design
As with the circuit breaker in #483, this PR deliberately builds on infrastructure AgentWatch already has rather than introducing a parallel notification system:
AlertingEngine(agentwatch/alerting/engine.py) — surfaced requests are routed throughalert_event(), reusing its Slack/PagerDuty webhook delivery, retry logic, and signing. The HITL layer adds escalation logic and decision tracking on top; it doesn't reimplement webhook plumbing.RiskLevelenum andAgentEvent.safety(agentwatch/core/schema.py) — the same risk tier the safety engine already assigns to every tool call.HITLOrchestratorworks standalone (e.g. for tests, or a deployment without alerting configured yet), and a delivery failure is caught and logged rather than breaking request bookkeeping (mirroring the same graceful-degradation pattern from the [Feat] Active Circuit Breaker with Safe Pause & Resume #483 circuit breaker).The three interaction patterns
answer()approve(),reject(), oredit()before the action proceedsRiskLevel.SAFEactions (and events with no safety check at all) are auto-approved immediately and never surfaced — no notification noise for routine activity. An agent can also force the QUESTION pattern explicitly (by supplying aquestion=argument tosubmit()) regardless of the event's risk level, since "the agent is asking for clarification" is a QUESTION by definition, independent of risk.Configurable thresholds
HITLThresholdsmaps eachRiskLevelto a pattern (orNonefor auto-approve), validates its own configuration (a non-positive timeout raises), and backfills any risk levels the caller didn't specify from sensible defaults — so a partially-specified policy can never produce aKeyErroron lookup. The default policy: SAFE auto-approves, LOW/MEDIUM notify, HIGH/CRITICAL require review.Decision workflow and audit trail
approve()/reject()/edit(payload)resolve a REVIEW request;answer(text)resolves a QUESTION;expire()marks a stale PENDING request as timed out.HumanDecisionin the audit trail, queryable globally (audit_trail()) or per-session (audit_for_session()).answer()on a REVIEW request) raisesHITLErrorwith a clear message rather than silently no-oping or corrupting state.Learning from human feedback — a bounded, explainable heuristic
The issue's "learning from human feedback" criterion is intentionally the most open-ended one, so this PR implements a concrete, explainable version rather than an opaque model:
FeedbackLearnertracks, per risk level, the approval-vs-rejection rate of genuine human REVIEW decisions (auto-approvals are explicitly excluded — they carry no signal about human judgment). Once a level accumulates enough samples (min_samples, default 10):>= relax_above, default 95%) suggests relaxing that level one step (e.g. REVIEW → QUESTION) — humans keep approving it, so blocking review may be unnecessary friction.<= tighten_below, default 50%) suggests tightening it one step (e.g. NOTIFY → QUESTION) — frequent rejections mean the level deserves more scrutiny, not less.CRITICALis never relaxed below REVIEW, regardless of approval rate — a hard floor, not a suggestion the learner can override.suggest()returns proposed changes with a human-readable reason (e.g."approval rate 100% over 10 decisions; review may be unnecessary overhead");apply()actually mutates the thresholds. Both are available — a deployment can choose to log suggestions for human review, or apply them automatically.This is explicitly not a machine-learning system: it's a small, auditable rate-based heuristic with hard-coded safety bounds, so its behavior is always predictable and explainable — the same design principle behind the honest, scoped-down proposal from the #401 feasibility discussion, applied here proactively rather than after overreaching.
Testing
45 tests, 100% line coverage across all five new files. Coverage breakdown:
requires_human()correctly distinguishes NOTIFY from QUESTION/REVIEW; invalid timeout raises; a partially-specified policy is backfilled;set_pattern()mutates correctly.question=forces the QUESTION pattern regardless of risk; custom summaries are respected.HITLError; expiring an already-resolved request raises.pending()correctly excludes auto-approved/resolved requests;get()returnsNonefor unknown ids; the audit trail records every decision (including auto-approvals) and can be filtered per-session;HumanDecision.to_dict()andHITLRequest.to_dict()round-trip correctly.min_samples) raises; approval-rate tracking (including that EDIT counts as approval); no suggestion belowmin_samples; correct relax/tighten suggestions at the configured boundaries; CRITICAL never relaxed below REVIEW even at 100% approval; a mid-range approval rate produces no suggestion; a level already at its floor (auto-approve) or ceiling (REVIEW) produces no further suggestion;apply()correctly mutates the thresholds in place.Additionally verified (learned directly from the #483 review, applied proactively here):
ruff check— clean.ruff format --check— clean, run explicitly on both source and test files from the start (this was the gate that initially failed on [Feat] Active Circuit Breaker with Safe Pause & Resume #483's first push; verified up front this time rather than after the fact).mypy— clean, no type errors across all 5 source files.bandit— clean.tests/test_alerting.py(the module this integrates with) and a broader slice of the existing suite still pass; the full package still imports cleanly.if current not in order: return currentin the relax/tighten logic) were initially unreachable dead code once test coverage revealed they could never be hit — rather than suppressing the coverage gap, they were removed and replaced with a single exhaustive_PATTERN_ORDERconstant, since the order list is provably exhaustive overInteractionPattern | None.Scope note
This PR delivers the core HITL orchestration: all three interaction patterns, configurable per-risk thresholds, the full approve/reject/edit/answer decision workflow, a complete audit trail, and the bounded feedback-learning heuristic — reusing the existing Slack/PagerDuty delivery from
AlertingEngine. Discord as a third delivery channel is intentionally left as a focused follow-up: it's an additive channel-validator change toagentwatch/alerting/channels.pythat doesn't touch the HITL orchestration logic itself, and keeping it separate lets this PR's review stay focused on the escalation/decision/audit/feedback logic, which is where the substantive new behavior lives.Closes #482.