Attribute Tagging
Attribute tagging maps OEM vehicle entities to canonical attributes from a platform-wide registry. Three entity types are supported:
- Features (binary) — options and packages mapped 1:1 to functional attributes (e.g. "Head-up Display" →
HEAD_UP_DISPLAY). Integrated intoFeatureAttributesComponent. - Features (multi-level) — paints, fabrics, wheels, and trims classified across multiple attributes simultaneously (e.g. "Obsidian Black Metallic" →
PAINT_COLOR=BLACK,PAINT_MATERIAL=METALLIC). Also integrated intoFeatureAttributesComponentvia per-type routing. - Models — vehicle models classified across multiple attribute categories simultaneously (e.g. "GLC 300 4MATIC" →
BODY_STYLE=SUV,SIZE_SEGMENT=MIDSIZE). Integrated intoModelAttributesComponent.
The library lives at ai_core.attributes. Storage is DynamoDB; the agent is pydantic_ai against claude-haiku-4-5 with native web search.
Components
| Module | Responsibility |
|---|---|
keys.py | FeatureKey protocol, NormalizedNameKey for features, NormalizedModelKey for models (variadic composite key) |
models.py | Attribute, ProposedMapping (with optional value), MultiMapping, NoMapping, NewAttributeProposal, NewValueProposal, MappingDecision union type |
store.py | DataStore (ABC) + DynamoDataStore — abstract interface and DynamoDB implementation with paginated scan/query helpers |
prompts/ | Jinja2 templates (.j2 files) for system prompts and result instructions for each entity type |
agent.py | MappingAgent — orchestrates override / cache lookup, prior-code auto-map, LLM agent fallback. TaggingTypeConfig and make_feature_type_config() for per-type agent generation |
tools.py | Record writers: write_mapping_records, write_no_mapping_record, write_proposal_record, write_value_proposal_record, check_prior_code_auto_map |
tagging.py | tag_features_with_llm() (binary), tag_features_by_type() (multi-level routing), tag_models_with_llm() |
Seed data and the CLI live inside the library:
| Path | Content |
|---|---|
attributes/cli/ | ai-attributes CLI — seed and review commands |
attributes/seed/seed.yaml | ~34 canonical feature-level attributes |
attributes/seed/model_seed.yaml | 8 model-level attributes (all multi-level) |
attributes/seed/feature_type_seed.yaml | 6 multi-level attributes for paint, fabric, wheel, trim |
Lookup priority
For every entity, the agent checks in this order:
- Manual override —
attribute-overridestable, keyed byfeature_key. Reviewer-set; always wins. - Cached mapping —
attribute-mappingsGSI onfeature_key(any status:APPROVEDorPENDING_REVIEW). Cache hit prevents re-invocation. - Confirmed no-mapping —
no-mappingstable (any status). Skip the agent. - Prior-code auto-map — every prior approved mapping for the entity's feature codes agrees on the same
attribute_idwithconfidence >= 0.9and the feature-key string similarity is >= 0.75. Writes new mapping rows withmapping_source = PRIOR_CODE_AUTO. - LLM agent —
claude-haiku-4-5invoked viapydantic_ai, withsearch_attributesandget_mappings_for_attributeexposed as tools plus nativeWebSearchTool.
Agent output and review routing
The agent returns one of four decision types:
| Result | Condition | Routing |
|---|---|---|
ProposedMapping | confidence >= threshold | Written to attribute-mappings with status = APPROVED |
ProposedMapping | confidence < threshold | Written with status = PENDING_REVIEW |
NoMapping | — | Written to no-mappings with status = PENDING_REVIEW |
NewAttributeProposal | — | Written to attribute-proposals with status = PENDING_REVIEW |
NewValueProposal | — | Written to attribute-proposals with status = PENDING_REVIEW |
The auto-approve thresholds are required per component: auto_approve_threshold for binary features (typically 0.95) and auto_approve_threshold_multi for multi-level attributes (typically 0.8).
DynamoDB tables
| Table | Hash key | GSIs | Purpose |
|---|---|---|---|
attribute-registry | attribute_id | — | Canonical attributes with optional values list and tagging_type |
attribute-mappings | mapping_id | feature_key-index, attribute_id-index, oem_feature_code-index | One row per (feature_key, oem_feature_code, model_id); status tracks review state |
attribute-overrides | feature_key | — | Manual reviewer overrides |
no-mappings | feature_key | — | Features confirmed to have no matching attribute |
attribute-proposals | proposal_id | — | LLM-proposed new attributes and values awaiting approval |
All tables use PAY_PER_REQUEST billing with point-in-time recovery and deletion protection enabled. Provisioned in deployments/aws/terraform/solutions/dagster-agent/dynamodb_attribute_tagging.tf.
Feature tagging (FeatureAttributesComponent)
Integrated as an optional step controlled by prompt_version. When set, the component runs LLM tagging after regex-based _tag_features().
If the input DataFrame has a feature_type column, tag_features_by_type() routes each type to a specialized agent:
| feature_type | Agent | Output | Example |
|---|---|---|---|
OPTION, PACKAGE | FEATURE_CONFIG (binary) | 1:1 FUNCTIONAL_ATTRIBUTE column | "Head-up Display" → HEAD_UP_DISPLAY |
PAINT | auto-generated from seed | PAINT_COLOR, PAINT_MATERIAL | "Obsidian Black Metallic" → BLACK, METALLIC |
FABRIC | auto-generated from seed | FABRIC_COLOR, FABRIC_MATERIAL | "Nappa Leather Black" → BLACK, LEATHER |
WHEEL | auto-generated from seed | WHEEL_DIAMETER | "19-Inch AMG" → 19 |
TRIM | auto-generated from seed | TRIM_CATEGORY | "Carbon Fiber Trim" → CARBON |
Multi-level agent configs are auto-generated by make_feature_type_config() from the attribute registry — any tagging_type with non-binary attributes gets its own MultiMapping agent. Multi-level results are written into the feature_attributes array column alongside regex results.
Without a feature_type column, all features fall through to binary tagging.
type: ai_core.components.FeatureAttributesComponent
attributes:
oem: mercedes
market: mbusa
name: features
start_date: "2025-01-01"
prompt_version: "v0.1" # enables LLM tagging
feature_code_column: option_id # OEM feature code column (default: option_id)
LLM tagging is enabled when both prompt_version and name_column are set. If name_column is omitted, the component skips LLM tagging.
Model tagging (ModelAttributesComponent)
ModelAttributesComponent produces an attributes/models asset by classifying each vehicle model across multiple attribute categories simultaneously (1:N mapping).
The component reads consolidated/models, synthesizes a description from configurable fields, and runs the LLM agent. Results are stored in a model_attributes column of type List(Struct(attribute_type, attribute_name, value)).
Multi-level attributes
Model attributes support categorical values. An attribute like BODY_STYLE has allowed values (SEDAN, SUV, COUPE, etc.) and the agent picks the matching value. The value field on ProposedMapping carries the selection. Binary model attributes (e.g. MAYBACH_BRAND) have no values list.
YAML config
type: ai_core.components.ModelAttributesComponent
attributes:
oem: mercedes
market: mbusa
start_date: "2025-01-01"
prompt_version: "v0.1"
max_models: 50
key_fields: [model_code, model_year]
description_fields: # DataFrame columns synthesized into LLM context
- name
- vehicle_class_code
- fuel_type
- body_style_name
- is_amg
- is_eq
- is_maybach
- is_green
- msrp
| Attribute | Required | Default | Purpose |
|---|---|---|---|
key_fields | yes | — | DataFrame columns forming the model's composite key |
description_fields | yes | — | Columns synthesized into the LLM prompt context |
max_models | no | 0 (no cap) | Cap on models processed per partition |
prompt_version | yes | — | Agent prompt version string; set to "" to disable LLM tagging |
concurrency | no | 10 | Max concurrent LLM calls |
skip_cache | no | false | Bypass DynamoDB cache (re-runs all entities through LLM) |
auto_approve_threshold | yes | — | Confidence threshold for auto-approving mappings |
Dagster resource
The AttributeStoreResource is registered in build_core_defs() and configured via environment variables:
| Variable | Purpose | Default |
|---|---|---|
ATTRIBUTE_STORE_TABLE_PREFIX | DynamoDB table name prefix | (empty) |
ATTRIBUTE_STORE_REGION | AWS region | us-east-1 |
DYNAMODB_LOCAL_ENDPOINT | Local DynamoDB endpoint for testing (local dev only) | unset |
Web search
The agent uses pydantic-ai's native WebSearchTool wrapped in NativeTool to research unfamiliar features when the registry search returns no promising candidates.