Skip to main content

cu-sdk-author-analyzer-classify-route

EXPERIMENTAL. Create and test a classify-and-route Azure AI Content Understanding pipeline for packets that contain multiple document types (e.g. invoice + bank statement + loan application in one PDF). Walks per-type schema authoring → outer classifier wiring → batch test → category-aware stdout summary using the typed ContentUnderstandingClient. Use when the user has mixed-document packets.

Informações da origem

Repositório
Azure/azure-sdk-for-python
Última atividade na origem
10 de agosto de 2026 às 23:38
Idioma detectado do SKILL.md
inglês
Estrelas
5.587
Forks
3.348

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
4 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
cu-sdk-author-analyzer-classify-route
description
EXPERIMENTAL. Create and test a classify-and-route Azure AI Content Understanding pipeline for packets that contain multiple document types (e.g. invoice + bank statement + loan application in one PDF). Walks per-type schema authoring → outer classifier wiring → batch test → category-aware stdout summary using the typed ContentUnderstandingClient. Use when the user has mixed-document packets.
# Author a Classify-and-Route Analyzer (mixed document packets) > **[EXPERIMENTAL - REQUIRED FIRST MESSAGE]:** Before asking questions, probing the > environment, or running commands, tell the user: "This analyzer-authoring skill > is experimental and may change. Review generated schemas and test results before > relying on them." Build a classify-and-route pipeline: one **outer classifier analyzer** that segments and labels a multi-document packet, plus one **inner extractor analyzer per document type**. The packet flows through the outer analyzer once; each segment is automatically routed to the matching inner analyzer for field extraction. **This is an iterative, human-in-the-loop workflow.** You will typically run the schema → test → review cycle multiple times to refine both the outer classifier descriptions and each inner schema's field descriptions before you're happy with both classification accuracy and extraction quality. > **[USE INSTEAD]:** If every page in the user's documents is the **same > type** (only invoices, only contracts, etc.), use > [`cu-sdk-author-analyzer`](../cu-sdk-author-analyzer/SKILL.md) instead. > Classify-and-route is for **mixed** packets. > **[ASK USER] Modality check (first thing to confirm):** > > "Are you working with **document** files — PDFs or images of pages? This > skill supports document modalities only." > > - If the user says **document** → continue with this skill. > - If the user says **audio**, **video**, or **image** → stop this skill. > Point them at the [REST tutorial](https://learn.microsoft.com/azure/ai-services/content-understanding/tutorial/create-custom-analyzer). > **[COPILOT INTERACTION MODEL]:** At each step marked with **[ASK USER]**, > pause execution and prompt the user before proceeding. ## Prerequisites Required: venv active, SDK installed, `.env` with `CONTENTUNDERSTANDING_ENDPOINT` (plus `CONTENTUNDERSTANDING_KEY` or `az login`), and `samples/sample_update_defaults.py` run once for this resource. Full setup lives in [`cu-sdk-setup`](../cu-sdk-setup/SKILL.md). > **[COPILOT] Probe first, then route on failure — do not duplicate setup logic here.** > > ```bash > python -c "import sys; print('venv:', 'ok' if sys.prefix != sys.base_prefix else 'INACTIVE')" > ( [ -f .env ] && grep -E '^CONTENTUNDERSTANDING_ENDPOINT=https?://' .env >/dev/null && echo 'endpoint: ok' ) || echo 'endpoint: MISSING' > ( [ -f .env ] && grep -E '^CONTENTUNDERSTANDING_KEY=.+' .env >/dev/null && echo 'key: set' ) || echo 'key: empty' > az account show >/dev/null 2>&1 && echo 'az: ok' || echo 'az: not logged in' > ``` > > | Failure | Route to | > |---|---| > | venv `INACTIVE` or `azure.ai.contentunderstanding` import fails | [`cu-sdk-setup`](../cu-sdk-setup/SKILL.md) Steps 2–3, then resume | > | endpoint `MISSING` | [`cu-sdk-setup`](../cu-sdk-setup/SKILL.md) Step 4 (env vars), then resume | > | endpoint `ok`, key `empty`, `az: not logged in` | [`cu-sdk-setup`](../cu-sdk-setup/SKILL.md) Step 4 auth section (run `az login` **or** add `CONTENTUNDERSTANDING_KEY` to `.env`), then resume | > | All env checks pass but service calls fail with model errors | [`cu-sdk-setup`](../cu-sdk-setup/SKILL.md) Step 6 (`sample_update_defaults.py`), then resume | > | All ok | ✅ Proceed to the Packet check below. | > > Never ask the user to paste an endpoint or API key into chat — they edit `.env` directly or run `az login`. > **[ASK USER] Packet check:** > 1. "Does each document in your packet contain more than one type of form (e.g. an invoice page followed by a bank statement page)?" — if no, route to `cu-sdk-author-analyzer`. > 2. "What types of documents appear in your packets?" — capture as the list of inner analyzers. ## Architecture ``` ┌──────────────────────────────┐ mixed packet ───► │ outer (classifier) analyzer │ │ baseAnalyzerId: prebuilt-… │ │ config.enableSegment: true │ │ config.contentCategories: │ │ invoice ────────►│──┐ │ bank_statement ────────►│──┼──► per-segment fields │ loan_application ────────►│──┘ └──────────────────────────────┘ │ inner analyzers (1 per type) │ ─────────────────────────── ▼ invoice extractor ◄──── routes here for invoice pages bank statement ext. ◄──── routes here for bank pages loan app extractor ◄──── routes here for loan pages ``` Key rules (also captured in [`cu-sdk-common-knowledge`](../cu-sdk-common-knowledge/SKILL.md) § "classify-and-route"): 1. **Category descriptions reference text anchors**, not visual cues (matches the two-stage pipeline rule for fields). 2. **`config.enableSegment` must be `true`** so the classifier can carve up the packet before routing. 3. **Inner analyzers must exist before** the outer classifier is created. The provided script handles ordering automatically. 4. **Category fill rate is per-category**, not packet-wide. The script's stdout summary uses the right denominator. ## Package directory ``` sdk/contentunderstanding/azure-ai-contentunderstanding ``` ## Scripts and templates ``` .github/skills/cu-sdk-author-analyzer-classify-route/ ├── scripts/ │ ├── create_and_test_router.py │ └── create_and_test_router.sh └── templates/ └── classifier_template.json # Starter outer-classifier schema for Step 3 ``` ## Workflow ### Step 1 — Identify the document types Run layout extraction (same as the single-type skill) on a representative packet to see the section headings: ```bash python .github/skills/cu-sdk-author-analyzer/scripts/extract_layout.py \ --input <packet.pdf> \ --output .local_only/layout/ ``` > **[COPILOT]** Read `.local_only/layout/<packet>.layout.md` **yourself** and > identify section headings, page breaks, and content shifts that suggest > different document types. Then present your analysis to the user for > confirmation — do **not** ask the user to read the layout cold. > > Example presentation: > > > "Based on the layout in `<packet>.layout.md` I see these document types: > > > > - **Pages 1–2** — appears to be an *invoice* (top heading 'Invoice', > > contains 'Invoice #' label and a line-item table) > > - **Pages 3–4** — appears to be a *bank statement* (top heading > > 'Account Statement', contains account number and a transaction table) > > - **Page 5** — appears to be a *loan application* (top heading 'Loan > > Application', applicant fields) > > > > Does this look right? Anything to add, remove, or rename before I > > draft schemas?" > > Only fall back to a blank `[ASK USER]` ("What types do you see?") if the > layout is too ambiguous to suggest types confidently. ### Step 2 — Draft one inner schema per type Treat each type as a single-doc-type analyzer (pick `baseAnalyzerId` from the table in [`cu-sdk-common-knowledge` § Choosing `baseAnalyzerId`](../cu-sdk-common-knowledge/SKILL.md#choosing-baseanalyzerid), then add `fieldSchema.fields`). Field descriptions follow the [two-stage pipeline rule](../cu-sdk-common-knowledge/SKILL.md#field-description-rule-the-two-stage-pipeline) — reference text and structure, never visual appearance. See [`cu-sdk-author-analyzer`](../cu-sdk-author-analyzer/SKILL.md) Step 2 for the full field-schema walkthrough. > **[COPILOT] Read best practices before drafting fields.** Before writing > any field description (in any inner schema or the outer classifier's > category descriptions), fetch the official CU best-practices page and > apply its guidance: > > 🔗 https://learn.microsoft.com/azure/ai-services/content-understanding/concepts/best-practices > > The same key principles apply here as in > [`cu-sdk-author-analyzer`](../cu-sdk-author-analyzer/SKILL.md#step-2--draft-a-json-field-schema): > be specific and concrete, use text anchors (not visual cues), include > alternative labels and format examples, prefer `extract` for verbatim > values, and keep the field count focused. > **Reference**: > [`samples/sample_create_classifier.py`](../../../samples/sample_create_classifier.py) > ships a complete worked example using `samples/sample_files/mixed_financial_docs.pdf` > with three categories — Invoice, Bank_Statement, Loan_Application. ### Step 3 — Draft the outer classifier schema The outer schema has **no** `fieldSchema`. Its job is classification + routing. Start from the template: ```bash mkdir -p .local_only/schemas cp .github/skills/cu-sdk-author-analyzer-classify-route/templates/classifier_template.json \ .local_only/schemas/<name>_classifier_v1.json ``` Example after editing (descriptions intentionally generic so they survive minor wording variants — see the **Category description rule** below the example for guidance on writing your own): ```json { "baseAnalyzerId": "prebuilt-document", "description": "Classify mixed financial packets and route to per-type extractors.", "config": { "enableSegment": true, "omitContent": true, "contentCategories": { "invoice": { "description": "A commercial invoice. Look for an invoice / bill heading or label, an invoice number, line-item table with quantities and prices, and a total amount.", "analyzerId": "invoice" }, "bank_statement": { "description": "A bank or account statement. Look for an account-statement heading, an account number, a statement period or date range, and a transaction table with running balance.", "analyzerId": "bank_statement" }, "loan_application": { "description": "A loan or credit application form. Look for an application heading, applicant or borrower fields, requested loan amount, and applicant signature.", "analyzerId": "loan_application" } } }, "models": { "completion": "gpt-5.2", "embedding": "text-embedding-3-large" } } ``` The `analyzerId` value in each category is **an alias** that the script resolves at runtime, matching the `--inner-schema alias=path` flags you pass. Two exceptions skip alias resolution: * Values starting with `prebuilt-` (e.g. `prebuilt-invoice`) are used as-is — no inner schema needed. Useful for routing a category at a service prebuilt extractor. * Categories without an `analyzerId` at all are classification-only — the segment is labelled but no fields are extracted. > **Why `omitContent: true`?** When omitted, the service also returns the > raw, un-segmented document content as an extra entry in `contents`. That > entry has no category, no fields, and shows up in the summary as a > confusing `(uncategorized)` row. Setting `omitContent: true` removes it. > **Category description rule — the example values above are demo-specific.** > > The category descriptions in the JSON above are tied to the demo packet > `samples/sample_files/mixed_financial_docs.pdf` (which uses the literal > headings `Invoice`, `Bank Statement`, `Loan Application`). **Do not copy > them verbatim.** When authoring for the user's own packet, write > descriptions based on what you observed in **the user's** layout output > from Step 1 — use the actual headings, labels, and structural markers > from their documents. > > Keep descriptions: > > - **Generic over surface form** — describe the *kind* of content > ("contains a transaction table with running balance") rather than > hardcoding one specific header string, so minor wording variants still > classify correctly. > - **Concrete enough to be discriminative** — include at least one anchor > that distinguishes this category from the others in the packet. > - **Text-anchored, not visual** — reference headings, labels, and > neighbouring text, never colour / font / position-without-text. Same > reason as the field-description rule in > [`cu-sdk-common-knowledge`](../cu-sdk-common-knowledge/SKILL.md#field-description-rule-the-two-stage-pipeline). ### Step 4 — Validate, create, and batch-test ```bash python .github/skills/cu-sdk-author-analyzer-classify-route/scripts/create_and_test_router.py \ --outer-schema .local_only/schemas/classifier.json \ --inner-schema invoice=.local_only/schemas/invoice.json \ --inner-schema bank_statement=.local_only/schemas/bank_statement.json \ --inner-schema loan_application=.local_only/schemas/loan_application.json \ --input samples/sample_files/mixed_financial_docs.pdf \ --output .local_only/test_results/v1 ``` > **Shortcut — `--schema-dir`:** if your inner schema filenames match the > outer-schema category aliases (e.g. `.local_only/schemas/invoice_v1.json` for category > `invoice`), replace every `--inner-schema alias=path` with a single > `--schema-dir .local_only/schemas/`. The script picks the newest matching file per > alias (alphabetical sort, so `invoice_v2.json` wins over `invoice_v1.json`). > **Iteration helper — `--reuse`:** add `--reuse` to name analyzers by a > sha1 of their schema (`<stem>_<hash[:8]>`) and skip creation when an > analyzer with that ID already exists. Re-running with the same schemas > is a no-op on the create side, so you don't pile up stale analyzers while > iterating. Edit a schema → hash changes → new analyzer is created. The script: 1. Validates every schema (exits with code **2** if any fails — no service call made). 2. Errors out if the outer schema references an alias that has no matching `--inner-schema`, or if you supply an `--inner-schema` that no category uses. 3. Creates inner analyzers first, then patches and creates the outer classifier. 4. Analyzes every input file, writing one JSON per file under `--output`. 5. Prints a **category-aware** stdout summary (per-category fill rate uses each category's segment count, not the packet-wide total). ### Step 5 — Read the category-aware summary Example output: ``` ======================================================================== [SUMMARY] (category-aware) category: bank_statement (1 segments)
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub