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.
Shield actions
Section titled “Shield actions”Every shield has an action that determines what happens when its threshold is crossed:
| Action | Behavior |
|---|---|
observe | Record the verdict and continue inference |
flag | Record a warning but do not terminate inference |
block | Reject 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.
The shield-config wrapper
Section titled “The shield-config wrapper”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." } }}| Field | Description |
|---|---|
connector | The underlying classifier connector |
action | observe, flag, or block |
threshold | Score at or above which flag/block triggers (default 0.5) |
target_label | Only this predicted label counts when set |
fail_closed | If true, reject the request when the shield errors or times out |
block_message | Custom 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." } }}Built-in prompt-injection detector
Section titled “Built-in prompt-injection detector”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
Section titled “LLM-based shields”LLM-based shields are full ResponseProfiles, so their config can use a registered model via model_ref.
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.
Empathy engine
Section titled “Empathy engine”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." } } } } ] }}| Strategy | Behavior |
|---|---|
none | No-op, useful as a placeholder |
override | Replace the entire response profile with rule.response |
merge | Deep-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.
Design tips
Section titled “Design tips”- Run cheap heuristic shields in
blockmode and expensive LLM shields inobservemode. - Set
fail_closed=truefor shields that guard sensitive content. - Use empathy
mergefor prompt-clause changes (language, tone, persona) andoverridefor hard stops. - Attach content shields to tool/topic registrations when the risk is in the returned data, not the request.