从头开始创建操作 create-action-from-scratch
添加平台未创建的功能。 在LLM Apps中定义操作,在链接的存储库中编写其处理程序,并根据需要添加构件。
在本指南中:了解如何定义操作、实施其处理程序以及连接可选小组件。 在本地测试该操作,部署应用程序,然后在支持的LLM平台上尝试该操作。
对于您的第一个应用,从基础结构设置开始,然后创建操作。 创建操作指南介绍了从网站自动生成和手动创建的过程。
开始之前
您需要:
- 现有的LLM应用程序。
- 链接的处理程序存储库。
- 存储库的本地克隆及其依赖项已安装。
- EDS项目(如果操作显示构件)。
- 用于生产结果的清除API或数据源。
规划操作
围绕一个明确的用户任务设计每个操作。 在打开UI之前,定义:
- 意图 — 用户试图实现的目标。
- 描述 — 何时选择操作。
- 输入 — 用户需要的最少信息。
- 结果 — 处理程序返回的文本和结构化数据。
- 行为 — 操作是读取数据、更改数据还是调用外部系统。
- 小组件 — 结果是否需要可视化界面。
例如,将此计划用于 搜索产品 操作:
Intent: Find products matching a category or search phrase
Inputs:
category: optional string
query: optional string
Result:
content: text summary
structuredContent: products and total count
Behavior: read-only, idempotent, open-world
Widget: product cards
将相关但不同的任务分开。 对于产品搜索和产品购买,请使用单独的操作,因为这两个任务具有不同的输入、风险和确认要求。
创建操作元数据
打开应用程序并选择操作,然后选择创建操作。
创建对话框包含 操作 和 小组件元数据 选项卡。 展开每个部分以配置其字段。
输入基本信息
输入:
- 操作名称 — 简短的任务名称,如搜索产品。
- 描述 — 说明何时使用操作及其返回的内容。
该对话框从名称(如search-products)派生出代码标识符。 标识符在创建后即已修复,并且必须在应用程序中唯一。 使用处理程序文件夹的确切标识符。
有用的描述是具体的:
Search the product catalog by category or keyword. Returns matching
products with their names, prices, categories, and image URLs.
避免模糊描述,如获取产品信息。 LLM平台使用说明来选择不同的操作。
选择注释
注释描述该操作的行为:
- 破坏性提示 — 该操作可以删除或永久更改数据。
- 幂等(相同的参数=无额外效果) — 重复相同的请求具有相同的效果。
- 开放世界提示 — 该操作与外部系统通信。
- 只读提示 — 该操作不会更改数据。
仅选择为true的注释。 例如,产品搜索通常为只读、幂等和开放世界。
添加OpenAI元数据
输入在操作运行期间和完成之后显示的短信:
Invoking: Searching products...
Invoked: Products found
对于包含小部件的操作,添加小部件描述。 构件描述与操作描述不同:
- 操作描述可帮助模型决定何时调用操作。
- 构件描述映射到
_meta["openai/widgetDescription"]并汇总呈现的组件所显示的内容,从而减少重复旁白。
LLM Apps将构件描述作为组件元数据应用。 不要从处理程序返回描述。
配置可见性
- 公开到AI模型允许模型选择操作。
- 在应用程序表面中显示为构件显示配置的构件。
对于纯文本操作,请在构件元数据 > 类型下选择无构件。 可见性控件不选择操作的构件类型。
添加输入参数
为处理程序接受的每个值添加一个参数。 每个参数需要:
- 名称 — 处理程序收到的键。
- 类型 — 字符串、数字、整数或布尔值。
- 描述 — 如何提取值。
- 必需 — 操作是否需要该值。
对于搜索产品:
category
Type: String
Required: No
Description: Product category used to narrow the catalog.
query
Type: String
Required: No
Description: Product name or search phrase.
使用稳定的参数名称。 更改名称还需要更改处理程序及其测试。
配置analytics configure-analytics
当您希望Analytics包含导致该操作的对话摘要时,启用收集用户意图。
如果您设置自己的Adobe Analytics或Customer Journey Analytics,则用户意图也会被发送到该处。 请参阅设置Adobe Analytics和Customer Journey Analytics。
默认情况下启用用户意图收集。 查看用户意图描述,其中包含请求摘要的说明。 将说明限制为非敏感且匿名的意图,并在隐私政策中描述您的数据收集。 该描述最多可接受2,000个字符。
有关完整的字段定义,请参阅操作和小组件字段。
配置构件
对于纯文本操作,请打开 构件元数据 并将 Type 设置为无构件。 然后,跳过其余的构件设置。
打开小组件元数据。
配置:
- 类型 — 为交互小组件选择Edge Delivery Services块。
- 构件域 — 构件的沙盒来源。 UI会根据应用程序提交要求和每个应用程序的唯一性来标识它;它与EDS页面URL不同。
- 首选边框 — 请求主机中的边框容器。
展开 模板URL 并输入:
- 脚本URL — EDS小组件入口点。
- 小组件URL — 为此操作发布的EDS页面。
典型URL包括:
Script URL:
https://main--<repo>--<owner>.aem.live/scripts/aem-embed.js
Widget URL:
https://main--<repo>--<owner>.aem.live/<widget-page>
仅授予所需的浏览器权限和CSP域。
仅当小组件需要浏览器功能(如地理位置)时才展开权限(特定于MCP)。 关闭未使用的权限。
在 CSP配置 下,添加资产和网络请求所需的源。 如果加载外部资源的构件的CSP不完整,则可能不会呈现该构件。
如果EDS项目或构件页面尚不存在,请完成自带EDS项目,然后返回到操作。
保存操作
选择新建操作。 该操作将显示在带有 未部署 徽章的“操作”页面上。
保存将创建元数据,但操作仍需要处理程序。
重新打开保存的操作时,编辑器具有LLM构件详细信息、操作详细信息和 Analytics 选项卡。 要保存以后的编辑,请选择保存更改。 创建对话框和保存的操作编辑器具有不同的布局。
实施处理程序
克隆链接的处理程序存储库并安装其依赖项:
npm install
创建:
actions/
└── search-products/
└── index.js
文件夹名称必须与操作编辑器中显示的操作的代码标识符匹配。
有关完整的结果合同和处理程序 — 小部件关系,请参阅自定义生成的处理程序。
处理程序合同
导出一个异步函数:
module.exports = async (args) => {
return {
content: [
{ type: 'text', text: 'Response for the LLM platform.' }
],
structuredContent: {
// Data for the widget.
}
};
};
该处理程序接收在UI中定义的参数。
返回content
LLM平台将content读为文本回退:
content: [
{ type: 'text', text: 'Found 3 matching products.' }
]
始终返回有用的content,即使操作具有小组件也是如此。
返回structuredContent
构件将structuredContent用作纯对象:
structuredContent: {
products: [
{ id: 'P-100', name: 'Product A', price: '$20' }
],
total: 1
}
形状必须与EDS块从bridge.toolResult中读取的内容匹配。
连接API
在服务器端处理程序中保留API调用。 此示例调用公共产品API。 在名为PRODUCT_API_ORIGIN的应用程序变量中配置API的HTTPS源。 该示例接受上面定义的query和category参数,至少需要一个参数,并且不需要凭据。
const { getVariable } = require('@adobe/llm-apps-runtime');
module.exports = async ({ query = '', category = '' } = {}, extra) => {
const normalizedQuery = typeof query === 'string' ? query.trim() : '';
const normalizedCategory = typeof category === 'string' ? category.trim() : '';
if (typeof query !== 'string' || typeof category !== 'string'
|| (!normalizedQuery && !normalizedCategory)
|| normalizedQuery.length > 200 || normalizedCategory.length > 100) {
return {
isError: true,
content: [{ type: 'text', text: 'Enter a valid product search.' }],
structuredContent: { products: [], total: 0 }
};
}
const apiOrigin = getVariable(extra, 'PRODUCT_API_ORIGIN');
if (!apiOrigin) {
throw new Error('Product API configuration is unavailable.');
}
const origin = new URL(apiOrigin);
if (origin.protocol !== 'https:') {
throw new Error('Product API configuration must use HTTPS.');
}
const url = new URL('/v1/products', origin);
if (normalizedQuery) {
url.searchParams.set('query', normalizedQuery);
}
if (normalizedCategory) {
url.searchParams.set('category', normalizedCategory);
}
const response = await fetch(url, {
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((product) => ({
id: product.id,
name: product.name,
price: product.price
}));
return {
content: [
{ type: 'text', text: `Found ${products.length} matching products.` }
],
structuredContent: {
products,
total: products.length
}
};
};
请勿在源代码、操作元数据、构件JavaScript、日志或面向用户的错误中放入API凭据。 变量和密码 UI当前仅支持非敏感变量,不支持密码。 请勿在变量中存储API令牌。 如果您的上游API需要凭据,请在连接它之前安排已获批准的服务器端凭据机制。
对于生产代码,请在将批准的字段映射到structuredContent中之前验证完整的上游响应。
添加处理程序测试
创建匹配测试:
test/
└── actions/
└── search-products.test.js
至少测试:
- 输入有效。
- 输入缺失或无效。
- 结果为空。
- API超时或失败。
- API数据的格式不正确。
- 构件应具有
structuredContent形状。
运行:
npm test
有关项目布局和本地MCP测试,请参阅本地处理程序开发和测试。
在本地测试操作
运行:
npm run dev:local
如果没有本地actions.json,服务器将发现具有最少元数据和无输入架构验证的处理程序。
使用MCP检查器或curl可以:
- 列出已注册的操作。
- 使用代表性参数调用新操作。
- 验证
content和structuredContent。 - 测试无效和空请求。
连接并测试构件
如果操作具有构件:
- 使构件读取处理程序的
structuredContent。 - 使用安全DOM API(如
textContent)呈现外部值。 - 添加加载、清空和错误状态。
- 在本地预览EDS页面。
- 验证CSP、CORS和小部件URL。
查看自带EDS项目。
部署和测试
如果元数据不存在匹配的处理程序,则部署会使用默认存根注册操作。 添加处理程序,然后才能为用户执行该操作。