@lark-base-open/base-database-cli 1.1.1 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/LICENSE +6 -0
  3. package/README.md +45 -27
  4. package/dist/ai-functions.d.ts +42 -0
  5. package/dist/ai-functions.d.ts.map +1 -0
  6. package/dist/ai-functions.js +327 -0
  7. package/dist/ai-functions.js.map +1 -0
  8. package/dist/archive.d.ts +21 -0
  9. package/dist/archive.d.ts.map +1 -1
  10. package/dist/archive.js +65 -10
  11. package/dist/archive.js.map +1 -1
  12. package/dist/auth.d.ts +5 -0
  13. package/dist/auth.d.ts.map +1 -1
  14. package/dist/auth.js +26 -3
  15. package/dist/auth.js.map +1 -1
  16. package/dist/check.d.ts +74 -0
  17. package/dist/check.d.ts.map +1 -0
  18. package/dist/check.js +283 -0
  19. package/dist/check.js.map +1 -0
  20. package/dist/dependency-policy.d.ts +10 -0
  21. package/dist/dependency-policy.d.ts.map +1 -0
  22. package/dist/dependency-policy.js +162 -0
  23. package/dist/dependency-policy.js.map +1 -0
  24. package/dist/deploy.d.ts +1 -0
  25. package/dist/deploy.d.ts.map +1 -1
  26. package/dist/deploy.js +122 -175
  27. package/dist/deploy.js.map +1 -1
  28. package/dist/environment-variables.d.ts +52 -0
  29. package/dist/environment-variables.d.ts.map +1 -0
  30. package/dist/environment-variables.js +563 -0
  31. package/dist/environment-variables.js.map +1 -0
  32. package/dist/environment.d.ts +1 -1
  33. package/dist/environment.js +1 -1
  34. package/dist/environment.js.map +1 -1
  35. package/dist/function-input-types.d.ts +10 -0
  36. package/dist/function-input-types.d.ts.map +1 -0
  37. package/dist/function-input-types.js +160 -0
  38. package/dist/function-input-types.js.map +1 -0
  39. package/dist/function-schema.d.ts.map +1 -1
  40. package/dist/function-schema.js +89 -36
  41. package/dist/function-schema.js.map +1 -1
  42. package/dist/init-field-retry.d.ts +17 -0
  43. package/dist/init-field-retry.d.ts.map +1 -0
  44. package/dist/init-field-retry.js +127 -0
  45. package/dist/init-field-retry.js.map +1 -0
  46. package/dist/init-journal.d.ts +98 -0
  47. package/dist/init-journal.d.ts.map +1 -0
  48. package/dist/init-journal.js +492 -0
  49. package/dist/init-journal.js.map +1 -0
  50. package/dist/init-schema.d.ts +8 -0
  51. package/dist/init-schema.d.ts.map +1 -0
  52. package/dist/init-schema.js +423 -0
  53. package/dist/init-schema.js.map +1 -0
  54. package/dist/init.d.ts +14 -0
  55. package/dist/init.d.ts.map +1 -1
  56. package/dist/init.js +883 -261
  57. package/dist/init.js.map +1 -1
  58. package/dist/lark-cli-error.d.ts +46 -0
  59. package/dist/lark-cli-error.d.ts.map +1 -0
  60. package/dist/lark-cli-error.js +171 -0
  61. package/dist/lark-cli-error.js.map +1 -0
  62. package/dist/lark-cli.d.ts +1 -1
  63. package/dist/lark-cli.d.ts.map +1 -1
  64. package/dist/lark-cli.js +66 -18
  65. package/dist/lark-cli.js.map +1 -1
  66. package/dist/mutation-schema.d.ts +146 -0
  67. package/dist/mutation-schema.d.ts.map +1 -0
  68. package/dist/mutation-schema.js +153 -0
  69. package/dist/mutation-schema.js.map +1 -0
  70. package/dist/mutation.d.ts +10 -0
  71. package/dist/mutation.d.ts.map +1 -0
  72. package/dist/mutation.js +131 -0
  73. package/dist/mutation.js.map +1 -0
  74. package/dist/npm.d.ts +1 -0
  75. package/dist/npm.d.ts.map +1 -1
  76. package/dist/npm.js +2 -2
  77. package/dist/npm.js.map +1 -1
  78. package/dist/preview-receipt.d.ts.map +1 -1
  79. package/dist/preview-receipt.js +15 -5
  80. package/dist/preview-receipt.js.map +1 -1
  81. package/dist/process.d.ts +3 -1
  82. package/dist/process.d.ts.map +1 -1
  83. package/dist/process.js +16 -4
  84. package/dist/process.js.map +1 -1
  85. package/dist/program.d.ts.map +1 -1
  86. package/dist/program.js +240 -41
  87. package/dist/program.js.map +1 -1
  88. package/dist/project-build.d.ts +25 -0
  89. package/dist/project-build.d.ts.map +1 -0
  90. package/dist/project-build.js +229 -0
  91. package/dist/project-build.js.map +1 -0
  92. package/dist/project-context.d.ts +344 -0
  93. package/dist/project-context.d.ts.map +1 -0
  94. package/dist/project-context.js +485 -0
  95. package/dist/project-context.js.map +1 -0
  96. package/dist/project-layout.d.ts +5 -0
  97. package/dist/project-layout.d.ts.map +1 -0
  98. package/dist/project-layout.js +24 -0
  99. package/dist/project-layout.js.map +1 -0
  100. package/dist/public-sql-error.d.ts +25 -0
  101. package/dist/public-sql-error.d.ts.map +1 -0
  102. package/dist/public-sql-error.js +81 -0
  103. package/dist/public-sql-error.js.map +1 -0
  104. package/dist/pull.d.ts.map +1 -1
  105. package/dist/pull.js +54 -15
  106. package/dist/pull.js.map +1 -1
  107. package/dist/site-readme.d.ts +3 -0
  108. package/dist/site-readme.d.ts.map +1 -0
  109. package/dist/site-readme.js +83 -0
  110. package/dist/site-readme.js.map +1 -0
  111. package/dist/workspace-fingerprint.d.ts +21 -1
  112. package/dist/workspace-fingerprint.d.ts.map +1 -1
  113. package/dist/workspace-fingerprint.js +81 -2
  114. package/dist/workspace-fingerprint.js.map +1 -1
  115. package/package.json +2 -1
  116. package/skills/lark-base-database/SKILL.md +66 -50
  117. package/skills/lark-base-database/references/ai-function.md +471 -0
  118. package/skills/lark-base-database/references/base-capability-routing.md +3 -3
  119. package/skills/lark-base-database/references/database-function-limitations.md +46 -0
  120. package/skills/lark-base-database/references/database-function.md +349 -0
  121. package/skills/lark-base-database/references/design/components/actions-fields.md +16 -0
  122. package/skills/lark-base-database/references/design/components/code/controls.css +122 -0
  123. package/skills/lark-base-database/references/design/components/code/controls.tsx +73 -0
  124. package/skills/lark-base-database/references/design/components/code/date-picker.tsx +42 -0
  125. package/skills/lark-base-database/references/design/components/code/floating.tsx +41 -0
  126. package/skills/lark-base-database/references/design/components/code/overlays.tsx +19 -0
  127. package/skills/lark-base-database/references/design/components/code/select.tsx +34 -0
  128. package/skills/lark-base-database/references/design/components/code/styles.d.ts +1 -0
  129. package/skills/lark-base-database/references/design/components/code/theme.css +49 -0
  130. package/skills/lark-base-database/references/design/components/code/usage.tsx +38 -0
  131. package/skills/lark-base-database/references/design/components/date-time-number.md +9 -0
  132. package/skills/lark-base-database/references/design/components/feedback-navigation.md +9 -0
  133. package/skills/lark-base-database/references/design/components/index.md +18 -0
  134. package/skills/lark-base-database/references/design/components/overlays.md +9 -0
  135. package/skills/lark-base-database/references/design/components/patterns/flat-business.md +7 -0
  136. package/skills/lark-base-database/references/design/components/selection.md +24 -0
  137. package/skills/lark-base-database/references/design/components.md +91 -0
  138. package/skills/lark-base-database/references/design/composition.md +90 -0
  139. package/skills/lark-base-database/references/design/control-recipes.md +107 -0
  140. package/skills/lark-base-database/references/design/examples.md +172 -0
  141. package/skills/lark-base-database/references/design/foundations.md +126 -0
  142. package/skills/lark-base-database/references/design/review.md +82 -0
  143. package/skills/lark-base-database/references/design/scenes/analysis-report.md +66 -0
  144. package/skills/lark-base-database/references/design/scenes/brand-showcase.md +70 -0
  145. package/skills/lark-base-database/references/design/scenes/business-operations.md +85 -0
  146. package/skills/lark-base-database/references/design/scenes/campaign-report.md +66 -0
  147. package/skills/lark-base-database/references/design/scenes/dashboard.md +77 -0
  148. package/skills/lark-base-database/references/design/scenes/festive-campaign.md +69 -0
  149. package/skills/lark-base-database/references/design/theme.md +27 -0
  150. package/skills/lark-base-database/references/design/typography.md +56 -0
  151. package/skills/lark-base-database/references/development.md +165 -32
  152. package/skills/lark-base-database/references/environment-variables.md +55 -0
  153. package/skills/lark-base-database/references/faas.md +41 -48
  154. package/skills/lark-base-database/references/file-transfer.md +25 -25
  155. package/skills/lark-base-database/references/frontend-design-guide.md +33 -77
  156. package/skills/lark-base-database/references/mutations.md +295 -0
  157. package/skills/lark-base-database/references/permission-design.md +24 -24
  158. package/skills/lark-base-database/references/release.md +51 -24
  159. package/skills/lark-base-database/references/style-guide.md +10 -67
  160. package/skills/lark-base-database/references/verification.md +15 -11
  161. package/skills/lark-base-database/references/workspace.md +46 -8
  162. package/templates/database-starter/README.md +34 -4
  163. package/templates/database-starter/database-functions/README.md +8 -0
  164. package/templates/database-starter/mutations/README.md +19 -0
  165. package/templates/database-starter/page/README.md +25 -19
  166. package/templates/database-starter/page/package-lock.json +8 -16
  167. package/templates/database-starter/page/package.json +2 -3
  168. package/templates/database-starter/page/src/App.tsx +23 -3
  169. package/templates/database-starter/page/src/components/UserSelectorEditor/index.tsx +32 -4
  170. package/templates/database-starter/page/src/components/UserSelectorPanel/index.tsx +104 -34
  171. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.test.ts +55 -11
  172. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.ts +68 -25
  173. package/templates/database-starter/page/src/components/database-runtime-ready.tsx +1 -1
  174. package/templates/database-starter/page/src/components/database-user-menu/index.tsx +4 -7
  175. package/templates/database-starter/page/src/components/feedback/page-state.tsx +40 -5
  176. package/templates/database-starter/page/src/components/floating-button/assets/edit-active.svg +3 -0
  177. package/templates/database-starter/page/src/components/floating-button/assets/edit-default.svg +3 -0
  178. package/templates/database-starter/page/src/components/floating-button/index.tsx +75 -112
  179. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.test.ts +107 -37
  180. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.ts +13 -50
  181. package/templates/database-starter/page/src/components/ui-language.test.tsx +101 -0
  182. package/templates/database-starter/page/src/examples/ai-function/ImageTaskOperation.tsx +202 -0
  183. package/templates/database-starter/page/src/examples/ai-function/README.md +14 -0
  184. package/templates/database-starter/page/src/examples/ai-function/TextStreamOperation.tsx +184 -0
  185. package/templates/database-starter/page/src/examples/ai-function/ai-copy.ts +173 -0
  186. package/templates/database-starter/page/src/examples/ai-function/ai-delta.test.ts +30 -0
  187. package/templates/database-starter/page/src/examples/ai-function/ai-delta.ts +26 -0
  188. package/templates/database-starter/page/src/examples/ai-function/ai-language.test.tsx +86 -0
  189. package/templates/database-starter/page/src/examples/ai-function/ai-media-panel.test.ts +201 -0
  190. package/templates/database-starter/page/src/examples/ai-function/ai-media-panel.tsx +382 -0
  191. package/templates/database-starter/page/src/lib/attachment-image.tsx +144 -0
  192. package/templates/database-starter/page/src/lib/database-function-inputs.generated.ts +4 -0
  193. package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +180 -76
  194. package/templates/database-starter/page/src/lib/database-runtime.test.ts +130 -10
  195. package/templates/database-starter/page/src/lib/database-runtime.ts +484 -51
  196. package/templates/database-starter/page/src/lib/date-time.ts +61 -0
  197. package/templates/database-starter/page/src/lib/ui-language.ts +86 -0
  198. package/templates/database-starter/page/src/main.tsx +1 -1
  199. package/templates/database-starter/page/tsconfig.json +2 -2
  200. package/templates/database-starter/page/vite.config.ts +1 -2
  201. package/templates/database-starter/prettierignore +1 -1
  202. package/templates/database-starter/server-functions/README.md +2 -2
  203. package/templates/database-starter/server-functions/package-lock.json +4 -4
  204. package/templates/database-starter/server-functions/package.json +1 -1
  205. package/dist/agent-guide.d.ts +0 -19
  206. package/dist/agent-guide.d.ts.map +0 -1
  207. package/dist/agent-guide.js +0 -31
  208. package/dist/agent-guide.js.map +0 -1
  209. package/dist/database-skill.d.ts +0 -24
  210. package/dist/database-skill.d.ts.map +0 -1
  211. package/dist/database-skill.js +0 -148
  212. package/dist/database-skill.js.map +0 -1
  213. package/skills/lark-base-database/references/base-sql-unsupported.md +0 -42
  214. package/skills/lark-base-database/references/database-capability-skill.md +0 -40
  215. package/skills/lark-base-database/references/sql-function.md +0 -289
  216. package/templates/database-capability/SKILL.md.tmpl +0 -58
  217. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.test.ts +0 -23
  218. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.tsx +0 -873
  219. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.test.ts +0 -25
  220. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.tsx +0 -197
  221. package/templates/database-starter/page/src/components/floating-button/utils/env.ts +0 -18
  222. package/templates/database-starter/sql/README.md +0 -8
@@ -0,0 +1,471 @@
1
+ # Base Database AI Function
2
+
3
+ 本文说明如何在 Page 中配置和调用 AI Function。AI Function 是随应用版本发布的 AI 能力:文本可以按页面需要使用流式或一次性结果;图像和视频生成始终先创建任务、再轮询结果。页面只按 `functionKey` 调用平台 AI 能力;Base 数据读取仍使用 Database Function,写入按[Mutation 与 FaaS 分工](mutations.md#写入方式选择)选择。
4
+
5
+ 每个 AI Function 的配置保存在 `ai/<functionKey>/meta.json`。配置决定模型、System Prompt、思考深度、尺寸、时长和变量;页面只传入 `functionKey`、本次的 `variables` 与 `messages` 或 `input`。
6
+
7
+ 本文中的 `/sites/`、`siteToken` 和 `Site*` 是 SDK 与平台接口的既有名称;它们都指向当前 Base Database 应用,不需要额外创建或绑定其他产品对象。本地工程中的应用标识始终从 `base-database.jsonc` 的 `appToken` 读取。
8
+
9
+ ## 阅读路径
10
+
11
+ 1. 第一次实现时先执行[零背景最短闭环](#零背景最短闭环)。
12
+ 2. 再按需求读取[四类能力与配置契约](#四类能力与配置契约)和[Page 调用方式](#page-调用方式)。
13
+ 3. 修改已有应用时必须先执行[恢复已有 AI Function](#恢复已有-ai-function),不能直接部署。
14
+ 4. 最后按[页面实现检查](#页面实现检查)和[区分三种调试成功](#区分三种调试成功)验收。
15
+
16
+ ## 零背景最短闭环
17
+
18
+ 开始前,先完成主流程的 `init` 或已有工程恢复,并在项目根目录确认存在 `base-database.jsonc`、`catalog.json` 和 `page/`。新建工程已经包含所需 Channel SDK;不要为了使用 AI Function 单独安装或替换 SDK。
19
+
20
+ AI Function 不是 FaaS 代码,也不是页面直接请求模型。一个可运行的 AI 功能必须同时有两部分:
21
+
22
+ ```text
23
+ ai/<functionKey>/meta.json # 应用版本中的托管配置
24
+ page/... # 使用同一个 functionKey 的页面调用代码
25
+ ```
26
+
27
+ 从项目根目录按以下顺序完成:
28
+
29
+ 1. 修改已有应用时,先按[恢复已有 AI Function](#恢复已有-ai-function)补齐远端配置;新建应用继续下一步。
30
+ 2. 查询 `model_profiles`,按 `capability` 选择真实 `profile_id`;禁止猜测示例 ID。
31
+ 3. 为每个 AI 功能选择唯一、稳定的 `functionKey`,创建严格 JSON 文件 `ai/<functionKey>/meta.json`。`functionKey` 只能含字母、数字、下划线和连字符,长度 1~64;目录名就是调用 key。
32
+ 4. 在 Page 中从 `./lib/database-runtime` 导入 helper,使用完全相同的 `functionKey`。调用 `callSiteAIFunction` 时显式传 `capability`;不要依赖 SDK 按 `messages` 或 `input` 猜调用链路。
33
+ 5. 对每个本地 AI Function 执行 `ai-functions validate <functionKey>`,只有返回 `is_success: true` 才继续。
34
+ 6. 执行一次普通 `deploy`。`deploy` 会构建页面、全量同步 `ai/` 到草稿版本并发布 Preview;正常发布流程不需要先手工执行 `ai-functions sync`。
35
+ 7. 执行 `ai-functions list`,核对目标 `functionKey`、`capability_type`、`model_profile_id` 和 `disabled=false`;再在命令返回的 `baseAppUrl` 页面发起一次真实调用。
36
+ 8. text/vision 使用显式流式入口时必须看到页面增量与 `onDone`,使用聚合入口时必须得到 `{type:'text'}`;image/video 必须轮询到 `succeeded` 并渲染生成资源。CLI `debug` 只用于辅助定位,不能代替页面链路验收。
37
+
38
+ 最小文本配置与页面调用如下。把 `replace-with-profile-id` 换成 `model_profiles` 返回的真实值。`showAnswer` 是你的页面状态更新函数,需要替换为实际 UI 代码:
39
+
40
+ ```json
41
+ {
42
+ "name": "菜品推荐",
43
+ "description": "根据菜单和用餐条件生成推荐",
44
+ "config": {
45
+ "capability_type": "text",
46
+ "output_mode": "stream",
47
+ "model_profile_id": "replace-with-profile-id",
48
+ "system_prompt": "只推荐 {{menu}} 中的菜品。",
49
+ "variables": [{ "name": "menu", "type": "string", "required": true }],
50
+ "text": {
51
+ "reasoning_depth": "standard",
52
+ "temperature": 0.7
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ```ts
59
+ import { callSiteAIFunction } from './lib/database-runtime';
60
+
61
+ const result = await callSiteAIFunction({
62
+ functionKey: 'recommend_dishes',
63
+ capability: 'text',
64
+ variables: { menu: '番茄炒蛋、宫保鸡丁、青椒肉丝' },
65
+ messages: [
66
+ {
67
+ role: 'user',
68
+ content: [{ type: 'text', text: '预算 100 元,推荐两道菜' }],
69
+ },
70
+ ],
71
+ });
72
+ if (result.type === 'text') showAnswer(result.content);
73
+ ```
74
+
75
+ ## AI Function 配置与验证
76
+
77
+ AI Function 和 Database Function 一样按应用版本保存:Preview 读取草稿版本,Prod 读取已发布版本。实现 AI 需求时,把每个 AI 功能建模为 `ai/<functionKey>/meta.json`,执行 `deploy` 后由 CLI 将 AI Function 全量同步到当前草稿版本。
78
+
79
+ 部署前先执行 AI Function 静态校验:`functionKey`、`capability_type`、`output_mode`、`model_profile_id`、变量声明、System Prompt 占位符和能力专属参数都必须符合当前服务端契约。运行态调用进入鉴权、额度、并发和模型调用链路;目标 Function 必须存在,且配置没有设置 `disabled: true`。
80
+
81
+ Preview 发布成功后,必须在页面里触发一次真实 AI Function 调用(`callSiteAIFunction` 或 `invokeSiteAIFunctionStream`):收到结果、`onMessage` 增量或 `onDone` 才能说明 Preview 运行态链路可用。目标 Function 不存在时检查本地声明和 Functions 同步结果;返回 `ai function is disabled` 时检查该 Function 的 `disabled` 配置;模型不可用时重新查询 `model_profiles` 并校验配置;额度、并发或服务错误按实际错误码处理。无法通过应用配置解决时,保留 `requestId` 和脱敏错误摘要,向平台支持反馈。
82
+
83
+ Prod 验证只能发生在用户明确确认并完成 `deploy --production --yes` 之后。Prod 发布成功后,在生产 `baseAppUrl` 页面再次触发真实 AI Function 调用;收到结果或 `onDone` 才能报告 Prod AI 链路已生效。发布 Prod 前,即使 Preview 已验证成功,也只能报告 Preview AI 可用。
84
+
85
+ ### 恢复已有 AI Function
86
+
87
+ `pull` 恢复的是应用源码制品,AI Function 配置不在源码归档中;`ai-functions sync` 把本地 `ai/` 当作全集立即写远端草稿(含空集合),而 `deploy` 只在本地存在 AI Function 声明时才做全量覆盖同步。修改已有应用时,在任何 deploy/sync 前执行:
88
+
89
+ 1. `ai-functions list` 取得草稿版本全部远端 key。
90
+ 2. 对每个 key 执行 `ai-functions pull <functionKey>`。该命令只把详情打印为 JSON,**不会写文件**;读取返回的 snake_case 字段 `function.name`、`function.description`、`function.config_json`,把 `config_json` 字符串解析成 JSON 对象后还原为 `ai/<functionKey>/meta.json`。`disabled` 位于这个 `config` 对象内,不在 `meta.json` 顶层。
91
+ 3. 比较远端 key 集合与本地 `ai/` 目录。需求外的远端 Function 必须保留所有配置值;JSON 缩进和字段顺序不影响语义。本地缺少任一远端 key,或 `config_json` 无法解析时,禁止 `ai-functions sync` 和显式删除操作,`deploy` 前也必须补齐。
92
+ 4. 只有用户明确授权删除具体 Function 后,才能让该 key 从本地全集缺失并执行全量同步。
93
+
94
+ 空 `ai/` 在 `deploy` 里是“本次不修改 AI Function”:CLI 会返回 `{ skipped: true, reason: 'no_local_ai_functions' }` 并跳过 AI Function 同步;仅 Page/Database Function/FaaS 会随本次 deploy 发布。`ai-functions sync` 与 deploy 不同,只要执行就是立即写远端草稿的全量覆盖操作,空集合会把远端 AI Function **全部删除**;因此仅在用户明确授权删除或有强规划时使用。常规发布不必单独执行 `ai-functions sync`。
95
+
96
+ ## 选择 AI Function
97
+
98
+ 需求包含 AI 对话、总结、分类、生成、文生图、文生视频或“查数据后让模型分析”时,把 AI 调用作为独立的业务动作。只要 AI 需要使用当前页面输入、当前记录、筛选条件或绑定 Base 数据,并把结果展示在页面中或进入后续业务动作,就使用 AI Function。
99
+
100
+ | 业务动作 | 数据路径 | 页面职责 |
101
+ | -------------- | -------------------------- | ------------------------------------------------------------------- |
102
+ | 查询 | Page → Database Function | 固定 Function ID,校验查询结果 |
103
+ | 写入 | Page → FaaS → Base Records | 固定 Function ID,由 FaaS 校验并写入 |
104
+ | AI 对话 / 生成 | Page → AI Function | 传 functionKey + variables + messages/input,消费结果,维护会话状态 |
105
+
106
+ 需要先查 Base 再问模型时,先完成 Database Function 查询,在 Page 内把经过裁剪的结果放入 `messages` 或 `variables`,再按 `functionKey` 调用 AI Function。需要把 AI 结果写回 Base 时,再调用对应 FaaS。
107
+
108
+ 典型路由:
109
+
110
+ | 用户诉求 | 选择 | 原因 |
111
+ | ---------------------------------------------- | -------------------- | ---------------------------------------------------------------------------- |
112
+ | 点餐系统根据口味、预算、人数和菜单自动推荐菜品 | AI Function(text) | Page 收集条件,Database Function 读取菜单,AI 生成推荐,后续可写购物车或订单 |
113
+ | CRM 按当前筛选出的客户生成跟进建议 | AI Function(text) | 输入来自页面筛选和 Base 查询结果,输出服务当前业务流程 |
114
+ | 根据描述和可选参考图生成单图或组图 | AI Function(image) | 图片生成能力,异步任务返回一张或多张图片资源 |
115
+ | 根据脚本生成宣传短视频 | AI Function(video) | 文生视频能力,异步任务提交 + 轮询 |
116
+
117
+ ## 四类能力与配置契约
118
+
119
+ 一个 AI Function 只承载一种能力,由 `capability_type` 决定用哪条运行链路与哪组配置字段:
120
+
121
+ | capability_type | output_mode | 运行链路 | 专属配置字段 |
122
+ | --------------- | ----------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
123
+ | `text` | `stream` / `sync` | 通过 SDK 获取文本流或完整文本 | `text.reasoning_depth`(light/standard/deep) / `text.temperature` |
124
+ | `vision` | `stream` / `sync` | 多模态理解;输入受下文 SDK 类型限制 | 同 text,且模型 Profile 必须声明本次使用的 image / video / audio / file 输入 |
125
+ | `image` | `sync` | 创建图片任务并轮询结果 | `image.size` / `image.response_format`(url/b64_json) / `image.sequential_image_generation`(auto/disabled) / `image.max_images` / `image.watermark` |
126
+ | `video` | `async` | 创建视频任务并轮询结果 | `video.resolution` / `video.ratio` / `video.duration` / `video.watermark` / `video.seed` |
127
+
128
+ `AIFunctionConfig` 结构(落 `ai/<functionKey>/meta.json` 的 `config` 字段):
129
+
130
+ `meta.json` 必须是严格 JSON,不能包含注释、尾逗号或 Markdown 占位符:
131
+
132
+ ```json
133
+ {
134
+ "name": "菜品推荐",
135
+ "description": "根据口味/预算/人数和菜单推荐菜品",
136
+ "config": {
137
+ "capability_type": "text",
138
+ "output_mode": "stream",
139
+ "model_profile_id": "replace-with-text-profile-id",
140
+ "system_prompt": "你是点餐助手,只推荐 {{menu}} 中的菜品。",
141
+ "variables": [{ "name": "menu", "type": "string", "required": true }],
142
+ "text": {
143
+ "reasoning_depth": "standard",
144
+ "temperature": 0.7
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ 静态校验(保存/发布前执行,按能力分支):`capability_type` 合法;`model_profile_id` 来自可用模型列表且能力匹配(文本模型不能配给文生图);`system_prompt` 里的 `{{var}}` 都在 `variables` 声明;`variables` 名称唯一、类型合法、required 项不带默认值;`output_mode` 与 `capability_type` 相容;文本 `temperature` 必须在 0~2 内;vision 要求 profile 至少声明 image / video / audio / file 中一种媒体输入,调用时每个 content part 还必须命中该 profile 的 `input_modalities`;image `max_images` 与 video `duration` 不超平台上限,video `seed` 必须为 -1 或更大。
151
+
152
+ 变量类型只能是 `string`、`number`、`boolean`、`object`。调用时 `variables` 的运行时类型必须匹配声明,required 变量不可缺失;`object` 同时接受对象和数组。`system_prompt` 适用于四类能力:text/vision 作为 system message,image/video 作为基础提示词;媒体调用的 `input.prompt` 会追加到这个基础提示词。
153
+
154
+ `disabled` 是 `config.disabled`,省略或 `false` 表示可调用,`true` 表示保留配置但停止调用。`video.seed=-1` 表示随机种子。
155
+
156
+ 图片生成和文生视频的最小配置分别如下。这里的 `output_mode` 是服务端能力配置约束,不代表浏览器可以同步得到最终媒体;Page 对 image/video 都必须创建任务并轮询。
157
+
158
+ ```json
159
+ {
160
+ "name": "生成海报",
161
+ "description": "根据商品描述生成海报",
162
+ "config": {
163
+ "capability_type": "image",
164
+ "output_mode": "sync",
165
+ "model_profile_id": "replace-with-image-profile-id",
166
+ "system_prompt": "生成适合商品展示的图片。",
167
+ "image": {
168
+ "size": "2048x2048",
169
+ "response_format": "url",
170
+ "sequential_image_generation": "disabled",
171
+ "max_images": 1,
172
+ "watermark": true
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ ```json
179
+ {
180
+ "name": "生成宣传视频",
181
+ "description": "根据脚本生成短视频",
182
+ "config": {
183
+ "capability_type": "video",
184
+ "output_mode": "async",
185
+ "model_profile_id": "replace-with-video-profile-id",
186
+ "system_prompt": "生成简洁、自然的宣传短视频。",
187
+ "video": {
188
+ "resolution": "720p",
189
+ "ratio": "16:9",
190
+ "duration": 5,
191
+ "watermark": true,
192
+ "seed": -1
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ 示例里的尺寸、时长和其他参数只是结构演示,必须根据 `model_profiles` 返回的能力与业务要求调整,再用 `ai-functions validate` 让服务端做最终校验。
199
+
200
+ 当前图片模型按 Seedream 5.0 lite 契约校验 `image.size`:可以使用分辨率档位 `2K`、`3K`、`4K`,也可以使用 `WIDTHxHEIGHT`。像素尺寸必须同时满足总像素数在 3,686,400~16,777,216 之间、宽高比在 1:16~16:1 之间;默认使用 `2048x2048`。服务端不会把非法尺寸自动放大或缩小,`ai-functions validate` 和运行时调用都会直接拒绝。
201
+
202
+ `image.sequential_image_generation` 决定单图或组图:
203
+
204
+ | 模式 | `sequential_image_generation` | `input.image` | 输出上限 |
205
+ | ---------- | ----------------------------- | --------------------- | ----------------------------------- |
206
+ | 文生单图 | `disabled` | 省略 | 1 张 |
207
+ | 单图生单图 | `disabled` | 单个字符串或 1 项数组 | 1 张 |
208
+ | 多图生单图 | `disabled` | 2~14 项数组 | 1 张 |
209
+ | 文生组图 | `auto` | 省略 | 最多 15 张 |
210
+ | 单图生组图 | `auto` | 单个字符串或 1 项数组 | 最多 14 张 |
211
+ | 多图生组图 | `auto` | 2~14 项数组 | 参考图数与最终生成图数之和不超过 15 |
212
+
213
+ `image.max_images` 为 1~15,只在 `auto` 下生效;`0` 或省略表示使用平台默认值。服务端会按参考图数量和模型 Profile 上限进一步收紧。
214
+
215
+ 参考图不是 AI Function 的固定配置,属于每次调用输入。Channel SDK 导出的 `SiteAIImageGenerationInput` 定义了公开类型;使用 `input.image` 传一条可访问 URL / `data:image/...;base64,...` 字符串,或传包含 1~14 个此类字符串的数组。
216
+
217
+ ### 参考图来源与传参
218
+
219
+ 先把源图转换为模型上游可读取的字符串引用,再放入 `input.image`。AI Function 接口接收 JSON,不接收 multipart 文件,也不把 Base `file_token` 自动转换成 URL。
220
+
221
+ | 源图来源 | `input.image` 的值 |
222
+ | ----------------- | ------------------------------------------------------------------------------------------------ |
223
+ | 公网可访问图片 | HTTPS URL |
224
+ | 浏览器本地 `File` | 用 `FileReader.readAsDataURL` 转成完整的 `data:image/...;base64,...`;只用于满足请求体限制的小图 |
225
+ | Base 附件 | 用 `fetchAttachmentDownloadUrl(context, ref)` 换取短时效 URL |
226
+ | 多张参考图 | 由上述字符串组成的 1~14 项数组 |
227
+
228
+ `file_token` 只是附件资源标识,不能直接传给模型;浏览器 `blob:` URL 也不能使用,因为它只在当前页面上下文有效。不要手工拼 Box 下载地址。Base 附件场景同时读取[文件传输](file-transfer.md#展示与下载),使用真实的 `tableId`、`fieldId`、`recordId` 和 `fileToken` 证明附件位置:
229
+
230
+ ```ts
231
+ import {
232
+ createSiteAIFunctionTask,
233
+ fetchAttachmentDownloadUrl,
234
+ resolveDatabaseContext,
235
+ } from './lib/database-runtime';
236
+
237
+ const context = await resolveDatabaseContext();
238
+ const sourceImageUrl = await fetchAttachmentDownloadUrl(context, {
239
+ tableId,
240
+ fieldId,
241
+ recordId,
242
+ fileToken,
243
+ });
244
+
245
+ const task = await createSiteAIFunctionTask({
246
+ functionKey: 'generate_poster',
247
+ capability: 'image',
248
+ input: {
249
+ prompt: '保留主体,生成夏日海报',
250
+ image: sourceImageUrl,
251
+ },
252
+ });
253
+ // 使用 getSiteAIFunctionTask(functionKey, task.taskId) 继续轮询。
254
+ ```
255
+
256
+ helper 负责提交附件位置并获取经过权限校验的临时 URL,页面不手工拼接下载地址或注入凭证。临时 URL 不持久化、不记录日志;在目标环境验证它无需浏览器 Cookie、可由模型访问且有效期覆盖模型读取。不满足时报告附件不可作为当前模型输入;用户同意改用其他来源后再处理,不能退回传 `file_token`。
257
+
258
+ 新建 Page 默认使用 `response_format: "url"`。平台配置层也接受 `b64_json`,但当前 Page helper 的媒体结果只暴露 `imageAssets[].url/token`,没有可直接渲染的 base64 字段;没有额外、已验证的附件展示链路时,不要选择 `b64_json`。
259
+
260
+ ## 可用模型 Profile
261
+
262
+ 需要选择模型或确认可用参数时,先通过 OpenAPI 获取当前应用可用的模型列表,不要猜 Profile ID:
263
+
264
+ ```bash
265
+ APP_TOKEN='<appToken-from-base-database.jsonc>'
266
+ base-database-cli lark-cli -- \
267
+ api GET "/open-apis/base/v3/sites/${APP_TOKEN}/ai/model_profiles" \
268
+ --json
269
+ ```
270
+
271
+ `APP_TOKEN` 必须取当前 `base-database.jsonc` 中的 `appToken`;对应接口模板是 `/open-apis/base/v3/sites/:token/ai/model_profiles`,其中兼容路径参数 `:token` 表示应用 appToken。查询必须带上当前应用的 token,不能省略该路径参数。
272
+
273
+ 响应中的 `profiles[].profile_id` 才能作为 `model_profile_id`;每个 profile 带 `capability` 标记(text/vision/image/video)和 `input_modalities`。image/video 生成能力必须匹配同能力 Profile;vision 可使用 vision Profile,或使用声明了 image / video / audio / file 中至少一种媒体输入的 text Profile。页面只提交该 Profile 声明支持的 content parts,不需要模型服务地址或模型凭证。
274
+
275
+ **profile 字段与 AI Function 配置字段不是一一对应**:profile 里的 `thinking_levels`(如 `minimal/low/medium/high/max`)描述的是上游模型自身能力,不能直接填进 `meta.json`。AI Function 配置只接受本文档列出的枚举值:`text.reasoning_depth` 只能是 `light` / `standard` / `deep`。把 profile 的 `thinking_levels` 值(例如 `low`)当作 `reasoning_depth` 会被 `ai-functions validate` 拒绝并返回 `[code=800004006] invalid reasoning_depth`。选参数看 profile 能力,配 `reasoning_depth` 用本文档枚举,两者不要混。
276
+
277
+ ## CLI 建模与调试
278
+
279
+ AI Function 在本地以声明式目录维护:每个 `ai/<functionKey>/meta.json` 声明一个 AI Function,`deploy` 时随发布走**全量覆盖**同步(新增、修改和删除均以本地全集为准)。
280
+
281
+ ```bash
282
+ # 列出应用中的 AI Function
283
+ base-database-cli ai-functions list
284
+
285
+ # 读取单个配置用于本地编辑 / diff
286
+ base-database-cli ai-functions pull <functionKey>
287
+
288
+ # 全量覆盖同步本地 ai/ 声明
289
+ base-database-cli ai-functions sync
290
+
291
+ # 保存/发布前静态校验单个 AI Function
292
+ base-database-cli ai-functions validate <functionKey>
293
+
294
+ # 调试 text/vision:聚合返回完整文本与 firstTokenMs
295
+ base-database-cli ai-functions debug <functionKey> \
296
+ --env preview --messages '[{"role":"user","content":[{"type":"text","text":"概括缺货风险"}]}]' --variables '{"menu":"..."}'
297
+
298
+ # 调试 image/video:两者都提交媒体任务并返回 taskId;image 可带一张或多张参考图
299
+ base-database-cli ai-functions debug <functionKey> \
300
+ --env preview \
301
+ --input '{"prompt":"保持角色一致,生成一组新品海报","image":["https://example.com/ref-1.png","https://example.com/ref-2.png"]}' \
302
+ --variables '{}'
303
+
304
+ # 查询文生图 / 文生视频任务
305
+ base-database-cli ai-functions task <functionKey> <taskId> --env preview
306
+
307
+ # 查看用量汇总(总量 + 按日期分布 + 按 Function/能力/错误码分维度)
308
+ base-database-cli ai-functions usage \
309
+ --env prod --from 20260901 --to 20260908 --function-key <functionKey> \
310
+ --model <profileId> --capability-type text
311
+ ```
312
+
313
+ CLI 调试对 text/vision 返回聚合文本,对 image/video 返回 taskId;随后使用 `ai-functions task` 查询任务。验证页面逐帧显示时,使用 `invokeSiteAIFunctionStream` 并观察回调。
314
+
315
+ 应用所有者可用 `ai-functions usage` 查询用量汇总。对应接口 `GET /open-apis/base/v3/sites/:token/ai_functions/usage/summary` 返回总量、`by_date`(Key 为 `YYYYMMDD`)、`by_function`、`by_capability`、`error_dist`。`from/to` 为 `YYYYMMDD` 区间(含),不传则聚合全部历史。`--model` 接受 `model_profiles` 返回的 `profiles[].profile_id`。
316
+
317
+ image / video 创建任务后返回 `taskId` 和状态。使用同一应用、Function 与环境轮询,处理 `queued`、`running`、`succeeded`、`failed`、`cancelled`,仅在成功终态读取生成资源。平台会在应用绑定的 Base 中维护任务记录;页面通过任务接口读状态,不直接修改这些记录。
318
+
319
+ `taskId` 是不透明的任务标识,原样传给查询接口;不要解析它或用 Base record ID 替代。创建失败时报告错误,不凭空构造 taskId。
320
+
321
+ ## Page 调用方式
322
+
323
+ 新建工程已包含本节所需的 Page runtime;实现 AI Function 时不需要安装、替换或选择 SDK。修改已有工程时,除非用户明确要求升级页面依赖,否则保留已有 `package.json` 与 lockfile,不因新增 AI Function 自行改依赖。
324
+
325
+ starter 的 `page/src/lib/database-runtime.ts` 导出 key 化调用入口。调用 `callSiteAIFunction` 时显式提供与配置一致的 `capability`:text/vision 走流式聚合,image/video 走任务提交。页面统一使用这些 helper,由 SDK 处理流式和媒体任务通信。Page 不得自行调用全局 `fetch` 绕过 SDK,也不得在代理失败时降级直连。
326
+
327
+ | 能力 | Page 本次输入 | 推荐入口 | 完成条件 |
328
+ | ------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- |
329
+ | text | `messages` + 可选 `variables` | `invokeSiteAIFunctionStream` 需要逐帧 UI;`callSiteAIFunction` 需额外传 `capability:'text'` 取得聚合文本 | `onDone` 或 `{type:'text'}` |
330
+ | vision | 多模态 `messages` + 可选 `variables` | 与 text 相同;统一入口传 `capability:'vision'`,输入类型必须属于模型 Profile 的 `input_modalities` | `onDone` 或 `{type:'text'}` |
331
+ | image | `capability:'image'` + `input` + 可选 `variables` | `createSiteAIFunctionTask`,也可用统一入口创建任务 | `succeeded` 且读取 `imageAssets` |
332
+ | video | `capability:'video'` + `input` + 可选 `variables` | `createSiteAIFunctionTask`,也可用统一入口创建任务 | `succeeded` 且读取 `videoAsset` |
333
+
334
+ ```ts
335
+ import {
336
+ callSiteAIFunction,
337
+ invokeSiteAIFunctionStream,
338
+ createSiteAIFunctionTask,
339
+ } from './lib/database-runtime';
340
+
341
+ // 本例调用 text,因此只处理 text 结果。
342
+ const result = await callSiteAIFunction({
343
+ functionKey: 'recommend_dishes',
344
+ capability: 'text',
345
+ variables: { menu: '...' },
346
+ messages: [
347
+ {
348
+ role: 'user',
349
+ content: [{ type: 'text', text: '按预算 100 元推荐两道菜' }],
350
+ },
351
+ ],
352
+ });
353
+ if (result.type === 'text') {
354
+ showAnswer(result.content); // 替换为实际的页面状态更新
355
+ }
356
+
357
+ // 显式流式(text/vision):逐帧增量
358
+ const stream = invokeSiteAIFunctionStream({
359
+ functionKey: 'recommend_dishes',
360
+ messages: [
361
+ { role: 'user', content: [{ type: 'text', text: '概括今天的缺货风险' }] },
362
+ ],
363
+ variables: { menu: '...' },
364
+ });
365
+ // reasoning 模型会先持续下发 delta.reasoningContent,再下发 delta.content。
366
+ // 只消费 content 会让「思考过程」区域一直为空,看起来像没有流式输出,必须两者都消费。
367
+ stream.onMessage((delta) => {
368
+ if (delta.reasoningContent) appendReasoning(delta.reasoningContent);
369
+ if (delta.content) appendAnswer(delta.content);
370
+ });
371
+ stream.onError((error) => showError(error.message));
372
+ stream.onDone(() => markAnswerComplete());
373
+ // 只在用户取消、组件卸载或新请求替代旧请求时调用;不要在注册回调后立即销毁。
374
+ const cancel = () => stream.destroy();
375
+ // appendReasoning、appendAnswer、showError、markAnswerComplete 都是页面自己的状态更新函数。
376
+
377
+ // 多模态理解仍使用 capability:'vision'。content part 统一为 text、image、video、audio、file;
378
+ // 实际可用组合以 model_profiles 返回的 input_modalities 为准。
379
+ const multimodal = invokeSiteAIFunctionStream({
380
+ functionKey: 'understand_assets',
381
+ messages: [
382
+ {
383
+ role: 'user',
384
+ content: [
385
+ { type: 'text', text: '总结这些素材' },
386
+ { type: 'image', url: imageUrl, detail: 'high' },
387
+ { type: 'video', url: videoUrl, fps: 2 },
388
+ { type: 'audio', url: audioUrl },
389
+ { type: 'file', url: pdfUrl, filename: 'report.pdf' },
390
+ ],
391
+ },
392
+ ],
393
+ });
394
+
395
+ // 图片生成 / 文生视频:提交后必须轮询。
396
+ // image 完整可复用实现见 starter 的 `ImageTaskOperation.tsx`;video 参考 `ai-media-panel.tsx` 中的 `AIVideoPanel`。
397
+ const task = await createSiteAIFunctionTask({
398
+ functionKey: 'make_promo',
399
+ capability: 'image',
400
+ input: {
401
+ prompt: '保持角色一致,生成一组新品海报',
402
+ image: ['https://example.com/ref-1.png', 'https://example.com/ref-2.png'],
403
+ },
404
+ });
405
+ ```
406
+
407
+ 公开协议不暴露供应商字段,五类 part 固定为 `{type:'text', text}`、`{type:'image', url, detail?}`、`{type:'video', url, fps?}`、`{type:'audio', url}`、`{type:'file', url, filename?}`。四类非文本输入统一使用 `url`,不支持 `file_id`;本地文件先编码成 data URL。后端 Ark adapter 会把 image/video 转成 `image_url`/`video_url` 对象,把音频 URL 转成 `input_audio.url + format`(format 按链接后缀推断),把音频 data URL 拆成 `input_audio.data + format`,把文件映射为 `file.file_url` 或 `file.file_data`。
408
+
409
+ 文件理解当前只支持 PDF,单文件不得超过 50 MB。公网 URL 必须是 HTTP(S),路径以 `.pdf` 结尾;若签名 URL 的路径没有后缀,必须补 `filename:'*.pdf'`。本地 PDF 使用 `data:application/pdf;base64,...`,并且必须带 `filename:'*.pdf'`。`filename` 只用于校验和 data URL 场景的 Ark 请求;URL 场景不会把它透传给供应商。Chat 文件理解返回仍是文本流,不新增文件输出类型。
410
+
411
+ `reasoningContent` 是模型思考过程增量,`content` 是最终回答增量:两者分区渲染,思考过程放较小的过程区域,最终回答进正式结果区。**要从结果里解析结构化列表时,基于最终 answer(`content` 累积值)解析,不要基于 reasoning 内容解析。** 业务页面只处理 SDK 返回的 `{ content?, reasoningContent?, raw }`,无需自行解析原始流式传输格式。
412
+
413
+ `SiteAIFunctionResult` 是可判别联合(用 `switch(result.type)` 判别,避免字符串解析媒体结果):`{ type:'text', content }` / `{ type:'image_task', taskId, status }` / `{ type:'video_task', taskId, status }`。Ark Chat 的 `choices.message.content` / `choices.delta.content` 是文本,因此 vision 表示多模态输入理解、文本输出;它不会在 Chat 响应中生成图片、视频或音频。需要媒体输出时必须显式调用 image/video AI Function 的任务接口,任务成功后分别从 `imageAssets` / `videoAsset` 读取结果。所有响应携带 `requestId`,用于监控与排查。
414
+
415
+ 媒体任务只允许运行时覆盖有限字段:image 使用 `input.prompt` / `input.size` / `input.image`,video 使用 `input.prompt` / `input.image_url` / `input.duration`;model、张数、比例、水印等保持在 AI Function 配置中。`input.image` 接受单个字符串或 1~14 项字符串数组;`input.duration` 必须是正数,并会按模型 Profile 上限收紧。图片生成参考 starter 的 `page/src/examples/ai-function/ImageTaskOperation.tsx`(只渲染 `imageAssets`),文生视频参考同目录 `ai-media-panel.tsx` 中的 `AIVideoPanel`(渲染 `videoAsset`)。两个示例都轮询并处理 `queued`、`running`、`succeeded`、`failed`、`cancelled`,在新请求时通过 `requestSeq` 忽略旧任务结果,且带总轮询时限(`pollDeadlineMs`,默认 5 分钟)以避免死轮询;组件卸载时也停止 timer。不要用没有总时限或取消处理的 `while` 轮询。
416
+
417
+ `messages[].content` 必须是非空 content part 数组,不接受字符串。业务调用只允许 `user` / `assistant`:`user` 可传 `text`、`image`、`video`、`audio`、`file`,`assistant` 只允许 `text`;System Prompt 只能由 AI Function 配置管理,调用方不得发送 `system`。每个 message 与 part 都采用严格字段白名单,多余字段会被拒绝。
418
+
419
+ 图片 `detail` 仅允许 `low` / `high` / `xhigh`,视频 `fps` 仅允许 `[0.2, 5]`。媒体与文件 `url` 必须是非空 HTTP(S) URL 或合法 data URL;音频 data URL 的 MIME 必须对应 `mp3` / `wav` / `aac` / `m4a` / `pcm` / `ac3` / `alac`。音频 HTTP(S) URL 的路径后缀必须是这些格式之一(Ark 要求显式 format,服务端按后缀推断,签名参数不受影响),没有可识别后缀的链接请改传音频 data URL。文件只接受 PDF,并遵循上一段的 filename 和大小约束。每个 part 还必须命中所选 Profile 的 `input_modalities`。不要用 `any` 或自行 `fetch` 绕过公开类型与服务端校验。
420
+
421
+ ## System prompt 与数据边界
422
+
423
+ System Prompt 在 **AI Function 配置**里维护,随版本快照进入线上;平台不代管、不隐藏,也**不承诺保护 System Prompt**,因此不得放入密钥、Token、内部凭据或不可向最终访问者暴露的内容。页面通过 `variables` 注入运行时值,占位符 `{{var}}` 由服务端渲染。
424
+
425
+ 传给模型的数据遵循最小必要原则。Database Function 查询结果先在 Page 内筛选字段和记录,只发送完成当前任务所需的内容;不得把整个 Catalog、整表数据、登录凭证或原始下游响应无差别塞入 `messages` / `variables`。
426
+
427
+ ## 计费与失败处理
428
+
429
+ - 模型/媒体用量计费到当前应用 owner;访问者发起请求不改变计费主体。页面文案如展示用量归属,应明确这一点。
430
+ - 平台还可能因 AI credit 耗尽、并发已满或调用超时而拒绝调用。页面仍需防止重复提交。
431
+ - 一个交互实例只维护一条活动流。发送期间禁用重复提交;用户取消、组件卸载或新请求替代旧请求时调用 `destroy()`。`onDone` / 返回结果表示正常结束;`onError` 表示失败;两者都必须结束 loading。
432
+
433
+ ### 页面如何处理运行态错误
434
+
435
+ 页面只能可靠地按错误性质处理。收到 AI credit、并发限制或服务错误时,保留 `error.code`、`error.message` 和本次 `requestId`,结束 loading,并按错误是否可重试给出提示;不要在页面里自动重试,更不要绕过 AI Function 直连模型。
436
+
437
+ AI credit 耗尽会作为不可重试的错误返回,消息为 `AI credit is exhausted`。应提示应用 owner 检查可用 credit 或联系平台支持,不能把它当作网络故障反复重试。
438
+
439
+ ## 页面实现检查
440
+
441
+ 1. 页面按 `functionKey` 调用;`callSiteAIFunction` 显式传与配置相同的 `capability`。System Prompt / model / 参数由 AI Function 配置声明,`messages/input` 与 `variables` 是本次运行输入。
442
+ 2. 指定模型或参数前先查 `/open-apis/base/v3/sites/:token/ai/model_profiles`;`:token` 取当前应用配置的 `appToken`,能力标记必须与 `capability_type` 匹配。
443
+ 3. 用 `switch(result.type)` 判别渲染,不直接渲染 `delta.raw`;流式每个面板只有一个活动 handle,取消与卸载均调用 `destroy()`。
444
+ 4. text/vision 流式同时消费 `delta.reasoningContent` 与 `delta.content`(或用 `readSiteAITextDelta` 取 `{ answer, reasoning }`),分区渲染;结构化解析只基于最终 answer。
445
+ 5. `onError` 与 `onDone` 都结束 loading;image/video 用 `getSiteAIFunctionTask` 轮询到 `succeeded`/`failed`/`cancelled` 收尾。
446
+ 6. vision 不绕过公开 `SiteAIMessage` 类型猜测媒体 content;只提交所选 Profile 的 `input_modalities` 已声明的 image / video / audio / file,未暴露对应输入契约时明确报告阻塞。
447
+ 7. AI 调用走 AI Function,页面不保存模型凭证。
448
+ 8. 与 Base 数据组合时,查询仍走 Database Function;写入按[Mutation 与 FaaS 分工](mutations.md#写入方式选择)选择。
449
+
450
+ ## 区分三种「调试成功」
451
+
452
+ AI Function 调试至少有三种互不等价的「成功」,不能互相替代地报告:
453
+
454
+ - **配置校验成功**:`ai-functions validate` 返回 `is_success: true`,只证明 `meta.json` 合法,不代表能跑通。
455
+ - **运行态调用成功**:接口能返回文本或 `taskId`,证明 Function 存在、未 `disabled`、鉴权与模型链路初步可用。CLI 对 text/vision 返回聚合文本。
456
+ - **UI 流式成功**:页面 `onMessage` 多次收到增量并实时渲染,证明 SDK 回调与业务组件的增量显示已接通。
457
+
458
+ 尤其对文本 AI Function:CLI 聚合返回成功或超时,都**不能**直接判断页面 SSE 是否正常。页面选择 `invokeSiteAIFunctionStream` 时,用真实页面验证多次 `onMessage` 和 `onDone`;选择 `callSiteAIFunction` 聚合时,验证得到 `{type:'text'}` 和完整 `content`。媒体能力用 task 接口验证终态。
459
+
460
+ ## AI Function 交付 checklist
461
+
462
+ ```text
463
+ 1. 查询 model_profiles,选择 capability 匹配的 profile_id。
464
+ 2. 写 ai/<functionKey>/meta.json,先 validate(reasoning_depth 用 light/standard/deep,不用 profile 的 thinking_levels)。
465
+ 3. 文本流式 UI 同时消费 delta.reasoningContent 与 delta.content。
466
+ 4. `callSiteAIFunction` 显式传 capability;文生图/文生视频使用 create task + poll task,不走文本 stream。
467
+ 5. 修改已有应用时,远端全部 key 已恢复到本地,未意外触发全量删除。
468
+ 6. record_id 不依赖短长度(^rec[A-Za-z0-9]{1,32}$)。
469
+ 7. deploy 后 ai-functions list 确认 function 存在且 disabled=false。
470
+ 8. 用真实页面按所选入口验证 text/vision(流式为多次 `onMessage` 增量和单次 `onDone`,错误时为 `onError` 且无 `onDone`;聚合为 text result);用 task 接口验证 image/video 终态。
471
+ ```
@@ -6,7 +6,7 @@
6
6
 
7
7
  能力归属无法确认时,在以下动作之前强制执行能力发现:
8
8
 
9
- - 选择 Page、SQL Function、FaaS 或原生 Base 的实现归属;
9
+ - 选择 Page、Database Function、Mutation、FaaS 或原生 Base 的实现归属;
10
10
  - 删除、简化或降级一项用户需求;
11
11
  - 声称多维表格、Base Database 或 `lark-cli` 不支持某项能力;
12
12
  - 用 FaaS 模拟定时触发、记录变化触发、提醒、通知、权限控制等 Base 本体行为。
@@ -54,8 +54,8 @@ lark-cli base +<resource-list-or-get> ... --as user
54
54
  查询完成后只选择以下一种结论:
55
55
 
56
56
  1. **原生 Base 实现**:能力由多维表格本体提供。通过 `lark-cli base +...` 配置,应用 只提供需要的交互页面。
57
- 2. **Base Database 实现**:能力属于页面交互、SQL 读取或由 Page 主动发起的即时业务写入,按 Page/SQL/FaaS 边界实现。
58
- 3. **组合实现**:例如 Page/FaaS 创建记录,原生 workflow 监听记录并通知;分别配置并独立验收两段链路。
57
+ 2. **Base Database 实现**:能力属于页面交互、Database Function 读取或由 Page 主动发起的即时业务写入,按 Page/Database Function 与[Mutation/FaaS 选择](mutations.md#写入方式选择)实现。
58
+ 3. **组合实现**:例如 Page 调用 Mutation/FaaS 创建记录,原生 workflow 监听记录并通知;分别配置并独立验收两段链路。
59
59
  4. **已证实的缺口或阻塞**:找不到当前 CLI 入口、命令明确不支持,或遇到权限或环境阻塞。准确描述为“当前 `lark-cli` 未暴露”“原生能力明确不支持”或“当前身份无权限”,不要互相替代。
60
60
 
61
61
  ## 执行边界
@@ -0,0 +1,46 @@
1
+ # Database Function 能力边界
2
+
3
+ 仅在判断支持集之外的查询语法、评审或排查查询语句,或者处理查询编译与运行兼容失败时读取。本文件是当前生成与兼容策略:明确不支持的协议不使用;“默认不生成”“支持不统一”属于保守生成边界,不据此声称平台整体不支持。已知限制正常开发直接遵守,只有版本或验证证据变化时复核。以下形态不写入常规 `main.sql`:
4
+
5
+ ## Stored Query 与分页
6
+
7
+ - 参数 kind 不使用 `OPTION`;SingleSelect 选项名使用 `TEXT` 参数,并在 Page/FaaS 按 Catalog 选项白名单约束输入后显式比较 `(option_field).name`。
8
+ - 不把 `OPTION`、`USER`、`CHAT`、`FILE`、`LOCATION`、`URL` 等对象类型声明为业务参数;参数只使用六种基础类型及其一维数组,允许的 null 位置以生成的 Schema 为准。
9
+ - 不通过移除服务端筛选、读取固定前 N 条后在 Page 分页来声称完整分页或全量导出。
10
+
11
+ ## 查询结构与引用
12
+
13
+ - 查询结构:`INSERT`、`UPDATE`、`DELETE`、`MERGE`、递归 CTE、嵌套 CTE、`LATERAL`,以及右侧会展开为多个 FROM item 的右深嵌套 JOIN。`SELECT *`、`table.*` 默认不生成,保持输出列稳定。`UNION`、`INTERSECT`、`EXCEPT`(可带 `ALL`)可用,但各分支对应列的类型必须完全相同。
14
+ - 无真实表依赖查询:`SELECT 1`、`SELECT $1`、仅 `FROM (SELECT 1) AS x`、仅常量 CTE 或仅 `UNNEST(...)` 的查询。绑定 Base 的 Database Function 必须直接或经由派生表/CTE 引用至少一个 Catalog 真实表,否则运行时可能因查询未引用可路由表而无法确定数据上下文。与 Base 数据无关的常量在 Page/FaaS 中用代码返回。
15
+ - 分组:`GROUP BY` 超过 5 项;需要更多展示列时拆成聚合查询与明细查询后在 Page/FaaS 合并。
16
+ - JOIN:`NATURAL JOIN`、`FULL JOIN ... USING`、JOIN group alias、`LEFT JOIN UNNEST(...)`、`INNER JOIN UNNEST(...)`、含 `GROUP BY` 或聚合函数的派生表 JOIN,以及单条查询超过 10 个 JOIN。`RIGHT JOIN`、`FULL JOIN`、`JOIN ... USING` 可用,常规生成仍优先 `INNER` / `LEFT JOIN`。
17
+ - 列成员:禁止 `table_alias.user_field.id` 和 `"人员字段".id`;UDT 列只使用 PostgreSQL 括号语法 `(table_alias.user_field).id` 或 `("人员字段").id`,`UNNEST` 后的 range alias 成员仍写 `u.id`。
18
+ - 派生对象:把 `USER`、`CHAT`、`FILE`、`URL` 完整对象投影到派生表后再 JOIN。静态校验可能接受该形态,实际执行仍可能返回查询不受支持的错误。
19
+
20
+ ## 类型、表达式与字段操作
21
+
22
+ - 类型与表达式:`CURRENT_DATE`、`DATE` 类型、`NOW()`,数字转 `TEXT`(`x::text`)、列值转 `TIMESTAMP` / `INTERVAL`(这两种转换只接受字符串字面量),以及固定支持集之外的函数调用。可用的显式转换是 `::int`、`::bigint`、`::float8`、`::numeric`、`::bool`。
23
+ - 非 PostgreSQL 写法不支持:`DATE_FORMAT` 改用 `TO_CHAR(ts, 'YYYY-MM')`,`ENDS_WITH` 改用 `LIKE '%x'`,单参数 `ARRAY_LENGTH(arr)` 改用 `ARRAY_LENGTH(arr, 1)` 或 `CARDINALITY(arr)`,`ROUND(x, n)` 改用 `ROUND(x::numeric, n)`,对小数取模 `x % n` 不支持。
24
+ - `||`、`CONCAT` 两侧都必须是 `TEXT`,不会把数字隐式转为文本;`COALESCE`、`NULLIF`、集合运算只在 `INT` 与 `DOUBLE` 之间统一为 `DOUBLE`,其他类型要先写成同类型。
25
+ - 条件聚合:不要写 `SUM/AVG(CASE WHEN ... THEN 1 ELSE 0 END)`;改用 `SUM(CASE WHEN ... THEN 1.0 ELSE 0.0 END)`。
26
+ - `ILIKE`、`NOT ILIKE`:字段支持不统一,正常查询使用 `LIKE`、`NOT LIKE`。
27
+ - 相关标量子查询在 WHERE、投影、HAVING、ORDER BY 中均不支持;先在派生表按关联键 GROUP BY,再 JOIN 回外层(复杂组合应拆成两个 Database Function)。非相关子查询只使用已校验的形态,不将这一限制泛化为所有子查询都不可用。
28
+ - 子查询表达式:`EXISTS` 在投影、`ORDER BY`、`OR` / `CASE` 下的形态默认不生成;相关子查询的非等值关联(例如 `inner.t < outer.t`)。改用派生表 JOIN,或用单独 Database Function 查询后在 Page/FaaS 合并。`WHERE` 中的 `IN` / `NOT IN` / `EXISTS` 子查询和等值相关子查询可用。
29
+ - 聚合与窗口函数:`PERCENTILE_CONT` / `PERCENTILE_DISC`、`MEDIAN`、`WITHIN GROUP`、聚合内 `ORDER BY`;带 `ORDER BY` 的聚合窗口(逐行累计)、`CURRENT ROW` 或有界的窗口范围、未写整分区范围的 `LAST_VALUE`;`STDDEV` / `VARIANCE` 作为窗口函数。`STDDEV`、`VARIANCE` 作为普通聚合可用,`ROW_NUMBER`、`RANK` 等排名窗口可用。
30
+ - 字符串函数:`LENGTH`、`CHAR_LENGTH`、`LEFT`、`RIGHT`、`SUBSTRING`、`LPAD`、`RPAD`、`TRIM` / `LTRIM` / `RTRIM`、`SPLIT` / `SPLIT_PART`、`REGEXP` / `RLIKE` / `REGEXP_REPLACE`;`STRPOS` / `POSITION` 取具体位置(只能与 0 或 1 比较,用于判断是否包含)。`REPLACE` 可用。
31
+ - 数字函数:`POWER`、`SQRT`、`LOG`、`EXP`、`TRUNC`;`MOD` 的参数不是整数。
32
+ - 日期函数:`DATEDIFF`、`AGE`,以及其他未列入支持集的时间间隔函数;月、季、年单位的 `INTERVAL`;`EXTRACT` / `DATE_PART` 的 `dow`、`doy`、`week`、`quarter`;`DATE_PART('epoch', ts)`(改写为 `EXTRACT(EPOCH FROM ts)`);`DATE_TRUNC` 截到时、分、秒,以及 `DATE_TRUNC` 结果再参与比较、相减或 `EXTRACT`。
33
+ - `rec_id`、`TEXT`、Phone:顺序比较、`BETWEEN`、`NOT BETWEEN`。文本日期字符串不能用 `<`、`<=`、`>`、`>=` 做范围筛选;需要日期范围时把字段建成 DateTime/TIMESTAMP,或改用 Text 支持的 `=`、`IN`、`LIKE` 后在 Page/FaaS 中判断范围。
34
+ - Checkbox:`IN`、顺序比较、`BETWEEN`、`NOT BETWEEN`。
35
+ - DateTime:`IN`、`NOT IN`、`LIKE`、`ILIKE`。
36
+ - SingleSelect:与 TEXT、`.name` 或 `.id` 等成员标量之间的隐式比较,顺序比较、`BETWEEN`、`LIKE`、`ILIKE`,以及与 `''` 空字符串比较;动态选项名筛选显式使用 `.name` 和 `TEXT` 参数。
37
+ - MultiSelect:`LIKE`。
38
+
39
+ - 参数化数组成员判断仅支持 `TEXT表达式 = ANY($n)`,且 `$n` 声明为 `TEXT[]`。其他类型先用同类型标量参数展开为等值条件;不要用 `IN ($n)` 展开数组参数。
40
+
41
+ ## 不要误判为整体不支持
42
+
43
+ - `LIKE $1` 本身可用,参数携带 `%`;复合查询失败时分别检查参数 kind 与错误节点,不因单条失败就判定参数化 `LIMIT/OFFSET` 不支持。
44
+ - OPTION 与同类型 OPTION 可以直接比较;与固定选项名或 `TEXT` 参数比较时必须显式访问 `.name` 或 `.id`。其他 UDT 同样不能和其成员标量隐式比较。
45
+ - 参数化 `LIMIT $n` / `OFFSET $n` 可用;页面输入不能直接决定返回规模,确需动态条数时由 Page/FaaS 先校验为非负整数并限制最大值。
46
+ - 支持集内的 `CASE`、`COALESCE`、`INNER JOIN`、`LEFT JOIN`、`CROSS JOIN`、非递归顶层 CTE、派生标量列、`FROM` 子查询、`WHERE` 中的 `IN` / `EXISTS` 子查询、逗号连接或 `CROSS JOIN` 形态的 `UNNEST`、`TIMESTAMP` 字面量、固定时长的 `INTERVAL`、`EXTRACT`、`TO_CHAR`、时间间隔运算、`ARRAY_AGG`、`STRING_AGG`、排名窗口、`CEIL` 和 `FLOOR` 可以使用;某个复合查询失败时先按错误节点缩小,不把这些能力整体降级。