Wire Prompt Garden imports into workspace and favorite flows, add the import dialog/panel affordances, persist Garden metadata/media into favorites, and document the external import contract.
17 KiB
Prompt Garden -> Prompt Optimizer 导入契约(External Import Contract)
本文档描述当前代码中已经落地的 Prompt Garden 外部导入行为,目标是让 Garden 侧和 Prompt Optimizer 侧按同一套实际契约对接。
设计目标:
- URL 只携带少量路由参数,由 Prompt Optimizer 自己去 Garden 拉完整内容
- Prompt Optimizer 固定从
VITE_PROMPT_GARDEN_BASE_URL拉取内容,不接受 URL 参数覆盖 - Garden API 返回统一的 v1 schema;普通导入会由 Prompt Optimizer 按子模式写入不同 session store
- Garden 的扩展元数据和素材快照可以跟随收藏一起保存,便于后续预览和复用
1. Prompt Optimizer 侧:导入触发与 URL 参数
Prompt Optimizer 在应用初始 session 恢复完成后检查当前路由 query。
- 如果存在
importCode,则触发一次导入 - 导入成功后会清理导入相关 query,避免刷新重复导入
- 导入失败时不会清理 query,因此刷新后会再次尝试
1.1 支持的 URL 参数
importCode(必填)- 外部提示词的唯一标识,例如
NB-001 - 可在导入码后追加示例选择后缀,例如
NB-001@ex-2 - 追加后缀时,Prompt Optimizer 仍请求
GET /api/prompt-source/NB-001,并把ex-2当作本次导入的示例选择
- 外部提示词的唯一标识,例如
subModeKey(可选)- 显式指定导入目标工作区
- 若未提供,则优先使用 Garden 返回的
optimizerTarget.subModeKey - 若 Garden 返回值无效,则退回当前路由;当前路由也无效时默认落到
basic-system
exampleId(可选)- 指定使用哪一个示例
- 若
importCode同时带@exampleId后缀,显式 URL 参数exampleId优先 - 若未提供,则默认使用
assets.examples[0] - 仅用于示例参数和 image2image 输入图回填,不改变 prompt 主体
saveToFavorites(可选)- 控制本次导入是否进入收藏流程
1/true/auto-> 自动保存到收藏confirm/dialog/manual-> 打开“保存收藏”对话框,并带预填数据- 只要启用
saveToFavorites,本次导入只处理收藏,不写入或覆盖当前工作区 - 其它值或省略 -> 作为“使用”导入,写入目标工作区,不触发收藏保存
1.2 subModeKey 支持值
basic-systembasic-userpro-multipro-variableimage-text2imageimage-image2image
1.3 URL 示例
-
导入到
basic-system:https://prompt.example.com/#/basic/system?importCode=NB-001
-
导入到
pro-multi,并显式指定示例:https://prompt.example.com/#/pro/multi?importCode=NB-001&exampleId=ex-2
-
导入到
pro-multi,并通过导入码后缀指定示例:https://prompt.example.com/#/pro/multi?importCode=NB-001@ex-2
-
从
image-image2image类型的 Garden 提示词导入为收藏,并弹出保存收藏对话框:https://prompt.example.com/#/image/image2image?importCode=NB-001&saveToFavorites=confirm
-
若希望 query 明确覆盖目标工作区:
https://prompt.example.com/#/basic/system?importCode=NB-001&subModeKey=basic-system
说明:
- 推荐 Garden 直接打开目标工作区路由,而不是总是打开根路由再依赖
subModeKey - 不带
saveToFavorites时,subModeKey用于决定写入哪个工作区 - 带
saveToFavorites时,subModeKey只用于收藏的模式预填充,不会触发工作区写入
2. Prompt Garden 侧:必须提供的 API
Prompt Optimizer 会调用:
GET {gardenBaseUrl}/api/prompt-source/{encodeURIComponent(importCode)}
其中:
gardenBaseUrl固定来自 Prompt Optimizer 的环境变量VITE_PROMPT_GARDEN_BASE_URL- 请求头固定包含
Accept: application/json - 当前实现使用浏览器端
fetch - 当前实现不会附加自定义认证头,也不会为跨站请求显式开启
credentials
如果 Garden API 需要登录态、Cookie 或额外鉴权,当前 Web/Extension 导入链路通常需要额外的同源部署或服务端代理支持。
3. API 返回格式(v1 schema)
当前实现只接受 v1 schema:
schema必须为prompt-garden.prompt.v1schemaVersion必须为1
不兼容旧版 { content, title } 回退协议。
3.1 成功响应示例
{
"schema": "prompt-garden.prompt.v1",
"schemaVersion": 1,
"importCode": "NB-001",
"optimizerTarget": {
"subModeKey": "pro-variable"
},
"prompt": {
"format": "text",
"text": "Write a launch post for {{product_name}} aimed at {{audience}}."
},
"variables": [
{
"name": "product_name",
"defaultValue": "Prompt Optimizer",
"description": "产品名称",
"type": "string",
"required": true
},
{
"name": "audience",
"defaultValue": "indie hackers",
"type": "enum",
"options": ["indie hackers", "founders", "designers"]
}
],
"assets": {
"cover": {
"url": "/prompt-assets/nb-001/cover.png"
},
"showcases": [
{
"id": "showcase-1",
"images": ["/prompt-assets/nb-001/showcase-1.png"],
"description": "封面图"
}
],
"examples": [
{
"id": "ex-1",
"parameters": {
"product_name": "Prompt Optimizer",
"audience": "founders"
}
}
]
},
"meta": {
"title": "Launch Post Writer",
"description": "用于生成发布帖的变量化提示词",
"tags": ["marketing", "launch"],
"categoryKey": "marketing"
}
}
3.2 顶层字段约束
schema:必填,固定为prompt-garden.prompt.v1schemaVersion:必填,固定为1optimizerTarget:必填optimizerTarget.subModeKey:必填,且应始终返回合法值(见 1.2)
prompt:必填variables:必填,允许为空数组[]importCode:可选但推荐返回- 若返回空值,Prompt Optimizer 会回退到 URL 中的
importCode
- 若返回空值,Prompt Optimizer 会回退到 URL 中的
assets:可选- 用于收藏快照、示例参数和素材预览
meta:可选- 用于收藏标题、描述、标签、分类等预填充
4. prompt 字段与子模式支持矩阵
4.1 prompt 定义
prompt 为对象:
{
"format": "text",
"text": "..."
}
或:
{
"format": "messages",
"messages": [
{
"id": "msg-1",
"role": "system",
"content": "..."
}
]
}
字段约束:
prompt.format:必填,可选值为text/messages- 当
format=text时,prompt.text必须是非空字符串 - 当
format=messages时,prompt.messages必须是非空数组
4.2 实际支持矩阵
虽然 schema 允许 text 和 messages 两种格式,但当前代码不是所有子模式都同等支持:
pro-multi- 支持
messages - 也支持
text,会被包装成一条system消息
- 支持
basic-system- 仅应返回
text
- 仅应返回
basic-user- 仅应返回
text
- 仅应返回
pro-variable- 仅应返回
text
- 仅应返回
image-text2image- 仅应返回
text
- 仅应返回
image-image2image- 仅应返回
text
- 仅应返回
强烈建议:
- 只有在目标为
pro-multi时才返回format=messages - 其它子模式统一返回
format=text
如果向非 pro-multi 子模式返回 messages,当前实现会在写入工作区时报错。
4.3 prompt.messages 项定义
prompt.messages 每项为:
{
"id": "optional-but-recommended",
"role": "system",
"content": "...",
"originalContent": "optional"
}
字段约束:
role:必填,可选值为system/user/assistant/toolcontent:必填,非空字符串id:建议提供,便于导入后稳定选中消息originalContent:可选;若未提供,会回退为content
5. variables 字段
variables 为数组,每项为:
{
"name": "variable_name",
"defaultValue": "optional",
"description": "optional",
"type": "string",
"required": true,
"options": ["a", "b"],
"source": "optional"
}
字段约束:
name:必填,必须符合 Prompt Optimizer 的变量命名规则- 推荐:
[a-zA-Z_][a-zA-Z0-9_]*
- 推荐:
defaultValue:可选,字符串description:可选,字符串type:可选,支持string/number/boolean/enumrequired:可选,布尔值options:可选,字符串数组source:可选,字符串
当前实现行为:
variables在 API 中是必填字段,即使没有变量也要返回[]- 非法变量名会被直接忽略
- 变量会写入对应子模式的 temporary variables
- 仅以下子模式支持 temporary variables:
pro-multipro-variableimage-text2imageimage-image2image
basic-system和basic-user不会接收 temporary variables
5.1 导入时的变量覆盖规则
当前实现会先把 temporary variables 重置为 Garden 返回的变量列表,但对“同名变量”的值使用保留策略:
- 如果用户当前已经有同名 temporary variable,则保留现有值
- 如果当前没有同名值,则使用
defaultValue - 如果 Garden 返回的是空数组
[],则会清空该子模式当前的 temporary variables
6. assets 与 meta 扩展字段
assets 和 meta 不是工作区 prompt 写入的必需字段,但当前实现已经支持它们,并会在“保存到收藏”场景中使用。
6.1 assets 结构
{
"assets": {
"cover": {
"url": "/prompt-assets/nb-001/cover.png"
},
"showcases": [
{
"id": "showcase-1",
"url": "/prompt-assets/nb-001/showcase-1.png",
"images": ["/prompt-assets/nb-001/showcase-1.png"],
"description": "optional"
}
],
"examples": [
{
"id": "ex-1",
"parameters": {
"var_name": "value"
},
"inputImages": ["/prompt-assets/nb-001/input-1.png"],
"description": "optional"
}
]
}
}
当前实现支持的含义:
assets.cover- 收藏预览中的封面图
assets.showcases- 收藏预览中的展示图
assets.examples- 可用于示例参数回填
- 对
image-image2image,还可用于示例输入图回填
素材 URL 可以是绝对地址,也可以是相对地址。
- 相对地址会基于
gardenBaseUrl归一化为绝对 URL - 当前实现会尝试把素材持久化为本地 asset id,用于收藏预览和离线引用
6.2 meta 结构
meta 中当前有实际用途的字段包括:
title- 收藏标题预填充
description- 收藏描述预填充
tags- 收藏标签预填充
categoryKey- 收藏分类预填充,优先级高于
category
- 收藏分类预填充,优先级高于
category- 收藏分类预填充,作为
categoryKey的回退
- 收藏分类预填充,作为
如果 meta.title 缺失,Prompt Optimizer 会退回到 prompt 内容首行生成收藏标题。
7. 示例选择与 image2image 输入图
7.1 exampleId 行为
导入时,Prompt Optimizer 会从 assets.examples 中选择一个示例:
- 若 URL 提供了
exampleId,优先按id精确匹配 - 若未提供或未匹配到,则回退到第一个示例
7.2 示例参数回填
当选中的示例包含 parameters 时:
- 只会给“已在
variables中声明过”的变量赋值 - 示例参数会覆盖该次导入中对应变量的默认值
- 不会凭空创建未声明变量
7.3 image-image2image 的输入图行为
当前实现和旧文档不同:
- 如果目标子模式是
image-image2image - 且选中的示例包含
inputImages - Prompt Optimizer 会尝试读取第一个
inputImages[0],并加载为当前工作区的输入图
如果图片读取失败:
- 整体导入仍然成功
- 用户会看到一个 warning toast
- 常见原因是 Garden 静态素材的 CORS 配置不正确
因此,若要支持 image2image 的“可复现实例导入”,Garden 不仅要开放 /api/prompt-source/*,也要开放示例图片地址。
8. 收藏联动(saveToFavorites)
8.1 自动保存
当 saveToFavorites=1|true|auto 时:
- Prompt Optimizer 会尝试自动保存到收藏
- 收藏内容来自 Garden 返回的 prompt
meta和assets会作为gardenSnapshot一起写入收藏 metadata- 不会写入或覆盖当前工作区
8.2 确认保存
当 saveToFavorites=confirm|dialog|manual 时:
- Prompt Optimizer 会打开“保存收藏”对话框
- 自动带入标题、描述、标签、分类、模式信息和
gardenSnapshot - 不会写入或覆盖当前工作区
8.3 去重与更新规则
Garden 联动保存收藏时,不按收藏内容去重,而按下面的组合键做 upsert:
gardenSnapshot.importCodegardenSnapshot.gardenBaseUrl
这意味着:
- 同一个 Garden 提示词重复导入会更新原收藏
- 不同 Garden 站点即使
importCode相同,也会被视为不同来源
9. 工作区写入时会被重置的状态
当 URL 不带 saveToFavorites 时,导入成功后 Prompt Optimizer 不只是更新 prompt,还会清理与旧工作区状态绑定的内容。
例如:
basic-system/basic-user- 清空测试内容、测试变体状态、优化结果、评估结果、当前版本列表
pro-multi- 清空 message chain map、测试变体状态、优化结果、评估结果
pro-variable- 清空测试内容、测试变体状态、优化结果、评估结果
image-text2image- 清空原始图结果和优化图结果
image-image2image- 清空当前输入图、原始图结果和优化图结果
- 若示例带输入图,会在清空后再尝试加载示例输入图
10. 占位符语法(强制)
Prompt Optimizer 仅支持 Mustache 风格变量占位符:
{{variable_name}}{{ variable_name }}
不支持:
{variable_name}
约束:
prompt.text和prompt.messages[].content中出现的变量占位符都必须使用{{...}}- Prompt Garden 不应返回
{var}风格占位符 - Prompt Optimizer 不会在导入时做占位符归一化
11. 失败语义
11.1 HTTP 失败
建议 Garden 使用以下语义:
400:importCode非法404:importCode不存在500:服务端错误
对 Prompt Optimizer 而言:
- 任意非 2xx 都会视为导入失败
- 用户侧只会看到通用失败提示
- 详细原因主要记录在浏览器 console
11.2 schema 校验失败
以下情况会直接失败:
- 缺少
VITE_PROMPT_GARDEN_BASE_URL schema不是prompt-garden.prompt.v1schemaVersion不是1- 缺少
optimizerTarget.subModeKey - 缺少
prompt.format format=text但prompt.text为空format=messages但prompt.messages为空- 缺少
variables
12. CORS / 静态素材建议
由于 Prompt Optimizer(Web/Extension)是纯前端应用,Garden 侧必须正确配置跨域。
至少需要覆盖两类资源:
/api/prompt-source/*assets.cover.url、assets.showcases[*].url/images[*]、assets.examples[*].images[*]、assets.examples[*].inputImages[*]指向的静态资源地址
建议:
/api/prompt-source/*返回:Access-Control-Allow-Origin: https://prompt.example.com,或你的实际部署域名
- 静态素材地址也返回相同的
Access-Control-Allow-Origin - 开发环境可临时使用
*联调
如果 image2image 示例输入图需要可用,inputImages 的 URL 也必须允许被浏览器跨域 fetch。
13. 环境变量
Prompt Optimizer 侧:
VITE_ENABLE_PROMPT_GARDEN_IMPORT=1或true- 产品内建默认值为
1 - 启用后会注册 Prompt Garden 导入逻辑
- 同时也会启用 Garden 收藏快照预览插件
- 产品内建默认值为
VITE_PROMPT_GARDEN_BASE_URL=https://garden.always200.com- Prompt Garden 的固定 base URL
- 不接受 URL 参数覆盖
说明:
- Web、浏览器扩展和桌面端打包时都会带上上述默认值
- 运行时配置仍可覆盖默认值
- Docker / Web
window.runtime_config推荐使用无前缀键:ENABLE_PROMPT_GARDEN_IMPORT、PROMPT_GARDEN_BASE_URL - 也兼容带前缀键:
VITE_ENABLE_PROMPT_GARDEN_IMPORT、VITE_PROMPT_GARDEN_BASE_URL
- Docker / Web
14. 可选集成(Integrations)机制
Prompt Optimizer 使用“可选集成”机制来实现低入侵扩展:
- 集成模块位于
packages/ui/src/integrations/ - 文件命名为
*.integration.ts - 每个模块导出
integration对象,并通过envFlag控制是否启用 - App 启动后统一调用
registerOptionalIntegrations(...)
Prompt Garden 相关文件:
packages/ui/src/integrations/prompt-garden.integration.tspackages/ui/src/integrations/prompt-garden.favorite-preview.ts
15. 参考实现
Prompt Optimizer 侧核心实现:
packages/ui/src/composables/app/useAppPromptGardenImport.tspackages/ui/src/components/PromptGardenFavoritePreviewPanel.vuepackages/ui/src/utils/garden-snapshot-preview.ts
主要测试:
packages/ui/tests/unit/composables/useAppPromptGardenImport.spec.tspackages/ui/tests/unit/components/GardenSnapshotPreview.spec.tspackages/ui/tests/unit/utils/garden-snapshot-preview.spec.ts