AI Contract Classifier — prompt, inputs & assumptions
How databook.nyc's Digital Services pages classify NYC technology contracts — the AI prompt, its tiered inputs, outputs, and caveats. Shared for expert review.
For expert review. This document describes exactly how the "Digital Services Analysis" pages on databook.nyc classify NYC technology contracts. Everything below is editorial/AI-derived enrichment layered on top of official, normalized government data — it is a prompt to investigate, not a determination. We are seeking feedback on whether the prompt, the inputs it relies on, and the outputs it produces are sound enough to publish and to inform renewal decisions.
- Where it runs: an offline batch job over contracts held by vendors tagged
digital_services. Results are stored and read by the public pages; a humancuratedoverride is never overwritten by re-runs. - Model / settings: Google Gemini 3.5 Flash,
temperature = 0, forced JSON output, ~20 contracts per request. - Source code:
api/classify_digital_contracts.py. - Status: v2 — the classifier now reads richer, multi-source scope text (see §1), not just the contract title. This directly addresses the biggest v1 critique. ~639 expiring-before-2030 contracts are classified today.
1. What the model sees (tiered inputs)
The model no longer judges from a bare title. For each contract we assemble the best available description by joining four official, already-normalized sources, and the prompt tells the model to weigh them in priority order (richest first):
| Priority | Field(s) given to model | Source (all official NYC data) | How joined | Coverage* |
|---|---|---|---|---|
| 1 | contract_purpose, expense_category |
Checkbook NYC (checkbook_contract_meta) |
by normalized contract ID | ~88% |
| 2 | scope_description, notice_category, selection_method, special_case_reason |
City Record / CROL notice for the PIN (longest description wins) | by PIN/EPIN | subset |
| 3 | commodity (Main Commodity), solicitation_name |
PASSPort solicitations | EPIN-prefix crosswalk | ~8% |
| 4 | contract_title, program, industry, vendor_name, agency |
PASSPort / MOCS contracts | base record | 100% |
| context | contract_type, term, award_amount vs current_amount, status, procurement_method |
PASSPort | base record | 100% |
* Share of the classified set for which that input is present and non-empty. The Checkbook contract purpose — the most authoritative statement of what a contract actually buys — is available for ~88% of contracts, so most judgments now rest on a real scope description. Where higher tiers are missing the model falls back to the title (a known weak case — see Assumption 2).
It still does NOT see: the full statement of work / deliverables, line items, actual spend, number of integrations, data sensitivity/classification, or user counts. The richest input is typically one to a few sentences.
2. The exact prompt (system instruction)
You classify New York City government technology contracts to help the city decide which EXPIRING contracts it should NOT renew.
Each contract comes with several fields — weigh them in roughly this priority when they are present and non-empty: contract_purpose and expense_category (from Checkbook NYC, the most authoritative description of what the contract buys), then scope_description and notice_category (from the City Record notice), then commodity and solicitation_name (from the originating solicitation), then contract_title, program and industry. The title alone is often vague; always prefer the richer contract_purpose / scope_description / commodity when available. contract_type, term, award_amount vs current_amount, status, procurement_method, selection_method and special_case_reason are context for the renewal judgment.
For each contract, judge:
- tech_relevant: Is this genuinely a digital / IT / software / data service or product? Return false for physical-world work mis-tagged as tech: pest control, painting, ship/drydock repair, physical security guards, fuel, waste hauling, generators/power equipment, office furniture/filing, medical supplies, building/HVAC systems, courier/delivery.
- is_license: Is this primarily the purchase of a SOFTWARE LICENSE or subscription (vs. custom development, staffing, consulting, hardware)?
- license_product: If is_license, the product/platform name (e.g. "Microsoft Office 365", "ArcGIS", "Salesforce"); else "".
- license_purpose: If is_license, what the software is used for, one short phrase; else "".
- function_category: A 2-4 word capability bucket. Prefer reusing common buckets: "Website/CMS", "GIS/mapping", "ERP/financials", "Case management", "Cybersecurity", "Data/analytics", "Office productivity", "Staffing/consulting", "Hardware/infrastructure", "Telecom/network", "Identity/access", "Payments", "Document management", "Non-tech". Use the commodity field as a strong hint when present.
- build_vs_buy: How plausibly could the city replace this by building its own solution with open-source software + modern AI tooling? "high" = simple, commoditized software a small team could now stand up (basic websites, forms, simple dashboards, chatbots); "medium" = feasible but real effort/integration; "low" = specialized/regulated/hardware/deeply-integrated platforms or non-tech.
- rationale: One sentence justifying the build_vs_buy rating, citing the scope/commodity evidence you relied on.
Base your judgment on the evidence provided; do not invent capabilities not implied by the fields. Return one result object per contract, preserving the given id.
3. The output (per contract)
| Field | Type | Meaning |
|---|---|---|
tech_relevant |
true/false | Is this genuinely a digital/IT service (vs. mis-tagged physical work)? |
is_license |
true/false | Is this primarily a software license/subscription? |
license_product |
text | Product name, if a license |
license_purpose |
text | One-phrase use of the software, if a license |
function_category |
text | One of ~14 capability buckets (see prompt) |
build_vs_buy |
high / medium / low | How replaceable with open-source + AI |
rationale |
text | One sentence justifying the build-vs-buy rating |
How each output is used on the site: tech_relevant=false hides the contract
from digital totals (cleanup of mis-tagged vendors); build_vs_buy=high raises a
"Build-your-own candidate" flag; licenses get a badge + "Licenses only" filter;
function_category powers a category filter.
4. Worked examples (live output)
These are real rows from the current run, chosen to show the tiered inputs at work. Note how several titles are just a vendor name + procurement code — the classification is only possible because of the richer Checkbook/solicitation/ notice text.
Opaque title → decoded by richer input
| Contract title (input tier 4) | Richer input used (tier 1–3) | Result |
|---|---|---|
K Systems Solutions LLC 25MI021301R0X00 |
"New and Renewal Licenses for Existing SurveyMonkey Software" | license · SurveyMonkey · Data/analytics · build=high |
Amendment-SPRUCE TECHNOLOGY INC-22EI014301R0J0 |
"Online referral portal for Early Intervention" | Website/CMS · build=high |
BIT Nintex Workflow Software 6300039X |
(purpose) Nintex workflow automation | license · Nintex Workflow · Office productivity · build=high |
Maintenance and support of Track IT |
"Maintenance and support of Track It!" | license · Track-It! · Case management · build=high ("basic IT ticketing… can easily be built") |
"Build-your-own" candidates (build=high)
| Title / scope | Result + rationale (verbatim, truncated) |
|---|---|
| OrgChart Now Software Subscription | Office productivity · "Org chart visualization is a highly commoditized capability that can easily be built…" |
| GoDiagram 10 Team License (MTA project) | Data/analytics · "GoDiagram is a software library for creating interactive diagrams, which can easily…" |
| MWBE Public Building Dashboard and Data Analysis | Data/analytics · "Building a public building dashboard and performing data analysis is highly feasible…" |
Non-tech catches (tech_relevant = false → removed from digital totals)
| Title | Richer input | Result |
|---|---|---|
CSB Multiyear High and Low Pressure Boiler… |
"High and Low Pressure Boilers Maintenance Services" | Non-tech — physical boiler maintenance |
Janitorial Services at DYFJ Secure Detention… |
(purpose) janitorial services | Non-tech |
Interagency Communications Committee Support |
"…Committee Support Renewal" (meeting support) | Non-tech — professional committee support |
Specialized / not easily replaceable (build=low)
| Title | Result + rationale |
|---|---|
| IT Consulting Services for CurRent NYC | Staffing/consulting · "professional IT consulting and development services…" |
| TLC Connect Quality Assurance Consultant | Staffing/consulting · "professional services contract for a Quality Assurance consultant…" |
5. Assumptions & judgment calls baked in (please critique)
- The framing is renewal-oriented, not neutral. The prompt's first line states the goal is deciding which contracts NOT to renew — this may bias the model toward "replaceable." Should this instead be a neutral capability classifier whose output a human weighs separately?
- Build-vs-buy is inferred from a short scope, not a full SOW. v2 feeds the Checkbook contract purpose / notice / commodity (mitigating the v1 "title only" problem for ~88% of contracts), but the richest input is still typically one to a few sentences. It has no view of integration footprint, data sensitivity, compliance regime, user base, or maintenance burden — the things that usually decide build-vs-buy in practice. And ~12% still fall back to the title alone.
- The "open-source + AI can replace it" premise is itself an assumption. It encodes an optimistic prior about the city's in-house build and ongoing maintenance/security capacity, and ignores total cost of ownership. "A small team could stand it up" is doing a lot of work.
tech_relevantis a hard-coded exclusion list (pest control, drydock, guards, fuel, waste…), not a principled definition. Dual-use cases (e.g. "security systems" = cybersecurity vs. physical alarm) are ambiguous; novel non-tech categories can slip through.is_licensecollapses a spectrum — pure license vs. SaaS subscription vs. license-plus-implementation-plus-support bundles are lumped together; "primarily a license" is undefined.function_categoryis a fixed, NYC-agnostic taxonomy chosen by us, not derived from how NYC actually organizes IT. Free-text drift is still possible.- No confidence or abstention. The model is asked to base its judgment on the evidence and not invent capabilities, but there is still no "insufficient information" output — a contract with only a thin title still gets a confident rating, indistinguishable from one backed by a full Checkbook purpose.
- Three coarse build-vs-buy tiers with thresholds defined only by examples ("basic websites, forms, chatbots" = high); no notion of who builds/maintains it, or over what time horizon.
- Single model, single pass. No second-model check or ensemble; human review
is optional (supported via a
curatedflag, but not required). - Input coverage is uneven and silent. Higher-tier inputs (notice text, commodity) are present for only a minority of contracts, so two contracts with the same rating may rest on very different evidence — and the output does not record which tier of input it used.
6. Questions we'd like expert input on
- Neutral classifier vs. explicitly renewal-oriented framing — which is appropriate for a public, official-data product?
- With richer scope now available for most contracts, is a build-vs-buy rating publishable (labeled heuristic), or does a credible call still require inputs we don't feed (full SOW, # of integrations, data classification, incumbency)?
- Should
build_vs_buycarry an explicit confidence and an "insufficient information" value — and should the output record which input tier it relied on? - Is the
function_categorytaxonomy right for NYC, or should it map to an existing city/federal IT category scheme (e.g. the Checkbook expense categories, NIGP commodity codes)? - Where is human review required vs. optional before a flag is shown publicly?
- Is the
tech_relevantdenylist the right mechanism, or should "digital service" be defined positively (e.g. from commodity/expense codes)?
Generated to support expert review of the databook.nyc Digital Services Analysis
classifier (v2 — tiered inputs). Proposed changes can be wired back into
api/classify_digital_contracts.py and re-run.