agentleFS
lauren-frameworklauren-framework/lauren-ai/skills/adding-guardrails/SKILL.md

adding-guardrails

Skill0 starsChanged 6 months agoMITView on GitHub

Adds content safety filters to lauren-ai agents using @use_guardrails for input and output checks. Use when an agent needs prompt injection protection, PII redaction, topic filtering, length limiting, or LLM-powered content evaluation. Also covers creating custom guardrail classes injectable via @guardrail.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use @use_guardrails on your @agent() class to plug in filter instances.

    line 59
  2. Very specific:

    Use @guardrail on a standalone class to make it injectable through the DI container so that it can be resolved and passed into …

    line 60
  3. Distinctive:

    Import them as separate symbols:

    line 63
  4. Very specific:

    When your guardrail requires injected dependencies (database clients, config, etc.), define it as a class decorated with @guardrail:

    line 127

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Use codemap find "SymbolName" to locate any symbol before reading — it gives exact file + line range and is faster than grep across the whole repo.

Adding Guardrails to Agents

Quick start

# NOTE: future annotations are allowed, but tool signature types must resolve at import time.
from lauren_ai import agent, tool, use_guardrails, use_tools, PromptInjectionFilter, PIIRedactor, LengthFilter

@tool()
async def lookup_policy(topic: str) -> dict:
    """Look up a company policy.

    Args:
        topic: The policy topic to retrieve.
    """
    return {"topic": topic, "policy": "..."}

@agent(model="claude-opus-4-6", system="You are a compliant assistant.")
@use_guardrails(
    input=[PromptInjectionFilter(), PIIRedactor()],
    output=[LengthFilter(max_chars=8000)],
)
@use_tools(lookup_policy)
class SafeAgent: ...

Run it:

from lauren_ai import AgentRunnerBase, LLMConfig

cfg = LLMConfig(provider="anthropic", model="claude-opus-4-6")
runner = AgentRunnerBase(transport=...)
result = await runner.run(SafeAgent(), "What is the refund policy?")
print(result.content)

Two guardrail decorators — critical distinction

DecoratorPurposeApplied to
@use_guardrails(input=[...], output=[...])Attaches guardrail instances to an @agent() classAgent class
@guardrail(kind="input"|"output"|"any")Marks a class as a DI-injectable guardrail provider (also applies @injectable)Guardrail class

These are two entirely different decorators for two different purposes. Do not confuse them:

  • Use @use_guardrails on your @agent() class to plug in filter instances.
  • Use @guardrail on a standalone class to make it injectable through the DI container so that it can be resolved and passed into @use_guardrails.

Import them as separate symbols:

from lauren_ai import use_guardrails, guardrail

Built-in guardrails

ClassDirectionWhat it does
PromptInjectionFilter()inputBlocks jailbreak and prompt-override attempts
PIIRedactor()input (or output)Redacts email, phone, SSN, credit card with [REDACTED]
LengthFilter(max_chars=N)outputTruncates output to at most N characters
TopicFilter(blocked=[...])input or outputBlocks messages containing listed keywords
LLMGuardrail(policy="...")input or outputUses a separate LLM call to evaluate against a policy

Full documentation and examples for each: see guardrails.md.


Decorator stacking position

@use_guardrails() must sit between @agent() (above) and @use_tools() (below). The full correct order:

@agent(model="claude-opus-4-6")   # outermost — applied last
@remember(...)                     # optional
@use_guardrails(input=[...])       # between remember and use_tools
@use_tools(my_tool)                # innermost — applied first
class MyAgent: ...

Python applies decorators bottom-up, so @use_tools runs first, then @use_guardrails, then @remember, and finally @agent which reads all collected metadata. Swapping the order silently drops tools or guardrails.


Conditional guardrails

None entries in the lists are silently dropped, which enables feature-flag or config-driven guard selection:

import os
from lauren_ai import agent, use_guardrails, PromptInjectionFilter, PIIRedactor

pii_guard = PIIRedactor() if os.getenv("ENABLE_PII_REDACTION") else None

@agent(model="claude-opus-4-6")
@use_guardrails(
    input=[PromptInjectionFilter(), pii_guard],  # pii_guard dropped if None
    output=[],
)
class ConditionalAgent: ...

DI-injectable guardrails with @guardrail

When your guardrail requires injected dependencies (database clients, config, etc.), define it as a class decorated with @guardrail:

from lauren_ai import guardrail, InputGuardrail, GuardrailContext, GuardrailDecision

@guardrail(kind="input")
class AllowlistFilter(InputGuardrail):
    """Blocks users not on the approved list."""

    def __init__(self, db_client: UserDatabase) -> None:
        self._db = db_client

    async def check(self, text: str, ctx: GuardrailContext) -> GuardrailDecision:
        user_id = ctx.metadata.get("user_id")
        if user_id and not await self._db.is_allowed(user_id):
            return GuardrailDecision.block("User not on allowlist.")
        return GuardrailDecision.allow()

@guardrail(kind="input") automatically applies @injectable, so the DI container can resolve AllowlistFilter with its dependencies and pass it to @use_guardrails:

@agent(model="claude-opus-4-6")
@use_guardrails(input=[AllowlistFilter])   # pass the class, DI resolves it
@use_tools(my_tool)
class ProtectedAgent: ...

Reference

See guardrails.md for:

  • Full API for each built-in guardrail
  • Implementing the InputGuardrail / OutputGuardrail protocol
  • GuardrailDecision API (allow(), allow(replacement=...), block(reason))
  • Stacking behaviour (first block short-circuits remaining guards)
  • GuardrailViolated exception handling

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.