[Multi-Agent Privacy] Add Adversarial agent with initial pipeline#338
[Multi-Agent Privacy] Add Adversarial agent with initial pipeline#338JCHAVEROT wants to merge 12 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a pre-cloud privacy “leakage loop” to the existing analyzer→detector→sanitizer flow by introducing an adversarial probe stage, a bounded escalation mechanism, and an optional HITL approval gate before any sanitized context is sent to a cloud model.
Changes:
- Introduces
AdversarialAgent+LeakageVerdictto probe sanitized context for residual identifiers and quasi-identifiers. - Adds an escalation ladder to progressively tighten policy (entities/threshold/engine/strategy) on detected leakage, with an iteration budget.
- Adds an optional HITL gate agent to summarize actions taken and pause for human approve/revise/reject, and wires the full LangGraph pipeline.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/mmore/privacy/sanitization/constants.py | Adds per-strategy guidance strings so the analyzer can map descriptive feedback to sanitization choices. |
| src/mmore/privacy/pipeline.py | New LangGraph wiring for analyzer→detector→sanitizer→adversary→gate, including bounded escalation routing. |
| src/mmore/privacy/leakage.py | Defines structured leakage verdict and escalation record dataclasses used across the loop. |
| src/mmore/privacy/escalation.py | Implements policy escalation steps (threshold/engine/strategy) and entity guidance merging. |
| src/mmore/privacy/config.py | Adds AttackVector, LeakageAdversaryConfig, and an interactive flag to PrivacyConfig. |
| src/mmore/privacy/agents/state.py | Extends shared PrivacyState with leakage + escalation + HITL fields and introduces PreCloudOutcome. |
| src/mmore/privacy/agents/gate.py | New HITL approval agent using interrupt() with a PII-free operational summary. |
| src/mmore/privacy/agents/analyzer.py | Enhances analyzer to handle loop re-entry, leak-targeted label expansion, and human-feedback-driven policy tweaks. |
| src/mmore/privacy/agents/adversary.py | New adversary agent that runs multiple attack vectors and returns the strongest leakage signal. |
| src/mmore/privacy/agents/init.py | Exports new adversary and gate agent symbols for package consumers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c742d63 to
107ec76
Compare
a872863 to
9d83368
Compare
1b801cf to
0f0d2a3
Compare
19fccde to
201382c
Compare
0f0d2a3 to
92a2b79
Compare
201382c to
4d7562a
Compare
| return "\n".join(f"- {name}: {desc}" for name, desc in guidance.items()) | ||
|
|
||
|
|
||
| def _clamp_confidence(value: object) -> float: |
There was a problem hiding this comment.
does it have to have object as type hint?
There was a problem hiding this comment.
You are right, replaced for float | int | str | None
186f101 to
71d1928
Compare
4d7562a to
ebcf59d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/mmore/privacy/agents/detector.py:124
from_config()still accepts dict-like configs at runtime viaload_config(...)(which supportsUnion[str, Dict, T]), but the type annotation was narrowed toPrivacyConfig | str. This is an API typing regression for callers that pass a dict. Consider reflecting the actual accepted types in the signature.
@classmethod
def from_config(
cls,
config: PrivacyConfig | str,
checkpointer: Optional[BaseCheckpointSaver] = None,
) -> Self:
if not isinstance(config, PrivacyConfig):
config = load_config(config, PrivacyConfig)
return cls(config, checkpointer=checkpointer)
src/mmore/privacy/agents/sanitizer.py:82
from_config()still accepts dict-like configs at runtime viaload_config(...), but the signature was narrowed toPrivacyConfig | str. If dict configs are supported (asload_configindicates), includedictin the annotation to avoid misleading API typing for callers.
@classmethod
def from_config(
cls,
config: PrivacyConfig | str,
checkpointer: Optional[BaseCheckpointSaver] = None,
) -> Self:
if not isinstance(config, PrivacyConfig):
config = load_config(config, PrivacyConfig)
llm_config = config.sanitization.llm if config.sanitization else None
return cls(config, llm_config, checkpointer=checkpointer)
6971012 to
fae2eb3
Compare
fe55399 to
c5fa763
Compare
Summary
Related issue: #294
Depends on: #337
Note that these commits were originally on a fork repo, a rebase was needed to target the branch on the origin repo
feat/privacy-agents-cleanThe privacy pipeline cleaned the retrieved context but never checked that the cleaned text was actually safe before sending it to the cloud model. After cleaning, an adversary agent looks for leftover identifiers. If it finds one, it writes a short PII-free remediation report (which detection engine, cleaning strategy, threshold, or entity labels to change) and the pipeline applies it and runs detection and cleaning again, up to a set number of tries. Once the text is clean, a human approves it (HITL)
Changes
AdversarialAgent(agents/adversary.py): probes the cleaned text for leftover identifiers and flags a leak when confidence is high enough. On a leak it also emits a remediation report, with knowledge of the available engines/strategies/params and of its own past reports, so it can see whether they were appliedmax_iterationsand marks the request unsafeagents/gate.py): shows a short PII-free summary and waits for a human choice (approve / revise / reject)pipeline.py) with the loop and the routing for the adversary and gateconfig.py):AttackVectorenum,LeakageAdversaryConfig, and aninteractiveflagNotes
max_iterations, a human revision does not so he/she can ask changes as many times as wanted