Skip to content

Shields & empathy

Shields are classifiers that run before the main LLM call. Their output is available to the empathy engine, which can override or merge the response profile based on what the classifiers detect.

Every shield has an action that determines what happens when its threshold is crossed:

ActionBehavior
observeRecord the verdict and continue inference
flagRecord a warning but do not terminate inference
blockReject the request and return a refusal

When action="block" triggers, inference terminates immediately. A shield.blocked.v1 event is recorded in the worklog, and the response is returned with status="error" because no LLM call ran. The configured persistence strategy still runs so the refusal is stored like any other turn.

Raw connector specs are still accepted for backward compatibility and default to action="observe". For explicit control, wrap the connector in a shield-config:

{
"shields": {
"toxicity": {
"provider_id": "shield-config",
"connector": {
"provider_id": "huggingface/text-classification",
"url": "https://your-hf-endpoint.com",
"token": { "$secretRef": "SHIELDS_PROVIDER_API_KEY" },
"label_map": { "LABEL_0": "safe", "LABEL_1": "toxic" }
},
"action": "block",
"threshold": 0.8,
"target_label": "toxic",
"fail_closed": true,
"block_message": "This request violates our safety policy."
}
}
}
FieldDescription
connectorThe underlying classifier connector
actionobserve, flag, or block
thresholdScore at or above which flag/block triggers (default 0.5)
target_labelOnly this predicted label counts when set
fail_closedIf true, reject the request when the shield errors or times out
block_messageCustom refusal text for blocked inputs or replaced results

Content shields on tool and topic registrations

Section titled “Content shields on tool and topic registrations”

ToolRegistration and TopicRegistration can carry their own shields map. These content shields run against the result of a tool execution or knowledge retrieval before it is returned to the model context.

When a blocking content shield triggers on a tool output or retrieved chunk, the offending result is replaced with the shield’s block_message (or a default refusal) and a shield.blocked.v1 event is recorded. This lets prompt-injection patterns in retrieved documents or dangerous tool outputs be sanitized without aborting the whole turn.

{
"registered_tool_id": "my-mcp-server",
"shields": {
"prompt-injection": {
"provider_id": "shield-config",
"connector": { "provider_id": "alquimia/prompt-injection-detection" },
"action": "block",
"threshold": 0.5,
"block_message": "This tool result was removed by a safety policy."
}
}
}

alquimia/prompt-injection-detection is a built-in heuristic + classifier shield that requires no external endpoint.

{
"shields": {
"prompt-injection": {
"provider_id": "shield-config",
"connector": {
"provider_id": "alquimia/prompt-injection-detection",
"heuristic_weight": 0.3,
"base_classifier_score": 0.0
},
"action": "block",
"threshold": 0.5
}
}
}

LLM-based shields are full ResponseProfiles, so their config can use a registered model via model_ref.

Terminal window
alquimia registry models add gpt-4o-mini-classifier \
--provider-id openai \
--params '{"model": "gpt-4o-mini", "temperature": 0.0, "api_key": {"$secretRef": "RESPONSE_PROVIDER_API_KEY"}}'
{
"shields": {
"intent-classifier": {
"provider_id": "shield-config",
"connector": {
"provider_id": "alquimia",
"profile": {
"system_prompt": "Classify the user intent. Output JSON: {\"intent\": \"...\"}",
"evaluation_strategy": {
"evaluation_strategy_id": "one-shoot",
"structured_output": {
"method": "json_schema",
"json_schema": {
"type": "object",
"properties": { "intent": { "type": "string" } },
"required": ["intent"]
}
}
}
},
"config": { "model_ref": "gpt-4o-mini-classifier" }
},
"action": "observe"
}
}
}

Per-shield params override the registered model parameters.

The empathy engine evaluates rules against shield outputs and runtime context. The first matching rule overrides or merges the response profile.

{
"empathy": {
"rules": [
{
"rule_id": "toxic-input",
"strategy": "override",
"description": "Respond with a safety message when toxicity is detected",
"requirements": ["toxicity"],
"conditions": ["toxicity['label'] == 'toxic'"],
"response": {
"provider_id": "fixed",
"message": "I'm sorry, I can't help with that request."
}
},
{
"rule_id": "formal-tone",
"strategy": "merge",
"description": "Use formal tone for enterprise users",
"requirements": ["intent-classifier"],
"conditions": ["intent-classifier.get('intent') == 'enterprise'"],
"response": {
"provider_id": "alquimia",
"profile": {
"prompt_clauses": {
"tone": "Use formal, professional language. Avoid contractions."
}
}
}
}
]
}
}
StrategyBehavior
noneNo-op, useful as a placeholder
overrideReplace the entire response profile with rule.response
mergeDeep-merge rule.response into the main profile

conditions are Python expressions evaluated with simpleeval. Context keys are the shield IDs listed in requirements. A rule is only evaluated when all of its requirements are present.

  • Run cheap heuristic shields in block mode and expensive LLM shields in observe mode.
  • Set fail_closed=true for shields that guard sensitive content.
  • Use empathy merge for prompt-clause changes (language, tone, persona) and override for hard stops.
  • Attach content shields to tool/topic registrations when the risk is in the returned data, not the request.