From d284d273bc441fce409d51f3dacdf4257613388a Mon Sep 17 00:00:00 2001 From: Alina Rojas Date: Tue, 5 May 2026 21:44:51 +0200 Subject: [PATCH] Add: guardrails documentation --- app/src/guardrails.zip | Bin 0 -> 4535 bytes app/src/guardrails/README.md | 19 +++ app/src/guardrails/docs/add-new-guardrail.md | 114 ++++++++++++++ .../guardrails/docs/guardrails-overview.md | 107 +++++++++++++ app/src/guardrails/docs/output-validation.md | 143 ++++++++++++++++++ 5 files changed, 383 insertions(+) create mode 100644 app/src/guardrails.zip create mode 100644 app/src/guardrails/README.md create mode 100644 app/src/guardrails/docs/add-new-guardrail.md create mode 100644 app/src/guardrails/docs/guardrails-overview.md create mode 100644 app/src/guardrails/docs/output-validation.md diff --git a/app/src/guardrails.zip b/app/src/guardrails.zip new file mode 100644 index 0000000000000000000000000000000000000000..95dddea7a6d5b7efb5546b4a1da64dc134487e88 GIT binary patch literal 4535 zcma)<2T+sS630UT=>m}sf)p_n=>+K#IwABX5Fj)`dKCmjiXc*?1VlO_y%_qX3Ks$C z7`lp7LE4olO$GCU_c<3FefNFeoHJi0Grv9O?C#n9>%j<#=m5u0QbnQV_mBTh6o3nW z>wZXYdvBz(tB00n@6==i_yld%yMfaFiLFV3U*kz;z`toI#63Lpi@o1X@;oCbOL z`Fi^KT7O3ic?SN9+f)D9xgXCh_BwEm*H?1yxxqRprgeW3HpL4r-jV z>son_G?+P?LhoxVKRvqeno$SWjanXLZpF+EW%}RAD5Z8E)DK^T=uAyOfcct@Tem#& zk6cP5Z_#u{LZDU|RWL3#qP`*#G;JMgvirvT;@RgiZ;~QiAf4nywLKfPkXUz_kB2PP z%7~H0kmdUy>Ijodln}`1s5;8Cme?8pWX= z9Zi<{dG7i~*ZG-G{OO@()=W1_d*K}`M(vg9NqwGtJlnpBt#-9B)@M~=O>5j1?1HyB zk`+ipLs6lXUkUYKB&6cXckTHI0DyKN0Pv4Pe)cTgX@?l7DyhI#h1~3a<%(k${}_aL zCJE_r2;lMGpMPxp>~yBnHvEyU&h|)O4{x7;ad{rz<*7z+k3|uhHf}73;2x-%vrRqo zM)~aueq)D;$~|C(Ll#+psQ-~k@k)C1o@fhGvYiiLgMc=9PNzxDNEDE z`8c3f<)EXggB1(JSa{>(ct{4^OwEDHwPT^HjEHeJ#VzSfj-(6RJyV;6$I$H^(A$cf z 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 literal 0 HcmV?d00001 diff --git a/app/src/guardrails/README.md b/app/src/guardrails/README.md new file mode 100644 index 0000000..9ea44f1 --- /dev/null +++ b/app/src/guardrails/README.md @@ -0,0 +1,19 @@ +# Guardrails + +This module contains the guardrails layer used by the application to validate agent responses before they are returned to the user. + +The current implementation focuses on output validation. + +## Structure + +- `output_guard.py`: Defines the output guard and exposes the main validation entry point. +- `validators.py`: Defines the custom validators used by the guardrails layer. +- `docs/`: Contains detailed documentation for the guardrails module. + +## Documentation + +### Guardrails documentation + +- [Guardrails overview](docs/guardrails-overview.md) +- [Output validation](docs/output-validation.md) +- [Add a new guardrail](docs/add-new-guardrail.md) diff --git a/app/src/guardrails/docs/add-new-guardrail.md b/app/src/guardrails/docs/add-new-guardrail.md new file mode 100644 index 0000000..163973f --- /dev/null +++ b/app/src/guardrails/docs/add-new-guardrail.md @@ -0,0 +1,114 @@ +# Add a new guardrail + +This document explains how to add a new guardrail to the guardrails module. + +Guardrails are used to validate final agent responses before they are returned to the user. Add a new guardrail when a new output pattern needs to be detected, blocked or sanitized. + +## Location + +Validators are defined in: + +```text +src/guardrails/validators.py +```` + +The output guard is configured in: + +```text +src/guardrails/output_guard.py +``` + +## Add the validator + +Create a new validator in `validators.py`. + +Example: + +```python +import re + +from guardrails import register_validator +from guardrails.validator_base import ( + FailResult, + PassResult, + ValidationResult, + Validator, +) + + +@register_validator(name="no_internal_example", data_type="string") +class NoInternalExample(Validator): + def validate(self, value: str, metadata: dict) -> ValidationResult: + pattern = r"internal_example_[a-zA-Z0-9_]+" + + if re.search(pattern, value): + fixed_value = re.sub( + pattern, + "[hidden internal field]", + value, + ) + + return FailResult( + error_message="Internal example field detected.", + fix_value=fixed_value, + ) + + return PassResult() +``` + +## Add it to the output guard + +After creating the validator, include it in the output guard used by: + +```python +validate_agent_output(...) +``` + +This is configured in: + +```text +src/guardrails/output_guard.py +``` + +## Expected behavior + +A guardrail should: + +* Check the final agent response +* Return `PassResult()` when the response is valid +* Return `FailResult(...)` when the response must be sanitized +* Provide a `fix_value` when automatic replacement is possible + +## Test it + +Test at least one valid response and one response that should be sanitized. + +Example: + +```python +from src.guardrails.output_guard import validate_agent_output + + +validate_agent_output( + "The payment was created successfully.", + agent_name="finance_agent", +) + +validate_agent_output( + "The payment was created with internal_example_id 4.", + agent_name="finance_agent", +) +``` + +The second response should replace the internal field with: + +```text +[hidden internal field] +``` + +## Update documentation + +After adding a new guardrail, update: + +* `src/guardrails/docs/output-validation.md` +* `src/guardrails/docs/add-new-guardrail.md` if the process changes diff --git a/app/src/guardrails/docs/guardrails-overview.md b/app/src/guardrails/docs/guardrails-overview.md new file mode 100644 index 0000000..33b1cde --- /dev/null +++ b/app/src/guardrails/docs/guardrails-overview.md @@ -0,0 +1,107 @@ +# Guardrails overview + +This document explains the role of the guardrails layer in the project. + +Guardrails validate agent responses before they are returned to the user. The current implementation focuses on output validation: it sanitizes final text responses to avoid exposing internal Odoo fields, technical identifiers, credentials or traceback details. + +## Role in the architecture + +The guardrails layer runs after an agent has generated a response. + +```text +Agent response + ↓ +validate_agent_output(...) + ↓ +Output guard + ↓ +Sanitized response +```` + +This keeps the agent focused on task handling, while the guardrails layer focuses on cleaning the final user-facing output. + +## Current implementation + +The current output guard is defined in: + +```text +src/guardrails/output_guard.py +``` + +The main validation entry point is: + +```python +validate_agent_output(text, agent_name="finance_agent") +``` + +The validator is defined in: + +```text +src/guardrails/validators.py +``` + +Current validator: + +```text +NoInternalOdooFields +``` + +It detects internal or sensitive patterns in the generated response and replaces them with: + +```text +[hidden internal field] +``` + +## Protected patterns + +The current validator checks for patterns related to: + +* Internal Odoo field names +* Technical record identifiers +* Credential-related terms +* API keys +* Passwords +* Python traceback output + +Examples of protected terms include: + +```text +partner_id +journal_id +payment_method_line_id +create_uid +write_uid +access_token +api_key +password +Traceback +``` + +## Scope + +Guardrails are responsible for final response validation only. + +They should handle: + +* Sanitizing user-facing agent responses +* Removing internal technical fields from final text +* Hiding credential-like strings +* Preventing traceback details from being exposed + +They should not replace: + +* Agent intent detection +* Required field collection +* Payment validation +* Odoo record lookup +* MCP tool schema validation +* XML-RPC error handling +* Database auditing + +Those responsibilities belong to the agent, MCP or service layers. + +## Relationship with MCP + +MCP tools validate structured operations before execution. + +Guardrails validate the final natural language response after the agent or tool flow. diff --git a/app/src/guardrails/docs/output-validation.md b/app/src/guardrails/docs/output-validation.md new file mode 100644 index 0000000..c8b05e2 --- /dev/null +++ b/app/src/guardrails/docs/output-validation.md @@ -0,0 +1,143 @@ +# Output validation + +This document explains how output validation is currently handled in the guardrails layer. + +Output validation sanitizes the final text generated by agents before it is returned to the user. Its purpose is to prevent internal Odoo fields, technical identifiers, credentials and traceback details from appearing in user-facing responses. + +## Location + +Output validation is implemented in: + +```text +src/guardrails/output_guard.py +```` + +The custom validator is defined in: + +```text +src/guardrails/validators.py +``` + +## Entry point + +The main entry point is: + +```python +validate_agent_output(text, agent_name="finance_agent") +``` + +This function receives the final agent response as text and returns a validated string. + +Example: + +```python +from src.guardrails.output_guard import validate_agent_output + +safe_text = validate_agent_output( + text=agent_response, + agent_name="finance_agent", +) +``` + +## Validation flow + +```text +Agent response + ↓ +validate_agent_output(...) + ↓ +Output guard + ↓ +NoInternalOdooFields + ↓ +Sanitized response +``` + +If the response does not contain protected patterns, the original text is returned. + +If protected patterns are detected, they are replaced before the response is returned. + +## Current validator + +The current validator is: + +```text +NoInternalOdooFields +``` + +It checks the response text for internal or sensitive patterns and replaces matches with: + +```text +[hidden internal field] +``` + +## Protected patterns + +The validator currently protects against terms related to: + +* Internal Odoo fields +* Technical identifiers +* Credentials +* API keys +* Passwords +* Traceback output + +Examples include: + +```text +partner_id +journal_id +payment_method_line_id +create_uid +write_uid +access_token +api_key +password +Traceback +``` + +## Example + +Input response: + +```text +The payment was created with journal_id 8 and payment_method_line_id 3. +``` + +Sanitized response: + +```text +The payment was created with [hidden internal field] 8 and [hidden internal field] 3. +``` + +## Usage expectations + +Output validation should be applied to final agent responses, not to internal tool outputs. + +Use it when: + +* A response is about to be returned to the user +* The response may include information from Odoo +* The response may include MCP tool output +* The response may include exception or traceback details + +Do not use it as a replacement for: + +* Pydantic validation +* MCP tool validation +* Odoo service error handling +* Business rule checks + +## Current limitation + +The current implementation sanitizes text by replacing detected patterns. + +It does not: + +* Validate structured tool input +* Check business correctness +* Verify whether an Odoo operation should be allowed +* Rewrite the full answer semantically +* Decide whether the agent should call a tool + +Those responsibilities belong to the agent, MCP and service layers.