自訂產生的處理常式 customize-generated-handler

IMPORTANT
Adobe LLM Apps目前在Beta中。
此處顯示的功能、工作流程和UI不一定代表產品的最終狀態。 若要加入Beta,請傳送電子郵件至llm-apps-beta@adobe.com。

平台會為每個產生的動作建立工作處理常式。 處理常式一開始會傳回範例資料,讓您測試完整的體驗。

使用本指南瞭解處理常式合約,並將範例資料取代為API或資料來源。

歷程:​尋找產生的處理常式→瞭解其輸入和結果,→連線您的系統→使Widget合約在測試和部署→保持一致。

尋找產生的處理常式

開啟上線期間選取的處理常式存放庫:

actions/
└── <action-name>/
    └── index.js

相符的測試會單獨儲存:

test/
└── actions/
    └── <action-name>.test.js

編輯產生的index.js。 請勿變更執行階段檔案,例如entry.js

處理常式合約

每個處理常式會匯出一個非同步函式:

module.exports = async (args) => {
  return {
    content: [
      { type: 'text', text: 'Response for the LLM platform.' }
    ],
    structuredContent: {
      // Data for the widget.
    }
  };
};

函式接收args物件並傳回結果物件。

輸入: args

args包含為LLM Apps中的動作定義的引數。

針對具有categoryquery引數的動作:

module.exports = async ({ category = '', query = '' } = {}) => {
  // Use the validated action arguments.
};

執行階段會在動作中繼資料包含inputSchema時驗證輸入結構描述,就像部署後一樣。 沒有actions.json的本機處理常式探索不會套用結構描述驗證。 處理常式應一律強制執行商業規則,例如支援的值、最大長度以及允許的組合。

輸出: content

一律傳回content。 這是由LLM平台及不顯示Widget的主機所讀取的內容部分陣列。

content: [
  {
    type: 'text',
    text: 'Found 3 products matching your search.'
  }
]

請保持此回應簡潔。 不要包含認證、內部錯誤或使用者無權檢視的資料。

輸出: structuredContent

動作有Widget時傳回structuredContent。 它必須是純物件,而非裸陣列。

structuredContent: {
  products: [
    {
      id: 'P-100',
      name: 'Frescopa House Blend',
      price: '$14.99'
    }
  ],
  total: 1
}

structuredContent會傳送至Widget,而非LLM。 僅傳回介面所需的欄位。

若為純文字動作,可省略structuredContent

處理常式 — Widget合約

處理常式和Widget共用一個合約: structuredContent的形狀。

Action arguments
      ↓
Handler
      ├── content → LLM text response
      └── structuredContent → Widget
                                  ↓
                           bridge.toolResult

Widget會從LLM應用程式SDK Bridge讀取處理常式結果:

export default async function decorate(block, bridge) {
  const result = await bridge.toolResult;
  const products = result?.structuredContent?.products ?? [];

  // Render products.
}

如果處理常式傳回:

structuredContent: {
  products: [...],
  total: 3
}

Widget必須讀取structuredContent.productsstructuredContent.total

變更欄位名稱或型別可能會中斷Widget。 一起更新處理常式、Widget和測試。

取代範例資料

產生的處理常式通常包含記憶體中的範例陣列。 以伺服器端呼叫您的系統來取代該資料查詢。

const API_ORIGIN = process.env.PRODUCT_API_ORIGIN;
const API_TOKEN = process.env.PRODUCT_API_TOKEN;

module.exports = async ({ query = '' } = {}) => {
  const normalizedQuery = String(query).trim();
  if (!normalizedQuery || normalizedQuery.length > 200) {
    return {
      content: [{ type: 'text', text: 'Enter a valid product search.' }],
      structuredContent: { products: [], total: 0 }
    };
  }

  if (!API_ORIGIN || !API_TOKEN) {
    throw new Error('Product API configuration is unavailable.');
  }

  const origin = new URL(API_ORIGIN);
  if (origin.protocol !== 'https:') {
    throw new Error('Product API configuration must use HTTPS.');
  }

  const url = new URL('/v1/products', origin);
  url.searchParams.set('query', normalizedQuery);

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${API_TOKEN}` },
    signal: AbortSignal.timeout(8000)
  });

  if (!response.ok) {
    throw new Error('Product service request failed.');
  }

  const payload = await response.json();
  if (!payload || !Array.isArray(payload.products)
      || !payload.products.every((product) =>
        product
        && typeof product.id === 'string'
        && typeof product.name === 'string'
        && typeof product.price === 'string')) {
    throw new Error('Product service returned an unexpected response.');
  }

  const products = payload.products.map(({ id, name, price }) => ({
    id,
    name,
    price
  }));

  return {
    content: [
      { type: 'text', text: `Found ${products.length} matching products.` }
    ],
    structuredContent: {
      products,
      total: products.length
    }
  };
};

在處理常式中保留受保護的網路存取。 切勿將API憑證放入Widget JavaScript或原始檔控制中。

處理預期狀態

保留每個結果的可預測輸出圖形。

找到個結果

{
  content: [{ type: 'text', text: 'Found 3 products.' }],
  structuredContent: { products: [...], total: 3 }
}

沒有結果

{
  content: [{ type: 'text', text: 'No matching products were found.' }],
  structuredContent: { products: [], total: 0 }
}

Widget現在可以呈現空白狀態,而不需猜測products是否存在。

對於服務失敗,會在不公開棧疊追蹤、權杖、內部主機或上游回應主體的情況下傳回或擲回安全錯誤。

測試合約

每當處理常式變更時,請更新產生的測試。 封面:

  • 有效且無效的引數。
  • 「結果」和「無結果」狀態。
  • API失敗和逾時。
  • API回應的格式錯誤。
  • content永遠存在。
  • structuredContent是純物件。
  • Widget預期的形狀。

執行:

npm test

如需本機MCP測試,請參閱本機處理常式開發與測試

部署變更

  1. 認可並推送處理常式變更。
  2. 如果資料形狀已變更,請更新並推送Widget。
  3. 將應用程式部署到Stage。
  4. 測試ChatGPT外掛程式
  5. 中繼成功後,請部署至生產環境。

接下來,請參閱自訂產生的Widget

recommendation-more-help
llm-apps-help-main-toc