Η ίδια η τεκμηρίωση πολιτικών της Microsoft λέει ότι ένα score threshold πάνω από το 0.2 μπορεί να οδηγήσει σε cache mismatch, που είναι ένας ευγενικός τρόπος να πεις ότι το gateway θα απαντήσει σε μια ερώτηση που ο καλών δεν έκανε. Κάθε άλλος μηχανισμός σε αυτή τη σειρά έχει αποτύχει θορυβωδώς: ένα 429, ένα 503, ένα ληγμένο batch job. Ένα semantic cache αποτυγχάνει επιστρέφοντας μια ρέουσα, καλοσχηματισμένη, γεμάτη σιγουριά απάντηση που ανήκει στο prompt κάποιου άλλου. Το Μέρος 5 έκανε τη δαπάνη αποδοτέα. Αυτό το μέρος την κάνει μικρότερη, και αντιμετωπίζει τον μηχανισμό που το πετυχαίνει ως επιφάνεια ορθότητας και όχι ως μοχλό κόστους.

Τι χρειάζεται πραγματικά το cache πριν δουλέψει

Το semantic caching στο API Management έχει μια λίστα προαπαιτούμενων που κρίνει μια επιλογή υποδομής την οποία δεν μπορείς να αναθεωρήσεις αργότερα, οπότε ανήκει πριν από την πολιτική και όχι μετά από αυτήν.

  • Ένα δεύτερο model deployment, για embeddings. Το deployment του chat completion εξυπηρετεί τις κλήσεις των καταναλωτών· ένα ξεχωριστό embeddings deployment είναι αυτό που χρησιμοποιεί το cache για να μετατρέψει ένα prompt σε vector. Αυτό είναι μια δεύτερη γραμμή χωρητικότητας, ένα δεύτερο quota, και ένα δεύτερο πράγμα για παρακολούθηση.
  • Αυθεντικοποίηση με managed identity από το API Management προς τα model APIs, την οποία απαιτεί η πολιτική: το embeddings-backend-auth πρέπει να οριστεί σε system-assigned. Δεν υπάρχει επιλογή βασισμένη σε key εδώ, που είναι ένας καλός περιορισμός.
  • Azure Managed Redis με ενεργοποιημένο το module RediSearch, ρυθμισμένο ως external cache στο API Management instance.

Το τελευταίο στοιχείο κουβαλά την παγίδα. Το RediSearch μπορεί να ενεργοποιηθεί μόνο όταν δημιουργείται το cache, και δεν μπορεί να προστεθεί σε ένα υπάρχον cache. Μια πλατφόρμα που ήδη τρέχει ένα Redis instance για sessions ή για rate-limit state δεν μπορεί απλώς να ενεργοποιήσει το semantic caching: χρειάζεται ένα νέο cache, προμηθευμένο με το module, και το Terraform του Μέρους 2 είναι το σημείο όπου καταγράφεται αυτή η απόφαση. Το να το ανακαλύψεις μέσα σε ένα sprint που είχε προϋπολογίσει ένα απόγευμα για το "enable caching" είναι το συνηθισμένο αποτέλεσμα.

Το ζεύγος πολιτικών

Το lookup πάει στο inbound, το store πάει στο outbound, και καθένα από το ζεύγος μπορεί να εμφανιστεί μόνο μία φορά ανά policy section. Και τα δύο είναι διαθέσιμα σε global, product, API, και operation scope, και σε classic, v2, consumption, και self-hosted gateways.

<inbound>
  <base />
  <llm-semantic-cache-lookup
      score-threshold="0.05"
      embeddings-backend-id="embeddings-backend"
      embeddings-backend-auth="system-assigned"
      ignore-system-messages="true"
      max-message-count="6">
    <vary-by>@(context.Subscription.Id)</vary-by>
  </llm-semantic-cache-lookup>
  <rate-limit calls="10" renewal-period="60" />
</inbound>
<outbound>
  <llm-semantic-cache-store duration="60" />
  <base />
</outbound>

Τέσσερα από αυτά τα attributes είναι αποφάσεις και όχι boilerplate.

Το score-threshold ορίζει πόσο στενά πρέπει να ταιριάζει ένα εισερχόμενο prompt με ένα cached, σε κλίμακα από 0.0 έως 1.0, όπου οι χαμηλότερες τιμές απαιτούν υψηλότερη σημασιολογική ομοιότητα. Αυτό αντιστρέφει ό,τι υποθέτουν οι περισσότεροι στην πρώτη ανάγνωση, και το να πάρεις την κατεύθυνση ανάποδα μετατρέπει ένα συντηρητικό cache σε ένα αδιάκριτο. Η καθοδήγηση της Microsoft είναι να ξεκινήσεις χαμηλά, γύρω στο 0.05, και να ρυθμίζεις προς την αναλογία hit προς miss που θέλεις, με τη ρητή προειδοποίηση ότι πάνω από το 0.2 τα mismatches γίνονται πιθανά και ότι οι ευαίσθητες περιπτώσεις χρήσης πρέπει να μένουν χαμηλότερα.

Το ignore-system-messages="true" αφαιρεί τα system messages πριν αξιολογηθεί η ομοιότητα, και συνιστάται. Μετράει περισσότερο απ' όσο ακούγεται: δύο εφαρμογές με πανομοιότυπες ερωτήσεις χρήστη και διαφορετικά system prompts θα φαίνονταν αλλιώς διαφορετικές στο cache, και μια μεμονωμένη εφαρμογή της οποίας το system prompt δέχεται μια επεξεργασία θα ακύρωνε ολόκληρο το cache της χωρίς κανέναν σημασιολογικό λόγο.

Το max-message-count παραλείπει το caching μόλις ένας διάλογος έχει περισσότερα από τον καθορισμένο αριθμό υπολειπόμενα μηνύματα. Οι μακριές συζητήσεις είναι ακριβώς το σημείο όπου ένα semantic match είναι λιγότερο αξιόπιστο, επειδή το νόημα του τελευταίου μηνύματος εξαρτάται από γύρους που το embedding δεν είδε ποτέ.

Το vary-by είναι το όριο απομόνωσης, και η επόμενη ενότητα εξηγεί γιατί είναι έλεγχος ασφάλειας.

Το vary-by δεν είναι διακοσμητική λεπτομέρεια του partitioning

Χωρίς vary-by, ένα cache μοιράζεται ανάμεσα σε κάθε καλούντα του API. Ένα prompt από τον βοηθό εξυπηρέτησης πελατών που περιέχει τα στοιχεία ενός πελάτη μπορεί να απαντηθεί από ένα cached completion, και ένα σημασιολογικά παρόμοιο prompt από διαφορετικό tenant μπορεί να απαντηθεί από την ίδια ακριβώς εγγραφή. Η τεκμηρίωση το λέει καθαρά: έλεγξε τη διασταυρούμενη πρόσβαση χρηστών σε εγγραφές του cache ορίζοντας το vary-by με συγκεκριμένα αναγνωριστικά χρήστη ή ομάδας χρηστών.

Για αυτή την πλατφόρμα το ελάχιστο είναι το subscription ID, που ευθυγραμμίζει το όριο του cache ακριβώς με το όριο tenancy που έχτισε το Μέρος 5. Όπου μια εφαρμογή εξυπηρετεί τελικούς χρήστες των οποίων τα δεδομένα δεν πρέπει να αναμειχθούν, η σωστή τιμή είναι ένα claim από το επικυρωμένο token και όχι οτιδήποτε μπορεί να ορίσει ο καλών σε ένα header, και αυτός είναι ο λόγος που το validate-jwt του Μέρους 5 γράφει το token του στο output-token-variable-name. Ένα vary-by που διαβάζει ένα μη επικυρωμένο header είναι μια διαρροή δεδομένων μεταξύ tenants με ένα configuration file ως βασική αιτία.

Το κόστος του στενού partitioning είναι χαμηλότερο hit rate, και αυτή η ανταλλαγή είναι πραγματική. Είναι όμως και η σωστή προεπιλογή: ένα cache που δεν διαρρέει ποτέ και εξοικονομεί 20 τοις εκατό νικά ένα που εξοικονομεί 45 τοις εκατό και κάποια στιγμή επιστρέφει την απάντηση του λάθος πελάτη.

Η εξάρτηση που μόλις πρόσθεσες

Το cache κάθεται στο request path κάθε κλήσης, που σημαίνει ότι το Redis βρίσκεται πλέον στο κρίσιμο μονοπάτι του gateway. Η σύσταση της Microsoft είναι συγκεκριμένη και αξίζει να ακολουθηθεί κατά γράμμα: τοποθέτησε μια πολιτική rate-limit ή rate-limit-by-key αμέσως μετά το cache lookup, ώστε το backend να μην κατακλυστεί αν το cache δεν είναι διαθέσιμο.

Σκέψου αυτή την αποτυχία μέχρι το τέλος. Μια κανονική μέρα, ένα σημαντικό μερίδιο της κίνησης δεν φτάνει ποτέ σε μοντέλο. Αν το Redis γίνει μη διαθέσιμο, καθένα από αυτά τα αιτήματα γίνεται μια πραγματική κλήση completion, ακαριαία, απέναντι σε ένα quota διαστασιολογημένο για τη σταθερή κατάσταση με cache. Η διακοπή του cache δεν υποβαθμίζει την πλατφόρμα, πολλαπλασιάζει το φορτίο της, και το rate limit είναι αυτό που μετατρέπει τη διακοπή σε throttling. Είναι το ίδιο μάθημα με οποιοδήποτε cache μπροστά από μια βάση δεδομένων, και εδώ ξεχνιέται ευκολότερα επειδή το cache παρουσιάστηκε ως βελτιστοποίηση κόστους και όχι ως εξάρτηση χωρητικότητας.

Το embeddings deployment αξίζει την ίδια σκέψη. Κάθε lookup περνά το εισερχόμενο prompt από embeddings, οπότε το μοντέλο embeddings χρειάζεται αρκετή χωρητικότητα και αρκετό μέγεθος context για τον όγκο και τα μήκη των prompts στην παραγωγή. Ένα embeddings deployment διαστασιολογημένο για proof of concept γίνεται το σημείο συμφόρησης για όλη την κίνηση, cached ή όχι, επειδή το lookup συμβαίνει πριν γίνει γνωστό το hit ή το miss.

Τι κάνει ένα cache hit στους αριθμούς του Μέρους 5

Ένα hit δεν είναι δωρεάν και δεν είναι completion. Κοστίζει μία κλήση embeddings, λίγο χρόνο Redis, και καθόλου completion tokens, που σημαίνει ότι το μοντέλο chargeback του Μέρους 5 χρειάζεται τρεις μικρές αλλαγές και όχι ξαναγράψιμο.

  • Η δαπάνη embeddings γίνεται γραμμή κόστους πλατφόρμας. Προκύπτει σε κάθε αίτημα, συμπεριλαμβανομένων των misses, και δεν αποδίδεται σε ένα completion. Είτε χρεώνεται στους tenants αναλογικά με το πλήθος αιτημάτων, είτε την απορροφά η πλατφόρμα. Διάλεξε ένα και κατέγραψέ το· μην το αφήσεις έξω από τη συμφωνία των αριθμών.
  • Το hit rate είναι νόμιμη διάσταση metric. Είναι χαμηλής cardinality και ανήκει στο dashboard δίπλα στη δαπάνη, επειδή ένα hit rate που πέφτει είναι συνήθως μια αλλαγή σε prompt template που κανείς δεν ανακοίνωσε.
  • Η εξοικονόμηση πρέπει να αναφέρεται ως αποφευγμένο κόστος, όχι ως δαπάνη. Ένας tenant του οποίου ο λογαριασμός έπεσε 30 τοις εκατό λόγω caching θα υποθέσει ότι ο χαμηλότερος αριθμός είναι η νέα βάση. Το να δείχνεις τα tokens που αποφεύχθηκαν δίπλα στα tokens που χρεώθηκαν κρατά αυτή τη συζήτηση τίμια, και τεκμηριώνει την αξία του Redis instance που πληρώνει η πλατφόρμα.

Πότε να μην κάνεις cache καθόλου

Το semantic caching ταιριάζει σε prompts υψηλού όγκου, χαμηλής διακύμανσης, χωρίς εξατομίκευση. Η αναζήτηση γνώσης λιανικής της εταιρείας πάνω στην τεκμηρίωση προϊόντων είναι κοντά στο ιδανικό. Οι υπόλοιπες περιπτώσεις δεν είναι, και η πολιτική πρέπει να οριοθετείται ανά API αντί να εφαρμόζεται καθολικά, που είναι ακριβώς ο λόγος που υποστηρίζει product και API scope.

Παράλειψέ το όπου η απάντηση εξαρτάται από την τρέχουσα ώρα ή από ζωντανή κατάσταση, επειδή ένα duration 60 δευτερολέπτων είναι παράθυρο ορθότητας, όχι απλώς προτίμηση φρεσκάδας. Παράλειψέ το για ροές tool-calling, όπου η έξοδος του μοντέλου είναι εντολή για δράση και όχι κείμενο για ανάγνωση, και μια ξεπερασμένη εντολή είναι λάθος ενέργεια. Παράλειψέ το για οτιδήποτε εξατομικευμένο πέρα από όσα μπορεί να διαμερίσει το vary-by. Και αντιμετώπισε τους μακρούς διαλόγους πολλών γύρων ως εκτός πεδίου ορίζοντας το max-message-count, αντί να ελπίζεις ότι το similarity score θα το προσέξει.

Τρόποι αποτυχίας που αξίζει να προσέχεις

  • Το threshold ρυθμισμένο ανάποδα. Υψηλότερο σημαίνει χαλαρότερο. Μια ομάδα που βελτιστοποιεί για hit rate το ανεβάζει στο 0.4, η αναλογία βελτιώνεται, και τα mismatches μένουν αόρατα μέχρι ένας χρήστης να αναφέρει μια απάντηση που δεν έχει καμία σχέση με την ερώτησή του.
  • Ένα vary-by που λείπει. Κανένα σφάλμα, καμία προειδοποίηση, και ένα κοινό cache για κάθε tenant του API.
  • Το RediSearch που ανακαλύπτεται αργά. Το module δεν μπορεί να προστεθεί σε υπάρχον cache, οπότε η απάντηση είναι ένα νέο cache και ένα migration, στη μέση μιας δουλειάς που υπέθεσε μια αλλαγή config.
  • Redis εκτός λειτουργίας, backend πλημμυρισμένο. Χωρίς το rate limit αμέσως μετά το lookup, μια διακοπή του cache φτάνει στο μοντέλο ως αιχμή κίνησης.
  • Ένα υποδιαστασιολογημένο embeddings deployment. Καλείται σε κάθε αίτημα, οπότε στραγγαλίζει hits και misses εξίσου, και το σύμπτωμα μοιάζει σαν το cache να κάνει τα πράγματα πιο αργά.
  • Μια αλλαγή σε prompt template που αδειάζει αθόρυβα το cache. Αξίζει alert πάνω στο hit rate αντί να το ανακαλύψεις στον λογαριασμό.

Τι κληρονομεί το Μέρος 7

Το cache απαντά στις επαναλήψεις. Δεν κάνει τίποτα για την πολύ συχνότερη περίπτωση όπου το μοντέλο χρειάζεται πληροφορία στην οποία δεν εκπαιδεύτηκε ποτέ, που είναι το πρόβλημα του retrieval, και η αναζήτηση γνώσης λιανικής της εταιρείας το περιμένει ήδη. Επόμενο: το Azure AI Search απέναντι σε μια αποκλειστική vector database, συγκρινόμενα με τους όρους που πραγματικά κρίνουν την επιλογή.

Διάβασε επίσης

Για την πλευρά υποδομής και πλατφόρμας της λειτουργίας κάτι τέτοιου σε κλίμακα, οι σημειώσεις πεδίου είναι στο ercan.cloud, και ο κόμβος είναι στο ercanermis.com.

Αναφορές