Compare commits

..

5 Commits

Author SHA1 Message Date
fangshuyu
4ee238c4a4 Fix missing help text for drive files patch 2026-06-04 17:25:45 +08:00
YH-1600
c000dc3a44 docs: refine lark-drive knowledge organize workflow (#1253)
Change-Id: I49b4f398d60c5bb073d6c8d61987bd16f1a29c4e
2026-06-04 15:31:46 +08:00
zhicong666-bytedance
256df8c0fb docs(vc-agent): require explicit leave request (#1260) 2026-06-04 14:33:57 +08:00
Huangwenbo-wb
7a0dbe057b docs(slides): add whiteboard element documentation and improve slide guidance (#1029)
* feat(slides): add whiteboard element support and reference documentation

- Add lark-slides-whiteboard.md covering SVG and Mermaid modes, routing
  rules, layout examples, known issues, and self-check checklist
- Register <whiteboard> in slides_xml_schema_definition.xml; remove it
  from the undefined element type list
- Update SKILL.md quick-reference table and按需再读 section to point to
  the new whiteboard reference
- Update xml-schema-quick-ref.md with <whiteboard> syntax examples
- Update slide create/get/replace references to include whiteboard as a
  valid <data> child element
- Tighten fallback_if_missing descriptions in planning-layer.md and
  asset-planning.md: replace "shapes" wording with neutral intent
  language and add "whiteboard diagrams" to the fallback tool lists

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(slides): refine whiteboard reference doc structure and content

- Restructure doc: common attributes and prerequisites moved to top
- Move design quality rules under SVG mode section
- Add z-order inline note to full-screen layout example
- Replace JS coordinate script with Python, broaden scope to decorative elements
- Delete redundant Mermaid examples (keep one complete whiteboard+flowchart)
- Add prerequisite link and references section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(slides): clarify chart vs whiteboard selection and fix doc gaps

- lark-slides-whiteboard: add chart vs whiteboard decision table at top;
  fix intro and SVG use-case list to remove bar/line (those belong to <chart>)
- SKILL.md: split whiteboard quick-ref row into chart row + whiteboard row;
  fix sidebar link label to match actual scope
- asset-planning: correct chart asset type — remove funnel/scatter (unsupported
  by <chart> XSD) and note they fall back to <whiteboard> SVG
- visual-planning: add one-line whiteboard preference hint to
  architecture-diagram and process-flow layout types
- validation-checklist: add Whiteboard Elements section noting slide.get
  does not return SVG/Mermaid content; content correctness requires manual
  visual sign-off

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(slides): add SVG decorative visibility principles

Add two design rules to SVG quality requirements: check background
luminance before writing SVG (dark bg requires higher contrast), and
use non-linear brightness jumps (e.g. 0.10→0.40→0.70→1.0) instead of
linear opacity stacking (0.04→0.08→0.12) which produces near-identical
layers on dark backgrounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(slides): add custom icon use case to whiteboard SVG

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(slides): fix whiteboard SVG rendering rules

- content area is determined by child element bounding box union, not svg width/height/viewBox/xmlns
- viewBox only purpose: provide reference for percentage-based attribute values; omit when using absolute coords
- remove redundant attributes from all svg examples, use bare <svg> tags
- drop positive/negative coordinate guidance; rendering rule simplified to bounding-box auto-scale
2026-06-04 11:58:09 +08:00
suhui928
8ce38793a7 feat: add contact skill domain guidance (#1144)
* feat(lark-contact): route user_profiles batch_query in skill

- Add user_profiles batch_query row to the routing table.
- Add a worked example next to the search-user one, with `lark-cli
  schema` first (best practice: don't guess `--data` / `--params`).
- Trim description: drop the duplicated trigger clause, add
  personal_status / signature to the capability list so routing picks
  this skill up for those queries.
2026-06-03 22:32:27 +08:00
30 changed files with 901 additions and 113 deletions

View File

@@ -70,7 +70,7 @@ func printResourceList(w io.Writer, spec map[string]interface{}, mode core.Stric
for _, methodName := range sortedKeys(methods) {
m, _ := methods[methodName].(map[string]interface{})
httpMethod := registry.GetStrFromMap(m, "httpMethod")
desc := registry.GetStrFromMap(m, "description")
desc := registry.GetMethodDescription(name, resName, methodName, m)
danger := ""
if d, _ := m["danger"].(bool); d {
danger = fmt.Sprintf(" %s[danger]%s", output.Red, output.Reset)
@@ -94,7 +94,7 @@ func printMethodDetail(w io.Writer, spec map[string]interface{}, resName, method
methodPath := registry.GetStrFromMap(method, "path")
fullPath := servicePath + "/" + methodPath
httpMethod := registry.GetStrFromMap(method, "httpMethod")
desc := registry.GetStrFromMap(method, "description")
desc := registry.GetMethodDescription(specName, resName, methodName, method)
isFileUpload, fileFieldNames := hasFileFields(method)
fmt.Fprintf(w, "%s%s.%s.%s%s\n\n", output.Bold, specName, resName, methodName, output.Reset)
@@ -679,7 +679,7 @@ func runPrettyMode(out io.Writer, parts []string, mode core.StrictMode) error {
for _, mName := range sortedKeys(methods) {
m, _ := methods[mName].(map[string]interface{})
httpMethod := registry.GetStrFromMap(m, "httpMethod")
desc := registry.GetStrFromMap(m, "description")
desc := registry.GetMethodDescription(serviceName, resName, mName, m)
fmt.Fprintf(out, " %-7s %s%s%s %s%s%s\n", httpMethod, output.Bold, mName, output.Reset, output.Dim, desc, output.Reset)
}
fmt.Fprintf(out, "\n%sUsage: lark-cli schema %s.%s.<method>%s\n", output.Dim, serviceName, resName, output.Reset)

View File

@@ -196,6 +196,25 @@ func TestSchemaCmd_PrettyUnchanged_KeyTextPresent(t *testing.T) {
}
}
func TestPrintMethodDetail_UsesDescriptionOverrideWhenMetadataIsEmpty(t *testing.T) {
spec := map[string]interface{}{
"name": "drive",
"servicePath": "/open-apis/drive/v1",
}
method := map[string]interface{}{
"httpMethod": "PATCH",
"path": "files/{file_token}",
"description": "",
}
var out bytes.Buffer
printMethodDetail(&out, spec, "files", "patch", method)
if !strings.Contains(out.String(), "修改文件标题") {
t.Fatalf("pretty output = %q, want to contain %q", out.String(), "修改文件标题")
}
}
func TestSchemaCmd_UnknownService(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, &core.CliConfig{
AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu,

View File

@@ -140,10 +140,10 @@ func NewCmdServiceMethod(f *cmdutil.Factory, spec, method map[string]interface{}
}
func NewCmdServiceMethodWithContext(ctx context.Context, f *cmdutil.Factory, spec, method map[string]interface{}, name, resName string, runF func(*ServiceMethodOptions) error) *cobra.Command {
desc := registry.GetStrFromMap(method, "description")
specName := registry.GetStrFromMap(spec, "name")
desc := registry.GetMethodDescription(specName, resName, name, method)
httpMethod := registry.GetStrFromMap(method, "httpMethod")
risk := registry.GetStrFromMap(method, "risk")
specName := registry.GetStrFromMap(spec, "name")
schemaPath := fmt.Sprintf("%s.%s.%s", specName, resName, name)
opts := &ServiceMethodOptions{

View File

@@ -166,6 +166,19 @@ func TestNewCmdServiceMethod_POSTHasDataFlag(t *testing.T) {
}
}
func TestNewCmdServiceMethod_UsesDescriptionOverrideWhenMetadataIsEmpty(t *testing.T) {
f := &cmdutil.Factory{}
cmd := NewCmdServiceMethod(f, driveSpec(),
map[string]interface{}{"description": "", "httpMethod": "PATCH"}, "patch", "files", nil)
if cmd.Short != "修改文件标题" {
t.Fatalf("Short = %q, want %q", cmd.Short, "修改文件标题")
}
if !strings.Contains(cmd.Long, "修改文件标题") {
t.Fatalf("Long = %q, want to contain %q", cmd.Long, "修改文件标题")
}
}
func TestNewCmdServiceMethod_RunFCallback(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, testConfig)

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package registry
import "strings"
var methodDescriptionOverrides = map[string]string{
"drive.files.patch": "修改文件标题",
}
// GetMethodDescription returns the method description from metadata, falling back
// to a curated override when the upstream meta has an empty description.
func GetMethodDescription(service, resource, method string, meta map[string]interface{}) string {
if desc := strings.TrimSpace(GetStrFromMap(meta, "description")); desc != "" {
return desc
}
return methodDescriptionOverrides[service+"."+resource+"."+method]
}

View File

@@ -223,6 +223,24 @@ func TestFilterScopes_TooFewParts(t *testing.T) {
}
}
func TestGetMethodDescription_UsesOverrideWhenMetadataIsEmpty(t *testing.T) {
got := GetMethodDescription("drive", "files", "patch", map[string]interface{}{
"description": " ",
})
if got != "修改文件标题" {
t.Fatalf("GetMethodDescription() = %q, want %q", got, "修改文件标题")
}
}
func TestGetMethodDescription_PrefersMetadataDescription(t *testing.T) {
got := GetMethodDescription("drive", "files", "patch", map[string]interface{}{
"description": "Rename a file",
})
if got != "Rename a file" {
t.Fatalf("GetMethodDescription() = %q, want %q", got, "Rename a file")
}
}
// --- Auto-approve functions ---
func TestLoadAutoApproveSet(t *testing.T) {

View File

@@ -0,0 +1,33 @@
## 选哪个命令
**user 身份和 bot 身份是两条完全独立的路径**。先确定当前身份,再按下表选命令:
| 想做什么 | user 身份 | bot 身份 |
|---|---|---|
| 按姓名 / 邮箱搜员工拿 open_id | [`+search-user`](references/lark-contact-search-user.md) | 不支持 |
| 已知 open_id 取他人资料 | `+search-user --user-ids <id>` | [`+get-user --user-id <id>`](references/lark-contact-get-user.md) |
| 查看自己 | `+get-user``+search-user --user-ids me` | 不支持 |
已知 open_id 只是想发消息 / 排日程,不必经过 contact —— 直接 [`lark-im`](../lark-im/SKILL.md) / [`lark-calendar`](../lark-calendar/SKILL.md)。
## 典型场景
```bash
# 找张三给他发消息:先搜,确认 open_id,再发
lark-cli contact +search-user --query "张三" --has-chatted --as user
lark-cli im +messages-send --user-id ou_xxx --text "Hi!"
```
搜索命中多条且后续操作有副作用(发消息、邀请会议等),把候选列给用户挑;不要擅自选第一条。
## 注意事项
- **41050 / Permission denied** 受当前身份的可见范围限制(两条命令都可能遇到)。换 bot 身份或让管理员调整可见范围,细节见 [`lark-shared`](../lark-shared/SKILL.md)。
- **跨租户用户**(`is_cross_tenant=true`)多数业务字段为空字符串,这是飞书可见性规则,下游做空值兜底。
- **ID 类型**:默认 `open_id``+get-user` 可改 `--user-id-type union_id|user_id`;`+search-user` 只接受 `open_id`
## 不在本 skill 范围
- 发消息 / 查聊天记录 → [`lark-im`](../lark-im/SKILL.md)
- 排日程 / 邀请会议 → [`lark-calendar`](../lark-calendar/SKILL.md)
- 部门树 / 按部门列员工 / 组织架构 → [`lark-openapi-explorer`](../lark-openapi-explorer/SKILL.md) 查找原生接口

View File

@@ -1,14 +1,16 @@
---
name: lark-contact
version: 1.0.0
description: "飞书 / Lark 通讯录,用于按姓名 / 邮箱把员工解析成 open_id,以及按 open_id 反查员工的姓名 / 部门 / 邮箱 / 联系方式。当用户说出某人姓名下一步需要发消息 / 加群 / 排日程时,先用本 skill 把姓名换成 ID;当输出里出现 open_id 需要展示成姓名给用户看,或用户直接询问某人的部门 / 邮箱 / 联系方式时,用本 skill 查。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。"
description: "飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名。当用户提到某人姓名下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。"
metadata:
requires:
bins: ["lark-cli"]
cliHelp: "lark-cli contact --help"
---
# lark-contact
# contact (v2)
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../lark-shared/SKILL.md`](../lark-shared/SKILL.md),其中包含认证、权限处理**
## 选哪个命令
@@ -19,17 +21,29 @@ metadata:
| 按姓名 / 邮箱搜员工拿 open_id | [`+search-user`](references/lark-contact-search-user.md) | 不支持 |
| 已知 open_id 取他人资料 | `+search-user --user-ids <id>` | [`+get-user --user-id <id>`](references/lark-contact-get-user.md) |
| 查看自己 | `+get-user``+search-user --user-ids me` | 不支持 |
| 查同事的个人状态 / 签名 | `user_profiles batch_query` | 不支持 |
已知 open_id 只是想发消息 / 排日程,不必经过 contact —— 直接 [`lark-im`](../lark-im/SKILL.md) / [`lark-calendar`](../lark-calendar/SKILL.md)。
## 典型场景
找张三给他发消息:先搜,确认 open_id,再发:
```bash
# 找张三给他发消息:先搜,确认 open_id,再发
lark-cli contact +search-user --query "张三" --has-chatted --as user
lark-cli im +messages-send --user-id ou_xxx --text "Hi!"
```
批量查同事的个人状态 / 个性签名(先用 schema 看参数)。
```bash
lark-cli schema contact.user_profiles.batch_query
lark-cli contact user_profiles batch_query \
--params '{"user_id_type":"open_id"}' \
--data '{"user_ids":["ou_xxx","ou_yyy"],"query_option":{"include_personal_status":true,"include_description":true}}' \
--as user
```
搜索命中多条且后续操作有副作用(发消息、邀请会议等),把候选列给用户挑;不要擅自选第一条。
## 注意事项
@@ -42,4 +56,4 @@ lark-cli im +messages-send --user-id ou_xxx --text "Hi!"
- 发消息 / 查聊天记录 → [`lark-im`](../lark-im/SKILL.md)
- 排日程 / 邀请会议 → [`lark-calendar`](../lark-calendar/SKILL.md)
- 部门树 / 按部门列员工 / 组织架构 ,通过 [`lark-openapi-explorer`](../lark-openapi-explorer/SKILL.md) 查找原生接口
- 部门树 / 按部门列员工 / 组织架构 [`lark-openapi-explorer`](../lark-openapi-explorer/SKILL.md) 查找原生接口

View File

@@ -295,12 +295,14 @@ lark-cli drive <resource> <method> [flags] # 调用 API
```
> **重要**:使用原生 API 时,必须先运行 `schema` 查看 `--data` / `--params` 参数结构,不要猜测字段格式。
>
> **高频原生命令:** 读取 Drive 文件夹清单时使用 `drive files list`,必须按 [`references/lark-drive-files-list.md`](references/lark-drive-files-list.md) 的模板通过 `--params` 传 `folder_token` / `page_token`,并手动处理分页;不要把 `--page-all` 输出直接交给 JSON 解析脚本。
### files
- `copy` — 复制文件
- `create_folder` — 新建文件夹
- `list` — 获取文件夹下的清单
- `list` — 获取文件夹下的清单;使用前阅读 [`references/lark-drive-files-list.md`](references/lark-drive-files-list.md)
- `patch` — 修改文件标题
### file.comments

View File

@@ -0,0 +1,158 @@
# drive files list原生 API读取 Drive 文件夹清单)
`drive files list` 是原生 API 命令,不是 shortcut。它用于读取 Drive 根目录或某个 Drive 文件夹的直接子项如果要递归盘点目录树Agent 必须基于返回的子文件夹 token 继续调用本命令。
## 什么时候使用
| 场景 | 是否使用 | 说明 |
|------|----------|------|
| 盘点一个已确认的 Drive 文件夹树 | 使用 | 从目标 `folder_token` 开始递归列取 |
| 盘点用户明确确认的 Drive 根目录 | 使用 | 第一层用空 `folder_token`,子文件夹继续按普通文件夹递归 |
| 验证移动 / 创建后的实际位置 | 使用 | 读取目标目录直接子项,再按需递归验证 |
| 根据关键词、标题、时间、owner 找资源 | 不使用 | 优先用 `drive +search` |
| 读取 Docx 正文内容 | 不使用 | 用 `docs +fetch --api-version v2` |
| 读取 Sheet / Base 内部数据 | 不使用 | 切到 `lark-sheets` / `lark-base` |
## 标准命令模板
读取普通文件夹:
```bash
lark-cli drive files list \
--params '{"folder_token":"<folder_token>","page_size":200}' \
--format json
```
继续翻页:
```bash
lark-cli drive files list \
--params '{"folder_token":"<folder_token>","page_size":200,"page_token":"<PAGE_TOKEN>"}' \
--format json
```
读取当前用户 Drive 根目录的直接子项:
```bash
lark-cli drive files list \
--params '{"folder_token":"","page_size":200}' \
--format json
```
也可以省略 `folder_token` 字段来请求根目录,但在 Agent 编排中建议显式传空字符串,避免把“忘记传参数”和“确认请求根目录”混在一起。
## 参数规则
1. `folder_token` 必须放在 `--params` JSON 里;不要使用不存在的 `--folder-token` flag。
2. `page_token` 必须放在 `--params` JSON 里;不要依赖 shell 变量拼接不完整的 JSON。
3. `page_size` 建议显式设置为 `200`。如果服务端或环境返回参数错误,再降级到服务端允许的值,并记录降级原因。
4. 调用前如果不确定字段结构,先运行 `lark-cli schema drive.files.list` 查看 `--params` 结构。
## 返回结构与解析
`--format json` 输出中Agent 只使用 `data` 中符合 `schema drive.files.list` 的 API 返回字段。
常用字段:
| 字段 | 用途 |
|------|------|
| `data.files` | 当前页直接子项列表 |
| `data.has_more` | 当前目录是否还有下一页 |
| `data.next_page_token` | 下一页 token`has_more=true` 时放回 `--params.page_token` |
| `data.files[].type` | 文件类型;等于 `folder` 时可递归 |
| `data.files[].token` | 当前资源 token文件夹递归时作为下一层 `folder_token` |
| `data.files[].name` | 生成路径和展示标题 |
| `data.files[].url` | 资源浏览器链接 |
| `data.files[].owner_id` | 资源所有者 |
| `data.files[].created_time` / `data.files[].modified_time` | 创建 / 更新时间 |
字段名以 `schema drive.files.list` 为准。Agent MUST 以实际返回为准;如果字段缺失,先用 `schema drive.files.list` 或一页样本确认结构,不要猜测。
## 根目录语义
1. `folder_token` 为空字符串或省略时,请求的是当前调用用户的 Drive 根目录直接子项。
2. 根目录返回值不是递归结果;不能把根目录第一页或直接子项数量当作整个云空间资源总量。
3. 根目录只作为目录树起点。返回的子文件夹必须用其自己的 `folder_token` 继续调用 `drive files list`
4. 根据 schema 描述,根目录第一层清单不支持分页且不返回快捷方式;不要基于根目录响应推断子文件夹内容、根目录第一层快捷方式或无法分页的根目录剩余项已经被覆盖。
## 递归盘点规则
1. 只对返回项中的 `folder` 类型继续递归。
2. 每个目录独立维护分页状态;一个目录的 `page_token` 不可复用于其他目录。
3. 对每个目录持续请求,直到返回 `has_more=false`。非根目录的普通文件夹清单可能返回 `type=shortcut` 条目;不要假设这些条目会携带 `shortcut_info` 目标信息。
4. 递归过程中生成稳定 `path`;不要只保存标题,否则同名资源无法区分。
5. URL、owner、创建时间和更新时间优先使用 `files.list` 返回字段;如果字段缺失或需要批量补齐,再使用 `drive metas batch_query`。不要从标题或路径猜元数据。
6. 深度、数量、每目录页数等限制只能作为内部批次 checkpoint不能作为递归完成条件。
7. 达到深度 checkpoint 时,把更深层子文件夹加入 continuation queue并在下一批从这些子文件夹继续保留原始 `path`
8. 达到数量 checkpoint 时,保存当前目录、当前页 token、剩余目录队列和已收集资源计数并立即继续下一批不要进入分析或规划阶段。
### 递归算法
Agent 盘点 Drive 文件夹树时,按以下顺序执行:
1. 初始化待处理队列,放入起点目录:
- 普通文件夹:`{folder_token:"<folder_token>", path:"<folder_name>"}`
- Drive 根目录:`{folder_token:"", path:""}`
2. 从队列取出一个目录,请求第一页。
3.`(folder_token, page_token)` 生成当前页 key同一页 key 只允许追加一次,避免 retry 时重复计数。
4.`data.files` 取当前页直接子项,按 `dedupe_key` 去重后生成 `path` 并加入结果集。
5. 如果新追加的子项是 `folder`,把子文件夹 token、子路径和 depth 加入队列。
6. 如果 `has_more=true`,取 `data.next_page_token` 继续请求同一目录下一页。
7. 同一目录分页结束后,再处理队列中的下一个目录。
8. 如果达到深度、数量或每目录页数 checkpoint把当前目录 / 页 token / 剩余队列 / 已访问页 key / dedupe key 写入 continuation queue并继续下一批。
9. 普通队列和 continuation queue 都为空,且没有分页 blocker 时,才可以认为本次确认范围盘点完成。
简化伪代码:
```text
queue = [root_or_start_folder]
visited_pages = set()
dedupe_keys = set()
while queue not empty:
folder = queue.pop()
page_token = folder.page_token or ""
retry_without_token = 0
while true:
page_key = (folder.folder_token, page_token or "first")
page = drive files list(folder.folder_token, page_token)
if page_key not in visited_pages:
append only files whose dedupe_key is not in dedupe_keys
enqueue newly appended child folders with folder_token, path, and depth
add page_key to visited_pages
if page.has_more != true:
break
next = page.next_page_token
if next is empty:
retry_without_token += 1
if retry_without_token >= 3:
record pagination blocker for folder
break
continue
page_token = next
retry_without_token = 0
```
## 分页与异常
1. 默认手动处理 `has_more` 和返回中的 `next_page_token`
2. 不要使用 `--page-all` 作为脚本 JSON 解析输入;自动翻页输出可能不适合直接 `json.loads`
3. 如果 `has_more=true` 但没有可用的 `next_page_token`,重试同一页最多 3 次。
4. 重试后仍无 continuation token 时,记录受影响的目录和 pagination blocker停止扩展该目录不要无限循环也不要宣称该目录已完整覆盖。
5. 如果触发深度、数量或每目录页数限制,把它视为批处理 checkpoint在确认范围内继续下一批而不是把当前结果说成完整。
6. 不要因为达到 `max_depth=3``max_items=500` 或类似单批阈值就结束盘点;只有队列耗尽或遇到权限 / API / 工具预算 blocker 才能结束当前确认范围的盘点。
## JSON 解析规则
1. stdout 是数据通道。脚本解析 JSON 时只读取 stdout。
2. stderr 可能包含刷新 token、进度、warning 或其他提示;不要把 stderr 合并进 JSON 输入,例如不要用 `2>&1` 后再 `json.loads`
3. 使用 `--format json` 保持 stdout 为结构化 JSON解析 Drive 文件清单时只读取 `data.files` / `data.has_more` / `data.next_page_token` 等 schema 字段。
4. 不要用根目录响应数量或当前页数量推断递归总量;递归总量必须由实际遍历并去重后的资源集合计算。
## 常见错误
| 错误用法 | 问题 | 正确做法 |
|----------|------|----------|
| `lark-cli drive files list --folder-token <token>` | `files.list` 不提供 `--folder-token` flag | 使用 `--params '{"folder_token":"<token>"}'` |
| 根目录返回 N 项就认为云空间只有 N 项 | 根目录只返回直接子项,不是递归结果 | 对返回的子文件夹继续递归 |
| `--page-all \| python json.loads(...)` | 自动翻页输出不适合作为单个 JSON 对象解析 | 手动使用 `page_token` 翻页并逐页解析 |
| `cmd 2>&1` 后解析 JSON | stderr 提示污染 JSON 输入 | 只解析 stdoutstderr 作为日志处理 |

View File

@@ -24,7 +24,8 @@ MUST:
4. Switch to `lark-sheets` / `lark-base` only when sheet / bitable title and path are insufficient.
5. Record read evidence for classification.
6. Continue reading low-confidence resources in internal batches until all supported low-confidence resources in the current inventory are processed or a blocker occurs.
7. Output progress / summary without asking the user to continue between batches.
7. Apply `Analysis Progress Reporting`.
8. Output progress / summary without asking the user to continue between batches.
Exit: low-confidence items are classified or marked `needs_review=true`.
@@ -93,6 +94,30 @@ Output this summary:
- After every 50 processed low-confidence resources.
- Once after low-confidence reading finishes.
- About every 60 seconds during long-running reads, even if fewer than 50 additional resources were processed.
### Analysis Progress Reporting
Applies to `CONTENT_READ`, `ISSUE_ANALYSIS`, and `RULE_GENERATION`.
Rules:
1. For `CONTENT_READ`, use `Low-Confidence Read Summary` as the progress report format.
2. For `ISSUE_ANALYSIS`, if analysis runs longer than about 60 seconds, output progress about every 60 seconds with current stage, processed resource count when known, detected problem type count when known, and the next analysis step.
3. For `RULE_GENERATION`, if classification rule or target-tree generation runs longer than about 60 seconds, output progress about every 60 seconds with current stage, classified item count when known, unresolved item count when known, and target category / path count when known.
4. Progress reports MUST be factual and stage-specific. Do not output generic "still running" messages without counts or the current stage.
5. Do not ask the user to continue between internal batches unless auth, permission, API, target scope, or environment blockers occur.
6. Do not expose internal chain-of-thought, raw tokens, or intermediate rule drafts.
Examples:
```text
分析进度:正在归纳整理问题,已处理 <processed_count>/<resource_count> 项资源,已识别 <problem_type_count> 类问题。继续生成整理思路,不会执行移动或创建。
```
```text
规则生成进度:正在生成分类规则和目标目录,已归类 <classified_count> 项,待人工确认 <needs_review_count> 项。继续生成完整计划前置数据。
```
## State: ISSUE_ANALYSIS
@@ -103,8 +128,9 @@ MUST:
1. Detect problems from organization perspective only. Do not generate research conclusions.
2. Generate an organization approach based on inventory, low-confidence read evidence, and detected problems.
3. Include how non-reused source containers will be handled after their contents are moved.
4. Output `Inventory And Organization Approach Decision`.
5. Stop and wait for the user to confirm the approach before `RULE_GENERATION`.
4. Apply `Analysis Progress Reporting`.
5. Output `Inventory And Organization Approach Decision`.
6. Stop and wait for the user to confirm the approach before `RULE_GENERATION`.
Problem rules:
@@ -161,10 +187,10 @@ MUST output evidence count or example paths. Do not output only abstract judgmen
是否基于这个整理思路生成目标目录和移动 / 创建计划?
你可以选择:
A. 基于这个思路生成目标目录和计划
B. 调整整理思路
C. 查看问题详情
D. 取消本次整理
1. 基于这个思路生成目标目录和计划
2. 调整整理思路
3. 查看问题详情
4. 取消本次整理
```
## State: RULE_GENERATION
@@ -181,7 +207,8 @@ MUST:
6. For non-reused source containers, ensure `target_tree` includes a source-container cleanup target, defaulting to `待人工确认/待清理旧目录`, unless the user explicitly asks to keep source containers in place.
7. Ensure target tree can contain every planned `target_path`.
8. Ensure the target tree contains a manual confirmation target named `待人工确认` unless the user explicitly provides an equivalent name.
9. Continue to `PLAN_GENERATION` without a separate target-tree-only confirmation.
9. Apply `Analysis Progress Reporting`.
10. Continue to `PLAN_GENERATION` without a separate target-tree-only confirmation.
### Classification

View File

@@ -10,8 +10,9 @@ Before executing rules in this file:
1. Follow [`../../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) for identity, auth, and permission handling.
2. For Wiki / personal library targets, follow [`../../lark-wiki/SKILL.md`](../../lark-wiki/SKILL.md).
3. For Drive search targets, follow [`lark-drive-search.md`](lark-drive-search.md).
4. For URL / token inspection, follow [`lark-drive-inspect.md`](lark-drive-inspect.md) and [`../../lark-wiki/references/lark-wiki-node-get.md`](../../lark-wiki/references/lark-wiki-node-get.md).
3. For Drive folder inventory, follow [`lark-drive-files-list.md`](lark-drive-files-list.md).
4. For Drive search targets, follow [`lark-drive-search.md`](lark-drive-search.md).
5. For URL / token inspection, follow [`lark-drive-inspect.md`](lark-drive-inspect.md) and [`../../lark-wiki/references/lark-wiki-node-get.md`](../../lark-wiki/references/lark-wiki-node-get.md).
## State: PARSE_SCOPE
@@ -87,6 +88,10 @@ Clarification template:
请确认是否按这个范围继续?
```
Scope confirmation is user-facing. It MUST confirm only the business scope, environment / profile, identity, and whether write operations will run.
Do not display internal batching controls in scope confirmation, including `max_depth`, `max_items`, `page_size`, page tokens, retry counts, or `partial=true`. For example, when the user confirms Drive root, say the scope is the Drive root tree; do not append "recursive depth at most 3" or "at most 500 resources".
## State: INVENTORY
Entry: `target_scope` confirmed.
@@ -96,20 +101,57 @@ MUST:
1. Recursively list resources according to target type.
2. Generate `path` during traversal.
3. Normalize all results to `ResourceItem`.
4. Track pagination, depth, and item limits.
5. Set `partial=true` when limits are hit.
6. Output `Inventory Summary`.
7. Continue to `CONTENT_READ` without asking the user unless auth, permission, API, target scope, or environment blockers occur.
4. Track pagination, depth, item limits, and continuation checkpoints.
5. Treat pagination, depth, item, and per-folder page limits as batching checkpoints; continue inventory in the confirmed scope unless blocked.
6. Set `partial=true` only when inventory cannot continue because of auth, permission, API / pagination failure after retries, API coverage limitations, tool budget, target scope, or environment blockers.
7. Apply `Inventory Progress Reporting`.
8. Output `Inventory Summary`.
9. Do not leave `INVENTORY` while `inventory_continuation_state` has queued folders, nodes, pages, or slices that can still be fetched.
10. Continue to `CONTENT_READ` without asking the user only after the confirmed scope is exhausted or blocked.
### Inventory Limits
### Inventory Batch Checkpoints
| Scope | Default Limit | If Limit Is Hit |
|-------|---------------|-----------------|
| Wiki recursion | `max_depth=3`, `max_items=500`; follow `lark-wiki-node-list` pagination | Set `partial=true`; list covered paths and suggested next first-level directories |
| Drive folder recursion | `max_depth=3`, `max_items=500`, max 10 pages per folder, `page_size=50` | Set `partial=true`; list folders not drilled into |
| Search discovery | `page_size=20`, `max_items=500`; continue pages until `has_more=false` or `max_items` is reached | Set `partial=true`; report collected_count, service_total when available, page_count, and continuation information |
| Scope | Internal Batch Checkpoint | Required Continuation |
|-------|---------------------------|-----------------------|
| Wiki recursion | `max_depth=3`, `max_items=500`; follow `lark-wiki-node-list` pagination | Record queued nodes / paths in `inventory_continuation_state` and immediately continue the next internal batch within the confirmed scope unless blocked |
| Drive folder tree | `max_depth=3`, `max_items=500`, max 10 pages per folder, `page_size=200` | Record queued folders / pages in `inventory_continuation_state` and immediately continue the next internal batch within the confirmed scope unless blocked |
| Search discovery | `page_size=20`, `max_items=500`; continue pages until `has_more=false` | Record remaining pages / slices in `inventory_continuation_state` and immediately continue the next internal batch within the confirmed scope unless blocked |
If the user explicitly asks for full processing, batch by first-level directory, Wiki space, or time window. Do not remove all limits in one run.
These checkpoints are pacing controls, not coverage limits. If the confirmed scope still has queued work after a checkpoint, continue with the next internal batch instead of presenting the current `resource_items` as final inventory or moving to content analysis.
When a depth checkpoint is reached, enqueue the child folders / nodes that would exceed the current batch depth; the next batch starts from those queued children with their original paths preserved. When an item checkpoint is reached, persist the current folder / node / page cursor plus the remaining queue, visited page keys, and resource dedupe keys, then continue from that checkpoint before analysis or planning.
If tool budget would be exceeded for a very large confirmed scope, stop only at that blocker, report that the inventory is incomplete, and suggest batching by first-level directory, Wiki space, or time window. Do not stop merely because a depth or item checkpoint was reached.
### Inventory Continuation Rules
1. Pagination, depth, item, and per-folder page limits are internal batching checkpoints.
2. When a checkpoint is reached, record `inventory_continuation_state` with `scope`, `queue`, `current_cursor`, `visited_page_keys`, `dedupe_keys`, and `blockers`; Drive queue entries MUST contain `folder_token`, `path`, `depth`, and `page_token`; Wiki queue entries MUST contain `space_id` / `node_token`, `path`, `depth`, and pagination cursor; search entries MUST contain query / filters and pagination cursor.
3. A depth checkpoint MUST enqueue deeper folders / nodes; it MUST NOT discard them or treat the current depth as final coverage.
4. An item-count checkpoint MUST persist the current cursor and queue; it MUST NOT transition to `CONTENT_READ`, `ISSUE_ANALYSIS`, or `PLAN_GENERATION` while fetchable work remains.
5. If `inventory_continuation_state` is missing, corrupt, or lacks required fields for the current scope, set `partial=true`, record the checkpoint blocker, and do not claim full coverage.
6. Do not set `partial=true` solely because a valid batching checkpoint was reached.
7. Set `partial=true` only when continuation is blocked by auth, permission, API / pagination failure after retries, API coverage limitations, tool budget, target scope, or environment blockers.
8. Do not claim full coverage until the continuation queue for the confirmed scope is exhausted or blocked.
### Inventory Progress Reporting
Inventory can be long-running when a Drive root, large folder tree, Wiki space, or broad search scope is confirmed.
Rules:
1. When inventory starts, output one concise stage notice with the confirmed scope type and the fact that no write operation will be executed.
2. If inventory runs longer than about 60 seconds, output progress about every 60 seconds.
3. Progress reports SHOULD include only fields that are currently known: scanned folders / nodes, collected resources, current depth, queued folders / nodes, current search page / slice, and current blocker if any.
4. When a batching checkpoint is reached and continuation will proceed automatically, report it as continuing inventory, not as a user action request.
5. Do not output filler such as "still running" without current counts or current stage.
6. Do not expose raw folder tokens, page tokens, retry logs, or `partial=true` unless the user explicitly asks to view inventory coverage details.
Example:
```text
盘点进度:已扫描 <scanned_container_count> 个目录 / 节点,收集 <resource_count> 项资源,队列剩余 <queued_container_count> 个目录 / 节点。继续盘点,不会执行移动或创建。
```
### Wiki Inventory Rules
@@ -120,11 +162,13 @@ If the user explicitly asks for full processing, batch by first-level directory,
### Drive Inventory Rules
1. Use CLI command family `drive files list` according to `lark-drive` API rules; its schema path is `drive.files.list`.
2. Recurse only into `folder` items.
3. Use `drive metas batch_query` when URL, owner, created time, or updated time is needed.
4. Continue pages by feeding `next_page_token` into request param `page_token`.
5. Prefer explicit `folder_token`; querying root with empty `folder_token` may return broad root data and may not paginate as expected.
1. Use `drive files list` according to [`lark-drive-files-list.md`](lark-drive-files-list.md); its schema path is `drive.files.list`.
2. Use the same Drive folder-tree traversal for Drive root and ordinary folders after the first request. Drive root differs only for the first-level request: it uses omitted or empty `folder_token`, does not support pagination, and does not return root-level shortcuts according to schema; returned child folders MUST still be listed by their own folder tokens like ordinary folders, and those ordinary folder lists may return `type=shortcut` entries. For a Drive root target, record this root-level shortcut coverage caveat, set `partial=true` only if the user requested full root-level shortcut coverage or root pagination cannot continue, and do not claim root-level shortcut coverage as complete.
3. Recurse only into `folder` items within the confirmed scope.
4. For each directory, continue pages manually by feeding the returned `next_page_token` into request param `page_token`. Do not rely on `--page-all` for inventory.
5. If a page returns `has_more=true` but no usable `next_page_token`, retry the same page request up to 3 times. If retries still cannot produce a continuation token, set `partial=true` for that directory and record the pagination blocker.
6. Use `drive metas batch_query` when URL, owner, created time, or updated time is needed.
7. Pagination blocker details such as `partial=true`, folder token, page token, and retry logs are internal by default. Do not show them to the user unless the user explicitly asks to view inventory coverage details.
### Search Inventory Rules
@@ -132,10 +176,11 @@ If the user explicitly asks for full processing, batch by first-level directory,
2. If a search result is a Wiki item and lacks `node_token`, resolve it with `drive +inspect` or `wiki +node-get` before dedupe.
3. If Wiki identity still cannot be resolved, keep the item, set `needs_review=true`, and record `needs_review_reason`.
4. For search scope, use `page_size=20` unless a lower value is required by the command.
5. Continue fetching pages until `has_more=false` or `max_items` is reached.
6. Do not stop at an arbitrary sample size such as first 5 pages unless the user explicitly asks for sampling or auth, permission, API, environment, or tool-budget blockers occur.
7. If `service_total` / result total is greater than collected items, set `partial=true` and show collected_count, service_total, page_count, and continuation information.
8. Do not present a partial search sample as complete inventory. Before generating a full organization plan from partial search results, ask whether to continue fetching more pages or proceed with sample-based planning.
5. Continue fetching pages until `has_more=false`.
6. If `max_items=500` is reached in one batch, record the current search cursor in `inventory_continuation_state` and continue the next internal batch without asking the user.
7. Do not stop at an arbitrary sample size such as first 5 pages unless the user explicitly asks for sampling or auth, permission, API, environment, or tool-budget blockers occur.
8. If `service_total` / result total is greater than collected items, treat it as continuation evidence: continue fetching when a cursor / page is available; set `partial=true` only if continuation is blocked.
9. Do not present a partial search sample as complete inventory. Before generating a full organization plan from partial search results, continue fetching available pages unless the user explicitly asked for sampling or a blocker prevents continuation.
## ResourceItem
@@ -179,7 +224,9 @@ ResourceItem rules:
## Inventory Summary
```text
已完成盘点。
已完成当前可覆盖范围盘点。
<仅当适用覆盖说明Drive 根目录第一层清单不返回快捷方式;本次盘点不包含根目录第一层快捷方式。根目录下子文件夹会按普通文件夹继续盘点,普通文件夹内返回的 `type=shortcut` 条目仍会被纳入资源清单。>
| 指标 | 数量 |
|------|------|
@@ -202,4 +249,5 @@ ResourceItem rules:
| Environment / profile is ambiguous | Ask user to confirm prod / BOE / PRE and profile | Do not cross environment boundaries |
| Missing API scope | Follow `lark-shared` permission handling and stop | Do not retry the same command repeatedly |
| Resource access denied | Stop and follow the main workflow `Permission Request Gate` | Do not request permission automatically or in batch |
| Pagination / depth / item limit reached | Set `partial=true`; record uncovered range and continuation command | Do not claim full coverage |
| Pagination / depth / item checkpoint reached | Record `inventory_continuation_state` and continue inventory in the confirmed scope | Do not set `partial=true` solely because a batching checkpoint was reached |
| Pagination cursor missing after retries / API pagination failure | Set `partial=true`; record the affected directory and blocker | Do not loop indefinitely or claim full coverage |

View File

@@ -24,7 +24,8 @@ MUST:
4. Apply `Plan Pagination`.
5. Set `active_plan_items` to the latest complete plan.
6. Keep complete plan internally even if only one page is displayed.
7. Output `Target Tree And Plan Overview` or requested plan page, then wait.
7. Apply `Plan Generation Progress Reporting`.
8. Output `Target Tree And Plan Overview` or requested plan page, then wait.
### Plan Generation
@@ -44,6 +45,25 @@ MUST:
| Target parent token unresolved | Keep plan item but block execution until token is resolved |
| Resource title is poor or inconsistent | Report the naming issue only; do not create rename or title-patch plan items |
### Plan Generation Progress Reporting
Plan generation can be long-running when `resource_items` is large or source-container parent / child move ordering is complex.
Rules:
1. If plan generation starts with more than 500 `resource_items`, output one concise start notice with the resource count and that no write operation is being executed.
2. If plan generation runs longer than about 60 seconds, output progress about every 60 seconds.
3. Progress reports SHOULD include only fields currently known: processed resource count, generated plan item count, create count, move count, source-container move count, review count, and current step.
4. Do not display unpaginated plan details as progress. Complete `plan_items` remain internal until the normal paginated output.
5. Do not ask the user to continue during plan generation unless auth, permission, API, target scope, or environment blockers occur.
6. Do not output filler such as "still running" without current counts or current step.
Example:
```text
计划生成进度:已处理 <processed_count>/<resource_count> 项资源,生成 <plan_item_count> 项计划,其中创建 <create_count> 项、移动 <move_count> 项。继续计算父子目录移动顺序,不会执行创建或移动。
```
## PlanItem
`PlanItem` is for internal execution. It may contain tokens and internal enums.
@@ -167,11 +187,11 @@ Confidence display map:
- 低置信度:<low_count> 项
你可以选择:
- 查看第 1 页明细
- 只看将创建的目录 / 节点
- 只看待人工确认项
- 只看高置信度移动项
- 进入执行确认
1. 查看第 1 页明细
2. 只看将创建的目录 / 节点
3. 只看待人工确认项
4. 只看高置信度移动项
5. 进入下一步:确认执行计划
```
If `total_count > 500`, say:
@@ -224,10 +244,10 @@ User-facing output:
说明:后续执行默认基于这份完整修正版计划,不是只执行刚才的修正项。
你可以选择:
A. 查看修正版计划总览
B. 查看本次修改涉及的资源
C. 进入执行确认
D. 继续调整
1. 查看修正版计划总览
2. 查看本次修改涉及的资源
3. 进入下一步:确认执行计划
4. 继续调整
```
If the user explicitly asks to execute only the corrected items, ask for confirmation before execution:
@@ -248,15 +268,15 @@ If the user explicitly asks to execute only the corrected items, ask for confirm
还有 <remaining_pages> 页未展示。
你可以回复:
- 继续看下一页
- 只看待人工确认项
- 只看低置信度项
- 进入执行确认
1. 继续看下一页
2. 只看待人工确认项
3. 只看低置信度项
4. 进入下一步:确认执行计划
```
## State: EXEC_CONFIRM
Entry: user asks to execute.
Entry: user asks to view execution confirmation or continue toward execution.
MUST:
@@ -284,17 +304,17 @@ Before execution confirmation, MUST show this notice:
When the user wants execution, ask for execution scope:
Execution confirmation options MUST be renumbered by currently available choices. Do not show disabled choices, and do not ask the user to reply with skipped letters.
Execution confirmation options MUST be numbered by currently available choices. Do not show disabled choices, and do not ask the user to reply with skipped numbers.
If a plan detail page is currently active:
```text
请确认执行范围:
A. 执行完整计划:<total_count> 项
B. 只执行当前页:<current_page_count> 项
C. 只执行高置信度项:<high_confidence_count> 项
D. 暂不执行,只保留方案
1. 执行完整计划:<total_count> 项
2. 只执行当前页:<current_page_count> 项
3. 只执行高置信度项:<high_confidence_count> 项
4. 暂不执行,只保留方案
本 workflow 只执行已确认范围内的创建、移动和必要的单资源权限申请;不会重命名任何资源。
```
@@ -304,9 +324,9 @@ If no plan detail page is currently active:
```text
请确认执行范围:
A. 执行完整计划:<total_count> 项
B. 只执行高置信度项:<high_confidence_count> 项
C. 暂不执行,只保留方案
1. 执行完整计划:<total_count> 项
2. 只执行高置信度项:<high_confidence_count> 项
3. 暂不执行,只保留方案
如需只执行某一页,请先查看计划明细页。

View File

@@ -89,7 +89,8 @@ Agent MUST maintain these internal fields during one workflow run:
| `environment_profile` | Current environment and CLI profile, such as prod / BOE / PRE and config profile |
| `identity` | `user` by default unless user explicitly asks for app / bot perspective |
| `resource_items` | Complete normalized resource list from discovery |
| `partial` | Whether inventory or content-read limits were hit |
| `partial` | Whether inventory or content read cannot fully continue because of auth, permission, API / pagination failure after retries, API coverage limitations, tool budget, or scope blockers; batching checkpoints alone are not partial |
| `inventory_continuation_state` | Structured checkpoint for continuing inventory batches within the confirmed scope. Must preserve `scope`, `queue`, `current_cursor`, `visited_page_keys`, `dedupe_keys`, and `blockers`; Drive queue entries carry `folder_token`, `path`, `depth`, and `page_token`; Wiki queue entries carry `space_id` / `node_token`, `path`, `depth`, and pagination cursor; search entries carry query / filters and pagination cursor. Missing or corrupt state is a blocker, not a completed inventory. |
| `low_confidence_items` | Items requiring mandatory partial content read |
| `issue_summary` | Problem types, counts, evidence paths, and suggested handling |
| `classification_rules` | Rules used to map resources to target paths |
@@ -211,6 +212,7 @@ Never request permission automatically, never batch permission requests, and nev
- [Rollback phase](lark-drive-workflow-knowledge-organize-rollback.md)
- [lark-shared](../../lark-shared/SKILL.md)
- [lark-drive](../SKILL.md)
- [lark-drive-files-list](lark-drive-files-list.md)
- [lark-drive-search](lark-drive-search.md)
- [lark-drive-inspect](lark-drive-inspect.md)
- [lark-drive-apply-permission](lark-drive-apply-permission.md)

View File

@@ -19,6 +19,8 @@ metadata:
| 编辑单个标题、文本块、图片或局部元素 | 优先块级替换/插入,不改页序 | `slides +replace-slide``lark-slides-replace-slide.md` |
| 读取或分析已有 PPT | 解析 slides/wiki token回读全文或单页 XML保存 `xml_presentation_id``slide_id``revision_id` | `xml_presentations.get``xml_presentation.slide.get` |
| 上传或使用图片 | 先上传为 `file_token`,禁止直接写 http(s) 外链 | `slides +media-upload`,或 `+create --slides``@./path` 占位符 |
| 在 slide 中绘制柱/条/折线/面积/雷达/饼等有数据序列的图表 | 使用原生 `<chart>` 元素 | `xml-schema-quick-ref.md` |
| 在 slide 中绘制流程图、时序图、架构图、散点图、漏斗图或装饰图案 | 必须先用 Read 工具读取参考文档,再生成 `<whiteboard>` 元素 | [`lark-slides-whiteboard.md`](references/lark-slides-whiteboard.md) |
| 用户提到模板、主题、版式 | 先检索模板,再摘要,必要时裁切骨架 | `template_tool.py search → summarize → extract` |
| 创建失败、空白页、3350001、布局异常 | 先回读状态,再按排障清单修复,不假设原操作原子成功 | `troubleshooting.md``validation-checklist.md` |
@@ -80,6 +82,7 @@ lark-cli auth login --domain slides
- 创建:[`lark-slides-create.md`](references/lark-slides-create.md)
- 编辑:[`lark-slides-edit-workflows.md`](references/lark-slides-edit-workflows.md)、[`lark-slides-replace-slide.md`](references/lark-slides-replace-slide.md)
- 图片:[`lark-slides-media-upload.md`](references/lark-slides-media-upload.md)
- 流程图 / 时序图 / 架构图 / 装饰图案:[`lark-slides-whiteboard.md`](references/lark-slides-whiteboard.md)
- 模板:[`template-catalog.md`](references/template-catalog.md)、[`scripts/template_tool.py`](scripts/template_tool.py)
- 排障:[`troubleshooting.md`](references/troubleshooting.md)
- 完整协议:[`slides_xml_schema_definition.xml`](references/slides_xml_schema_definition.xml)
@@ -183,7 +186,7 @@ lark-cli slides xml_presentation.slide create \
--data "$(jq -n --arg content '<slide xmlns="http://www.larkoffice.com/sml/2.0">
<style><fill><fillColor color="BACKGROUND_COLOR"/></fill></style>
<data>
在这里放置 shape、line、table、chart 等元素
在这里放置 shape、line、table、chart、whiteboard 等元素
</data>
</slide>' '{slide:{content:$content}}')"

View File

@@ -1,13 +1,13 @@
# Asset Planning
新建演示文稿或大幅改写页面时,在写入 `slide_plan.json` 前后都可以参考本文件。目标是让 agent 主动识别有价值的图、图标、图表、截图或示意图需求,同时保持 deck 在没有真实素材时也能完整执行。
新建演示文稿或大幅改写页面时,在写入 `slide_plan.json` 前后都可以参考本文件。目标是让 agent 主动识别有价值的图、图标、图表、流程图、时序图、架构图、装饰图案、截图或示意图需求,同时保持 deck 在没有真实素材时也能完整执行。
本文件只定义轻量资产规划。不要把它理解成素材采集流程。
## Core Rules
- `asset_need` is metadata only. It can guide page design, but it must not require web search, local download, media upload, or external tools.
- Every planned asset must include a fallback visual plan so the slide can be generated with XML shapes, text, arrows, tables, simple charts, or placeholder regions.
- Every planned asset must include a fallback visual plan so the slide can be generated with XML shapes, text, arrows, tables, simple charts, whiteboard diagrams, or placeholder regions.
- Asset needs must serve the page's `key_message` and `visual_focus`. Do not add decorative assets that do not clarify the page.
- Prefer a few high-value asset plans over one asset on every page. For a 6-page technical or business deck, plan assets on at least 3 pages when the content allows.
- If a real local asset already exists or the user provides one, it can be used through the normal media-upload workflow. Still keep `fallback_if_missing` in the plan.
@@ -22,7 +22,7 @@ Use an object for one planned asset, or an array when a page genuinely needs mul
"asset_type": "architecture_diagram",
"purpose": "Show how API gateway, planner, XML generator, and Slides API interact.",
"suggested_query": "agent native slides runtime architecture diagram",
"fallback_if_missing": "Draw grouped boxes and arrows with XML shapes; use labels instead of an image."
"fallback_if_missing": "Draw grouped boxes connected by arrows with short labels."
}
```
@@ -43,7 +43,7 @@ For a page without a meaningful asset need, use:
- `architecture_diagram`: system components, data flow, dependency map, or model structure.
- `icon`: small semantic symbol for a concept, step, role, or status.
- `logo`: brand, product, team, or customer mark.
- `chart`: line, bar, pie, funnel, scatter, or chart-like data visual.
- `chart`: line, bar, pie, radar, area, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
- `infographic`: composed visual explanation, usually combining labels, numbers, and simple shapes.
- `screenshot`: product UI, terminal output, workflow state, or page capture.
- `flow_diagram`: process, sequence, decision tree, or mechanism diagram.
@@ -118,7 +118,7 @@ Business comparison page:
When generating XML:
1. If an asset exists and the workflow supports it, place it in the planned visual region.
2. If no asset exists, immediately render `fallback_if_missing` with XML-native shapes, text, lines, arrows, tables, or chart-like elements.
2. If no asset exists, immediately render `fallback_if_missing` with XML-native shapes, text, lines, arrows, tables, whiteboard diagrams, or chart-like elements.
3. Size the fallback to satisfy `visual_focus`; it should be a real page element, not a tiny decoration.
4. Keep text-density limits. Do not compensate for missing assets by adding long bullet text.
5. After creation, fetch the presentation and verify asset pages are not blank and that each planned fallback is visible when no real asset was used.

View File

@@ -88,10 +88,11 @@ lark-cli slides +replace-slide --as user \
| `<table>` | 表格 | 整表替换会**重建内部 td id**,旧 td block_id 立即失效 |
| `<td>` | 单元格局部替换 | 只能 `block_replace`,不能 `block_insert``block_id` 必须是最新 `slide.get` 拿到的 td id |
| `<chart>` | 图表line/bar/column/pie/area/radar/combo | 必须嵌 `<chartPlotArea>` + `<chartData>` + `<dim1>/<dim2>/<chartField>` |
| `<whiteboard>` | 画板SVG 或 Mermaid | 内嵌 `<svg>``<mermaid>``slide.get` 返回结构不含内部数据,但可直接写完整新 XML 做 `block_replace` 覆盖;详见 [`lark-slides-whiteboard.md`](lark-slides-whiteboard.md) |
**不可作为根元素**
- `<video>` / `<audio>` / `<whiteboard>` —— SML 2.0 没有这个原生元素;`<undefined type="video|audio|whiteboard">` 是**导出时**的占位符(服务端遇到不支持的类型时用它代替),**不能写入**。尝试 insert/replace 都会返回 3350001。
- `<video>` / `<audio>` —— SML 2.0 没有这个原生元素;`<undefined type="video|audio">` 是**导出时**的占位符(服务端遇到不支持的类型时用它代替),**不能写入**。尝试 insert/replace 都会返回 3350001。
### 最小 XML 片段JSON 嵌入时记得把 `"` 转义成 `\"`

View File

@@ -0,0 +1,330 @@
# Whiteboard 画板元素
`<whiteboard>` 放在 `<data>` 内,内部可放 **SVG****Mermaid**,用于绘制流程图、时序图、架构图、散点图、漏斗图、自定义图标、装饰图案等 `<chart>``<shape>` 难以覆盖的视觉内容。
> 前置条件:使用本文档前先阅读 [lark-slides SKILL.md](../SKILL.md)。
---
## `<chart>` 还是 `<whiteboard>`
**先判断内容类型,再进入本文档:**
| 场景 | 推荐元素 |
|------|---------|
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
| 散点图、漏斗图(`<chart>` 不支持) | `<whiteboard>` SVG |
| 流程图、时序图、架构图、类图、ER 图等拓扑图 | `<whiteboard>` Mermaid 或 SVG |
| 自定义图标、徽标、示意性图形(需要 path/polygon 精确控制) | `<whiteboard>` SVG |
| 进度条、波浪背景、装饰图案、像素级自定义可视化 | `<whiteboard>` SVG |
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG 手绘——原生渲染更省力且质量更高。
---
## whiteboard 公共属性
| 属性 | 必需 | 说明 |
|------|------|------|
| `topLeftX` | 是 | 左上角 X 坐标slide 坐标系slide 默认宽 960 |
| `topLeftY` | 是 | 左上角 Y 坐标slide 坐标系slide 默认高 540 |
| `width` | 是 | 画板宽度(像素) |
| `height` | 是 | 画板高度(像素) |
> SVG 模式下 `<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"`;内容大小由子元素包围盒决定,`width`/`height`/`viewBox` 不影响渲染(仅当元素属性使用百分比值时需要 `viewBox` 提供计算基准。Mermaid 模式不需要额外属性。
SVG 内的坐标相对于 whiteboard 自身左上角0,0与 slide 坐标系无关。
---
## SVG 还是 Mermaid
选择分两步:**先看图表类型,再看当前模型身份**。
### 第一步:图表类型优先判断
以下类型**推荐 Mermaid**,自动布局、代码简洁;如需精确匹配品牌配色或自定义节点样式,可改用 SVG
| 图表类型 | Mermaid 关键字 |
|----------|--------------|
| 流程图、决策树、架构图 | `flowchart TD` / `flowchart LR` |
| 时序图 | `sequenceDiagram` |
| 类图 | `classDiagram` |
| 饼图 | `pie` |
| 甘特图 | `gantt` |
| 状态图 | `stateDiagram-v2` |
| 思维导图 | `mindmap` |
| ER 图 | `erDiagram` |
### 第二步:数据图表与装饰元素按模型身份选路径
上表以外的场景散点图、漏斗图、进度条、时间线、波浪背景、星点纹理等需要精确控制坐标和配色SVG 表达力更强,但各模型生成 SVG 的能力有差异:
| 模型身份 | 路径 |
|----------|------|
| Claude / Gemini / GPT / GLM | **SVG** — 精确控制坐标、颜色、透明度 |
| Doubao / Seed / Other | **Mermaid** — 用 `pie``gantt` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
> **先自报身份再选路径**:在决定使用 SVG 之前,确认当前模型属于哪一类。不要跳过这一步。
---
## 模式一SVG
### ⚠️ 设计品质要求
在 slide 里嵌入 `<whiteboard>` 的目的是**提升视觉质量**,不是把数字堆进去。
- **不要只用矩形加文字应付**:通篇纯白底色 + 方块 + 黑字等于白做,这是不及格输出
- **数据图表必须有坐标系**:坐标轴、网格线、数值标注缺一不可,不要只画柱子或点
- **字号必须有层级**:标题 ≠ 标签 ≠ 数值,混用同一字号会消灭视觉焦点
- **配色要与 slide 主题呼应**:深色 slide 背景下图表用透明底或深色卡片;浅色背景下避免再加纯白底块
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、折线面积、点装饰等细节拉开与默认模板的差距
- **写 SVG 前先判断背景亮度**:背景亮度 < 30% 时,装饰元素"对比不足"比"过强"危害更大,宁重勿轻;
- **装饰层次用亮度跳跃,不用线性叠透明度**`α=0.04→0.08→0.12` 的等差递增在深色底上几乎看不出差异(相邻层亮度差 ≈20正确做法是非线性跳跃如 `0.10→0.40→0.70→1.0`,相邻层亮度差 ≥60。
### 语法
```xml
<whiteboard width="400" height="300" topLeftX="500" topLeftY="120">
<svg xmlns="http://www.w3.org/2000/svg">
<rect x="50" y="50" width="80" height="200" rx="4" fill="rgba(59,130,246,0.85)"/>
<text x="90" y="270" text-anchor="middle" font-size="12" fill="rgba(100,116,139,1)">ABC</text>
</svg>
</whiteboard>
```
`<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"``width`/`height`/`viewBox` 无需填写,若元素属性使用百分比值则需额外声明 `viewBox`
### ⚠️ 渲染包围盒规则
whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内容区域,自适应缩放到容器。
`<svg>` 上的 `width``height``viewBox` 不影响内容区域的计算,但 `viewBox` 有一个实际用途:**为百分比属性提供计算基准**。若元素使用 `width="50%"` 等百分比值,必须声明 `viewBox` 才能正确解析;绝对坐标元素则无需关心。推荐统一使用绝对坐标,避免引入百分比依赖。
### 支持的 SVG 元素
| 元素 | 说明 | 典型用途 |
|------|------|---------|
| `<rect>` | 矩形,支持 `rx` 圆角 | 柱图、卡片、进度条 |
| `<circle>` | 圆 | 节点、装饰点、环形图 |
| `<ellipse>` | 椭圆 | 自定义轮廓图形 |
| `<line>` | 直线 | 坐标轴、分隔线 |
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、折线、弧形 |
| `<text>` | 文本,支持中文 | 标签、数值 |
| `<polygon>` | 多边形 | 箭头、星形、面积填充 |
| `<g>` | 分组 | 批量变换、语义分组 |
| `<linearGradient>` | 线性渐变定义,配合 `fill="url(#id)"` 使用 | 渐变背景、渐变填充 |
**颜色:** 统一用 `rgba(R,G,B,A)`,对深浅背景都友好。
**虚线:** `stroke-dasharray="4,4"` 用于网格线 / 坐标轴。
**变换:** `transform="translate(x,y)"` / `rotate(deg cx cy)` / `scale(n)` 均支持。
---
### 元素计算
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围不限于数据图表——装饰性点阵、等间距圆、重复图案同样适用。
> **主动去算**:写 SVG 之前先运行脚本,把输出当注释贴在 `<svg>` 开头,再照着填坐标。估值几乎每次都需要反复调整,跳过这步反而更慢。
**数据图表(柱状图范式)**
```python
W, H = 360, 260
origin_x, origin_y = 50, 216 # 左下角SVG Y 轴向下
cw, ch = 290, 184
data, y_max = [120, 160, 90], 200
bar_w = int(cw / len(data) * 0.62)
for i, v in enumerate(data):
cx = round(origin_x + (i + 0.5) * cw / len(data))
y = round(origin_y - v / y_max * ch)
print(f"bar-{i}: x={cx - bar_w//2} y={y} w={bar_w} h={round(origin_y - y)}")
```
折线图:`x = origin_x + i/(n-1)*cw``y = origin_y - (v-y_min)/(y_max-y_min)*ch`
**装饰性元素(等间距范式)**
```python
n, total_w, cy, r = 8, 340, 40, 4
step = total_w / (n - 1)
for i in range(n):
print(f"circle-{i}: cx={round(i * step)} cy={cy} r={r}")
```
**最大包围盒 → whiteboard 尺寸**
所有元素坐标算完后,汇总出整体包围盒,直接作为 whiteboard 的 `width`/`height`
```python
# 每个元素登记 (x, y, w, h),含 stroke 外扩
elements = [
(10, 20, 80, 160), # bar-0
(107, 10, 80, 170), # bar-1
(204, 40, 80, 140), # bar-2
(0, 0, 300, 1), # x-axis
]
xs = [x for x, y, w, h in elements]
ys = [y for x, y, w, h in elements]
x2 = [x + w for x, y, w, h in elements]
y2 = [y + h for x, y, w, h in elements]
wb_w = max(x2) - min(xs)
wb_h = max(y2) - min(ys)
print(f"whiteboard width={wb_w} height={wb_h}")
```
输出即 `<whiteboard width=... height=...>` 的值,无需手动估算。
---
### 布局模式
**全屏装饰层**
```xml
<whiteboard width="960" height="540" topLeftX="0" topLeftY="0">
<svg xmlns="http://www.w3.org/2000/svg">
...
</svg>
</whiteboard>
```
> ⚠️ 全屏装饰 whiteboard 必须放在所有 `<shape>` / `<img>` / `<table>` 之前否则会遮挡文字内容。XML 中元素位置越靠后,渲染层级越高。
**侧栏图表(与文字 shape 并排)**
```xml
<!-- 左侧文字 -->
<shape type="text" topLeftX="60" topLeftY="120" width="500" height="340">...</shape>
<!-- 右侧图表 -->
<whiteboard width="340" height="340" topLeftX="580" topLeftY="120">
<svg xmlns="http://www.w3.org/2000/svg">
...
</svg>
</whiteboard>
```
**底部装饰条**
```xml
<whiteboard width="960" height="100" topLeftX="0" topLeftY="440">
<svg xmlns="http://www.w3.org/2000/svg">
...
</svg>
</whiteboard>
```
---
### 禁止使用的 SVG 特性
以下特性在 slide `<whiteboard>` 渲染端不支持或行为不可预测,必须避免:
| 禁止 | 原因 | 替代方案 |
|------|------|---------|
| `<radialGradient>` | 渲染失败 | 用 `<linearGradient>``rgba()` 透明度模拟深浅层次 |
| `<filter>`(阴影、模糊等) | 渲染失败 | 用半透明 `<rect>` 叠加模拟阴影 |
| `<clipPath>` / `<mask>` | 渲染失败 | 调整元素坐标和尺寸自然裁切 |
| `<pattern>` | 渲染失败 | 手动铺 `<circle>` / `<rect>` 点阵 |
| `skewX` / `skewY` / `matrix(...)` | 空间扭曲,降级渲染 | 用 `rotate` + `translate` 替代 |
| `<image>` 外链 URL | 不支持外链 | 先上传得到 file_token再用 `<img>` 元素 |
---
## 模式二Mermaid
### 语法
```xml
<whiteboard topLeftX="72" topLeftY="60" width="816" height="360">
<mermaid>
<![CDATA[
flowchart TD
A[检查 lark-cli 与 jq] --> B[编写每页 slide XML]
B --> C[通过 jq 生成 slides JSON]
C --> D[执行 slides +create]
D --> E[读取 xml_presentation_id]
E --> F[回读并验证创建结果]
]]>
</mermaid>
</whiteboard>
```
**关键点:**
- 内容用 `<![CDATA[...]]>` 包裹——Mermaid 语法里的 `[``>``-->` 是 XML 特殊字符CDATA 避免转义问题
- whiteboard 只需 `topLeftX``topLeftY``width``height`
### 支持的 Mermaid 图表类型
| 类型 | 关键字 | 适用场景 |
|------|--------|---------|
| 流程图 | `flowchart TD` / `flowchart LR` | 业务流程、决策树、工作流 |
| 时序图 | `sequenceDiagram` | 系统交互、API 调用链 |
| 甘特图 | `gantt` | 项目计划、里程碑 |
| 饼图 | `pie` | 占比数据 |
| 类图 | `classDiagram` | 对象关系、架构设计 |
| ER 图 | `erDiagram` | 数据库结构 |
| 状态图 | `stateDiagram-v2` | 状态机、生命周期 |
| 思维导图 | `mindmap` | 主题梳理、知识架构 |
| 用户旅程 | `journey` | 用户体验路径 |
### Mermaid 布局建议
Mermaid 图表会自动撑满 whiteboard 区域。建议:
- 流程图留足高度,节点较多时适当增加 height比如 400-480
- 避免一页放超过 15 个节点,内容太密时考虑分页
- 推荐尺寸参考:
| 图表类型 | 建议 width | 建议 height |
|---------|-----------|------------|
| 流程图5-8 节点) | 720-816 | 300-400 |
| 时序图3-5 参与者) | 720-816 | 320-420 |
| 饼图 | 500-600 | 300-360 |
| 甘特图 | 816 | 280-360 |
| 思维导图 | 816 | 380-480 |
---
## 注意事项 & 已知问题
### z-orderSVG 模式)
whiteboard 在 XML 中的位置决定渲染层级:在 shape 前 → 在下层;在 shape 后 → 在上层。全屏装饰 whiteboard 应放在所有 shape 之前。
### Mermaid CDATA 必要性
Mermaid 语法包含 `[``>``-->`,不用 CDATA 直接写会破坏 XML 解析。始终使用 `<![CDATA[ ... ]]>`
---
## 快速自检清单
**SVG 模式——结构检查:**
- [ ] `<svg>` 声明了 `xmlns="http://www.w3.org/2000/svg"`
- [ ] whiteboard 的 `width`/`height` 由所有元素的最大包围盒(含 stroke 外扩)计算得出,不手动估值
- [ ] `topLeftX + width ≤ 960``topLeftY + height ≤ 540`
- [ ]`<radialGradient>` / `<filter>` / `<clipPath>`
- [ ] 文字 `y` 坐标为 baseline 位置,最小值 ≥ font-size避免被裁切
**SVG 模式——视觉品质检查:**
- [ ] 坐标轴、网格线、数值标注齐全,没有"裸柱子"或"裸折线"
- [ ] 字号有层级:标题 > 数值 > 轴标签,非全部相同
- [ ] 单一数据系列用同一颜色,多系列用不同颜色且对比充足
- [ ] 轴标签与图表元素互不遮挡,留有足够空间
- [ ] 坐标推导有注释(写明 originX/Y、chartW/H、数据映射公式
**Mermaid 模式:**
- [ ] 内容包在 `<![CDATA[...]]>`
- [ ] CDATA 结束符 `]]>` 不出现在 Mermaid 代码本身中
- [ ] `topLeftX + width ≤ 960``topLeftY + height ≤ 540`
- [ ] 节点数量合理(单图不超过 15-20 个节点)
**通用:**
- [ ] XML 标签全部闭合,属性引号完整
- [ ] 如果失败,检查是否是偶发 5001000重试一次
---
## 参考
- [lark-slides SKILL.md](../SKILL.md)

View File

@@ -162,7 +162,7 @@ lark-cli slides xml_presentation.slide create --as user \
| 元素 | 说明 |
|------|------|
| `<style>` | 页面样式(背景填充) |
| `<data>` | 图形元素容器shape、img、table、chart 等) |
| `<data>` | 图形元素容器shape、img、table、chart、whiteboard 等) |
| `<note>` | 演讲者备注 |
> [!IMPORTANT]

View File

@@ -94,7 +94,7 @@ lark-cli slides xml_presentation.slide get --as user --params '{
## 注意事项
1. **执行前必做**`lark-cli schema slides.xml_presentation.slide.get` 查看最新参数结构
2. **block_id 提取**:返回 XML 里每个顶层块shape、img、table 等)的 `id` 属性即为 `block_id`,通常是 3 字符短码,例如 `<shape id="bUn" ...>`。用以下命令列出当前页所有 block_id
2. **block_id 提取**:返回 XML 里每个顶层块shape、img、table、chart、whiteboard 等)的 `id` 属性即为 `block_id`,通常是 3 字符短码,例如 `<shape id="bUn" ...>`。用以下命令列出当前页所有 block_id
```bash
lark-cli slides xml_presentation.slide get --as user \

View File

@@ -171,12 +171,13 @@ lark-cli slides xml_presentation.slide replace --as user --params '{
## 注意事项
1. **parts 原子事务**:任一条失败整批回滚,不会出现"前几条成功、后几条失败"的中间态。
2. **block_id 的获取**`slide.get` 返回的 XML 里每个块shape、img、table、chart 等)会带 3 位 short element ID用这个值填 `block_id` / `insert_before_block_id`
2. **block_id 的获取**`slide.get` 返回的 XML 里每个块shape、img、table、chart、whiteboard 等)会带 3 位 short element ID用这个值填 `block_id` / `insert_before_block_id`
3. **`<img>` 必须用 file_token**:不能用外链 URL——先 [`slides +media-upload`](lark-slides-media-upload.md) 拿 token。
4. **不能字段级 patch**:要改一个块的某个属性(比如只改 `topLeftX`),得写整块新 XML 走 `block_replace`API 不支持"只改一个字段"。
5. **`block_replace` 要求 `replacement` 根元素带 `id="<block_id>"`**:底层 API 的硬约束,缺失会返回 3350001。推荐走 shortcut [`+replace-slide`](lark-slides-replace-slide.md)——它会自动把 `id` 注入到 `replacement` 根元素上,用户写 XML 时不用自己加。
6. **`<shape>` 必须有 `<content/>` 子元素**SML 2.0 schema 要求,缺失同样触发 3350001。shortcut [`+replace-slide`](lark-slides-replace-slide.md) 会自动注入 `<content/>`,直接调底层 API 需要自己加。
7. **执行前必做**`lark-cli schema slides.xml_presentation.slide.replace` 查看最新参数结构
7. **`<whiteboard>` 返回结构不含内部数据**`slide.get` 返回的 whiteboard 块只有外层标签和位置属性SVG / Mermaid 内容不会随 XML 一起返回。但 `block_replace` 仍然可以强行覆盖——直接写入完整新 whiteboard XML 即可
8. **执行前必做**`lark-cli schema slides.xml_presentation.slide.replace` 查看最新参数结构。
## 相关命令

View File

@@ -185,15 +185,15 @@ Use an object for one planned asset, an array for multiple real needs, or `asset
- `asset_type`: one of `paper_figure`, `architecture_diagram`, `icon`, `logo`, `chart`, `infographic`, `screenshot`, `flow_diagram`, or `none`.
- `purpose`: why this asset helps the page's key message.
- `suggested_query`: short future lookup hint only; do not execute it unless separately requested.
- `fallback_if_missing`: concrete XML-native visual plan using shapes, arrows, labels, tables, simple charts, or placeholder panels.
- `fallback_if_missing`: concrete XML-native visual plan using shapes, labels, tables, whiteboard diagrams, or placeholder panels.
For detailed rules and examples, read `asset-planning.md`.
Good examples:
- `{"asset_type":"architecture_diagram","purpose":"Explain component relationships.","suggested_query":"service architecture diagram","fallback_if_missing":"Draw grouped boxes and arrows with short labels."}`
- `{"asset_type":"architecture_diagram","purpose":"Explain component relationships.","suggested_query":"service architecture diagram","fallback_if_missing":"Draw a component diagram with grouped boxes, connector arrows, and short labels."}`
- `{"asset_type":"logo","purpose":"Identify the customer context.","suggested_query":"customer logo","fallback_if_missing":"Use a text label in a small badge."}`
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Draw a simple line chart with shapes and value labels."}`
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Draw a simple trend line chart with axis labels and data points."}`
## XML Generation Contract

View File

@@ -935,7 +935,7 @@
单页幻灯片结构
子元素:
- style: 页面样式(背景色等), style的fill默认颜色为白色rgba(255, 255, 255, 1)
- data: 页面元素容器(shape/line/polyline/img/table/icon/chart/undefined)
- data: 页面元素容器(shape/line/polyline/img/table/icon/chart/whiteboard/undefined)
- note: 演讲者备注
</xs:documentation>
</xs:annotation>
@@ -960,6 +960,7 @@
<xs:element ref="sml:table"/>
<xs:element ref="sml:icon"/>
<xs:element ref="sml:chart"/>
<xs:element ref="sml:whiteboard"/>
<xs:element ref="sml:undefined"/>
</xs:choice>
</xs:complexType>
@@ -1206,7 +1207,7 @@
未定义元素, 用于处理不支持的形状类型, 当导出时遇到不支持的type数据时, 使用此元素替代
属性说明:
- id: 元素唯一标识符(可选)
- type: 原始的不支持的类型名称, 包括 video(视频), audio(音频), whiteboard(画板)
- type: 原始的不支持的类型名称, 包括 video(视频), audio(音频)
</xs:documentation>
</xs:annotation>
<xs:complexType>
@@ -3001,4 +3002,48 @@
<xs:attribute name="alpha" type="sml:AlphaType" use="optional" default="1"/>
</xs:complexType>
</xs:element>
<!-- 画板元素 -->
<xs:element name="whiteboard">
<xs:annotation>
<xs:documentation>
画板元素, 用于在幻灯片中嵌入 Mermaid 或 SVG 绘制内容。
属性说明:
- id: 画板唯一标识符(可选)
- topLeftX/topLeftY: 左上角坐标, 必须
- width/height: 宽高尺寸, 必须
- flipX/flipY: 水平/垂直翻转
- alpha: 不透明度[0,1]
子元素(mermaid 与 svg 二选一):
- mermaid: Mermaid 源码文本, 可使用 CDATA 包裹
适用场景: 流程图、时序图、思维导图、类图、甘特图、饼图等结构化图表
特点: 用简短的文本声明描述图表逻辑, 由渲染引擎自动布局, 无需手动计算坐标
示例: &lt;mermaid&gt;&lt;![CDATA[flowchart TD\n A[开始] --&gt; B[结束]]]&gt;&lt;/mermaid&gt;
- svg: SVG 内容
适用场景: 需要精确控制坐标、配色、路径的自定义图形
特点: 像素级精确定位,支持 rect/circle/path/text/polygon/g/linearGradient 等元素radialGradient/filter/clipPath/mask/pattern 不支持,需手动计算所有坐标
示例: &lt;svg xmlns="http://www.w3.org/2000/svg"&gt;...&lt;/svg&gt;xmlns 必填width/height/viewBox 不影响渲染,仅百分比属性值场景需声明 viewBox
- border: 边框样式, 可选, 无border标签代表无边框, 空border标签代表使用默认样式
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element name="mermaid" type="xs:string" />
<xs:any namespace="http://www.w3.org/2000/svg" processContents="skip"/>
</xs:choice>
<xs:element name="border" type="sml:BorderType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="optional"/>
<xs:attribute name="topLeftX" type="sml:XType" use="required"/>
<xs:attribute name="topLeftY" type="sml:YType" use="required"/>
<xs:attribute name="width" type="sml:PositiveSize" use="required"/>
<xs:attribute name="height" type="sml:PositiveSize" use="required"/>
<xs:attribute name="flipX" type="xs:boolean" use="optional" default="false"/>
<xs:attribute name="flipY" type="xs:boolean" use="optional" default="false"/>
<xs:attribute name="alpha" type="sml:AlphaType" use="optional" default="1"/>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -76,6 +76,14 @@ python3 skills/lark-slides/scripts/xml_text_overlap_lint.py --input <presentatio
- 大量形状坐标完全相同,导致主体内容重叠。
- 渐变背景回退成空白或白底,导致文字不可读。
## Whiteboard Elements
`slide.get` 回读 XML 时,`<whiteboard>` 块只返回位置属性(`topLeftX``topLeftY``width``height`SVG / Mermaid 内容**不随 XML 返回**。
- whiteboard 验证只能核对坐标是否越界:`topLeftX + width ≤ 960``topLeftY + height ≤ 540`
- SVG 和 Mermaid 内容的正确性无法通过回读 XML 验证,需要人工视觉验收。
- 不要在验证记录中声称 whiteboard 内容已验证,除非用户确认了视觉效果。
## Layout And Overflow Risk
优先修复这些明显风险:

View File

@@ -168,6 +168,8 @@ Text:
Purpose: explain components, dependencies, or system flow.
Implementation: prefer `<whiteboard>` (see `lark-slides-whiteboard.md`); use `<shape>` + `<line>` only as fallback.
Geometry:
- Main visual area should be a diagram, not prose.
- Use grouped boxes, lanes, arrows or lines, and short labels.
@@ -182,6 +184,8 @@ Text:
Purpose: show operational steps, workflow, or cause-effect path.
Implementation: prefer `<whiteboard>` (see `lark-slides-whiteboard.md`); use `<shape>` + `<line>` only as fallback.
Geometry:
- Use numbered steps connected by arrows or lines.
- 3-5 steps is ideal for one slide. If there are more, group them into phases.

View File

@@ -44,7 +44,7 @@
**子元素:**
- `<style>?` - 页面样式,目前可放 `<fill>`
- `<data>?` - 页面元素容器,可放 `shape``line``polyline``img``table``icon``chart``undefined`
- `<data>?` - 页面元素容器,可放 `shape``line``polyline``img``table``icon``chart``whiteboard``undefined`
- `<note>?` - 演讲者备注,内部可放 `<content>`
## theme 与文本类型
@@ -142,6 +142,34 @@ XSD 中的 `title`、`headline`、`sub-headline`、`body`、`caption` 主要出
<icon iconType="iconpark/Base/setting.svg" topLeftX="80" topLeftY="120" width="32" height="32"/>
```
### whiteboard
```xml
<!-- SVG 模式:数据图表、装饰元素 -->
<whiteboard topLeftX="580" topLeftY="120" width="340" height="280">
<svg xmlns="http://www.w3.org/2000/svg">
<rect x="60" y="80" width="40" height="140" rx="3" fill="rgba(59,130,246,0.85)"/>
<text x="80" y="238" text-anchor="middle" font-size="11" fill="rgba(100,116,139,1)">ABC</text>
</svg>
</whiteboard>
<!-- Mermaid 模式:流程图、时序图等结构化图表 -->
<whiteboard topLeftX="72" topLeftY="100" width="816" height="340">
<mermaid>
<![CDATA[
flowchart LR
A[开始] --> B{判断}
B -- 是 --> C[执行]
B -- 否 --> D[结束]
]]>
</mermaid>
</whiteboard>
```
SVG 模式:`<svg>` 需声明 `xmlns="http://www.w3.org/2000/svg"`,内容大小由子元素包围盒决定;`width`/`height`/`viewBox` 不影响渲染,仅当元素使用百分比属性值时需声明 `viewBox`。\
Mermaid 模式:内容用 `<![CDATA[...]]>` 包裹,避免 `[``>``-->` 等字符破坏 XML 解析。\
详细用法见 [lark-slides-whiteboard.md](lark-slides-whiteboard.md)。
## 颜色与样式
### fill

View File

@@ -36,7 +36,7 @@ metadata:
| "会议现在还开着,谁刚加入了"、"会议里谁在发言"、"有人共享屏幕吗"**进行中会议**,且**机器人已入会** | **本 skill** `+meeting-events` |
| "退出会议"、"让机器人离开" | **本 skill** `+meeting-leave` |
| "昨天那场会有谁参加过"、"搜昨天的会"、"查纪要/逐字稿/录制" | [`lark-vc`](../lark-vc/SKILL.md) |
| "帮我参会,结束后把纪要发到群" 等跨阶段场景 | 按序编排:本 skill入会 → 读事件 离会)→ [`lark-vc`](../lark-vc/SKILL.md) / [`lark-minutes`](../lark-minutes/SKILL.md)拉纪要→ [`lark-im`](../lark-im/SKILL.md)发群 |
| "帮我参会,结束后把纪要发到群" 等跨阶段场景 | 按序编排:本 skill入会 → 读事件会议结束后用 [`lark-vc`](../lark-vc/SKILL.md) / [`lark-minutes`](../lark-minutes/SKILL.md) 拉纪要 → [`lark-im`](../lark-im/SKILL.md) 发群 |
## 核心场景
@@ -66,12 +66,12 @@ metadata:
### 3. 离开会议(写操作)
1. 任务完成、或用户要求结束时,用 `+meeting-leave --meeting-id <从 +meeting-join 拿到的 meeting.id>`
1. 只有用户明确要求机器人退出 / 离开 / 结束参会时,`+meeting-leave --meeting-id <从 +meeting-join 拿到的 meeting.id>`;不要把任务完成当作离会指令
2. `--meeting-id` **必须**是 `+meeting-join` 返回的长数字 `meeting.id`**不接受 9 位会议号**
3. 离会**立即生效**,机器人从会议的参会人列表中消失,对其他参会人可见;若需要重新入会,再跑一次 `+meeting-join` 即可(非真正"不可逆")。
4. 仅支持 `user` 身份。
### 4. Agent 参会最小闭环示范
### 4. Agent 参会示范
```bash
# 1. 入会,捕获 meeting.id
@@ -83,13 +83,12 @@ MID=$(echo "$JOIN" | jq -r '.data.meeting.id')
# 典型间隔 10-30 秒
lark-cli vc +meeting-events --meeting-id "$MID" --page-all --format pretty
# 3. 任务完成或用户要求结束时离会
lark-cli vc +meeting-leave --meeting-id "$MID"
# 4. 会后可选:取纪要 / 逐字稿(跨到 lark-vc
# 3. 会后可选:取纪要 / 逐字稿(跨到 lark-vc
lark-cli vc +notes --meeting-ids "$MID"
```
如果用户随后明确要求退出 / 离开 / 结束参会,再单独调用 `lark-cli vc +meeting-leave --meeting-id "$MID"`
## Shortcuts
Shortcut 是对常用操作的高级封装(`lark-cli vc +<verb> [flags]`)。

View File

@@ -238,7 +238,7 @@ lark-cli vc +meeting-events \
## 参考
- [lark-vc-agent-meeting-join](lark-vc-agent-meeting-join.md) — 先真实入会
- [lark-vc-agent-meeting-leave](lark-vc-agent-meeting-leave.md) — 完成任务后离会
- [lark-vc-agent-meeting-leave](lark-vc-agent-meeting-leave.md) — 用户明确要求时离会
- [lark-vc-search](../../lark-vc/references/lark-vc-search.md) — 搜索历史会议(获取 meeting_id
- [lark-vc-recording](../../lark-vc/references/lark-vc-recording.md) — 查询 minute_token
- [lark-vc-notes](../../lark-vc/references/lark-vc-notes.md) — 获取会议纪要

View File

@@ -84,14 +84,14 @@ lark-cli vc +meeting-join --meeting-number 123456789 --dry-run
## Agent 组合场景
### 场景 1加入会议 → 离开会议(最小闭环)
### 场景 1加入会议 → 监听会中事件
```bash
# 第 1 步:加入会议,记录返回的 meeting.id
lark-cli vc +meeting-join --meeting-number 123456789
# 第 2 步:完成任务后,使用上一步返回的 meeting.id 离开会议
lark-cli vc +meeting-leave --meeting-id <meeting.id>
# 第 2 步:使用返回的 meeting.id 查询会中事件
lark-cli vc +meeting-events --meeting-id <meeting.id> --page-all --format pretty
```
### 场景 2加入会议 → 会后拉取纪要 / 录制
@@ -100,13 +100,10 @@ lark-cli vc +meeting-leave --meeting-id <meeting.id>
# 第 1 步:加入并参会
lark-cli vc +meeting-join --meeting-number 123456789
# 第 2 步:
lark-cli vc +meeting-leave --meeting-id <meeting.id>
# 第 3 步:会议结束后,查询录制(拿到 minute_token
# 第 2 步:会议结束后,查询录制(拿到 minute_token
lark-cli vc +recording --meeting-ids <meeting.id>
# 第 4 步:查询会议纪要(总结 / 待办 / 章节 / 逐字稿)
# 第 3 步:查询会议纪要(总结 / 待办 / 章节 / 逐字稿)
lark-cli vc +notes --meeting-ids <meeting.id>
```
@@ -123,7 +120,7 @@ lark-cli vc +notes --meeting-ids <meeting.id>
## 提示
- 仅在 Agent 需要**真实加入**会议(例如参会机器人、会中助手)时使用;只拉取会议数据不需要入会。
- 入会会让机器人立即出现在参会列表;若要回退,直接 `+meeting-leave` 即可。参数格式不确定时可选 `--dry-run` 预览,但不是必经步骤。
- 入会会让机器人立即出现在参会列表;若用户要求退出 / 离开 / 结束参会,直接 `+meeting-leave` 即可。参数格式不确定时可选 `--dry-run` 预览,但不是必经步骤。
- 执行成功后,立即记录返回的 `meeting.id`,用于后续 `+meeting-leave` / `+meeting-events`
## 参考

View File

@@ -44,7 +44,7 @@ lark-cli vc +meeting-leave --meeting-id 69xxxxxxxxxxxxx28 --dry-run
### 4. 离会立即生效,对其他参会人可见
机器人会立刻从参会列表消失;若会议启用了录制/纪要bot 的参会时段到此截止。确认任务完成再调用;如需要重新入会,再跑 `+meeting-join` 即可(非真正"不可逆")。
机器人会立刻从参会列表消失;若会议启用了录制/纪要bot 的参会时段到此截止。只有在用户明确要求退出 / 离开 / 结束参会时才调用;如需要重新入会,再跑 `+meeting-join` 即可(非真正"不可逆")。
## 输出结果
@@ -59,29 +59,28 @@ lark-cli vc +meeting-leave --meeting-id 69xxxxxxxxxxxxx28 --dry-run
## Agent 组合场景
### 场景 1加入 → 完成任务 → 离开(最小闭环)
### 场景 1加入 → 用户明确要求时离开
```bash
# 第 1 步:加入会议,记录 meeting.id
lark-cli vc +meeting-join --meeting-number 123456789
# 第 2 步:在会中完成任务(如监听发言、记录信息等)
# 第 2 步:在会中处理用户请求(如监听发言、记录信息等)
# ...
# 第 3 步:使用上一步记录的 meeting.id 离会
# 第 3 步:仅在用户明确要求退出 / 离开 / 结束参会时,使用上一步记录的 meeting.id 离会
lark-cli vc +meeting-leave --meeting-id <meeting.id>
```
### 场景 2会后补拉产物
### 场景 2会后补拉产物(不需要离会)
如果用户只是要求会议结束后拉录制、纪要或逐字稿,不要先调用 `+meeting-leave`;直接跨到 `lark-vc` 查询会后产物。
```bash
# 第 1 步:离会后会议仍在进行或已结束
lark-cli vc +meeting-leave --meeting-id <meeting.id>
# 第 2 步:会议结束后查询录制
# 第 1 步:会议结束后查询录制
lark-cli vc +recording --meeting-ids <meeting.id>
# 第 3 步:查询会议纪要
# 第 2 步:查询会议纪要
lark-cli vc +notes --meeting-ids <meeting.id>
```
@@ -95,9 +94,9 @@ lark-cli vc +notes --meeting-ids <meeting.id>
## 提示
- 离会会让机器人从参会列表消失,对其他参会人可见若需要重新入会直接再 `+meeting-join`,不是真正的"不可逆"。参数格式不确定时可选 `--dry-run` 预览。
- `+meeting-join` 成对使用:能 join 的身份才能 leave。
- `meeting_id` 必须来自 `+meeting-join` 返回值,不要用 9 位会议号。
- 只有用户明确要求退出 / 离开 / 结束参会时才调用;离会会让机器人从参会列表消失,对其他参会人可见若需要重新入会直接再 `+meeting-join`,不是真正的"不可逆"。参数格式不确定时可选 `--dry-run` 预览。
- `+meeting-leave` 依赖 `+meeting-join` 返回的 `meeting.id`,但不是每次 join 后都必须调用 leave。
- `meeting_id` 优先使用 `+meeting-join` 返回`meeting.id`;如果来自 `+search`,也必须先确认当前身份就在该会议中。不要用 9 位会议号。
## 参考