Skip to main content
Glama
GloriaABK

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.