From 38556f5aa4444eaf869705e547b362d57b8e01e2 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Wed, 8 Apr 2026 16:28:07 +0800 Subject: [PATCH 1/9] feat(base): add +record-search json passthrough shortcut --- shortcuts/base/base_dryrun_ops_test.go | 23 +++++++ shortcuts/base/base_execute_test.go | 53 ++++++++++++++++ shortcuts/base/base_shortcuts_test.go | 5 +- shortcuts/base/record_ops.go | 30 +++++++++ shortcuts/base/record_search.go | 28 +++++++++ shortcuts/base/shortcuts.go | 1 + skills/lark-base/SKILL.md | 19 +++--- .../references/lark-base-record-list.md | 2 + .../references/lark-base-record-search.md | 62 +++++++++++++++++++ .../lark-base/references/lark-base-record.md | 1 + 10 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 shortcuts/base/record_search.go create mode 100644 skills/lark-base/references/lark-base-record-search.md diff --git a/shortcuts/base/base_dryrun_ops_test.go b/shortcuts/base/base_dryrun_ops_test.go index 3898826b7..a156f363e 100644 --- a/shortcuts/base/base_dryrun_ops_test.go +++ b/shortcuts/base/base_dryrun_ops_test.go @@ -70,6 +70,29 @@ func TestDryRunRecordOps(t *testing.T) { ) assertDryRunContains(t, dryRunRecordList(ctx, listRT), "GET /open-apis/base/v3/bases/app_x/tables/tbl_1/records", "offset=0", "limit=200", "view_id=viw_1") + searchRT := newBaseTestRuntime( + map[string]string{ + "base-token": "app_x", + "table-id": "tbl_1", + "json": `{"view_id":"viw_1","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"filter":{"conjunction":"and","conditions":[]},"offset":-1,"limit":500}`, + }, + nil, nil, + ) + assertDryRunContains( + t, + dryRunRecordSearch(ctx, searchRT), + "POST /open-apis/base/v3/bases/app_x/tables/tbl_1/records/search", + `"view_id":"viw_1"`, + `"keyword":"Created"`, + `"search_fields":["Title","fld_owner"]`, + `"select_fields":["Title","fld_owner"]`, + `"filter":{`, + `"conjunction":"and"`, + `"conditions":[]`, + `"offset":-1`, + `"limit":500`, + ) + upsertCreateRT := newBaseTestRuntime( map[string]string{"base-token": "app_x", "table-id": "tbl_1", "json": `{"Name":"A"}`}, nil, nil, diff --git a/shortcuts/base/base_execute_test.go b/shortcuts/base/base_execute_test.go index 46ec996d9..c48f785ab 100644 --- a/shortcuts/base/base_execute_test.go +++ b/shortcuts/base/base_execute_test.go @@ -494,6 +494,59 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) { } }) + t.Run("search", func(t *testing.T) { + factory, stdout, reg := newExecuteFactory(t) + searchStub := &httpmock.Stub{ + Method: "POST", + URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/records/search", + Body: map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{ + "fields": []interface{}{"Title", "Owner"}, + "field_id_list": []interface{}{"fld_title", "fld_owner"}, + "record_id_list": []interface{}{"rec_1"}, + "data": []interface{}{[]interface{}{"Created by AI", "Alice"}}, + "has_more": false, + "query_context": map[string]interface{}{ + "record_scope": "filtered_records", + "field_scope": "selected_fields", + "search_scope": "fld_title(Title)", + }, + }, + }, + } + reg.Register(searchStub) + if err := runShortcut( + t, + BaseRecordSearch, + []string{ + "+record-search", + "--base-token", "app_x", + "--table-id", "tbl_x", + "--json", `{"view_id":"vew_x","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"filter":{"conjunction":"and","conditions":[{"field_name":"Title","operator":"contains","value":["Created"]}]},"offset":0,"limit":2}`, + }, + factory, + stdout, + ); err != nil { + t.Fatalf("err=%v", err) + } + if got := stdout.String(); !strings.Contains(got, `"record_id_list"`) || !strings.Contains(got, `"rec_1"`) || !strings.Contains(got, `"query_context"`) { + t.Fatalf("stdout=%s", got) + } + body := string(searchStub.CapturedBody) + if !strings.Contains(body, `"view_id":"vew_x"`) || + !strings.Contains(body, `"keyword":"Created"`) || + !strings.Contains(body, `"search_fields":["Title","fld_owner"]`) || + !strings.Contains(body, `"select_fields":["Title","fld_owner"]`) || + !strings.Contains(body, `"filter":{`) || + !strings.Contains(body, `"conjunction":"and"`) || + !strings.Contains(body, `"conditions":[{"field_name":"Title","operator":"contains","value":["Created"]}]`) || + !strings.Contains(body, `"offset":0`) || + !strings.Contains(body, `"limit":2`) { + t.Fatalf("captured body=%s", body) + } + }) + t.Run("get", func(t *testing.T) { factory, stdout, reg := newExecuteFactory(t) reg.Register(&httpmock.Stub{ diff --git a/shortcuts/base/base_shortcuts_test.go b/shortcuts/base/base_shortcuts_test.go index a6f1c61d0..0acbd3290 100644 --- a/shortcuts/base/base_shortcuts_test.go +++ b/shortcuts/base/base_shortcuts_test.go @@ -114,7 +114,7 @@ func TestShortcutsCatalog(t *testing.T) { "+table-list", "+table-get", "+table-create", "+table-update", "+table-delete", "+field-list", "+field-get", "+field-create", "+field-update", "+field-delete", "+field-search-options", "+view-list", "+view-get", "+view-create", "+view-delete", "+view-get-filter", "+view-set-filter", "+view-get-group", "+view-set-group", "+view-get-sort", "+view-set-sort", "+view-get-timebar", "+view-set-timebar", "+view-get-card", "+view-set-card", "+view-rename", - "+record-list", "+record-get", "+record-upsert", "+record-upload-attachment", "+record-delete", + "+record-list", "+record-search", "+record-get", "+record-upsert", "+record-upload-attachment", "+record-delete", "+record-history-list", "+base-get", "+base-copy", "+base-create", "+role-create", "+role-delete", "+role-update", "+role-list", "+role-get", "+advperm-enable", "+advperm-disable", @@ -238,6 +238,9 @@ func TestBaseRecordValidate(t *testing.T) { if BaseRecordList.Validate != nil { t.Fatalf("record list validate should be nil after removing --fields") } + if BaseRecordSearch.Validate != nil { + t.Fatalf("record search validate should be nil for API passthrough") + } if BaseRecordGet.Validate != nil { t.Fatalf("record get validate should be nil after removing --fields") } diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index 280b1c580..7490436be 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -34,6 +34,15 @@ func dryRunRecordGet(_ context.Context, runtime *common.RuntimeContext) *common. Set("record_id", runtime.Str("record-id")) } +func dryRunRecordSearch(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { + body, _ := parseRecordSearchBody(runtime) + return common.NewDryRunAPI(). + POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/search"). + Body(body). + Set("base_token", runtime.Str("base-token")). + Set("table_id", baseTableID(runtime)) +} + func dryRunRecordUpsert(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { body, _ := parseJSONObject(runtime.Str("json"), "json") if recordID := runtime.Str("record-id"); recordID != "" { @@ -105,6 +114,19 @@ func executeRecordGet(runtime *common.RuntimeContext) error { return nil } +func executeRecordSearch(runtime *common.RuntimeContext) error { + body, err := parseRecordSearchBody(runtime) + if err != nil { + return err + } + data, err := baseV3Call(runtime, "POST", baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "records", "search"), nil, body) + if err != nil { + return err + } + runtime.Out(data, nil) + return nil +} + func executeRecordUpsert(runtime *common.RuntimeContext) error { body, err := parseJSONObject(runtime.Str("json"), "json") if err != nil { @@ -136,3 +158,11 @@ func executeRecordDelete(runtime *common.RuntimeContext) error { runtime.Out(map[string]interface{}{"deleted": true, "record_id": runtime.Str("record-id")}, nil) return nil } + +func parseRecordSearchBody(runtime *common.RuntimeContext) (map[string]interface{}, error) { + body, err := parseJSONObject(runtime.Str("json"), "json") + if err != nil { + return nil, err + } + return body, nil +} diff --git a/shortcuts/base/record_search.go b/shortcuts/base/record_search.go new file mode 100644 index 000000000..276547c5e --- /dev/null +++ b/shortcuts/base/record_search.go @@ -0,0 +1,28 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package base + +import ( + "context" + + "github.com/larksuite/cli/shortcuts/common" +) + +var BaseRecordSearch = common.Shortcut{ + Service: "base", + Command: "+record-search", + Description: "Search records in a table", + Risk: "read", + Scopes: []string{"base:record:read"}, + AuthTypes: authTypes(), + Flags: []common.Flag{ + baseTokenFlag(true), + tableRefFlag(true), + {Name: "json", Desc: "record search JSON object", Required: true}, + }, + DryRun: dryRunRecordSearch, + Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { + return executeRecordSearch(runtime) + }, +} diff --git a/shortcuts/base/shortcuts.go b/shortcuts/base/shortcuts.go index fea4ebd7e..c699ddef3 100644 --- a/shortcuts/base/shortcuts.go +++ b/shortcuts/base/shortcuts.go @@ -35,6 +35,7 @@ func Shortcuts() []common.Shortcut { BaseViewSetCard, BaseViewRename, BaseRecordList, + BaseRecordSearch, BaseRecordGet, BaseRecordUpsert, BaseRecordUploadAttachment, diff --git a/skills/lark-base/SKILL.md b/skills/lark-base/SKILL.md index 23f4abc87..0299ca115 100644 --- a/skills/lark-base/SKILL.md +++ b/skills/lark-base/SKILL.md @@ -20,7 +20,7 @@ metadata: - 临时统计 / 聚合分析 → `+data-query` - 要把结果长期显示在表里 → formula 字段 - 用户明确要 lookup,或确实更适合 `from/select/where/aggregate` → lookup 字段 - - 明细读取 / 导出 → `+record-list / +record-get` + - 明细读取 / 条件检索 / 导出 → `+record-search / +record-list / +record-get` 2. **先拿结构,再写命令** - 至少先拿当前表结构:`+field-list` 或 `+table-get` - 跨表场景必须再查**目标表**的结构 @@ -33,7 +33,7 @@ metadata: ## Agent 禁止行为 -- 不要把 `+record-list` 当聚合分析引擎 +- 不要把 `+record-list / +record-search` 当聚合分析引擎 - 不要没读 guide 就直接创建 formula / lookup 字段 - 不要凭自然语言猜表名、字段名、公式表达式里的字段引用 - 不要把系统字段、formula 字段、lookup 字段当成 `+record-upsert` 的写入目标 @@ -59,8 +59,8 @@ metadata: - 特征:要把结果长期显示在 Base 里,跟随记录自动更新。 3. **显式要求 Lookup,或确实要按 source/select/where/aggregate 建模** → 用 lookup 字段 - 默认仍优先考虑 formula。lookup 只在用户明确要求、或更符合固定查找配置时使用。 -4. **原始记录读取 / 明细导出** → `+record-list / +record-get` - - 不要把 `+record-list` 当分析引擎;它负责取明细,不负责聚合计算。 +4. **原始记录读取 / 条件检索 / 明细导出** → `+record-search / +record-list / +record-get` + - 不要把 `+record-list / +record-search` 当分析引擎;它们负责取明细,不负责聚合计算。 ## 公式 / Lookup 专项规则 @@ -104,9 +104,9 @@ metadata: 1. **只使用原子命令** — 使用 `+table-list / +table-get / +field-create / +record-upsert / +view-set-filter / +record-history-list / +base-get` 这类一命令一动作的写法,不使用旧聚合式 `+table / +field / +record / +view / +history / +workspace` 2. **写记录前先读字段结构** — 先调用 `+field-list` 获取字段结构,再读 [lark-base-shortcut-record-value.md](references/lark-base-shortcut-record-value.md) 确认各字段类型的写入值格式 3. **写字段前先看字段属性规范** — 先读 [lark-base-shortcut-field-properties.md](references/lark-base-shortcut-field-properties.md) 确认 `+field-create/+field-update` 的 JSON 结构 -4. **筛选查询按视图能力执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md) 和 [lark-base-record-list.md](references/lark-base-record-list.md),通过 `+view-set-filter` + `+record-list` 组合完成筛选读取 +4. **筛选查询按场景执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md)、[lark-base-record-list.md](references/lark-base-record-list.md)、[lark-base-record-search.md](references/lark-base-record-search.md);视图筛选读取用 `+view-set-filter` + `+record-list`,关键词/字段检索用 `+record-search` 5. **对记录进行分析(涉及"最高/最低/总计/平均/排名/比较/数量"等分析意图)** — 先读 [lark-base-data-query.md](references/lark-base-data-query.md),通过 `+data-query` 进行数据筛选聚合的服务端计算 -6. **聚合分析与取数互斥** — 需要分组统计 / SUM / MAX / AVG / COUNT 时,必须使用 `+data-query`(服务端计算),禁止用 `+record-list` 拉全量记录再手动计算;反之,`+data-query` 不返回原始记录,取数场景仍走 `+record-list / +record-get` +6. **聚合分析与取数互斥** — 需要分组统计 / SUM / MAX / AVG / COUNT 时,必须使用 `+data-query`(服务端计算),禁止用 `+record-list / +record-search` 拉全量记录再手动计算;反之,`+data-query` 不返回原始记录,取数场景走 `+record-search / +record-list / +record-get` 7. **所有 `+xxx-list` 禁止并发调用** — `+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list` 只能串行执行 8. **批量上限 500 条/次** — 同一表建议串行写入,并在批次间延迟 0.5–1 秒 9. **统一参数名** — 一律使用 `--base-token`,不使用旧 `--app-token` @@ -132,9 +132,10 @@ metadata: | 创建 / 更新字段 | `lark-cli base +field-create` / `+field-update` | 使用 `--json` | | 创建 / 更新公式字段 | `lark-cli base +field-create` / `+field-update` | `type=formula`;先读 formula guide,再创建 / 更新 | | 创建 / 更新 lookup 字段 | `lark-cli base +field-create` / `+field-update` | `type=lookup`;先读 lookup guide,再创建 / 更新,默认先判断 formula 是否更合适 | +| 搜索记录(关键词 + 指定字段) | `lark-cli base +record-search` | 透传搜索参数;适合条件检索,不用于聚合分析 | | 列表 / 获取记录 | `lark-cli base +record-list` / `+record-get` | 原子命令,如果需要`聚合计算`,`分组统计` 推荐走 `+data-query` | | 创建 / 更新记录 | `lark-cli base +record-upsert` | `--table-id [--record-id] --json` | -| 聚合分析 / 比较排序 / 求最值 / 筛选统计 | `lark-cli base +data-query` | 不要用 `+record-list` 拉全量数据再手动计算,需使用 `+data-query` 走服务端计算 | +| 聚合分析 / 比较排序 / 求最值 / 筛选统计 | `lark-cli base +data-query` | 不要用 `+record-list / +record-search` 拉全量数据再手动计算,需使用 `+data-query` 走服务端计算 | | 配置 / 查询视图 | `lark-cli base +view-*` | `list/get/create/delete/get-*/set-*/rename` | | 查看记录历史 | `lark-cli base +record-history-list` | 按表和记录查询变更历史 | | 按视图筛选查询 | `lark-cli base +view-set-filter` + `lark-cli base +record-list` | 组合调用 | @@ -156,6 +157,7 @@ metadata: - **Base token 口径统一**:统一使用 `--base-token` - **`+xxx-list` 调用纪律**:`+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list / +dashboard-list / +dashboard-block-list / +workflow-list` 禁止并发调用;批量执行时只能串行 - **`+record-list` 分页规则**:`--limit` 最大 `200`。先拉首批并检查返回 `has_more`;仅当 `has_more=true` 且用户明确需要更多数据(如“全部导出/全量明细/继续下一页”)时再继续翻页。用户只要样例或前 N 条时,不要继续拉全量 +- **`+record-search` 使用规则**:仅通过 `--json` 传搜索请求体;`keyword/search_fields/offset/limit` 等字段合法性由 API 侧按 schema 校验 - **字段可写性先判断**:存储字段才可写;公式 / lookup / 系统字段默认只读,写记录时应跳过 - **公式能力要主动想到**:用户说“算一下”“生成标签”“判断是否异常”“跨表汇总”“按日期差预警”时,要先判断是否应该建公式字段,而不是只返回手工分析方案 - **lookup 不是默认首选**:lookup 只在用户明确要求或确实更适合固定查找模型时使用;常规计算、跨表聚合和条件判断优先 formula @@ -271,6 +273,7 @@ https://{domain}/base/{base-token}?table={table-id}&view={view-id} - [lookup-field-guide.md](references/lookup-field-guide.md) — lookup 字段配置规则、where/aggregate 约束、与 formula 的取舍 - [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md) — 视图筛选配置 - [lark-base-record-list.md](references/lark-base-record-list.md) — 记录列表读取与分页 +- [lark-base-record-search.md](references/lark-base-record-search.md) — 记录条件检索(关键词 + 字段范围) - [lark-base-advperm-enable.md](references/lark-base-advperm-enable.md) — `+advperm-enable` 启用高级权限 - [lark-base-advperm-disable.md](references/lark-base-advperm-disable.md) — `+advperm-disable` 停用高级权限 - [lark-base-role-list.md](references/lark-base-role-list.md) — `+role-list` 列出角色 @@ -293,7 +296,7 @@ https://{domain}/base/{base-token}?table={table-id}&view={view-id} |----------|------| | [`table commands`](references/lark-base-table.md) | `+table-list / +table-get / +table-create / +table-update / +table-delete` | | [`field commands`](references/lark-base-field.md) | `+field-list / +field-get / +field-create / +field-update / +field-delete / +field-search-options` | -| [`record commands`](references/lark-base-record.md) | `+record-list / +record-get / +record-upsert / +record-upload-attachment / +record-delete` | +| [`record commands`](references/lark-base-record.md) | `+record-search / +record-list / +record-get / +record-upsert / +record-upload-attachment / +record-delete` | | [`view commands`](references/lark-base-view.md) | `+view-list / +view-get / +view-create / +view-delete / +view-get-* / +view-set-* / +view-rename` | | [`data-query commands`](references/lark-base-data-query.md) | `+data-query` | | [`history commands`](references/lark-base-history.md) | `+record-history-list` | diff --git a/skills/lark-base/references/lark-base-record-list.md b/skills/lark-base/references/lark-base-record-list.md index faaca74ff..6e7ca674e 100644 --- a/skills/lark-base/references/lark-base-record-list.md +++ b/skills/lark-base/references/lark-base-record-list.md @@ -4,6 +4,8 @@ 分页列出一张表里的记录;可按视图过滤。 +> 如果需求是“关键词 + 指定字段范围检索”,优先使用 [lark-base-record-search.md](lark-base-record-search.md)。 + ## 返回关键字段 | 字段 | 类型 | 说明 | diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md new file mode 100644 index 000000000..21b7a1647 --- /dev/null +++ b/skills/lark-base/references/lark-base-record-search.md @@ -0,0 +1,62 @@ +# base +record-search + +> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 + +按关键词在指定字段范围内检索记录;CLI 侧通过 `--json` 透传请求体。 + +## 适用场景 + +- 需要“按关键词 + 指定字段”快速检索记录。 +- 需要附带 `view_id / select_fields` 控制检索范围与返回字段。 +- 不用于聚合统计。涉及 SUM/AVG/COUNT/MAX/MIN 时改用 `+data-query`。 + +## 推荐命令 + +```bash +lark-cli base +record-search \ + --base-token app_xxx \ + --table-id tbl_xxx \ + --json '{"keyword":"Created","search_fields":["Title","fld_owner"],"offset":0,"limit":100}' + +lark-cli base +record-search \ + --base-token app_xxx \ + --table-id tbl_xxx \ + --json '{"view_id":"viw_xxx","keyword":"Alice","search_fields":["Title","Owner"],"select_fields":["Title","Owner","Status"],"offset":0,"limit":50}' +``` + +## 参数 + +| 参数 | 必填 | 说明 | +|------|------|------| +| `--base-token ` | 是 | Base Token | +| `--table-id ` | 是 | 表 ID 或表名 | +| `--json ` | 是 | 搜索请求体 JSON(结构要求见下方“JSON 要求”) | + +## API 入参详情 + +**HTTP 方法和路径:** + +``` +POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/search +``` + +### JSON 格式要求 + +| 字段 | 必填 | 类型 | 约束 | +|------|------|------|------| +| `view_id` | 否 | string | 无额外约束 | +| `keyword` | 是 | string | 非空,最小长度 `1` | +| `search_fields` | 是 | string[] | 数组长度 `1-100`;每项是 `FieldRef`(字符串,长度 `1-100`) | +| `select_fields` | 否 | string[] | 数组长度 `<=100`;每项是 `FieldRef`(字符串,长度 `1-100`) | +| `offset` | 否 | int | `>=0`,默认 `0` | +| `limit` | 否 | int | `1-200`,默认 `10` | + +## 坑点 + +- ⚠️ `+record-search` 用于检索,不用于聚合分析;聚合场景使用 `+data-query`。 + +## 参考 + +- [lark-base-record.md](lark-base-record.md) — record 索引页 +- [lark-base-record-list.md](lark-base-record-list.md) — 分页列表读取 +- [lark-base-data-query.md](lark-base-data-query.md) — 聚合分析 diff --git a/skills/lark-base/references/lark-base-record.md b/skills/lark-base/references/lark-base-record.md index 15b29393f..c407757a4 100644 --- a/skills/lark-base/references/lark-base-record.md +++ b/skills/lark-base/references/lark-base-record.md @@ -8,6 +8,7 @@ record 相关命令索引。 | 文档 | 命令 | 说明 | |------|------|------| +| [lark-base-record-search.md](lark-base-record-search.md) | `+record-search` | 按关键词和字段范围检索记录 | | [lark-base-record-list.md](lark-base-record-list.md) | `+record-list` | 分页列记录 | | [lark-base-record-get.md](lark-base-record-get.md) | `+record-get` | 获取单条记录 | | [lark-base-record-upsert.md](lark-base-record-upsert.md) | `+record-upsert` | 创建或更新记录 | From 037a1c43706c7cfaa9bb319976429f4c10e1c9d0 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Wed, 8 Apr 2026 20:07:56 +0800 Subject: [PATCH 2/9] docs(base): refine record-search wording and field constraints --- skills/lark-base/SKILL.md | 10 +++++----- skills/lark-base/references/lark-base-record-list.md | 2 +- skills/lark-base/references/lark-base-record-search.md | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/skills/lark-base/SKILL.md b/skills/lark-base/SKILL.md index 0299ca115..ff6f0fcff 100644 --- a/skills/lark-base/SKILL.md +++ b/skills/lark-base/SKILL.md @@ -20,7 +20,7 @@ metadata: - 临时统计 / 聚合分析 → `+data-query` - 要把结果长期显示在表里 → formula 字段 - 用户明确要 lookup,或确实更适合 `from/select/where/aggregate` → lookup 字段 - - 明细读取 / 条件检索 / 导出 → `+record-search / +record-list / +record-get` + - 明细读取 / 关键词检索 / 导出 → `+record-search / +record-list / +record-get` 2. **先拿结构,再写命令** - 至少先拿当前表结构:`+field-list` 或 `+table-get` - 跨表场景必须再查**目标表**的结构 @@ -59,7 +59,7 @@ metadata: - 特征:要把结果长期显示在 Base 里,跟随记录自动更新。 3. **显式要求 Lookup,或确实要按 source/select/where/aggregate 建模** → 用 lookup 字段 - 默认仍优先考虑 formula。lookup 只在用户明确要求、或更符合固定查找配置时使用。 -4. **原始记录读取 / 条件检索 / 明细导出** → `+record-search / +record-list / +record-get` +4. **原始记录读取 / 关键词检索 / 明细导出** → `+record-search / +record-list / +record-get` - 不要把 `+record-list / +record-search` 当分析引擎;它们负责取明细,不负责聚合计算。 ## 公式 / Lookup 专项规则 @@ -104,7 +104,7 @@ metadata: 1. **只使用原子命令** — 使用 `+table-list / +table-get / +field-create / +record-upsert / +view-set-filter / +record-history-list / +base-get` 这类一命令一动作的写法,不使用旧聚合式 `+table / +field / +record / +view / +history / +workspace` 2. **写记录前先读字段结构** — 先调用 `+field-list` 获取字段结构,再读 [lark-base-shortcut-record-value.md](references/lark-base-shortcut-record-value.md) 确认各字段类型的写入值格式 3. **写字段前先看字段属性规范** — 先读 [lark-base-shortcut-field-properties.md](references/lark-base-shortcut-field-properties.md) 确认 `+field-create/+field-update` 的 JSON 结构 -4. **筛选查询按场景执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md)、[lark-base-record-list.md](references/lark-base-record-list.md)、[lark-base-record-search.md](references/lark-base-record-search.md);视图筛选读取用 `+view-set-filter` + `+record-list`,关键词/字段检索用 `+record-search` +4. **筛选查询按场景执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md)、[lark-base-record-list.md](references/lark-base-record-list.md)、[lark-base-record-search.md](references/lark-base-record-search.md);视图筛选读取用 `+view-set-filter` + `+record-list`,关键词检索用 `+record-search` 5. **对记录进行分析(涉及"最高/最低/总计/平均/排名/比较/数量"等分析意图)** — 先读 [lark-base-data-query.md](references/lark-base-data-query.md),通过 `+data-query` 进行数据筛选聚合的服务端计算 6. **聚合分析与取数互斥** — 需要分组统计 / SUM / MAX / AVG / COUNT 时,必须使用 `+data-query`(服务端计算),禁止用 `+record-list / +record-search` 拉全量记录再手动计算;反之,`+data-query` 不返回原始记录,取数场景走 `+record-search / +record-list / +record-get` 7. **所有 `+xxx-list` 禁止并发调用** — `+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list` 只能串行执行 @@ -132,7 +132,7 @@ metadata: | 创建 / 更新字段 | `lark-cli base +field-create` / `+field-update` | 使用 `--json` | | 创建 / 更新公式字段 | `lark-cli base +field-create` / `+field-update` | `type=formula`;先读 formula guide,再创建 / 更新 | | 创建 / 更新 lookup 字段 | `lark-cli base +field-create` / `+field-update` | `type=lookup`;先读 lookup guide,再创建 / 更新,默认先判断 formula 是否更合适 | -| 搜索记录(关键词 + 指定字段) | `lark-cli base +record-search` | 透传搜索参数;适合条件检索,不用于聚合分析 | +| 关键词搜索记录 | `lark-cli base +record-search` | 透传搜索参数;用于关键词检索,不用于聚合分析 | | 列表 / 获取记录 | `lark-cli base +record-list` / `+record-get` | 原子命令,如果需要`聚合计算`,`分组统计` 推荐走 `+data-query` | | 创建 / 更新记录 | `lark-cli base +record-upsert` | `--table-id [--record-id] --json` | | 聚合分析 / 比较排序 / 求最值 / 筛选统计 | `lark-cli base +data-query` | 不要用 `+record-list / +record-search` 拉全量数据再手动计算,需使用 `+data-query` 走服务端计算 | @@ -273,7 +273,7 @@ https://{domain}/base/{base-token}?table={table-id}&view={view-id} - [lookup-field-guide.md](references/lookup-field-guide.md) — lookup 字段配置规则、where/aggregate 约束、与 formula 的取舍 - [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md) — 视图筛选配置 - [lark-base-record-list.md](references/lark-base-record-list.md) — 记录列表读取与分页 -- [lark-base-record-search.md](references/lark-base-record-search.md) — 记录条件检索(关键词 + 字段范围) +- [lark-base-record-search.md](references/lark-base-record-search.md) — 关键词搜索记录 - [lark-base-advperm-enable.md](references/lark-base-advperm-enable.md) — `+advperm-enable` 启用高级权限 - [lark-base-advperm-disable.md](references/lark-base-advperm-disable.md) — `+advperm-disable` 停用高级权限 - [lark-base-role-list.md](references/lark-base-role-list.md) — `+role-list` 列出角色 diff --git a/skills/lark-base/references/lark-base-record-list.md b/skills/lark-base/references/lark-base-record-list.md index 6e7ca674e..eed48aef3 100644 --- a/skills/lark-base/references/lark-base-record-list.md +++ b/skills/lark-base/references/lark-base-record-list.md @@ -4,7 +4,7 @@ 分页列出一张表里的记录;可按视图过滤。 -> 如果需求是“关键词 + 指定字段范围检索”,优先使用 [lark-base-record-search.md](lark-base-record-search.md)。 +> 如果需求是关键词检索,优先使用 [lark-base-record-search.md](lark-base-record-search.md)。 ## 返回关键字段 diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md index 21b7a1647..7442518a0 100644 --- a/skills/lark-base/references/lark-base-record-search.md +++ b/skills/lark-base/references/lark-base-record-search.md @@ -2,11 +2,11 @@ > **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。 -按关键词在指定字段范围内检索记录;CLI 侧通过 `--json` 透传请求体。 +按关键词检索记录;CLI 侧通过 `--json` 透传请求体。 ## 适用场景 -- 需要“按关键词 + 指定字段”快速检索记录。 +- 需要关键词检索记录。 - 需要附带 `view_id / select_fields` 控制检索范围与返回字段。 - 不用于聚合统计。涉及 SUM/AVG/COUNT/MAX/MIN 时改用 `+data-query`。 @@ -46,14 +46,15 @@ POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/search |------|------|------|------| | `view_id` | 否 | string | 无额外约束 | | `keyword` | 是 | string | 非空,最小长度 `1` | -| `search_fields` | 是 | string[] | 数组长度 `1-100`;每项是 `FieldRef`(字符串,长度 `1-100`) | -| `select_fields` | 否 | string[] | 数组长度 `<=100`;每项是 `FieldRef`(字符串,长度 `1-100`) | +| `search_fields` | 是 | string[] | 数组长度 `1-20`;每项是字段 `field_id` 或字段名,代表在这些字段中做关键词搜索 | +| `select_fields` | 否 | string[] | 数组长度 `<=50`;每项是字段 `field_id` 或字段名 | | `offset` | 否 | int | `>=0`,默认 `0` | | `limit` | 否 | int | `1-200`,默认 `10` | ## 坑点 - ⚠️ `+record-search` 用于检索,不用于聚合分析;聚合场景使用 `+data-query`。 +- ⚠️ 部分字段不支持搜索(例如 `attachment`、`link`);传入后通常不会报错,但可能导致无法命中对应记录。 ## 参考 From c9f7f643b556620a2f2934f2e91f1bd79e768f22 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Wed, 8 Apr 2026 20:37:08 +0800 Subject: [PATCH 3/9] docs(base): prefer record-list unless keyword is explicit --- skills/lark-base/SKILL.md | 3 ++- skills/lark-base/references/lark-base-record-list.md | 2 +- skills/lark-base/references/lark-base-record-search.md | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/skills/lark-base/SKILL.md b/skills/lark-base/SKILL.md index ff6f0fcff..5ef37f8e2 100644 --- a/skills/lark-base/SKILL.md +++ b/skills/lark-base/SKILL.md @@ -104,7 +104,7 @@ metadata: 1. **只使用原子命令** — 使用 `+table-list / +table-get / +field-create / +record-upsert / +view-set-filter / +record-history-list / +base-get` 这类一命令一动作的写法,不使用旧聚合式 `+table / +field / +record / +view / +history / +workspace` 2. **写记录前先读字段结构** — 先调用 `+field-list` 获取字段结构,再读 [lark-base-shortcut-record-value.md](references/lark-base-shortcut-record-value.md) 确认各字段类型的写入值格式 3. **写字段前先看字段属性规范** — 先读 [lark-base-shortcut-field-properties.md](references/lark-base-shortcut-field-properties.md) 确认 `+field-create/+field-update` 的 JSON 结构 -4. **筛选查询按场景执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md)、[lark-base-record-list.md](references/lark-base-record-list.md)、[lark-base-record-search.md](references/lark-base-record-search.md);视图筛选读取用 `+view-set-filter` + `+record-list`,关键词检索用 `+record-search` +4. **筛选查询按场景执行** — 先读 [lark-base-view-set-filter.md](references/lark-base-view-set-filter.md)、[lark-base-record-list.md](references/lark-base-record-list.md)、[lark-base-record-search.md](references/lark-base-record-search.md);默认优先 `+record-list`,仅当用户提供明确搜索关键词时使用 `+record-search`;视图筛选读取用 `+view-set-filter` + `+record-list` 5. **对记录进行分析(涉及"最高/最低/总计/平均/排名/比较/数量"等分析意图)** — 先读 [lark-base-data-query.md](references/lark-base-data-query.md),通过 `+data-query` 进行数据筛选聚合的服务端计算 6. **聚合分析与取数互斥** — 需要分组统计 / SUM / MAX / AVG / COUNT 时,必须使用 `+data-query`(服务端计算),禁止用 `+record-list / +record-search` 拉全量记录再手动计算;反之,`+data-query` 不返回原始记录,取数场景走 `+record-search / +record-list / +record-get` 7. **所有 `+xxx-list` 禁止并发调用** — `+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list` 只能串行执行 @@ -157,6 +157,7 @@ metadata: - **Base token 口径统一**:统一使用 `--base-token` - **`+xxx-list` 调用纪律**:`+table-list / +field-list / +record-list / +view-list / +record-history-list / +role-list / +dashboard-list / +dashboard-block-list / +workflow-list` 禁止并发调用;批量执行时只能串行 - **`+record-list` 分页规则**:`--limit` 最大 `200`。先拉首批并检查返回 `has_more`;仅当 `has_more=true` 且用户明确需要更多数据(如“全部导出/全量明细/继续下一页”)时再继续翻页。用户只要样例或前 N 条时,不要继续拉全量 +- **`+record-list / +record-search` 选择规则**:优先使用 `+record-list`;仅当用户给出明确搜索关键词时,才使用 `+record-search` - **`+record-search` 使用规则**:仅通过 `--json` 传搜索请求体;`keyword/search_fields/offset/limit` 等字段合法性由 API 侧按 schema 校验 - **字段可写性先判断**:存储字段才可写;公式 / lookup / 系统字段默认只读,写记录时应跳过 - **公式能力要主动想到**:用户说“算一下”“生成标签”“判断是否异常”“跨表汇总”“按日期差预警”时,要先判断是否应该建公式字段,而不是只返回手工分析方案 diff --git a/skills/lark-base/references/lark-base-record-list.md b/skills/lark-base/references/lark-base-record-list.md index eed48aef3..ab635b6df 100644 --- a/skills/lark-base/references/lark-base-record-list.md +++ b/skills/lark-base/references/lark-base-record-list.md @@ -4,7 +4,7 @@ 分页列出一张表里的记录;可按视图过滤。 -> 如果需求是关键词检索,优先使用 [lark-base-record-search.md](lark-base-record-search.md)。 +> 默认优先使用 `+record-list`;仅当用户提供明确搜索关键词时,才使用 [lark-base-record-search.md](lark-base-record-search.md)。 ## 返回关键字段 diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md index 7442518a0..77b26a4c1 100644 --- a/skills/lark-base/references/lark-base-record-search.md +++ b/skills/lark-base/references/lark-base-record-search.md @@ -7,6 +7,7 @@ ## 适用场景 - 需要关键词检索记录。 +- 用户已提供明确搜索关键词(`keyword`)。 - 需要附带 `view_id / select_fields` 控制检索范围与返回字段。 - 不用于聚合统计。涉及 SUM/AVG/COUNT/MAX/MIN 时改用 `+data-query`。 From 387240fbf2db2a06a481019aeac4bd0036bc125a Mon Sep 17 00:00:00 2001 From: kongenpei Date: Wed, 8 Apr 2026 23:53:33 +0800 Subject: [PATCH 4/9] refactor(base): inline record-search parsing and align tests --- shortcuts/base/base_dryrun_ops_test.go | 5 +---- shortcuts/base/base_execute_test.go | 5 +---- shortcuts/base/record_ops.go | 12 ++---------- .../lark-base/references/lark-base-record-search.md | 2 +- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/shortcuts/base/base_dryrun_ops_test.go b/shortcuts/base/base_dryrun_ops_test.go index a156f363e..67efb5db8 100644 --- a/shortcuts/base/base_dryrun_ops_test.go +++ b/shortcuts/base/base_dryrun_ops_test.go @@ -74,7 +74,7 @@ func TestDryRunRecordOps(t *testing.T) { map[string]string{ "base-token": "app_x", "table-id": "tbl_1", - "json": `{"view_id":"viw_1","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"filter":{"conjunction":"and","conditions":[]},"offset":-1,"limit":500}`, + "json": `{"view_id":"viw_1","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"offset":-1,"limit":500}`, }, nil, nil, ) @@ -86,9 +86,6 @@ func TestDryRunRecordOps(t *testing.T) { `"keyword":"Created"`, `"search_fields":["Title","fld_owner"]`, `"select_fields":["Title","fld_owner"]`, - `"filter":{`, - `"conjunction":"and"`, - `"conditions":[]`, `"offset":-1`, `"limit":500`, ) diff --git a/shortcuts/base/base_execute_test.go b/shortcuts/base/base_execute_test.go index c48f785ab..a412702f2 100644 --- a/shortcuts/base/base_execute_test.go +++ b/shortcuts/base/base_execute_test.go @@ -523,7 +523,7 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) { "+record-search", "--base-token", "app_x", "--table-id", "tbl_x", - "--json", `{"view_id":"vew_x","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"filter":{"conjunction":"and","conditions":[{"field_name":"Title","operator":"contains","value":["Created"]}]},"offset":0,"limit":2}`, + "--json", `{"view_id":"vew_x","keyword":"Created","search_fields":["Title","fld_owner"],"select_fields":["Title","fld_owner"],"offset":0,"limit":2}`, }, factory, stdout, @@ -538,9 +538,6 @@ func TestBaseRecordExecuteReadCreateDelete(t *testing.T) { !strings.Contains(body, `"keyword":"Created"`) || !strings.Contains(body, `"search_fields":["Title","fld_owner"]`) || !strings.Contains(body, `"select_fields":["Title","fld_owner"]`) || - !strings.Contains(body, `"filter":{`) || - !strings.Contains(body, `"conjunction":"and"`) || - !strings.Contains(body, `"conditions":[{"field_name":"Title","operator":"contains","value":["Created"]}]`) || !strings.Contains(body, `"offset":0`) || !strings.Contains(body, `"limit":2`) { t.Fatalf("captured body=%s", body) diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index 7490436be..57c4e3d85 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -35,7 +35,7 @@ func dryRunRecordGet(_ context.Context, runtime *common.RuntimeContext) *common. } func dryRunRecordSearch(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { - body, _ := parseRecordSearchBody(runtime) + body, _ := parseJSONObject(runtime.Str("json"), "json") return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/search"). Body(body). @@ -115,7 +115,7 @@ func executeRecordGet(runtime *common.RuntimeContext) error { } func executeRecordSearch(runtime *common.RuntimeContext) error { - body, err := parseRecordSearchBody(runtime) + body, err := parseJSONObject(runtime.Str("json"), "json") if err != nil { return err } @@ -158,11 +158,3 @@ func executeRecordDelete(runtime *common.RuntimeContext) error { runtime.Out(map[string]interface{}{"deleted": true, "record_id": runtime.Str("record-id")}, nil) return nil } - -func parseRecordSearchBody(runtime *common.RuntimeContext) (map[string]interface{}, error) { - body, err := parseJSONObject(runtime.Str("json"), "json") - if err != nil { - return nil, err - } - return body, nil -} diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md index 77b26a4c1..5c6f274cb 100644 --- a/skills/lark-base/references/lark-base-record-search.md +++ b/skills/lark-base/references/lark-base-record-search.md @@ -37,7 +37,7 @@ lark-cli base +record-search \ **HTTP 方法和路径:** -``` +```http POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/search ``` From 43a79e34a8df832bb37f156806e6b0372b5154d8 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Thu, 9 Apr 2026 00:11:01 +0800 Subject: [PATCH 5/9] refactor(base): remove noop record validate hook --- shortcuts/base/base_shortcuts_test.go | 8 ++------ shortcuts/base/record_ops.go | 4 ---- shortcuts/base/record_upsert.go | 3 --- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/shortcuts/base/base_shortcuts_test.go b/shortcuts/base/base_shortcuts_test.go index 0acbd3290..229737a4c 100644 --- a/shortcuts/base/base_shortcuts_test.go +++ b/shortcuts/base/base_shortcuts_test.go @@ -234,7 +234,6 @@ func TestBaseTableValidate(t *testing.T) { } func TestBaseRecordValidate(t *testing.T) { - ctx := context.Background() if BaseRecordList.Validate != nil { t.Fatalf("record list validate should be nil after removing --fields") } @@ -244,11 +243,8 @@ func TestBaseRecordValidate(t *testing.T) { if BaseRecordGet.Validate != nil { t.Fatalf("record get validate should be nil after removing --fields") } - if err := BaseRecordUpsert.Validate(ctx, newBaseTestRuntime(map[string]string{"base-token": "b", "table-id": "tbl_1", "json": `{"Name":"A"}`}, nil, nil)); err != nil { - t.Fatalf("upsert validate err=%v", err) - } - if err := BaseRecordUpsert.Validate(ctx, newBaseTestRuntime(map[string]string{"base-token": "b", "table-id": "tbl_1", "json": "{"}, nil, nil)); err != nil { - t.Fatalf("invalid record json should bypass CLI validate, err=%v", err) + if BaseRecordUpsert.Validate != nil { + t.Fatalf("record upsert validate should be nil for API passthrough") } } diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index 57c4e3d85..0ee64f965 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -83,10 +83,6 @@ func dryRunRecordHistoryList(_ context.Context, runtime *common.RuntimeContext) Set("base_token", runtime.Str("base-token")) } -func validateRecordJSON(runtime *common.RuntimeContext) error { - return nil -} - func executeRecordList(runtime *common.RuntimeContext) error { offset := runtime.Int("offset") if offset < 0 { diff --git a/shortcuts/base/record_upsert.go b/shortcuts/base/record_upsert.go index 0ff68309e..e495791c8 100644 --- a/shortcuts/base/record_upsert.go +++ b/shortcuts/base/record_upsert.go @@ -22,9 +22,6 @@ var BaseRecordUpsert = common.Shortcut{ recordRefFlag(false), {Name: "json", Desc: "record JSON object", Required: true}, }, - Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { - return validateRecordJSON(runtime) - }, DryRun: dryRunRecordUpsert, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordUpsert(runtime) From a7c147869a7fa99dcc5f040a70ee20f8ec0a91c5 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Thu, 9 Apr 2026 17:27:04 +0800 Subject: [PATCH 6/9] docs(base): unify record example token placeholders --- skills/lark-base/references/lark-base-record-list.md | 10 +++++----- skills/lark-base/references/lark-base-record-search.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/skills/lark-base/references/lark-base-record-list.md b/skills/lark-base/references/lark-base-record-list.md index ab635b6df..efe8935ce 100644 --- a/skills/lark-base/references/lark-base-record-list.md +++ b/skills/lark-base/references/lark-base-record-list.md @@ -26,15 +26,15 @@ ```bash lark-cli base +record-list \ - --base-token app_xxx \ - --table-id tbl_xxx \ + --base-token XXXXXX \ + --table-id tblXXX \ --offset 0 \ --limit 100 lark-cli base +record-list \ - --base-token app_xxx \ - --table-id tbl_xxx \ - --view-id viw_xxx \ + --base-token XXXXXX \ + --table-id tblXXX \ + --view-id vewXXX \ --offset 0 \ --limit 50 ``` diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md index 5c6f274cb..8232d18fd 100644 --- a/skills/lark-base/references/lark-base-record-search.md +++ b/skills/lark-base/references/lark-base-record-search.md @@ -15,14 +15,14 @@ ```bash lark-cli base +record-search \ - --base-token app_xxx \ - --table-id tbl_xxx \ + --base-token XXXXXX \ + --table-id tblXXX \ --json '{"keyword":"Created","search_fields":["Title","fld_owner"],"offset":0,"limit":100}' lark-cli base +record-search \ - --base-token app_xxx \ - --table-id tbl_xxx \ - --json '{"view_id":"viw_xxx","keyword":"Alice","search_fields":["Title","Owner"],"select_fields":["Title","Owner","Status"],"offset":0,"limit":50}' + --base-token XXXXXX \ + --table-id tblXXX \ + --json '{"view_id":"vewXXX","keyword":"Alice","search_fields":["Title","Owner"],"select_fields":["Title","Owner","Status"],"offset":0,"limit":50}' ``` ## 参数 From 90fe2d21bfd59a2178d1ded481ed2b802685a99f Mon Sep 17 00:00:00 2001 From: kongenpei Date: Thu, 9 Apr 2026 17:56:07 +0800 Subject: [PATCH 7/9] fix: align record search JSON parsing with parse context --- shortcuts/base/record_ops.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shortcuts/base/record_ops.go b/shortcuts/base/record_ops.go index b92ee4b43..047882395 100644 --- a/shortcuts/base/record_ops.go +++ b/shortcuts/base/record_ops.go @@ -35,7 +35,8 @@ func dryRunRecordGet(_ context.Context, runtime *common.RuntimeContext) *common. } func dryRunRecordSearch(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { - body, _ := parseJSONObject(runtime.Str("json"), "json") + pc := newParseCtx(runtime) + body, _ := parseJSONObject(pc, runtime.Str("json"), "json") return common.NewDryRunAPI(). POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/records/search"). Body(body). @@ -112,7 +113,8 @@ func executeRecordGet(runtime *common.RuntimeContext) error { } func executeRecordSearch(runtime *common.RuntimeContext) error { - body, err := parseJSONObject(runtime.Str("json"), "json") + pc := newParseCtx(runtime) + body, err := parseJSONObject(pc, runtime.Str("json"), "json") if err != nil { return err } From de4eddda06fa2d2e4f29d90462cce489c6d49702 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Thu, 9 Apr 2026 20:51:21 +0800 Subject: [PATCH 8/9] feat: add help tips for base record search --- shortcuts/base/record_search.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shortcuts/base/record_search.go b/shortcuts/base/record_search.go index 276547c5e..fd9fc199d 100644 --- a/shortcuts/base/record_search.go +++ b/shortcuts/base/record_search.go @@ -21,6 +21,10 @@ var BaseRecordSearch = common.Shortcut{ tableRefFlag(true), {Name: "json", Desc: "record search JSON object", Required: true}, }, + Tips: []string{ + `Example: --json '{"keyword":"Alice","search_fields":["Name"]}'`, + "Agent hint: use the lark-base skill's record-search guide for usage and limits.", + }, DryRun: dryRunRecordSearch, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { return executeRecordSearch(runtime) From 02ff0365056ec79c3467436ffb30793269114772 Mon Sep 17 00:00:00 2001 From: kongenpei Date: Fri, 10 Apr 2026 16:53:56 +0800 Subject: [PATCH 9/9] docs: refine base record search reference --- skills/lark-base/references/lark-base-record-list.md | 2 +- skills/lark-base/references/lark-base-record-search.md | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/skills/lark-base/references/lark-base-record-list.md b/skills/lark-base/references/lark-base-record-list.md index 53c68b7e9..e7d74ed14 100644 --- a/skills/lark-base/references/lark-base-record-list.md +++ b/skills/lark-base/references/lark-base-record-list.md @@ -41,7 +41,7 @@ lark-cli base +record-list \ --base-token XXXXXX \ --table-id tblXXX \ --view-id vewXXX \ - --field-id fld_status \ + --field-id fldStatus \ --field-id 项目名称 \ --offset 0 \ --limit 50 diff --git a/skills/lark-base/references/lark-base-record-search.md b/skills/lark-base/references/lark-base-record-search.md index 8232d18fd..d4cbf6e2d 100644 --- a/skills/lark-base/references/lark-base-record-search.md +++ b/skills/lark-base/references/lark-base-record-search.md @@ -33,6 +33,14 @@ lark-cli base +record-search \ | `--table-id ` | 是 | 表 ID 或表名 | | `--json ` | 是 | 搜索请求体 JSON(结构要求见下方“JSON 要求”) | +## 返回关键字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `query_context.record_scope` | string | 记录范围:`all_records`(全表)/ `view_filtered_records`(按 `view_id` 限定到视图记录) | +| `query_context.field_scope` | string | 字段范围:`selected_fields`(显式传 `select_fields`)/ `view_visible_fields`(未传 `select_fields` 且按视图可见字段)/ `all_fields`(未传 `select_fields` 且无视图限制) | +| `query_context.search_scope` | string | 实际参与搜索的字段集合,格式类似 `fieldTitle(Title), fieldOwner(Owner)` | + ## API 入参详情 **HTTP 方法和路径:** @@ -45,7 +53,7 @@ POST /open-apis/base/v3/bases/:base_token/tables/:table_id/records/search | 字段 | 必填 | 类型 | 约束 | |------|------|------|------| -| `view_id` | 否 | string | 无额外约束 | +| `view_id` | 否 | string | 传入后仅在该视图范围内搜索,并默认按该视图可见字段返回结果 | | `keyword` | 是 | string | 非空,最小长度 `1` | | `search_fields` | 是 | string[] | 数组长度 `1-20`;每项是字段 `field_id` 或字段名,代表在这些字段中做关键词搜索 | | `select_fields` | 否 | string[] | 数组长度 `<=50`;每项是字段 `field_id` 或字段名 |