🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Taste

by @indigokarasu

Generates personalized recommendations from real consumption data by scanning email/calendar, enriching venues, and explaining suggestions with prior behavior.

Versionv3.0.1
Downloads331
TERMINAL
clawhub install ocas-taste

πŸ“– About This Skill


name: ocas-taste source: https://github.com/indigokarasu/taste install: openclaw skill install https://github.com/indigokarasu/taste description: Use when generating personalized recommendations grounded in real consumption signals (purchases, visits, plays, watches), scanning email and calendar for consumption data, enriching venue entities with taste-relevant attributes, exploring cross-domain discovery based on actual behavior, checking taste model status, or producing periodic taste pattern reports. Trigger phrases: 'recommend', 'what would I like', 'based on what I've liked', 'suggest something similar', 'my taste', 'what should I try', 'scan my email', 'what have I been eating', 'restaurant recommendations', 'update taste'. Do not use for generic search, editorial top-10 lists, or ad-copy generation. metadata: {"openclaw":{"emoji":"🎯"}}

Taste

Taste builds a personalized taste model from real consumption signals β€” purchases, restaurant visits, food delivery orders, hotel stays, music plays, and movie watches. It scans the user's email and calendar to automatically extract these signals, enriches venue entities with taste-relevant attributes (cuisine, price point, neighborhood, vibe) via Google Maps and web search, and uses temporal decay so recent behavior outweighs stale history. Every recommendation names the specific prior consumption that justifies it, respects dietary restrictions, and only suggests places the user hasn't been.

When to use

  • Scanning email and calendar for consumption signals (restaurant bookings, delivery orders, hotel stays, purchases)
  • Personalized recommendations grounded in real prior behavior
  • Cross-domain discovery based on actual taste signals
  • "What else would I like" reasoning with named evidence
  • Enriching venue/item entities with taste-relevant attributes
  • Taste model status check
  • Weekly or periodic taste pattern summary
  • Do not use

  • Generic web research β€” use Sift
  • Editorial/top-10 style recommendations without personalization
  • Ad-copy or sales-oriented product suggestions
  • Inference of sensitive identity traits from behavior
  • Responsibility boundary

    Taste owns behavior-driven preference modeling, consumption signal extraction from email/calendar, entity enrichment for taste profiling, and evidence-backed recommendations.

    Taste does not own: web research (Sift), social graph (Weave), knowledge graph (Elephas), pattern analysis (Corvus), browsing interpretation (Thread).

    Commands

  • taste.scan β€” scan the user's email and calendar for consumption signals; extract, deduplicate, and promote to signals; queue new items for enrichment
  • taste.scan.report β€” summarize last scan: extractions processed, signals created, cancellations, dedup matches pending review
  • taste.ingest.signal β€” manually record a consumption signal (purchase, visit, play, watch, stay)
  • taste.enrich.item β€” enrich an item with taste-relevant attributes via Google Maps lookup and web search
  • taste.query.recommend β€” generate recommendations grounded in consumption history, enriched attributes, and frequency patterns; respects dietary restrictions; only suggests new places
  • taste.query.serendipity β€” find novel but defensible cross-domain connections
  • taste.model.status β€” return model state: signal count, domains active, enrichment coverage, staleness
  • taste.report.weekly β€” generate a weekly taste pattern summary
  • taste.journal β€” write journal for the current run; called at end of every run
  • taste.update β€” pull latest from GitHub source; preserves journals and data
  • Operating invariants

  • Evidence-first: recommendations must reference specific consumed items
  • Discovery-only: never recommend places the user has already been (exception: seasonal menu changes)
  • Dietary safety: never recommend venues that conflict with stated dietary restrictions or preferences
  • Signal decay: older signals degrade unless reinforced
  • Frequency matters: repeat visits/orders are a strong signal and must be tracked and weighted
  • No speculative identity inference from taste signals
  • Explainability: every recommendation explains the link to prior consumption
  • First-party signals outrank enriched metadata
  • Disabled domains do not appear in recommendations
  • Confidence reflects actual evidence strength, not rhetorical certainty
  • Always use the user's email account, never the agent's account
  • Workflows

    Email/calendar scan workflow (taste.scan)

    1. Access the user's email and search for transactional messages from known services (see references/email_extraction.md for sender allowlist) 2. Access the user's Google Calendar for restaurant reservations and hotel bookings 3. For each matching message/event, extract structured data into an ExtractionRecord 4. Classify email_type: confirmation, reminder, update, cancellation, receipt 5. Compute dedup_key and run dedup pass (see references/email_extraction.md) 6. Exclude cancelled events from promotion 7. Promote valid, non-duplicate extractions to ConsumptionSignals 8. Create or update ItemRecords (increment signal_count, append to visit_dates) 9. Queue unenriched items for enrichment 10. Persist all records 11. Write journal

    Enrichment workflow (taste.enrich.item)

    1. For items with enriched: false, look up the venue/item on Google Maps 2. Extract taste-relevant attributes: cuisine, price level, neighborhood, vibe, rating (see references/enrichment.md) 3. If Google Maps data is insufficient, use web search to fill gaps 4. Update ItemRecord metadata with enriched attributes 5. Set enriched: true and enriched_at 6. Evaluate and create LinkRecords between items sharing attributes 7. Persist updates

    Signal ingestion workflow (taste.ingest.signal)

    1. Receive or normalize input signal 2. Validate domain and signal structure 3. Persist signal 4. Create or update ItemRecord 5. Queue for enrichment if new item 6. Write journal

    Recommendation workflow (taste.query.recommend)

    1. Load all active signals, apply temporal decay (see references/signal_policy.md) 2. Compute effective item strength with frequency and recency bonuses (see references/strength_model.md) 3. Rank items by effective strength within each domain 4. Identify taste patterns from enriched attributes (cuisine clusters, price preferences, neighborhood tendencies) 5. Generate recommendations for *new* venues that match identified patterns 6. Verify each recommendation against dietary restrictions (config.json β†’ user_preferences) 7. Verify each recommendation is not a place the user has visited (check signals/items) 8. Include evidence-linked explanation citing specific consumed items and frequency 9. Write journal

    Signal weighting and decay

    Signal strength and recency both matter. See references/strength_model.md for full model. Key points:

  • Config: decay.halflife_days (default 180)
  • Stale signals weaken unless reinforced by repeat consumption
  • Frequency bonus: +0.05 per repeat visit, capped at +0.15
  • Recency bonus: +0.05 if last signal within 30 days
  • Storage layout

    ~/openclaw/data/ocas-taste/
      config.json
      signals.jsonl
      items.jsonl
      links.jsonl
      decisions.jsonl
      extractions.jsonl
      reports/

    ~/openclaw/journals/ocas-taste/ YYYY-MM-DD/ {run_id}.json

    Default config.json:

    {
      "skill_id": "ocas-taste",
      "skill_version": "3.0.0",
      "config_version": "2",
      "created_at": "",
      "updated_at": "",
      "domains": {
        "enabled": ["music", "restaurant", "book", "movie", "product", "travel", "event"]
      },
      "decay": {
        "halflife_days": 180
      },
      "retention": {
        "days": 0,
        "max_records": 10000
      },
      "email_scan": {
        "enabled": true,
        "last_scan_timestamp": null,
        "extraction_confidence_threshold": 0.6,
        "auto_promote_threshold": 0.8
      },
      "email_sources": {
        "doordash": { "sender_patterns": ["no-reply@doordash.com", "orders@doordash.com"], "domain": "restaurant", "source_type": "purchase" },
        "instacart": { "sender_patterns": ["no-reply@instacart.com"], "domain": "product", "source_type": "purchase" },
        "good_eggs": { "sender_patterns": ["*@goodeggs.com"], "domain": "product", "source_type": "purchase" },
        "tock": { "sender_patterns": ["*@exploretock.com"], "domain": "restaurant", "source_type": "visit" },
        "opentable": { "sender_patterns": ["*@opentable.com"], "domain": "restaurant", "source_type": "visit" },
        "yelp": { "sender_patterns": ["no-reply@yelp.com"], "domain": "restaurant", "source_type": "visit" },
        "amazon": { "sender_patterns": ["auto-confirm@amazon.com", "ship-confirm@amazon.com"], "domain": "product", "source_type": "purchase" },
        "hotels": { "sender_patterns": ["*@booking.com", "*@hotels.com", "*@marriott.com", "*@hilton.com", "*@hyatt.com", "*@ihg.com", "*@airbnb.com"], "domain": "travel", "source_type": "stay" }
      },
      "strength": {
        "base_purchase": 0.80,
        "base_visit": 0.70,
        "base_stay": 0.75,
        "base_play": 0.60,
        "base_watch": 0.60,
        "base_manual": 0.60,
        "frequency_bonus_per_visit": 0.05,
        "frequency_bonus_cap": 0.15,
        "recency_bonus_days": 30,
        "recency_bonus_value": 0.05
      },
      "user_preferences": {
        "dietary_restrictions": [],
        "dietary_preferences": [],
        "cuisine_dislikes": [],
        "notes": ""
      }
    }
    

    OKRs

    Universal OKRs from spec-ocas-journal.md apply to all runs.

    skill_okrs:
      - name: recommendation_evidence_rate
        metric: fraction of recommendations citing at least one consumed item
        direction: maximize
        target: 1.0
        evaluation_window: 30_runs
      - name: serendipity_novelty
        metric: fraction of serendipity results crossing domain boundaries
        direction: maximize
        target: 0.80
        evaluation_window: 30_runs
      - name: signal_freshness
        metric: fraction of active signals within decay half-life
        direction: maximize
        target: 0.60
        evaluation_window: 30_runs
      - name: email_extraction_coverage
        metric: fraction of transactional emails successfully extracted with confidence >= threshold
        direction: maximize
        target: 0.90
        evaluation_window: 30_runs
      - name: dedup_accuracy
        metric: fraction of dedup groupings not subsequently corrected by manual review
        direction: maximize
        target: 0.95
        evaluation_window: 30_runs
      - name: enrichment_coverage
        metric: fraction of items with enriched = true
        direction: maximize
        target: 0.90
        evaluation_window: 30_runs
    

    Skill cooperation

  • User's email and Google Calendar β€” signal extraction (user's account only, never agent's)
  • Google Maps β€” entity enrichment (cuisine, price level, neighborhood, vibe, rating)
  • Web search β€” backup enrichment when Google Maps data is insufficient
  • Sift β€” additional item enrichment via web research
  • Elephas β€” read Chronicle (read-only) for entity context
  • Thread β€” may use Thread signals to detect emerging taste patterns
  • Journal outputs

    Observation Journal β€” all signal ingestion, scan, enrichment, query, and report runs.

    Initialization

    On first invocation of any Taste command, run taste.init:

    1. Create ~/openclaw/data/ocas-taste/ and subdirectories (reports/) 2. Write default config.json with all fields if absent 3. Create empty JSONL files: signals.jsonl, items.jsonl, links.jsonl, decisions.jsonl, extractions.jsonl 4. Create ~/openclaw/journals/ocas-taste/ 5. Register cron job taste:update if not already present (check openclaw cron list first) 6. Log initialization as a DecisionRecord in decisions.jsonl

    Background tasks

    | Job name | Mechanism | Schedule | Command | |---|---|---|---| | taste:update | cron | 0 0 * * * (midnight daily) | taste.update |

    openclaw cron add --name taste:update --schedule "0 0 * * *" --command "taste.update" --sessionTarget isolated --lightContext true --timezone America/Los_Angeles
    

    Self-update

    taste.update pulls the latest package from the source: URL in this file's frontmatter. Runs silently β€” no output unless the version changed or an error occurred.

    1. Read source: from frontmatter β†’ extract {owner}/{repo} from URL 2. Read local version from skill.json 3. Fetch remote version: gh api "repos/{owner}/{repo}/contents/skill.json" --jq '.content' | base64 -d | python3 -c "import sys,json;print(json.load(sys.stdin)['version'])" 4. If remote version equals local version β†’ stop silently 5. Download and install:

       TMPDIR=$(mktemp -d)
       gh api "repos/{owner}/{repo}/tarball/main" > "$TMPDIR/archive.tar.gz"
       mkdir "$TMPDIR/extracted"
       tar xzf "$TMPDIR/archive.tar.gz" -C "$TMPDIR/extracted" --strip-components=1
       cp -R "$TMPDIR/extracted/"* ./
       rm -rf "$TMPDIR"
       
    6. On failure β†’ retry once. If second attempt fails, report the error and stop. 7. Output exactly: I updated Taste from version {old} to {new}

    Visibility

    public

    Support file map

    | File | When to read | |---|---| | references/schemas.md | Before creating signals, items, links, extractions, or recommendations | | references/signal_policy.md | Before decay calculations or domain gating | | references/strength_model.md | Before computing signal strength or ranking items | | references/email_extraction.md | Before running taste.scan; sender allowlist and dedup rules | | references/enrichment.md | Before running taste.enrich.item; what to look up and extract per domain | | references/recommendation_style.md | Before generating recommendations or reports | | references/journal.md | Before taste.journal; at end of every run |

    ⚑ When to Use

    TriggerAction
    - Personalized recommendations grounded in real prior behavior
    - Cross-domain discovery based on actual taste signals
    - "What else would I like" reasoning with named evidence
    - Enriching venue/item entities with taste-relevant attributes
    - Taste model status check
    - Weekly or periodic taste pattern summary