{
  "schema_version": 2,
  "id": "operate/iris/agent-playbook/self-managed/extraction",
  "title": "Configure background FAQ extraction",
  "url": "https://redis.io/docs/latest/operate/iris/agent-playbook/self-managed/extraction/",
  "summary": "Connect Redis Agent Memory, run the Agent Playbooks extraction worker, and create, enable, pause, and remove extraction bindings.",
  "tags": [
    "docs",
    "operate",
    "iris"
  ],
  "last_updated": "2026-11-04T01:09:56+02:00",
  "page_type": "content",
  "content_hash": "ce43b092a0db0257fc2861b4b1cd4bd119e497289ad32dfaa2f1037c211b8d6d",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Background extraction reads idle Redis Agent Memory sessions and creates frequently asked question (FAQ) proposals for human review. It doesn't publish FAQs or create skills. An accepted proposal still needs review and publication before agents receive it.\n\nExtraction is optional. It needs Redis Agent Memory in the same tenant, a persistent queue Redis, a generation model, and workload identity between the two products. It needs no Identity Service subject grants, lookup credentials, or Identity Service control trust."
    },
    {
      "id": "how-extraction-is-wired",
      "title": "How extraction is wired",
      "role": "content",
      "text": "| Caller | Calls | Credential | Trust you configure |\n| --- | --- | --- | --- |\n| Agent Playbooks control plane | Redis Agent Memory control plane, to list stores and confirm the source store's tenant | Projected ServiceAccount token, audience `api://memory-cp` | `controlplane.configData.auth.workload_identity` on Redis Agent Memory, role `viewer` |\n| Extraction worker | Redis Agent Memory data plane, to read sessions | Projected ServiceAccount token, audience `api://memory-dp` | `memory.auth.jwt_subjects` on Redis Agent Memory, `authority: static`, `mem-store:*` read |\n| Extraction worker | Agent Playbooks data plane, to search and propose | Projected ServiceAccount token, audience `api://playbook-dp` | Rendered by this chart when the worker is enabled |\n\nWith release `playbook`, namespace `<namespace-name>`, and `fullnameOverride: playbook`, the two subjects are:\n\n- `system:serviceaccount:<namespace-name>:playbook-controlplane`\n- `system:serviceaccount:<namespace-name>:playbook-worker`\n\nIf you change the namespace or resource names, change these subjects everywhere."
    },
    {
      "id": "extraction-with-the-redis-iris-suite-chart",
      "title": "Extraction with the Redis Iris suite chart",
      "role": "content",
      "text": "With the [`redis-iris` Helm chart](https://redis.io/docs/latest/operate/iris/agent-playbook/self-managed/deploy#install-with-the-redis-iris-suite-chart), set `global.iris.extraction.enabled: true` instead of the `extraction` values on this page. The suite derives the extraction connection, `worker.enabled`, and `worker.sourceURL`, and configures Redis Agent Memory to trust `system:serviceaccount:<namespace-name>:redis-agent-playbook-controlplane` and `system:serviceaccount:<namespace-name>:redis-agent-playbook-worker`, so skip [Configure Redis Agent Memory trust](#configure-redis-agent-memory-trust). You still create the worker Secret and set `playbook.worker.existingSecret` and `playbook.worker.region`. Leave `config.existingSecret` unset on both Redis Agent Memory planes and on the Agent Playbooks control plane."
    },
    {
      "id": "plan-the-connection",
      "title": "Plan the connection",
      "role": "content",
      "text": "Prepare these values before you enable extraction:\n\n| Value | Purpose |\n| --- | --- |\n| Redis Agent Memory store | The source store for a binding. Its tenant must match `tenantId`. |\n| ServiceAccount issuer and JSON Web Key Set (JWKS) URI | Validate the projected control-plane and worker tokens. Both must use `https://`. The issuer must exactly match the issuer Redis Agent Memory accepts. |\n| Redis Agent Memory control-plane URL | Let the Agent Playbooks control plane discover stores and check ownership. |\n| Redis Agent Memory data-plane URL | Let the worker read sessions. |\n| Queue Redis URL | Store extraction jobs. Use persistence, `noeviction`, and capacity monitoring. Don't reuse the Agent Playbooks metadata or content Redis. |\n| Generation provider and model | Extract and consolidate FAQ candidates. These credentials are separate from the embedding configuration. |\n\nUse certificate-verified endpoints. Redis Agent Memory must be able to fetch the JWKS without a bearer token. See [Prepare worker identity for your platform](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/deploy#prepare-worker-identity-for-your-platform) for where each Kubernetes platform serves its issuer. Allow network paths for the control plane, worker, queue Redis, both data planes, and the model provider."
    },
    {
      "id": "configure-redis-agent-memory-trust",
      "title": "Configure Redis Agent Memory trust",
      "role": "configuration",
      "text": "Add these settings to the Redis Agent Memory values. Merge the list entries with its existing worker identity and authentication settings, and keep `memory.auth.method: agent_key`:\n\n[code example]\n\nSet `tenant` to the Agent Playbooks `tenantId`. Don't give the control-plane subject a suite administrator role: that bypasses the ownership checks extraction relies on. Upgrade the Redis Agent Memory release, and wait for its planes to be ready.\n\nSee the [Redis Agent Memory authentication guide](https://redis.io/docs/latest/operate/iris/agent-memory/self-managed/authentication) before you change an existing authentication configuration."
    },
    {
      "id": "configure-agent-playbooks-extraction",
      "title": "Configure Agent Playbooks extraction",
      "role": "configuration",
      "text": "Save the private worker configuration as `worker.yaml`:\n\n[code example]\n\nThe reasoning mapping tells the client how to send the worker's `low` reasoning effort to this model. `generation_api` accepts `chat_completions` or `responses`. Don't add `worker.target_token`; the chart supplies the worker's own token for the Agent Playbooks data plane.\n\nCreate the Secret:\n\n[code example]\n\nSave `playbook-extraction-values.yaml`:\n\n[code example]\n\n- `ramControlPlaneBaseURL` is the Redis Agent Memory control plane. `extraction.enabled` requires it.\n- `worker.sourceURL` is the Redis Agent Memory data plane. `worker.targetURL` defaults to this release's data-plane Service; set an `https://` endpoint when the security profile requires it.\n- `worker.region` names the queues, `playbook-extraction:<region>:scan` and `playbook-extraction:<region>:extract`. Run one active regional deployment per binding inventory.\n- Bindings start disabled, so the worker reads nothing until you [enable a binding](#create-and-enable-a-binding).\n\nApply both values files:\n\n[code example]\n\nStartup fails when the source URL, model, region, or token file is missing, or when the queue Redis is unreachable. Check the worker's `/health/readiness` and `/metrics` on port `9203` before you enable a binding."
    },
    {
      "id": "create-and-enable-a-binding",
      "title": "Create and enable a binding",
      "role": "content",
      "text": "These requests need the admin token or an OpenID Connect (OIDC) token with the `product_admin` or `suite_admin` role. Operators and viewers can't list sources or create or enable bindings. Each playbook has one binding.\n\nList the Redis Agent Memory stores the control plane can see:\n\n[code example]\n\nCreate a binding for one store:\n\n[code example]\n\nA successful create returns `201` with `state: READY` and `enabled: false`. It confirms that the store exists in your tenant and that the caller may connect it. It doesn't check model access or worker connectivity. Binding IDs start with `bind_`.\n\nEnable the binding with its current `revision`:\n\n[code example]\n\nEnabling checks the store and the caller again. A stale `expectedRevision` returns `412`; read the binding again and rebuild `activation.json`. If the check fails, the binding is unchanged. A binding that later fails reports `state: BLOCKED` with a `blockedReason`, such as `source_not_found` or `source_access_denied`.\n\nThe Iris command-line interface (CLI) runs the same steps: `iris agent-playbooks extraction bind <memory-store-id> --enable`, then `extraction list`, `show`, `pause`, `enable`, and `delete`.\n\nThe worker looks for due bindings every five minutes (`worker.scanInterval`, default `300s`). A session must stay idle for 30 minutes by default before the worker extracts from it. Set `job.idleWindowSeconds` from 60 to 86,400 when you create or update a binding. Neither setting is an end-to-end freshness guarantee: queue and model time also affect when a proposal appears.\n\nFor the first run, send a small session with a known FAQ to the bound store, let it go idle, and review the proposals and evidence in the playbook. A healthy deployment can produce no proposal for a session that has no reusable FAQ."
    },
    {
      "id": "tune-the-worker",
      "title": "Tune the worker",
      "role": "content",
      "text": "| Value | Default | Purpose |\n| --- | --- | --- |\n| `worker.scanInterval` | `300s` | How often the worker looks for due bindings. Use the same value on every worker that shares a queue, and restart them after a change. Shorter intervals add source-listing load. |\n| `worker.timeout` | `2m` | FAQ strategy timeout. |\n| `worker.reasoningEffort` | `low` | Reasoning effort sent through the model's reasoning mapping. |\n| `worker.maxInputTokens` | `8192` | Input cap for each model request. |\n| `worker.maxCompletionTokens` | `4096` | Output cap for each model request. |\n| `worker.scanConcurrency`, `worker.extractConcurrency` | `1` | Consumers per queue per replica. |\n| `worker.maxPending` | `1000` | Bound on unfinished tasks per queue. |\n\nThese settings apply to every binding. Model and credential changes apply to unfinished and new sessions; completed sessions aren't extracted again. Keep the first deployment at one replica and one extraction consumer, and qualify model quality and load before you scale."
    },
    {
      "id": "monitor-extraction",
      "title": "Monitor extraction",
      "role": "content",
      "text": "Scrape `/metrics` on port `9203` of every worker pod, and alert on sustained queue age, permanent failures, exhausted attempts, lease loss, provider errors, and dispatch staleness. Useful signals include:\n\n- `iris_jobqueue_oldest_pending_seconds`, for backlog. Use `max` across pods, not a sum.\n- `iris_jobqueue_processing_duration_seconds_count{processing_outcome=\"permanent_failure\"}`\n- `iris_playbook_extraction_runs_total{exhausted=\"true\"}`\n- `iris_playbook_extraction_dispatch_last_success_age_seconds`\n\nFour charged failures exhaust a session's attempts. An empty queue doesn't prove that every session was processed."
    },
    {
      "id": "pause-or-remove-extraction",
      "title": "Pause or remove extraction",
      "role": "content",
      "text": "To pause a binding, send `enabled: false` with its current `expectedRevision`. To remove it, send `DELETE` to the binding; the request returns `202`. Both work while Redis Agent Memory is unavailable. Deleting a binding doesn't delete Redis Agent Memory data, proposals, or FAQs.\n\nWork the worker already accepted can still finish after a pause or delete. The worker's static read access lasts until you remove its subject from the Redis Agent Memory values and both data planes roll out.\n\nTo stop new extraction configuration while keeping the worker and connection, set `extraction.enabled: false`. Bindings can still be paused and deleted.\n\nTo remove extraction from the deployment, delete the bindings first, then set `extraction.enabled`, `extraction.connection.enabled`, and `worker.enabled` to `false` together, and remove both subjects from the Redis Agent Memory values. The chart refuses an enabled connection without the worker unless `extraction.connection.workerSubjects` lists another worker."
    }
  ],
  "examples": [
    {
      "id": "configure-redis-agent-memory-trust-ex0",
      "language": "yaml",
      "code": "memory:\n  auth:\n    method: agent_key\n    jwt_subjects:\n      - issuer: https://<service-account-issuer>\n        jwks_uri: https://<service-account-jwks-uri>\n        audience: [api://memory-dp]\n        subjects:\n          - subject: system:serviceaccount:<namespace-name>:playbook-worker\n            authority: static\n            roles: [viewer]\n            resources:\n              \"mem-store:*\": {permissions: [read]}\n\ncontrolplane:\n  configData:\n    auth:\n      workload_identity:\n        enabled: true\n        issuer: https://<service-account-issuer>\n        jwks_uri: https://<service-account-jwks-uri>\n        audience: [api://memory-cp]\n        subjects:\n          - subject: system:serviceaccount:<namespace-name>:playbook-controlplane\n            tenant: default\n            roles: [viewer]",
      "section_id": "configure-redis-agent-memory-trust"
    },
    {
      "id": "configure-agent-playbooks-extraction-ex0",
      "language": "yaml",
      "code": "inference_providers:\n  private-generation:\n    protocol: openai\n    endpoint:\n      base_url: https://<generation-provider-host>/v1\n      generation_api: chat_completions\n    http_client:\n      timeout: 30s\n      max_retry_attempts: 0\n\nworker:\n  redis:\n    urls:\n      - rediss://<queue-user>:<queue-password>@<queue-host>:6379\n  llm:\n    provider: private-generation\n    models:\n      default_chat_model: <generation-model-name>\n    reasoning_mappings:\n      - model_patterns:\n          - <generation-model-name>\n        values:\n          low:\n            reasoning_effort: low\n    credentials:\n      type: static\n      api_key: <generation-provider-api-key>",
      "section_id": "configure-agent-playbooks-extraction"
    },
    {
      "id": "configure-agent-playbooks-extraction-ex1",
      "language": "bash",
      "code": "kubectl -n <namespace-name> create secret generic playbook-extraction-worker \\\n  --from-file=worker.yaml=./worker.yaml",
      "section_id": "configure-agent-playbooks-extraction"
    },
    {
      "id": "configure-agent-playbooks-extraction-ex2",
      "language": "yaml",
      "code": "extraction:\n  enabled: true\n  connection:\n    enabled: true\n    issuer: https://<service-account-issuer>\n    jwksURI: https://<service-account-jwks-uri>\n    ramControlPlaneBaseURL: https://<memory-control-plane-host>\n\nworker:\n  enabled: true\n  replicaCount: 1\n  region: <region>\n  existingSecret: playbook-extraction-worker\n  sourceURL: https://<memory-data-plane-host>\n  scanConcurrency: 1\n  extractConcurrency: 1",
      "section_id": "configure-agent-playbooks-extraction"
    },
    {
      "id": "configure-agent-playbooks-extraction-ex3",
      "language": "bash",
      "code": "helm upgrade playbook redis-ai/redis-agent-playbook \\\n  --version 1.0.0 \\\n  --namespace <namespace-name> \\\n  -f playbook-values.yaml \\\n  -f playbook-extraction-values.yaml \\\n  --wait --timeout 10m\nkubectl -n <namespace-name> rollout status deployment/playbook-worker",
      "section_id": "configure-agent-playbooks-extraction"
    },
    {
      "id": "create-and-enable-a-binding-ex0",
      "language": "bash",
      "code": "curl --fail-with-body -sS \\\n  \"$IRIS_AGENT_PLAYBOOKS_SERVICE_URL/iris/v1/agent-playbooks/playbooks/$IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID/extraction/stores\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_SERVICE_TOKEN\" | jq '.items'",
      "section_id": "create-and-enable-a-binding"
    },
    {
      "id": "create-and-enable-a-binding-ex1",
      "language": "bash",
      "code": "jq -n --arg store '<memory-store-id>' \\\n  '{source: {kind: \"REDIS_AGENT_MEMORY\", storeId: $store}, job: {kind: \"FAQ\"}}' \\\n  > binding-request.json\n\ncurl --fail-with-body -sS \\\n  -X POST \"$IRIS_AGENT_PLAYBOOKS_SERVICE_URL/iris/v1/agent-playbooks/playbooks/$IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID/extraction/bindings\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_SERVICE_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  --data-binary @binding-request.json > binding.json\nexport BINDING_ID=\"$(jq -er '.bindingId' binding.json)\"",
      "section_id": "create-and-enable-a-binding"
    },
    {
      "id": "create-and-enable-a-binding-ex2",
      "language": "bash",
      "code": "curl --fail-with-body -sS \\\n  \"$IRIS_AGENT_PLAYBOOKS_SERVICE_URL/iris/v1/agent-playbooks/playbooks/$IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID/extraction/bindings/$BINDING_ID\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_SERVICE_TOKEN\" > binding.json\n\njq '{expectedRevision: .revision, enabled: true}' binding.json > activation.json\n\ncurl --fail-with-body -sS \\\n  -X PATCH \"$IRIS_AGENT_PLAYBOOKS_SERVICE_URL/iris/v1/agent-playbooks/playbooks/$IRIS_AGENT_PLAYBOOKS_PLAYBOOK_ID/extraction/bindings/$BINDING_ID\" \\\n  -H \"Authorization: Bearer $IRIS_AGENT_PLAYBOOKS_SERVICE_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  --data-binary @activation.json > binding.json",
      "section_id": "create-and-enable-a-binding"
    }
  ]
}
