הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.
לעיון במסמכי התיעוד של
Apigee Edge
מעבד התוספים של Apigee מאפשר לכם להחיל יכולות של AI Gateway מ-Apigee על תעבורה שלא עוברת דרך שרת proxy של Apigee. לדוגמה, שירות שפועל ב-Google Kubernetes Engine, API שמנוהל על ידי שער אחר או שרת MCP לסוכן AI. מכיוון שנתיב התנועה שונה משרת proxy רגיל של API, חלק מהגדרות המדיניות ספציפיות למעבד התוסף. בדף הזה מפורטים השיקולים האלה ומוצגות דוגמאות להגדרות. בכל קטע מדיניות יש קישור למדריך המלא של המדיניות.
המדיניות שמופיעה בדף הזה חלה באותו אופן בכל מקום שבו מעבד התוספים מצורף:
- למאזן עומסים של אפליקציות, כתוסף תעבורה, ששולט בתעבורה לקצה העורפי שמאחוריו. במאמר תחילת העבודה עם מעבד התוספים של Apigee מוסבר איך להגדיר את האפשרות הזו.
- ל-Agent Gateway, כהרחבת הרשאות, ששולטת בשיחות יוצאות של סוכן AI – למודל שלו, ולכלים ולשרתי MCP שהוא משתמש בהם – בלי לשנות את הסוכן. הוראות להגדרה מפורטות במאמר חיבור מעבד התוסף של Apigee ל-Agent Gateway.
שיקולים עיקריים
השיקולים הבאים רלוונטיים כשמצרפים מדיניות כלשהי לשרת proxy של מעבד תוספים. במדריך למתחילים מוסבר איפה לצרף את המדיניות למעבד התוספים.
התאמת הדוגמאות ל-API של המודל
בדוגמאות שבדף הזה נעשה שימוש במבנה הבקשה והתגובה של Gemini. ספקי מודלים אחרים פועלים באותו אופן: UserPromptSource, LLMTokenUsageSource ו-LLMModelSource הם תבניות של הודעות, ולכן צריך להגדיר אותם למיקום המקביל במטען הייעודי (payload) של ה-API. המדיניות עצמה לא השתנתה.
הגדרת היקף תנועת הגולשים שמעובדת
ב-proxy ל-API רגיל, מקצים נתיב בסיסי והלקוחות קוראים לכתובת ה-URL הספציפית הזו, כך שה-proxy מקבל רק את תעבורת הנתונים שמיועדת לו. למעבד התוסף אין נתיב בסיס, ולכן צריך להגדיר את היקף התנועה שלו בשני מקומות: בתוסף, שקובע מה מגיע ל-Apigee בכלל, ובשרת ה-proxy, שקובע מה מופעל על התנועה שמגיעה. משתמשים בשניהם.
מסננים קודם את התוסף, כדי שתנועה שלא רוצים לנהל לא תישלח אף פעם אל Apigee:
- בתוסף להפניית תנועה, מגדירים תנאי התאמה בשפת CEL בשרשרת התוספים, לדוגמה
matchCondition.celExpression: 'request.host == "example.com"'. - בתוסף הרשאות, מתאימים מארחים וקידומות של נתיבים בקטע [
httpRules.to.operations] במדיניות ההרשאות.
לאחר מכן מגדירים את ההיקף של כל מדיניות בתוך ה-proxy. שרת proxy יחיד לעיבוד תוספים מקבל את כל מה שהתוסף בוחר, ועדיין יכול להיות שילוב של כמה דברים: קריאות למודל של סוכן AI, קריאות למצב ולסשן וקריאות לטלמטריה יכולות לחלוק מארח. מדיניות שבודקת מטען ייעודי (payload) של מודל נכשלת בקריאה שלא כוללת מטען ייעודי, ומדיניות שנכשלת חוסמת את הבקשה. מצרפים כל מדיניות עם תנאי שמגדיר את היקף המדיניות לתנועה הרצויה:
<!-- Run only on the model (generateContent) call --> <Step> <Name>My-Policy</Name> <Condition>(request.uri Like "*generateContent*")</Condition> </Step> <!-- Or scope by backend host --> <Step> <Name>My-Policy</Name> <Condition>(request.header.host = "backend.example.com")</Condition> </Step>
שימוש בשרת Proxy ללא יעד
שרתי proxy של מעבדי תוספים מעבדים תנועה שנקלטה ולא כוללים נקודת קצה יעד. פורסים אותם כשרתי proxy ניתנים להרחבה. כל ה-proxy בסביבת מעבד התוספים צריכים להיות מאותו סוג.
קריאת תוכן השיחה שיוּרטה
מדיניות שבודקת את מטען הנתונים פועלת על ההודעה כפי שהיא מופיעה בחיבור. במקרה של קריאה למודל, זהו הבקשה והתשובה של המודל. לדוגמה, ההנחיה למשתמש היא $.contents[-1].parts[-1].text והתשובה של המודל היא $.candidates[-1].content.parts[-1].text.
מתן הרשאות לחשבון שירות למדיניות שקוראת לשירותי Google
למדיניות שקוראת לשירות Google – לדוגמה, הגנה מוגברת על המודל, או בדיקות ההטמעה והאינדקס שמשמשות לשמירת נתונים במטמון סמנטי – נדרש חשבון שירות לפריסה. פורסים את ה-proxy עם הפרמטר serviceAccount.
בטיחות AI עם הגנה מוגברת על המודל
מצרפים את כללי המדיניות SanitizeUserPrompt ו-SanitizeModelResponse בהיקף של הקריאה למודל. הוראות להגדרת תבניות זמינות במאמר תחילת העבודה עם הגנה מוגברת על המודל.
<SanitizeUserPrompt name="SUP-sanitize" continueOnError="false"> <ModelArmor> <TemplateName>projects/PROJECT/locations/LOCATION/templates/TEMPLATE</TemplateName> </ModelArmor> <UserPromptSource>{jsonPath('$.contents[-1].parts[-1].text',request.content,true)}</UserPromptSource> </SanitizeUserPrompt>
מצרפים את SUP-sanitize לתהליך הבקשה עם התנאי (request.uri Like "*generateContent*"). כשהפרומפט תואם לתבנית הגנה מוגברת על המודל, המדיניות דוחה את הבקשה, כך שהפרומפט אף פעם לא מגיע למודל.
שמירה סמנטית במטמון
מצרפים את מדיניות SemanticCacheLookup לתהליך הבקשה ואת מדיניות SemanticCachePopulate לתהליך התגובה, שתיהן בהיקף של קריאה למודל. הוראות להגדרת אינדקס והטמעה מופיעות במאמר תחילת העבודה עם שמירת נתונים במטמון סמנטי. כשבקשה תואמת להנחיה ששמורה במטמון, התשובה מוגשת מהמטמון בלי הפעלת המודל.
מגבלות על טוקנים בקריאות למודלים
שני כללים מגבילים את השימוש בטוקנים של מודל שפה גדול (LLM) בקריאה למודל. מגדירים את ההיקף של שניהם לקריאה למודל עם התנאי (request.uri Like "*generateContent*"). הוראות להגדרה מפורטות במאמר תחילת העבודה עם מדיניות אסימוני LLM.
הגבלת מספר הטוקנים בפרומפט
המדיניות PromptTokenLimit מגבילה את מספר האסימונים על סמך ההנחיה למשתמש – הגבלת שימוש בהנחיות. מצרפים אותו לתהליך הבקשה. הוא קורא את הפרומפט מהבקשה שנקלטה ודוחה את הקריאה כשהקצב חורג מהמגבלה, כך שפרומפט גדול מדי אף פעם לא מגיע למודל. בדוגמה הבאה, ההנחיות מוגבלות ל-1,000 טוקנים לדקה:
<PromptTokenLimit continueOnError="false" enabled="true" name="PTL-limit-prompt"> <Rate>1000pm</Rate> <UserPromptSource>{jsonPath('$.contents[-1].parts[-1].text',request.content,true)}</UserPromptSource> </PromptTokenLimit>
הגבלת צריכת טוקנים של תשובות
המדיניות LLMTokenQuota אוכפת מכסת צריכת טוקנים לאורך פרק זמן מסוים, וסופרת את הטוקנים שמוחזרים בתגובת המודל. מצרפים מופע של EnforceOnly לזרימת הבקשות כדי לדחות קריאות אחרי שהמכסה חורגת, ומופע של CountOnly לזרימת התגובות כדי לספור את האסימונים שנעשה בהם שימוש, שנקראים מ-$.usageMetadata.candidatesTokenCount. נותנים לשני המקרים את אותו SharedName כדי שהם יעדכנו מונה יחיד. המדיניות הזו דורשת פרוקסי שניתן להרחבה.
הזוג הבא מגביל את מספר האסימונים ל-15,000 לכל 30 דקות:
<!-- Request flow: reject when the token quota is exceeded --> <LLMTokenQuota name="LTQ-enforce" type="rollingwindow"> <SharedName>llm-token-counter</SharedName> <EnforceOnly>true</EnforceOnly> <Allow count="15000"/> <Interval>30</Interval> <TimeUnit>minute</TimeUnit> <Distributed>true</Distributed> </LLMTokenQuota> <!-- Response flow: count the tokens used in the model response --> <LLMTokenQuota name="LTQ-count" type="rollingwindow"> <SharedName>llm-token-counter</SharedName> <CountOnly>true</CountOnly> <Allow count="15000"/> <Interval>30</Interval> <TimeUnit>minute</TimeUnit> <Distributed>true</Distributed> <LLMTokenUsageSource>{jsonPath('$.usageMetadata.candidatesTokenCount',response.content,true)}</LLMTokenUsageSource> </LLMTokenQuota>
ניהול תנועה: מכסת שימוש, הרשאה ומניעת עליות פתאומיות בנפח התנועה
כללי המדיניות האלה נאכפים על תנועה דרך מעבד התוספים לכל שרת עורפי, כולל שרתים עורפיים שלא מתארחים ב-Apigee, כמו API שמתארח ב-GKE או כלי או שרת MCP שסוכן AI קורא להם. מגדירים את ההיקף של כל מדיניות ל-Backend שרוצים להגן עליו:
<Step><Name>Verify-API-Key</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step> <Step><Name>Quota-Limit</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step> <Step><Name>Spike-Arrest</Name><Condition>(request.header.host = "backend.example.com")</Condition></Step>
- הרשאה: שימוש במדיניות VerifyAPIKey או OAuthV2. שיחות לא מורשות נדחות לפני שהן מגיעות לקצה העורפי.
- Quota: משתמשים במדיניות Quota כדי לאכוף מגבלה מדויקת על שיחות. מגדירים את המדיניות כמבוזרת וסינכרונית, כך שההגבלה נאכפת כספירה משותפת יחידה בזמן הריצה.
- הגבלת ספייקים: שימוש במדיניות SpikeArrest כדי למתן את העלייה הפתאומית בתנועת הגולשים. האכיפה של מדיניות הגבלת תנועה פתאומית מתבצעת לכל מעבד הודעות, ולא מבטיחה שיעור מדויק של תנועה גלובלית. כשצריך להגדיר מגבלה מדויקת, צריך להשתמש במדיניות הקצאה.
שינוי הודעות וחילוץ משתנים
אפשר להשתמש במדיניות AssignMessage כדי להוסיף, לשנות או להסיר חלקים מהודעה – כותרות, פרמטרים של שאילתות או מטען ייעודי (payload) – ובמדיניות ExtractVariables כדי לקרוא ערכים מתוך הודעה למשתנים שאפשר להשתמש בהם במדיניות מאוחרת יותר. בעזרת מעבד התוספים, שתי המדיניות פועלות בתהליך הבקשה, על הבקשה שנקלטה, ובתהליך התגובה, על התגובה מהקצה העורפי. בדומה לכל מדיניות של מעבד תוספים, צריך להגדיר לכל קובץ מצורף תנאי כדי שהמדיניות תפעל רק על התנועה הרצויה.
בדוגמה הבאה נעשה שימוש ב-ExtractVariables כדי לקרוא שדה מגוף הבקשה, ובזרימת התגובה כדי לקרוא שדה מגוף התגובה:
<!-- Request flow: read a field from the intercepted request --> <ExtractVariables name="EV-from-request"> <Source>request</Source> <JSONPayload> <Variable name="user.prompt"> <JSONPath>$.contents[-1].parts[-1].text</JSONPath> </Variable> </JSONPayload> </ExtractVariables> <!-- Response flow: read a field from the backend response --> <ExtractVariables name="EV-from-response"> <Source>response</Source> <JSONPayload> <Variable name="model.answer"> <JSONPath>$.candidates[-1].content.parts[-1].text</JSONPath> </Variable> </JSONPayload> </ExtractVariables>
בדוגמה הבאה משתמשים ברכיב AssignMessage כדי להגדיר כותרת בבקשה לפני שהיא מגיעה לשרת העורפי, ובמענה לפני שהוא חוזר למתקשר:
<!-- Request flow: add a header to the intercepted request --> <AssignMessage name="AM-set-request-header"> <Set> <Headers> <Header name="X-Apigee-Processed">true</Header> </Headers> </Set> <AssignTo createNew="false" type="request"/> </AssignMessage> <!-- Response flow: add a header to the backend response --> <AssignMessage name="AM-set-response-header"> <Set> <Headers> <Header name="X-Apigee-Cache">miss</Header> </Headers> </Set> <AssignTo createNew="false" type="response"/> </AssignMessage>