Skip to main content

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 into FeatureAttributesComponent.
  • 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 into FeatureAttributesComponent via per-type routing.
  • Models — vehicle models classified across multiple attribute categories simultaneously (e.g. "GLC 300 4MATIC" → BODY_STYLE=SUV, SIZE_SEGMENT=MIDSIZE). Integrated into ModelAttributesComponent.

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

ModuleResponsibility
keys.pyFeatureKey protocol, NormalizedNameKey for features, NormalizedModelKey for models (variadic composite key)
models.pyAttribute, ProposedMapping (with optional value), MultiMapping, NoMapping, NewAttributeProposal, NewValueProposal, MappingDecision union type
store.pyDataStore (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.pyMappingAgent — orchestrates override / cache lookup, prior-code auto-map, LLM agent fallback. TaggingTypeConfig and make_feature_type_config() for per-type agent generation
tools.pyRecord writers: write_mapping_records, write_no_mapping_record, write_proposal_record, write_value_proposal_record, check_prior_code_auto_map
tagging.pytag_features_with_llm() (binary), tag_features_by_type() (multi-level routing), tag_models_with_llm()

Seed data and the CLI live inside the library:

PathContent
attributes/cli/ai-attributes CLI — seed and review commands
attributes/seed/seed.yaml~34 canonical feature-level attributes
attributes/seed/model_seed.yaml8 model-level attributes (all multi-level)
attributes/seed/feature_type_seed.yaml6 multi-level attributes for paint, fabric, wheel, trim

Lookup priority

For every entity, the agent checks in this order:

  1. Manual overrideattribute-overrides table, keyed by feature_key. Reviewer-set; always wins.
  2. Cached mappingattribute-mappings GSI on feature_key (any status: APPROVED or PENDING_REVIEW). Cache hit prevents re-invocation.
  3. Confirmed no-mappingno-mappings table (any status). Skip the agent.
  4. Prior-code auto-map — every prior approved mapping for the entity's feature codes agrees on the same attribute_id with confidence >= 0.9 and the feature-key string similarity is >= 0.75. Writes new mapping rows with mapping_source = PRIOR_CODE_AUTO.
  5. LLM agentclaude-haiku-4-5 invoked via pydantic_ai, with search_attributes and get_mappings_for_attribute exposed as tools plus native WebSearchTool.

Agent output and review routing

The agent returns one of four decision types:

ResultConditionRouting
ProposedMappingconfidence >= thresholdWritten to attribute-mappings with status = APPROVED
ProposedMappingconfidence < thresholdWritten with status = PENDING_REVIEW
NoMappingWritten to no-mappings with status = PENDING_REVIEW
NewAttributeProposalWritten to attribute-proposals with status = PENDING_REVIEW
NewValueProposalWritten 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

TableHash keyGSIsPurpose
attribute-registryattribute_idCanonical attributes with optional values list and tagging_type
attribute-mappingsmapping_idfeature_key-index, attribute_id-index, oem_feature_code-indexOne row per (feature_key, oem_feature_code, model_id); status tracks review state
attribute-overridesfeature_keyManual reviewer overrides
no-mappingsfeature_keyFeatures confirmed to have no matching attribute
attribute-proposalsproposal_idLLM-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_typeAgentOutputExample
OPTION, PACKAGEFEATURE_CONFIG (binary)1:1 FUNCTIONAL_ATTRIBUTE column"Head-up Display" → HEAD_UP_DISPLAY
PAINTauto-generated from seedPAINT_COLOR, PAINT_MATERIAL"Obsidian Black Metallic" → BLACK, METALLIC
FABRICauto-generated from seedFABRIC_COLOR, FABRIC_MATERIAL"Nappa Leather Black" → BLACK, LEATHER
WHEELauto-generated from seedWHEEL_DIAMETER"19-Inch AMG" → 19
TRIMauto-generated from seedTRIM_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
AttributeRequiredDefaultPurpose
key_fieldsyesDataFrame columns forming the model's composite key
description_fieldsyesColumns synthesized into the LLM prompt context
max_modelsno0 (no cap)Cap on models processed per partition
prompt_versionyesAgent prompt version string; set to "" to disable LLM tagging
concurrencyno10Max concurrent LLM calls
skip_cachenofalseBypass DynamoDB cache (re-runs all entities through LLM)
auto_approve_thresholdyesConfidence threshold for auto-approving mappings

Dagster resource

The AttributeStoreResource is registered in build_core_defs() and configured via environment variables:

VariablePurposeDefault
ATTRIBUTE_STORE_TABLE_PREFIXDynamoDB table name prefix(empty)
ATTRIBUTE_STORE_REGIONAWS regionus-east-1
DYNAMODB_LOCAL_ENDPOINTLocal DynamoDB endpoint for testing (local dev only)unset

The agent uses pydantic-ai's native WebSearchTool wrapped in NativeTool to research unfamiliar features when the registry search returns no promising candidates.