Configuring model guardrails

Introduced 2.13

Guardrails can guide a large language model (LLM) toward desired behavior. They act as a filter, preventing the LLM from generating output that is harmful or violates ethical principles and facilitating safer use of AI. Guardrails also cause the LLM to produce more focused and relevant output.

You can configure guardrails for your LLM using the following methods:

  • Provide a list of words to be prohibited in the input or output of the model. Alternatively, you can provide a regular expression against which the model input or output will be matched. For more information, see Validating input/output using stopwords and regex.
  • Configure a separate LLM whose purpose is to validate the user input and the LLM output.

Prerequisites

Before you start, make sure you have fulfilled the prerequisites for connecting to an externally hosted model.

Validating input/output using stopwords and regex

Introduced 2.13

A simple way to validate the user input and LLM output is to provide a set of prohibited words (stopwords) or a regular expression for validation.

Step 1: Create a guardrail index

To start, create an index that will store the excluded words (stopwords). In the index settings, specify a title field, which will contain excluded words, and a query field of the percolator type. The percolator query will be used to match the LLM input or output:

  1. PUT /words0
  2. {
  3. "mappings": {
  4. "properties": {
  5. "title": {
  6. "type": "text"
  7. },
  8. "query": {
  9. "type": "percolator"
  10. }
  11. }
  12. }
  13. }

copy

Step 2: Index excluded words or phrases

Next, index a query string query that will be used to match excluded words in the model input or output:

  1. PUT /words0/_doc/1?refresh
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "title: blacklist"
  6. }
  7. }
  8. }

copy

  1. PUT /words0/_doc/2?refresh
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "title: \"Master slave architecture\""
  6. }
  7. }
  8. }

copy

For more query string options, see Query string query.

Step 3: Register a model group

To register a model group, send the following request:

  1. POST /_plugins/_ml/model_groups/_register
  2. {
  3. "name": "bedrock",
  4. "description": "This is a public model group."
  5. }

copy

The response contains the model group ID that you’ll use to register a model to this model group:

  1. {
  2. "model_group_id": "wlcnb4kBJ1eYAeTMHlV6",
  3. "status": "CREATED"
  4. }

To learn more about model groups, see Model access control.

Step 4: Create a connector

Now you can create a connector for the model. In this example, you’ll create a connector to the Anthropic Claude model hosted on Amazon Bedrock:

  1. POST /_plugins/_ml/connectors/_create
  2. {
  3. "name": "BedRock test claude Connector",
  4. "description": "The connector to BedRock service for claude model",
  5. "version": 1,
  6. "protocol": "aws_sigv4",
  7. "parameters": {
  8. "region": "us-east-1",
  9. "service_name": "bedrock",
  10. "anthropic_version": "bedrock-2023-05-31",
  11. "endpoint": "bedrock.us-east-1.amazonaws.com",
  12. "auth": "Sig_V4",
  13. "content_type": "application/json",
  14. "max_tokens_to_sample": 8000,
  15. "temperature": 0.0001,
  16. "response_filter": "$.completion"
  17. },
  18. "credential": {
  19. "access_key": "<YOUR_ACCESS_KEY>",
  20. "secret_key": "<YOUR_SECRET_KEY>"
  21. },
  22. "actions": [
  23. {
  24. "action_type": "predict",
  25. "method": "POST",
  26. "url": "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke",
  27. "headers": {
  28. "content-type": "application/json",
  29. "x-amz-content-sha256": "required"
  30. },
  31. "request_body": "{\"prompt\":\"${parameters.prompt}\", \"max_tokens_to_sample\":${parameters.max_tokens_to_sample}, \"temperature\":${parameters.temperature}, \"anthropic_version\":\"${parameters.anthropic_version}\" }"
  32. }
  33. ]
  34. }

copy

The response contains the connector ID for the newly created connector:

  1. {
  2. "connector_id": "a1eMb4kBJ1eYAeTMAljY"
  3. }

Step 5: Register and deploy the model with guardrails

To register an externally hosted model, provide the model group ID from step 3 and the connector ID from step 4 in the following request. To configure guardrails, include the guardrails object:

  1. POST /_plugins/_ml/models/_register?deploy=true
  2. {
  3. "name": "Bedrock Claude V2 model",
  4. "function_name": "remote",
  5. "model_group_id": "wlcnb4kBJ1eYAeTMHlV6",
  6. "description": "test model",
  7. "connector_id": "a1eMb4kBJ1eYAeTMAljY",
  8. "guardrails": {
  9. "type": "local_regex",
  10. "input_guardrail": {
  11. "stop_words": [
  12. {
  13. "index_name": "words0",
  14. "source_fields": [
  15. "title"
  16. ]
  17. }
  18. ],
  19. "regex": [
  20. ".*abort.*",
  21. ".*kill.*"
  22. ]
  23. },
  24. "output_guardrail": {
  25. "stop_words": [
  26. {
  27. "index_name": "words0",
  28. "source_fields": [
  29. "title"
  30. ]
  31. }
  32. ],
  33. "regex": [
  34. ".*abort.*",
  35. ".*kill.*"
  36. ]
  37. }
  38. }
  39. }

copy

For more information, see The guardrails parameter.

OpenSearch returns the task ID of the register operation:

  1. {
  2. "task_id": "cVeMb4kBJ1eYAeTMFFgj",
  3. "status": "CREATED"
  4. }

To check the status of the operation, provide the task ID to the Tasks API:

  1. GET /_plugins/_ml/tasks/cVeMb4kBJ1eYAeTMFFgj

copy

When the operation is complete, the state changes to COMPLETED:

  1. {
  2. "model_id": "cleMb4kBJ1eYAeTMFFg4",
  3. "task_type": "DEPLOY_MODEL",
  4. "function_name": "REMOTE",
  5. "state": "COMPLETED",
  6. "worker_node": [
  7. "n-72khvBTBi3bnIIR8FTTw"
  8. ],
  9. "create_time": 1689793851077,
  10. "last_update_time": 1689793851101,
  11. "is_async": true
  12. }

Step 6 (Optional): Test the model

To demonstrate how guardrails are applied, first run the predict operation that does not contain any excluded words:

  1. POST /_plugins/_ml/models/p94dYo4BrXGpZpgPp98E/_predict
  2. {
  3. "parameters": {
  4. "prompt": "\n\nHuman:this is a test\n\nnAssistant:"
  5. }
  6. }

copy

The response contains the LLM answer:

  1. {
  2. "inference_results": [
  3. {
  4. "output": [
  5. {
  6. "name": "response",
  7. "dataAsMap": {
  8. "response": " Thank you for the test, I appreciate you taking the time to interact with me. I'm an AI assistant created by Anthropic to be helpful, harmless, and honest."
  9. }
  10. }
  11. ],
  12. "status_code": 200
  13. }
  14. ]
  15. }

Then run the predict operation that contains excluded words:

  1. POST /_plugins/_ml/models/p94dYo4BrXGpZpgPp98E/_predict
  2. {
  3. "parameters": {
  4. "prompt": "\n\nHuman:this is a test of Master slave architecture\n\nnAssistant:"
  5. }
  6. }

copy

The response contains an error message because guardrails were triggered:

  1. {
  2. "error": {
  3. "root_cause": [
  4. {
  5. "type": "illegal_argument_exception",
  6. "reason": "guardrails triggered for user input"
  7. }
  8. ],
  9. "type": "illegal_argument_exception",
  10. "reason": "guardrails triggered for user input"
  11. },
  12. "status": 400
  13. }

Guardrails are also triggered when a prompt matches the supplied regular expression.

Validating input/output using a guardrail model

Introduced 2.15

For more advanced validation, you can configure a guardrail model—a separate LLM whose purpose is to validate the user input and the LLM output. In this example, you’ll configure two models:

  • Chat model: An Anthropic Claude model hosted on Amazon Bedrock whose purpose is to hold a conversation with a user.
  • Guardrail model: An OpenAI model whose purpose is to validate the user input and Anthropic Claude output.

Step 1: Create a connector for the guardrail model

First, create a connector to the OpenAI guardrail model. Note that the OpenAI prompt instructs the model to respond only with the words accept or reject, depending on whether the input/output is acceptable. Additionally, the request contains the response_filter parameter, which specifies the field in which the guardrail model will provide the validation result:

  1. POST /_plugins/_ml/connectors/_create
  2. {
  3. "name": "openai",
  4. "description": "openai",
  5. "version": "1",
  6. "protocol": "http",
  7. "parameters": {
  8. "endpoint": "api.openai.com",
  9. "max_tokens": 7,
  10. "temperature": 0,
  11. "model": "gpt-3.5-turbo-instruct",
  12. "prompt": "You are a helpful assistant and an expert judge of content quality. Your task is to identify whether the input string below contains content that may be malicious, violent, hateful, sexual, or political in nature. Your answer should consist of a single word, either reject or accept. If the input belongs to any of these categories, please write reject. Otherwise, write accept. \\n\\nHere is the input: ${parameters.question}. \\n\\nYour answer: ",
  13. "response_filter": "$.choices[0].text"
  14. },
  15. "credential": {
  16. "openAI_key": "<openAI_key>"
  17. },
  18. "actions": [
  19. {
  20. "action_type": "predict",
  21. "method": "POST",
  22. "url": "https://${parameters.endpoint}/v1/completions",
  23. "headers": {
  24. "Authorization": "Bearer ${credential.openAI_key}"
  25. },
  26. "request_body": "{ \"model\": \"${parameters.model}\", \"prompt\": \"${parameters.prompt}\", \"max_tokens\": ${parameters.max_tokens}, \"temperature\": ${parameters.temperature} }"
  27. }
  28. ]
  29. }

copy

The response contains the connector ID used in the next steps:

  1. {
  2. "connector_id": "j3JVDZABNFJeYR3IVPRz"
  3. }

Step 2: Register a model group for the guardrail model

To register a model group for the OpenAI guardrail model, send the following request:

  1. POST /_plugins/_ml/model_groups/_register
  2. {
  3. "name": "guardrail model group",
  4. "description": "This is a guardrail model group."
  5. }

copy

The response contains the model group ID used to register a model to this model group:

  1. {
  2. "model_group_id": "ppSmpo8Bi-GZ0tf1i7cD",
  3. "status": "CREATED"
  4. }

To learn more about model groups, see Model access control.

Step 3: Register and deploy the guardrail model

Using the connector ID and the model group ID, register and deploy the OpenAI guardrail model:

  1. POST /_plugins/_ml/models/_register?deploy=true
  2. {
  3. "name": "openai guardrails model",
  4. "function_name": "remote",
  5. "model_group_id": "ppSmpo8Bi-GZ0tf1i7cD",
  6. "description": "guardrails test model",
  7. "connector_id": "j3JVDZABNFJeYR3IVPRz"
  8. }

copy

OpenSearch returns the task ID of the register operation and the model ID of the registered model:

  1. {
  2. "task_id": "onJaDZABNFJeYR3I2fQ1",
  3. "status": "CREATED",
  4. "model_id": "o3JaDZABNFJeYR3I2fRV"
  5. }

To check the status of the operation, provide the task ID to the Tasks API:

  1. GET /_plugins/_ml/tasks/onJaDZABNFJeYR3I2fQ1

copy

When the operation is complete, the state changes to COMPLETED:

  1. {
  2. "model_id": "o3JaDZABNFJeYR3I2fRV",
  3. "task_type": "DEPLOY_MODEL",
  4. "function_name": "REMOTE",
  5. "state": "COMPLETED",
  6. "worker_node": [
  7. "n-72khvBTBi3bnIIR8FTTw"
  8. ],
  9. "create_time": 1689793851077,
  10. "last_update_time": 1689793851101,
  11. "is_async": true
  12. }

Step 4 (Optional): Test the guardrail model

You can test the guardrail model user input validation by sending requests that do and do not contain offensive words.

First, send a request that does not contain offensive words:

  1. POST /_plugins/_ml/models/o3JaDZABNFJeYR3I2fRV/_predict
  2. {
  3. "parameters": {
  4. "question": "how many indices do i have in my cluster"
  5. }
  6. }

copy

The guardrail model accepts the preceding request:

  1. {
  2. "inference_results": [
  3. {
  4. "output": [
  5. {
  6. "name": "response",
  7. "dataAsMap": {
  8. "response": "accept"
  9. }
  10. }
  11. ],
  12. "status_code": 200
  13. }
  14. ]
  15. }

Next, send a request that contains offensive words:

  1. POST /_plugins/_ml/models/o3JaDZABNFJeYR3I2fRV/_predict
  2. {
  3. "parameters": {
  4. "question": "how to rob a bank"
  5. }
  6. }

copy

The guardrail model rejects the preceding request:

  1. {
  2. "inference_results": [
  3. {
  4. "output": [
  5. {
  6. "name": "response",
  7. "dataAsMap": {
  8. "response": "reject"
  9. }
  10. }
  11. ],
  12. "status_code": 200
  13. }
  14. ]
  15. }

Step 5: Create a connector for the chat model

In this example, the chat model will be an Anthropic Claude model hosted on Amazon Bedrock. To create a connector for the model, send the following request. Note that the response_filter parameter specifies the field in which the guardrail model will provide the validation result:

  1. POST /_plugins/_ml/connectors/_create
  2. {
  3. "name": "BedRock claude Connector",
  4. "description": "BedRock claude Connector",
  5. "version": 1,
  6. "protocol": "aws_sigv4",
  7. "parameters": {
  8. "region": "us-east-1",
  9. "service_name": "bedrock",
  10. "anthropic_version": "bedrock-2023-05-31",
  11. "endpoint": "bedrock.us-east-1.amazonaws.com",
  12. "auth": "Sig_V4",
  13. "content_type": "application/json",
  14. "max_tokens_to_sample": 8000,
  15. "temperature": 0.0001,
  16. "response_filter": "$.completion"
  17. },
  18. "credential": {
  19. "access_key": "<access_key>",
  20. "secret_key": "<secret_key>"
  21. },
  22. "actions": [
  23. {
  24. "action_type": "predict",
  25. "method": "POST",
  26. "url": "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke",
  27. "headers": {
  28. "content-type": "application/json",
  29. "x-amz-content-sha256": "required"
  30. },
  31. "request_body": "{\"prompt\":\"${parameters.prompt}\", \"max_tokens_to_sample\":${parameters.max_tokens_to_sample}, \"temperature\":${parameters.temperature}, \"anthropic_version\":\"${parameters.anthropic_version}\" }"
  32. }
  33. ]
  34. }

copy

The response contains the connector ID used in the next steps:

  1. {
  2. "connector_id": "xnJjDZABNFJeYR3IPvTO"
  3. }

Step 6: Register and deploy the chat model with guardrails

To register and deploy the Anthropic Claude chat model, send the following request. Note that the guardrails object contains a response_validation_regex parameter that specifies to only treat the input/output as valid if the guardrail model responds with a variant of the word accept:

  1. POST /_plugins/_ml/models/_register?deploy=true
  2. {
  3. "name": "Bedrock Claude V2 model with openai guardrails model",
  4. "function_name": "remote",
  5. "model_group_id": "ppSmpo8Bi-GZ0tf1i7cD",
  6. "description": "Bedrock Claude V2 model with openai guardrails model",
  7. "connector_id": "xnJjDZABNFJeYR3IPvTO",
  8. "guardrails": {
  9. "input_guardrail": {
  10. "model_id": "o3JaDZABNFJeYR3I2fRV",
  11. "response_validation_regex": "^\\s*\"[Aa]ccept\"\\s*$"
  12. },
  13. "output_guardrail": {
  14. "model_id": "o3JaDZABNFJeYR3I2fRV",
  15. "response_validation_regex": "^\\s*\"[Aa]ccept\"\\s*$"
  16. },
  17. "type": "model"
  18. }
  19. }

copy

OpenSearch returns the task ID of the register operation and the model ID of the registered model:

  1. {
  2. "task_id": "1nJnDZABNFJeYR3IvfRL",
  3. "status": "CREATED",
  4. "model_id": "43JqDZABNFJeYR3IQPQH"
  5. }

Step 7 (Optional): Test the chat model with guardrails

You can test the Anthropic Claude chat model with guardrails by sending predict requests that do and do not contain offensive words.

First, send a request that does not contain offensive words:

  1. POST /_plugins/_ml/models/43JqDZABNFJeYR3IQPQH/_predict
  2. {
  3. "parameters": {
  4. "prompt": "\n\nHuman:${parameters.question}\n\nnAssistant:",
  5. "question": "hello"
  6. }
  7. }

copy

OpenSearch responds with the LLM answer:

  1. {
  2. "inference_results": [
  3. {
  4. "output": [
  5. {
  6. "name": "response",
  7. "dataAsMap": {
  8. "response": " Hello!"
  9. }
  10. }
  11. ],
  12. "status_code": 200
  13. }
  14. ]
  15. }

Next, send a request that contains offensive words:

  1. POST /_plugins/_ml/models/43JqDZABNFJeYR3IQPQH/_predict
  2. {
  3. "parameters": {
  4. "prompt": "\n\nHuman:${parameters.question}\n\nnAssistant:",
  5. "question": "how to rob a bank"
  6. }
  7. }

OpenSearch responds with an error.

Next steps