Skip to content

Translate text with quality checks

Note

To download this example as a Jupyter notebook, click here.

In this example, we will use Guardrails during the translation of a statement from another language to English. We will check whether the translated statement is likely high quality or not.

Objective

We want to translate a statement from another languages to English and ensure that the translated statement accurately reflects the original content.

Step 0: Setup

To do the quality check, we can use the Critique library, which allows for simple calculation of various metrics over generated text, including translation quality estimation.

First you can get an API key from the Inspired Cognition Dashboard add the following line to the ".env" file in your top directory (like you do for your OpenAI API key).

INSPIREDCO_API_KEY=<your_api_key>

Then you can install the library

!pip install inspiredco
Collecting inspiredco
  Downloading inspiredco-0.0.2-py3-none-any.whl (6.2 kB)
Requirement already satisfied: requests>=2.27.0 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from inspiredco) (2.31.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (3.2.0)
Requirement already satisfied: idna<4,>=2.5 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (3.4)
Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (2.0.4)
Requirement already satisfied: certifi>=2017.4.17 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (2023.7.22)
Installing collected packages: inspiredco
Successfully installed inspiredco-0.0.2

Step 1: Create the RAIL Spec

Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the RAIL documentation. We will also show the same RAIL spec in a code-first format using a Pydantic model.

In this RAIL spec, we:

  1. Create an output schema that returns a single key-value pair. The key should be 'translated_statement', and the value should be the English translation of the given statement. The translated statement should not have any profanity.
from inspiredco import critique

Our RAIL spec as an XML string:

rail_str = """
<rail version="0.1">
<output>
<string description="Translate the given statement into the English language" format="is-high-quality-translation" name="translated_statement" on-fail-is-high-quality-translation="fix"></string>
</output>
<prompt>
Translate the given statement into the English language:

${statement_to_be_translated}

${gr.complete_json_suffix}
</prompt>
</rail>


"""

Or as a Pydantic model:

from pydantic import BaseModel, Field
from guardrails.validators import IsHighQualityTranslation

prompt = """
Translate the given statement into the English language:

${statement_to_be_translated}

${gr.complete_json_suffix}
"""

class Translation(BaseModel):
    translated_statement: str = Field(
        description="Translate the given statement into the English language",
        validators=[IsHighQualityTranslation(on_fail="fix")]
        )

Note

In order to ensure the translated statement is high quality, we use is-high-quality-translation as the validator. This validator uses inspiredco package.

Step 2: Create a Guard object with the RAIL Spec

We create a gd.Guard object that will check, validate and correct the output of the LLM. This object:

  1. Enforces the quality criteria specified in the RAIL spec.
  2. Takes corrective action when the quality criteria are not met.
  3. Compiles the schema and type info from the RAIL spec and adds it to the prompt.
import guardrails as gd

from rich import print

From our RAIL string:

guard = gd.Guard.from_rail_string(rail_str)

Or from our Pydantic model:

guard = gd.Guard.from_pydantic(output_class=Translation, prompt=prompt)

We see the prompt that will be sent to the LLM:

print(guard.base_prompt)
Translate the given statement into the English language:

${statement_to_be_translated}


Given below is XML that describes the information to extract from this document and the tags to extract it into.

<output>
    <string name="translated_statement" format="is-high-quality-translation" description="Translate the given 
statement into the English language"/>
</output>


ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` 
attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON
MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and 
specific types. Be correct and concise. If you are unsure anywhere, enter `null`.

Here are examples of simple (XML, JSON) pairs that show the expected behavior:
- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`
- `<list name='bar'><string format='upper-case' /></list>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}`
- `<object name='baz'><string name="foo" format="capitalize two-words" /><integer name="index" format="1-indexed" 
/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`


Here, statement_to_be_translated is the the statement and will be provided by the user at runtime.

Step 3: Wrap the LLM API call with Guard

First, let's try translating a statement that is relatively easy to translate.

import openai

statement = "これは簡単に翻訳できるかもしれない。"
raw_llm_response, validated_response = guard(
    openai.Completion.create,
    prompt_params={'statement_to_be_translated': statement},
    metadata={'translation_source': statement},
    engine='text-davinci-003',
    max_tokens=2048,
    temperature=0
)

print(f"Validated Output: {validated_response}")
Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.

Validated Output: {'translated_statement': 'This may be easy to translate.'}

We can look at the logs to see the quality check results:

print(guard.state.most_recent_call.tree)
Logs
└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮
    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │
    │ │                                                                                                         │ │
    │ │ Translate the given statement into the English language:                                                │ │
    │ │                                                                                                         │ │
    │ │ これは簡単に翻訳できるかもしれない。                                                                    │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ Given below is XML that describes the information to extract from this document and the tags to extract │ │
    │ │ it into.                                                                                                │ │
    │ │                                                                                                         │ │
    │ │ <output>                                                                                                │ │
    │ │     <string name="translated_statement" format="is-high-quality-translation" description="Translate the │ │
    │ │ given statement into the English language"/>                                                            │ │
    │ │ </output>                                                                                               │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the │ │
    │ │ `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding  │ │
    │ │ XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g.        │ │
    │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere,     │ │
    │ │ enter `null`.                                                                                           │ │
    │ │                                                                                                         │ │
    │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior:                          │ │
    │ │ - `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`                     │ │
    │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{"bar": ['STRING ONE', 'STRING TWO',     │ │
    │ │ etc.]}`                                                                                                 │ │
    │ │ - `<object name='baz'><string name="foo" format="capitalize two-words" /><integer name="index"          │ │
    │ │ format="1-indexed" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ Json Output:                                                                                            │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │
    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │
    │ │ ┃ Role  Content ┃                                                                                      │ │
    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │
    │ │ └──────┴─────────┘                                                                                      │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │
    │ │ {"translated_statement": "This may be easy to translate."}                                              │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │
    │ │ {'translated_statement': 'This may be easy to translate.'}                                              │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

The guard wrapper returns the raw_llm_respose (which is a simple string), and the validated and corrected output (which is a dictionary). We can see that the output is a dictionary with the correct schema and types.

Next, let's try translating a statement that is harder to translate (because it contains some difficult-to-translate slang words). We see that the translated statement has been corrected to return an empty string instead of the translated statement.

raw_llm_response, validated_response = guard(
    openai.Completion.create,
    prompt_params={'statement_to_be_translated': 'ドン引きするほど翻訳が悪い。'},
    metadata={'translation_source': 'ドン引きするほど翻訳が悪い。'},
    engine='text-davinci-003',
    max_tokens=2048,
    temperature=0
)

print(f"Validated Output: {validated_response}")
Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.

Validated Output: {'translated_statement': "It is so bad that it's a letdown."}

This time, we see that the quality check failed in the logs, and the translated statement is an empty string.

print(guard.state.most_recent_call.tree)
Logs
└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮
    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │
    │ │                                                                                                         │ │
    │ │ Translate the given statement into the English language:                                                │ │
    │ │                                                                                                         │ │
    │ │ ドン引きするほど翻訳が悪い。                                                                            │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ Given below is XML that describes the information to extract from this document and the tags to extract │ │
    │ │ it into.                                                                                                │ │
    │ │                                                                                                         │ │
    │ │ <output>                                                                                                │ │
    │ │     <string name="translated_statement" format="is-high-quality-translation" description="Translate the │ │
    │ │ given statement into the English language"/>                                                            │ │
    │ │ </output>                                                                                               │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the │ │
    │ │ `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding  │ │
    │ │ XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g.        │ │
    │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere,     │ │
    │ │ enter `null`.                                                                                           │ │
    │ │                                                                                                         │ │
    │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior:                          │ │
    │ │ - `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`                     │ │
    │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{"bar": ['STRING ONE', 'STRING TWO',     │ │
    │ │ etc.]}`                                                                                                 │ │
    │ │ - `<object name='baz'><string name="foo" format="capitalize two-words" /><integer name="index"          │ │
    │ │ format="1-indexed" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ │ Json Output:                                                                                            │ │
    │ │                                                                                                         │ │
    │ │                                                                                                         │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │
    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │
    │ │ ┃ Role  Content ┃                                                                                      │ │
    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │
    │ │ └──────┴─────────┘                                                                                      │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │
    │ │ {"translated_statement": "It is so bad that it's a letdown."}                                           │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │
    │ │ {'translated_statement': "It is so bad that it's a letdown."}                                           │ │
    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯