全部文档

取数与流水线(pipeline)

读者:动作不仅写本地库,还要从工作区 数据连接 拉数、再组装结果的人。

来源 sdk/appsdk/取数与流水线.md

读者:动作不仅写本地库,还要从工作区 数据连接 拉数、再组装结果的人。 关联动作与handlers · 应用包结构 · 范例技能包 backend/internal/skillfromchat/bundled/employee-profile/


1. 什么时候需要取数流水线?

场景做法
只在本应用 SQLite 里增删查内置 CRUD,不要 pipeline
点一次按钮,按固定步骤查多个预定义查询,再拼报告references/pipeline.json + 编排脚本
步骤经常变、要给助手/人可读把步骤写在 pipeline,而不是硬编码在十几处 Python

员工画像是标准范例:主档 → 联系方式 → 组织 → 合同 → 薪资 → 证件照 → 组装 HTML。


2. 推荐代码结构

logic/
  handlers.py           # 只分发动作;成功/失败写历史
  generate_xxx.py       # 读 pipeline、逐步 query_run、调组装、save_upload
scripts/
  assemble_xxx.py       # 纯函数:数据 + 模板 + 映射 → HTML 字符串
assets/
  xxx.html              # 结果版式
references/
  pipeline.json         # 步骤声明
  field-mapping.json    # 字段如何填进模板(可选)

职责划分

  • pipeline:声明「查什么、参数从哪来、结果存成哪一段」
  • generate_*:执行与容错(可选步骤、备用查询、warnings)
  • assemble_*:展示,不直接打数据连接
  • handlers:产品动作边界与登记

3. pipeline.json 核心形状

{
  "version": 1,
  "id": "my-report-v1",
  "display_name": "我的报表",
  "input": {
    "emp_name": { "type": "string", "required": true, "label": "员工姓名" }
  },
  "steps": [
    {
      "id": "resolve_employee",
      "label": "解析员工主档",
      "query_id": "employee_by_name",
      "params": { "empName": "{{input.emp_name}}" },
      "save_as": "employee",
      "optional": false
    },
    {
      "id": "fetch_org",
      "label": "组织岗位",
      "query_id": "employee_org_by_emp_id",
      "params": { "empId": "{{employee.rows[0].empId}}" },
      "save_as": "org",
      "optional": true
    }
  ],
  "assemble": {
    "script_path": "scripts/assemble_xxx.py",
    "template_path": "assets/xxx.html",
    "field_mapping_path": "references/field-mapping.json"
  }
}

3.1 步骤字段说明

字段含义
query_id数据连接里的 预定义查询 id(必须已配置)
params查询参数;支持 {{input.xxx}}{{employee.rows[0].empId}} 这类占位
save_as本步结果在后续绑定中的名字
optionaltrue:失败或空结果记入 warnings,不整单失败
fallback_step主查询无数据时的备用 query_id + params(如证件照)

应用内编排脚本会读取这些字段;tool / action 等键在技能侧对话执行时更有意义,应用内通常直接 platform.query_run

3.2 与数据连接的关系

  • 工作区需已配置数据连接,且查询 id 与 pipeline 一致。
  • 用户须有权执行这些查询。
  • 可用 platform.query_list() 先看当前有哪些 id,避免硬失败。

4. 编排脚本在做什么(逻辑骨架)

伪代码(与员工画像 generate_profile.py 同类):

run_generate(params, ctx):
  校验必填输入
  platform = ctx["platform"]
  pipeline = 读取 references/pipeline.json
  bindings = { input: params }
  warnings = []

  for step in pipeline.steps:
    解析 params 模板
    block = platform.query_run(query_id, params)
    若空且有 fallback → 再查一次
    若仍失败且 optional → warnings.append(...); block = 空
    若失败且非 optional → return 错误
    bindings[save_as] = block

  html = assemble(bindings, template, field_mapping)
  up = platform.save_upload(文件名, html, "text/html; charset=utf-8")
  return { ok, upload_id, warnings, ... }

消歧:主档多行时返回 code: "disambiguate" + candidates,让用户补填员工 ID 后再提交(界面已支持展示这类错误)。


5. 组装与字段映射

  • 模板assets/*.html,可用占位符或由脚本替换。
  • field-mapping.json:描述「报告某一栏来自哪次查询的哪个字段」、空值、打码等(员工画像有完整样例)。
  • 按图做结果页:见 视图与布局 §4 路径 A

组装脚本应尽量 无平台桥调用,便于单测与在技能侧复用。


6. handlers 如何挂上流水线

from generate_xxx import run_generate

def handle(action, params, ctx):
    if action == "report.generate":
        result = run_generate(params, ctx)
        # 可选:把 status / upload_id 写入 app.db 历史表
        return result
    ...

界面:action_formreport.generate;历史:list + 实体含 upload_id/status


7. 必须具备的清单(取数类应用)

  • [ ] 工作区数据连接可用,pipeline 中每个非 optional 的 query_id 存在
  • [ ] logic/handlers.py 动作 id 与 app.json 一致
  • [ ] 编排脚本使用 ctx["platform"],无禁用网络库
  • [ ] references/pipeline.json(或脚本内等效步骤)与真实查询参数名一致
  • [ ] 需要预览时:save_upload + 返回 upload_id
  • [ ] 部分失败时返回 warnings,避免静默丢字段

8. 应用包面板(浏览与白名单编辑)

打开应用后可用「应用包」查看包内文件树并预览内容。

能力说明
浏览app.jsonmanifest.json、以及 logic/ scripts/ references/ assets/ skills/ 下的文本文件
编辑并保存仅白名单路径(与应用开发助手可写范围一致,如 logic/*.pyscripts/*.pyreferences/*.{json,md}assets/profile.htmlskills/SKILL.md
只读app.json / manifest.json 等:界面描述请用应用开发助手或对照规范另改

HTTP(成员):

  • GET .../apps/{appId}/package/files
  • GET .../apps/{appId}/package/file?rel_path=
  • PUT .../apps/{appId}/package/file body { rel_path, content }

9. 不要把这些放进 pipeline

内容应放在
按钮文案、列表列app.json
权限模型数据连接 ACL / 工作区成员
任意 SQL预定义查询(管理端或对话写入),脚本只 query_run
像素级操作台 UI平台插件,不是 pipeline