Redis Agent Playbooks quickstart

Publish an FAQ, retrieve it for an agent, and update the answer with Python, HTTP, or the Iris CLI.

Use Redis Agent Playbooks to publish a FAQ, retrieve its answer to “Where can I see how my refund is progressing?”, and update the answer agents receive.

Before you begin

Create a playbook and wait until it is Ready. Create an authoring key with write and publish permissions. Both include read.

This walkthrough needs no agent framework or model-provider key.

Follow one tab throughout the page. The Python SDK and command-line interface (CLI) require Python 3.11 or later. The cURL and CLI examples use Bash and jq.

Connect to the playbook

  1. For Python or CLI examples, install the unified package:

    python -m venv .venv
    source .venv/bin/activate
    python -m pip install 'redis-iris>=1.0.0'
    
  2. Copy the Endpoint and Playbook ID from the console's Connect dialog. Save them with your authoring key in .env. Replace the placeholders in this file. Use the endpoint's base address, including https://, without the /iris/data/v1/agent-playbooks/... path.

    IRIS_AGENT_PLAYBOOKS_URL='<data-plane-endpoint>'
    IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID='<playbook-id>'
    IRIS_AGENT_PLAYBOOKS_API_KEY='<playbook-api-key>'
    
  3. Load the file in the shell where you run the examples:

    set -a; source .env; set +a
    
  4. Expand an example to copy the complete program. Save it as quickstart.py, quickstart.cli.sh, or quickstart.sh (cURL), then run python quickstart.py, bash quickstart.cli.sh, or bash quickstart.sh.

Keep .env out of source control and keys out of skill files and model prompts. Use iris.toml profiles for several deployments. All three clients use the authoring key in .env for this walkthrough.

AgentPlaybooks.from_env() reads the connection values. Its public .client property exposes authoring, search, and review operations. The CLI reads the same environment variables and uses --json when saving or processing responses.

from redis_iris.agent_playbooks import AgentPlaybooks

playbook = AgentPlaybooks.from_env()
print(playbook.client.get_playbook_health())
set -euo pipefail

iris agent-playbooks health
set -euo pipefail
PLAYBOOK_API="${IRIS_AGENT_PLAYBOOKS_URL%/}/iris/data/v1/agent-playbooks/playbooks/$IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID"
curl --fail-with-body --silent --show-error \
  "$PLAYBOOK_API/health" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" | jq .

Keep the printed identifier for cleanup. SDK and cURL examples create another entry when rerun. The CLI uses refund-status as a stable entry key: faqs push creates missing content, appends changed content, and skips unchanged content. Start with an unused key for this walkthrough. After an uncertain write, inspect entry state before retrying.

Create an FAQ

Create one answer with a locale and topic for retrieval filters. Save the returned entry identifier and version.

faq_content = {
    "intents": ["Check a refund", "Find an existing refund"],
    "attributes": {"locale": "en-US", "topic": "refund"},
    "payload": {
        "faq": {
            "question": "How do I check the status of a refund?",
            "answer": (
                "Open the order in your order history and select Refund status. "
                "If no refund is listed, contact support with your order number."
            ),
        }
    },
}
draft = playbook.client.create_faq(content=faq_content)
faq_id = draft.entry.entry_id
version = draft.version.version
print(faq_id, version, draft.entry.publication)
cat > faq.json <<'JSON'
{
  "entryKey": "refund-status",
  "question": "How do I check the status of a refund?",
  "answer": "Open the order in your order history and select Refund status. If no refund is listed, contact support with your order number.",
  "intents": ["Check a refund", "Find an existing refund"],
  "attributes": {"locale": "en-US", "topic": "refund"}
}
JSON
iris agent-playbooks faqs validate faq.json
iris --json agent-playbooks faqs push faq.json > faq-draft.json
FAQ_ID=$(jq -er '.results[0].entryId' faq-draft.json)
VERSION=$(jq -er '.results[0].version' faq-draft.json)
jq . faq-draft.json
cat > faq.json <<'JSON'
{
  "content": {
    "intents": ["Check a refund", "Find an existing refund"],
    "attributes": {"locale": "en-US", "topic": "refund"},
    "payload": {"faq": {
      "question": "How do I check the status of a refund?",
      "answer": "Open the order in your order history and select Refund status. If no refund is listed, contact support with your order number."
    }}
  }
}
JSON
curl --fail-with-body --silent --show-error \
  -X POST "$PLAYBOOK_API/faqs" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' --data-binary @faq.json > draft.json
FAQ_ID=$(jq -er '.entry.entryId' draft.json)
VERSION=$(jq -er '.version.version' draft.json)
jq '{entryId: .entry.entryId, version: .version.version, publication: .entry.publication}' draft.json

The first version is 1, and publication is absent or empty. You can review the entry, but search and discovery cannot return it yet.

The CLI prepares a flat FAQ JSON file and validates it locally before pushing it. Its result is an action report containing the entry identifier and version, rather than the HTTP creation response. push leaves the version unpublished unless you request publication.

Review and publish the answer

Read the exact version you created.

stored = playbook.client.get_faq_version(faq_id=faq_id, version=version)
print(stored.content)
iris --json agent-playbooks faqs show "$FAQ_ID" --version "$VERSION" | jq .content
curl --fail-with-body --silent --show-error \
  "$PLAYBOOK_API/faqs/$FAQ_ID/versions/$VERSION" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" | jq .content

Review the question and answer. Then publish the version you read.

playbook.client.publish_faq(faq_id=faq_id, version=version)
iris agent-playbooks faqs publish "$FAQ_ID" --version "$VERSION"
curl --fail-with-body --silent --show-error \
  -X PUT "$PLAYBOOK_API/faqs/$FAQ_ID/publication" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --argjson version "$VERSION" '{version: $version}')" | jq .publication

The publication response identifies the version that agents can retrieve.

Search with a different question

Search with a paraphrase of the stored question.

Check for your entry identifier, version 1, and the answer about order history. Scores and rankings vary with the embedding model and other entries. If no match appears, repeat only the search after a short wait, then check the locale filter and similarity threshold.

Discover FAQs for an agent

Discovery returns FAQ answers and skill previews in one request. This example filters for refund guidance to add to the agent's context.

discovery = playbook.discover(
    "Help a customer find their refund status",
    filter_={"eq": {"name": "topic", "value": "refund"}},
)
print(discovery.as_prompt())
cat > discover.json <<'JSON'
{
  "query": "Help a customer find their refund status",
  "filter": {"eq": {"name": "topic", "value": "refund"}},
  "limit": 5
}
JSON
iris --json agent-playbooks discover --input discover.json | jq .
curl --fail-with-body --silent --show-error \
  -X POST "$PLAYBOOK_API/entries/discover" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"query":"Help a customer find their refund status","filter":{"eq":{"name":"topic","value":"refund"}},"limit":5}' | jq .

With no pins, pinnedEntries is empty. Matching published entries appear in discoveredEntries. Python exposes these collections as discovery.pinned and discovery.discovered; as_prompt() formats them for agent context. When you add skills later, discovery returns their previews without loading all their instructions.

Publish an updated answer

Append a complete replacement version to the same entry. Include the attributes and intents you want to retain.

from copy import deepcopy

updated_content = deepcopy(faq_content)
updated_content["payload"]["faq"]["answer"] += (
    " Support can check the current stage but cannot guarantee a bank posting date."
)
updated = playbook.client.append_faq_version(faq_id=faq_id, content=updated_content)
print("Review this version:", updated.version.version, updated.version.content)
jq '.answer += " Support can check the current stage but cannot guarantee a bank posting date."' \
  faq.json > faq-update.json
iris --json agent-playbooks faqs push faq-update.json > faq-updated.json
UPDATED_VERSION=$(jq -er '.results[0].version' faq-updated.json)
iris --json agent-playbooks faqs show "$FAQ_ID" --version "$UPDATED_VERSION" | jq .content
jq '.content.payload.faq.answer += " Support can check the current stage but cannot guarantee a bank posting date."' \
  faq.json > faq-update.json
curl --fail-with-body --silent --show-error \
  -X POST "$PLAYBOOK_API/faqs/$FAQ_ID/versions" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' --data-binary @faq-update.json > updated.json
jq .version updated.json

The identifier stays the same, and search still serves the first publication. Review and publish the new version, then search again.

playbook.client.publish_faq(faq_id=faq_id, version=updated.version.version)
updated_results = playbook.client.search_faqs(
    query="Where can I see how my refund is progressing?",
    filter_={"eq": {"name": "locale", "value": "en-US"}},
    limit=5,
)
for match in updated_results.items:
    if match.entry_id == faq_id and match.faq is not None:
        print("Published answer:", match.version, match.faq.answer)
iris agent-playbooks faqs publish "$FAQ_ID" --version "$UPDATED_VERSION"
iris --json agent-playbooks faqs search --input search.json | jq .items
curl --fail-with-body --silent --show-error \
  -X PUT "$PLAYBOOK_API/faqs/$FAQ_ID/publication" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq '{version: .version.version}' updated.json)" | jq .publication
curl --fail-with-body --silent --show-error \
  -X POST "$PLAYBOOK_API/faqs/search" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"query":"Where can I see how my refund is progressing?","filter":{"eq":{"name":"locale","value":"en-US"}},"limit":5}' \
  | jq --arg id "$FAQ_ID" '.items[] | select(.entryId == $id) | {version, answer: .faq.answer}'

The result for your FAQ should now identify version 2 and include the added sentence about bank posting dates. If it still shows version 1 or is absent, repeat only the search.

Clean up

Keep the sample published while testing an agent. Complete-program copies retain the FAQ. When you finish, set PLAYBOOK_FAQ_ID to the printed entry identifier and run the deletion example with the same connection values. Withdraw it instead to retain history.

playbook.client.delete_faq(faq_id=faq_id)
iris agent-playbooks faqs delete "$PLAYBOOK_FAQ_ID" --yes
curl --fail-with-body --silent --show-error \
  -X DELETE "$PLAYBOOK_API/faqs/$FAQ_ID" \
  -H "Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_API_KEY"

Use Python asynchronously

Unified Python helpers use names such as adiscover and askill. Generated operations on .client use an _async suffix. This standalone example uses async with to close connections after the search. Save it as async_search.py and run python async_search.py with the same environment variables and a reader key. Keep an FAQ published to test matching.

AgentPlaybooksSettings.timeout sets the request timeout in seconds. Generated .client operations accept timeout_ms in milliseconds. For custom transport settings, construct a generated client and pass it to AgentPlaybooks(client=...). See the generated client's custom HTTP client documentation for ownership and configuration details.

Next steps

RATE THIS PAGE
Back to top ↑