Persuasion Detector
by GloriaABK
README.md
# Persuasion Detector
An MCP-based agent that analyzes text (news articles, ads, political
messaging, chatbot output) for the presence of Cialdini's six principles
of persuasion (Reciprocity, Commitment/Consistency, Social Proof,
Authority, Liking, Scarcity). Built for journalists and media researchers
who want a structured, evidence-backed read on *how* a piece of text is
trying to persuade its reader, not just *whether* it is.
This project grew out of my background in social psychology (personality
and social psychology were my strongest areas) combined with prior work on
news trust and content provenance at SFI MediaFutures. It's deliberately
built as a production-oriented tool, not a notebook demo. The file
structure below mirrors the components you'd expect in a real internal AI
platform (access control, guardrails, observability, retrieval), rather
than an idealized, all-in-one script.
## Why this exists
Most "persuasion detection" demos either hardcode a handful of keyword
rules, or throw raw text at an LLM with no grounding and no guardrails.
This project tries to do better on both fronts:
- **Grounded, not just prompted.** Scoring is backed by a small
retrieval-augmented (RAG) pipeline: labeled reference examples are
embedded via Voyage AI and stored in a local vector database (Chroma),
so the model has concrete precedent to reason from, not just a bare
list of principle names.
- **Guarded, not just trusted.** Input is validated for length and
checked (via regex pattern-matching) for common prompt-injection
attempts before it ever reaches the model. The model's own output is
constrained with a Pydantic schema: Claude cannot return a persuasion
score outside 0 to 1, or a principle name outside the real six.
- **Observable, not a black box.** Every request is logged, with
deliberate care not to log raw user text, for GDPR reasons, so the
system's behavior over time (rejection rates, failure rates) is
actually inspectable.
## Architecture
\```
Text input
│
▼
Guardrails (length + injection checks)
│
▼
Retrieval (Voyage embeddings + Chroma vector search)
│
▼
Prompt construction (delimited user text + principle definitions + examples)
│
▼
Claude (structured output via Pydantic schema)
│
▼
Formatted persuasion report
\```
## Project structure, mapped to platform components
The structure deliberately separates concerns the way a production AI
platform typically would: access/model configuration, guardrails and
policy, observability, and retrieval, rather than one monolithic script:
| File | Platform concern | What it does |
|---|---|---|
| `config.py` | Access / model configuration | Centralizes API keys, model selection, and tuning parameters; fails fast if required secrets are missing |
| `guardrails.py` | Guardrails, policy, compliance | Input length validation, regex-based prompt-injection detection, Pydantic-enforced output schema |
| `logging_setup.py` | Logging and security monitoring | Structured logging to console and file; deliberately excludes raw user text from logs |
| `principles.py` | Domain knowledge | Definitions of Cialdini's six principles, used both for scoring guidance and as the schema's source of truth |
| `reference_data.py` + `retrieval.py` | AI-recognition/RAG | Labeled example set, embedded via Voyage AI and retrieved via Chroma vector search to ground each analysis |
| `server.py` | MCP integration | The MCP tool itself, orchestrating the full pipeline end to end |
| `tests/` | Quality assurance | pytest coverage of the guardrail functions, including a regression test for a prompt-injection variant the original substring-matching approach missed |
## Tech stack
- **Language model:** Claude (Anthropic API), structured output via
`client.messages.parse()` and a Pydantic schema
- **Embeddings / retrieval:** Voyage AI + ChromaDB (local, persistent
vector store)
- **Protocol:** Model Context Protocol (MCP), via the official `mcp`
Python SDK
- **Validation:** Pydantic v2
- **Testing:** pytest
## A deliberate design choice worth naming
The six persuasion principles are defined in two places: as a `Literal`
type constraint in `guardrails.py`, and as full definitions in
`principles.py`. This is intentional, not an oversight. The `Literal`
constraint is a strict, independent safety gate on the model's *output*,
kept hardcoded so that a change to the *content* source
(`principles.py`) can never silently loosen what's accepted as valid.
## What this project doesn't do
No content moderation or topic filtering. Journalists legitimately need
to write about difficult subjects. The tool's job is to identify
persuasive *technique*, not to police *topic*, and conflating the two
would undermine its actual purpose.
## Credit
Informed by patterns from NVIDIA's NeMo Guardrails framework, though not
taken as a dependency. This project's guardrail surface is narrow
enough that a full general-purpose framework would have added
complexity without a matching benefit.This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues