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.

معلومات المصدر

المستودع
Azure/azure-sdk-for-python
آخر نشاط في المصدر
١٠ أغسطس ٢٠٢٦ في ٢٣:٣٨
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥٬٥٨٧
التفرعات
٣٬٣٤٨

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
4 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
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)
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub