> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 工作原理

> 理解按行计算的执行方式、Python 运行时和调用预算，以及 Function 与结果处理的职责。

Function 是一次有明确输入和输出的短计算。例如，接收一条转账事件并提取业务字段，或接收池地址和区块 ID，查询储备并计算价格。调用方提供参数，BlockX 执行函数，再将返回值交给调用方或结果处理器。

## 为什么以一次输入为单位

BlockX 的主要计算方式是将许多输入拆成独立的函数调用。一个调用可以处理一条源记录，并进行少量表查询、链上读取或子函数调用；它也可以返回多条记录，不要求输入与输出行数一一对应。

这种划分让平台能够并行处理大量输入，并在等待数据访问时继续执行其他调用。适合放入 Function 的工作通常有明确边界：字段转换、事件解析、对象状态计算等。历史遍历、持续监听和任务编排放在[驱动脚本](/zh/guides/notebooks/how-it-works)中，跨大量记录的聚合可使用[SQL 分析](/zh/guides/analytics/how-it-works)。

## 一次调用如何执行

控制台测试和 OpenAPI 使用同步调用入口，在请求内等待结果。Notebook 还可以将一组调用提交为 **Task（计算任务）**，由 BlockX 调度执行：

1. 调用方描述输入和计算函数。任务可以直接携带输入，也可以指定从哪些源记录生成调用。
2. Worker（执行节点）接收任务，将独立调用分配给本机的 Python 执行器池。同一任务由一个 Worker 管理，多个任务可以分布在不同 Worker 上。
3. 执行器运行函数。访问 BlockDB、链上节点或其他 Function 时，SDK 将请求交回 BlockX 处理，再把结果送回函数继续执行。
4. 任务直接发起的调用（根调用）全部成功完成后，任务汇总返回值，并执行配置的结果处理器。子函数的返回值先回到父函数，参与父函数的计算。

平台可以同时处理多个独立调用。某次调用等待数据访问时，其他调用仍可继续执行，因此不需要让整批输入依次等待。

函数中的长时间循环仍会占用该次调用的执行预算。大规模计算应拆成多次有界调用，而不是在一个 Function 中持续遍历全部历史。

## 输入与输出

控制台中的 Python Function 使用 `_` 作为入口。参数配置定义输入的名称、类型和顺序，调用时按位置与入口参数对应。参数的取值格式见[数据类型](/zh/reference/data-types)。

返回值应能表示为 JSON 数据，例如数字、字符串、布尔值、列表、字典或 `None`。将 SDK 返回的对象转换成所需字段后再返回，不要直接返回表对象、迭代器或客户端实例。

`print()` 用于记录调试信息；调用方需要使用的数据应通过 `return` 返回。

## 独立调用与代码版本

任务内的独立调用没有执行顺序保证。计算时不要依赖“上一条输入已经执行”，也不要用模块全局变量在调用之间累计结果。需要跨调用保留的数据，应由调用方汇总或写入数据表。

同一任务在开始执行时固定所使用的函数代码版本，包含任务内调用的子函数。任务执行中保存新代码，不会让它前半段使用旧代码、后半段使用新代码；新版本可供后续任务使用。

BlockX 可以在任务内合并相同代码与参数的调用、复用返回结果，并对部分失败调用重试。因此，函数应尽量根据显式输入计算结果，不依赖调用次数、当前时间或隐含的可变状态。需要可重复的链上计算时，将区块 ID 作为输入，并将它传给相关数据读取；固定代码版本本身不会固定所读取的数据。

## 运行环境与访问边界

Function 在平台提供的受限 Python 环境中运行，支持业务计算，以及通过 SDK 读取数据、查询链上状态和调用其他 Function。

编写函数时，应按这一运行环境组织代码：

* 使用支持的 SDK 读取表、查询链上状态和调用其他 Function，不依赖任意安装的软件包或直接访问操作系统、外部网络。
* Notebook 中定义的计算函数也应自行导入依赖，并通过参数接收数据，避免依赖驱动脚本的全局变量或闭包。
* SDK 在 Notebook 中可用的接口，不代表都能在 Function 中使用。持续订阅、任务提交和运行管理由驱动脚本负责。

同步调用入口会拒绝写操作。任务执行路径可以提供部分 SDK 写接口，但直接产生副作用会增加重试和复用的难度；用于多种调用入口的 Function，宜通过返回值表达计算结果，将写入交给调用方或任务的结果处理器。

## 调用预算与任务期限

控制台测试、OpenAPI 和任务调用使用不同的时间控制，不能用一个入口的成功判断另一个入口一定成功。

| 范围      | 时间如何约束执行                       | 对使用的影响                   |
| ------- | ------------------------------ | ------------------------ |
| 同步请求    | 使用请求期限，嵌套调用受外层剩余时间约束           | OpenAPI 需要按实际请求入口验证耗时    |
| 任务内单次调用 | 累计 CPU 执行与后端数据访问耗时；内部排队不计入这份预算 | 减少重复读取和过重的单次计算           |
| 整个任务    | 另有从开始到结束的期限，包含等待和结果处理          | 单次调用足够快，也可能因整体等待或任务过大而超时 |

子函数调用共享外层的时间约束，嵌套调用不会获得无限的新预算。对可合并的读取使用批量接口，并按独立输入拆分计算，有助于控制一次调用的耗时。

任务内的调用重试与重新提交整个任务是两件事。BlockX 不自动重跑整个失败任务；调用方应根据任务结果决定后续处理，并考虑已经发生的写入。

## 计算与结果处理

同一个 Function 可以供不同调用方使用，返回结果后的动作取决于调用方式：

| 调用方式        | 结果如何使用                |
| ----------- | --------------------- |
| 控制台测试       | 展示返回值与日志              |
| 其他 Function | 继续参与当前计算              |
| Notebook    | 由脚本读取、汇总或处理返回值        |
| Pipeline    | 由目标表的结果处理逻辑写入记录       |
| OpenAPI     | 作为 HTTP 响应的 `data` 返回 |

用于 Pipeline 的计算函数通常返回一条记录、记录列表或 `None`。`None` 表示不为该输入产生记录；返回记录需要符合目标表结构，由 Pipeline 配置的结果处理器写入。一次 `return` 只完成计算，并不单独代表目标表写入成功。

任务汇总的返回值不保证与输入顺序一致。如果调用方需要将结果对应回输入，应在返回记录中保留业务 ID。
