Skip to content

附录和资源 ​

术语表和术语 ​

Workflow(工作流) ​

在 AmritaSense 中,工作流是由节点(Node)按特定顺序编排而成的异步执行流。它不是一个静态的图结构,而是一个可被解释器步进执行、支持中断与跳转的指令序列。

Node(节点) ​

工作流的最小执行单元。任何被 @Node() 装饰的 Python 函数或协程都是一个节点。节点是原子的——它要么完全执行,要么完全不执行。条件判断、循环体、异常处理器,一切都是节点。

PointerVector(指针向量) ​

AmritaSense 的核心寻址数据结构。它是一个变长整数数组,每个维度对应一个嵌套层级,该维度上的数值表示当前层级内的偏移索引。在解释器主循环中,PointerVector 扮演程序计数器(PC)的角色,始终指向当前正在执行的节点。

Bubble(气泡作用域) ​

由括号 () 包裹的节点组在编译后形成的独立地址空间。每个 Bubble 拥有独立的 near 地址空间,内部的跳转操作不会影响外层。Bubble 是 AmritaSense 实现作用域隔离和数据封装的底层机制。

Instruction Set(指令集) ​

AmritaSense 提供的一套完备的控制流原语,包括 IF/ELIF/ELSE(条件分支)、WHILE/DO-WHILE(循环)、JMP(无条件跳转)、INVOKE(子程序调用)、CALL / RET(函数块调用与返回)、INT / IRET(中断转移)、PUSH_CONTEXT / POP_CONTEXT(上下文快照)、Try/CATCH/THEN/FINALLY(异常处理)、NOP(哨兵)和 RESET(强制终止)。所有指令在编译期展开为底层节点组合,运行时通过指针跳转完成。

Self-Compile Instruction(自编译指令) ​

实现 SelfCompileInstruction 接口的指令类。它们在 render() 阶段通过 extract() 方法自动展开为标准的 NodeCompose 结构。内置指令和开发者自定义指令都基于这一机制,实现了编译期优化和运行时的零开销。

Compose Contract(组合契约) ​

AmritaSense 中的“组合”由 amrita_sense.node.abc_base 中的两个抽象契约描述:AbstractComposeOriginal(源组合——可迭代、可链式追加、可渲染)与 AbstractCompose[AddressCalculator](渲染图——只读、可索引、绑定 calc)。日常代码中使用的具体类 NodeCompose 与 NodeComposeRendered 是这两个契约的默认实现,功能完备。抽象契约只为 Mock 与扩展而设:任何满足契约的对象都可被渲染器或 WorkflowInterpreter 消费。参见 Compose 契约。

Interrupt(流程中断) ​

AmritaSense 提供的协作式中断机制。工作流在指定标记点主动挂起,将控制权交还给外部系统。外部系统可以在此窗口期内检查状态、修改变量,然后通过 resume() 恢复执行。这是构建调试器和外部监控系统的基础能力。用于控制转移的那一类——外部调用落在 CALL / INT 节点上——见下方的 Trap 词条。

Trap(陷阱) ​

目标为 CALL 或 INT 节点的外中断,而非普通处理节点。当外部调用方对运行中的解释器触发 call_sub(interrupt=True) 时,call_sub 压入停驻地址 C 并进入目标。若目标是 CALL,CALL 会压入自身地址 P 再跳走,于是地址栈上变成 [C, P];call_sub 的 finally 块再把 P 弹走(它是被进入的节点压的),随后例程末尾的 RET 弹出 C。因为 RET 恢复到保存地址再加一,净效果是:trap 消耗掉解释器停驻的那一轮,执行从停驻地址 + 1 继续。

这正是 trap 与普通注入的分野:普通处理节点只会在栈上留下 [C],call_sub 把它弹走,停驻节点之后照常执行。INT 通过上下文栈产生同样的 trap 效果——它的 IRET 恢复快照后从 C + 1 继续。

trap 目标不得自行压/弹 _ret_addr_stack:call_sub 的 pop 是无条件的,不会校验弹出的是谁压的。CALL 与 INT 恰好满足这一契约。完整的地址栈推演见外部中断调用,可运行版本见 demos/d10_call_trap.py。

Depends(依赖注入) ​

借鉴 FastAPI 的依赖注入模式。节点通过在函数签名中声明 Depends(factory) 来声明自己需要的资源。AmritaSense 的依赖解析系统支持并发解析、运行时注入和类型匹配。若工厂函数返回 None,工作流将直接终止。

Alias(别名) ​

通过 ALIAS 指令为节点绑定的全局唯一符号名。编译期注册到 alias2vector_map,供 JMP 和 INVOKE 在运行时查表解析。这是 AmritaSense 符号寻址体系的基础。

Subprogram(子程序) ​

通过 ARCHIVED_NODES 指令定义的、被 SubprogramJumpNode 跳过、仅通过 INVOKE 或外部注入访问的节点序列。子程序可以存储中断处理逻辑、调试工具或可复用的功能模块,正常执行流不受其存在的影响。若要归档完整节点组合(如函数体),改用 ARCHIVED_SEGMENT;FN / INTER_FN 在其上构建命名函数块(参见函数块调用)。

其他核心术语 ​

  • 解释锁(Interpret Lock):aiologic.Lock 实例,保证每次只有一个节点在执行,是外部安全调用的互斥基础
  • 跳转标记(Jump Mark):状态寄存器的 JMP 位,置起时解释器跳过常规的指针推进步骤,下一轮从跳转目标开始
  • 异常穿透(Exception Penetration):通过 exception_ignored 标记的异常不会被任何 CATCH 块捕获,直达顶层处理器
  • Call Stack(调用栈):Stack[PointerVector],管理子程序调用的返回地址
  • DI Cache(DI 缓存):DICache — WorkflowInterpreter 内部基于 LRU 的缓存,存储已解析的依赖注入 kwargs。键为 hash((id(node.func), args_hash)),避免相同节点函数在相同参数类型下重复解析 DI。载体为最大 2048 条的 LRUCache。由 unsafe 标志 WORKFLOW_DI_NO_CACHE、WORKFLOW_DI_PRELOAD_CACHE 和 WORKFLOW_DI_PRELOAD_BATCH 控制。
  • Address Calculator(地址计算器):AddressCalculator — 通过 NodeComposeRendered.calc 暴露的无状态地址计算工具,提供 advance()、resolve_alias()、find_addr()、find_addr_safe() 方法。将原先散落在解释器 _ptr_cache 中的指针推进逻辑固化为编译图的一部分。
  • Debugger(调试器):amrita_sense.debugger 模块提供的一套 REPL 优先的纯函数式调试工具包。包含状态检查(inspect、where、backtrace、list_nodes、list_sub_intp)、步进执行(step、step_over、step_out、cont)和断点管理(break_at_tag、break_at_addr、clear_break_*、list_breaks)。通过组合式中间件(middleware)注入实现,不修改核心运行时。同步函数可直接在 REPL 中调用而无需 await。
  • Breakpoint(断点):标记在特定节点标签或地址上的执行暂停点。通过 amrita_sense.debugger 的 break_at_tag() 和 break_at_addr() 设置,支持条件表达式(condition 参数)。命中断点时抛出 BreakpointHit(继承 BaseException 而非 Exception,避免触发 panic 机制),由 cont() 捕获后暂停执行。

缩写词 ​

  • API:Application Programming Interface(应用程序编程接口)
  • DI:Dependency Injection(依赖注入)
  • PC:Program Counter(程序计数器)
  • JSON:JavaScript Object Notation(JavaScript 对象表示法)
  • HTTP:Hypertext Transfer Protocol(超文本传输协议)
  • ISA:Instruction Set Architecture(指令集架构)

原语(Primitive) ​

原语是计算机体系结构中的核心概念,指处理器指令集架构(ISA)中定义的不可再分的最小操作单元。在 ISA 中,原语指示了处理器能够执行的最基本能力——如加法、数据加载、条件跳转——所有复杂的程序最终都由这些原语组合而成。原语决定了"硬件能做什么",软件则通过原语的组合实现任意复杂的逻辑。

在 AmritaSense 中,工作流同样构建在一组原语之上:

  • 节点是执行原语:每一个 @Node() 包装的函数都是不可再分的原子执行单元。解释器不会在节点内部中断执行,节点要么完整运行,要么完全不运行
  • 指令是控制流原语:IF、JMP、INVOKE、Try 等指令是流程控制的最小语义单元。它们定义了解释器能执行的最基本控制流操作——条件跳转、无条件跳转、子程序调用、异常捕获
  • 指令是架构边界:正如 ISA 定义了硬件与软件之间的契约,AmritaSense 的指令集定义了"编译器能生成什么"与"解释器能执行什么"之间的稳定边界。自编译指令(SelfCompileInstruction)在编译期展开为底层原语节点,运行时仅处理这些已展开的原语

原语的核心价值在于简单与完备的统一:每个原语只做一件事,但一组原语的组合可以表达任意复杂的逻辑。这正是 AmritaSense "极简即真理"设计哲学的理论根源。

项目资源 ​

GitHub 仓库 ​

官方网站 ​

  • AmritaSense 文档:https://sense.amritabot.com(即此页面)
  • 综合指南与教程:此文档站提供完整的指南和 API 参考

贡献指南 ​

欢迎对 AmritaSense 做出贡献。以下是贡献流程:

  1. Fork 仓库:创建项目的个人副本
  2. 创建分支:在新分支中进行更改
  3. 编写测试:确保更改不破坏现有功能
  4. 更新文档:保持文档与代码同步
  5. 提交拉取请求:描述更改内容并提交审核

代码风格指南:

  • 遵循 PEP 8 Python 风格指南
  • 为所有公共函数和类编写文档字符串
  • 为所有函数参数和返回值使用类型提示
  • 保持函数聚焦和简洁
  • 核心业务逻辑必须由人工编写(详见仓库中的 AIGC 政策)

更多信息请参考各项目仓库中的 CONTRIBUTING.md 文件。

许可证 ​

  • AmritaSense:根据 Apache 2.0 许可证发布

有关完整许可证文本,请参阅仓库中的 LICENSE 文件。

社区和支持 ​

讨论与反馈 ​

问题提交 ​

报告问题时请遵循以下步骤:

  1. 搜索现有问题以避免重复
  2. 提供清晰、描述性的标题
  3. 包含完整的重现步骤和代码片段
  4. 指定运行环境(操作系统、Python 版本、库版本)

行为准则 ​

Amrita 社区遵循贡献者盟约行为准则:

  • 尊重他人:无论背景如何,都应尊重每个人
  • 建设性:提供建设性的反馈和建议
  • 包容:欢迎来自各种背景的人
  • 关注质量:努力提高项目质量

设计哲学与相关资源 ​

设计哲学 ​

  • "一切皆是节点":条件判断、循环体、异常处理器——它们都是 Node 的实例
  • "指令替代图":工作流是线性节点数组上的非线性执行流,跳转即指针改写
  • "极简即真理":用最少的代码实现完备的控制流

相关技术资源 ​

推荐阅读 ​

  • "流程图何必是图"——理解 AmritaSense 设计理念的核心文章
  • "KISS 原则":Keep It Simple, Stupid——AmritaSense 遵循的设计哲学
  • "Unix 哲学":小、专注、可组合——AmritaSense 的模块化设计基础

已弃用的指令名(1.0.0) ​

渲染后的工作流图本身就是一段带地址映射的指令序列,因此 amrita_sense.debugger 的反汇编视图一直打印 CPU 风格的助记符。AmritaSense 1.0.0 把公开指令改名,让 API 与反汇编终于用同一套词汇。

新旧对照表 ​

旧名(≤ 0.8)新名(1.0+)类型旧名仍可导入说明
GOTOJMP函数✅直接替换
PUSH_STACKPUSH_RET函数✅直接替换
RET_FARRET函数✅直接替换
PUSH_AND_GOTO(from_adr, to_adr)CALL(to_adr, *, from_adr=None)函数✅参数顺序已变
CALLINVOKE函数❌无别名,会静默改变语义,见下
INTERRUPT_INTOINT函数✅直接替换
INTERRUPT_RETIRET函数✅直接替换
INTERRUPTRESET常量✅直接替换,无静态告警
INTERRUPT_KEEP_CTXSUSPEND常量✅直接替换,无静态告警
CallNodeInvokeNode类✅直接替换

ALIAS 有意保留原名:运行时的词汇体系全是 alias(alias2vector_map、AddressCalculator.resolve_alias、AliasNotFoundError),而 AliasNode 是占据真实地址槽的可寻址节点——叫 LABEL 既不一致也不准确,因为汇编里的 LABEL 是零宽的。

⚠️ CALL 会静默改变语义 ​

这是唯一没有兼容别名的改名,也是最需要多看两眼的一处。

旧的 CALL(alias) 执行一次单步 call_sub。该名字被远调用指令接管后,CALL(alias) 的含义变成"压入返回地址并跳转"——它不会报错,只是做了另一件事。旧行为现在叫 INVOKE(alias)。

用下面的命令审计代码:

bash
grep -rn '\bCALL(' --include='*.py'

把每一处旧的 CALL(x) 改为 INVOKE(x)。

⚠️ 常量没有静态弃用告警 ​

INTERRUPT 与 INTERRUPT_KEEP_CTX 是模块级常量,不是函数。PEP 702(@deprecated)没有针对变量的装饰器形式,因此类型检查器无法标记它们:它们是静默别名。若你在用它们,请手工检索:

bash
grep -rn '\bINTERRUPT\b' --include='*.py'

上表中的函数改名则带有静态标记,只要开启 reportDeprecated,类型检查器就会提示:

toml
[tool.pyright]
reportDeprecated = "warning"   # 默认为关闭

⚠️ BuiltinTags 的字符串值已变 ​

与改名指令对应的 BuiltinTags 成员现在持有新值,旧成员名以同值别名保留(BuiltinTags.RET_FAR is BuiltinTags.RET)。别名会出现在 __members__ 中,但不出现在 list(BuiltinTags) 里。

成员(旧)成员(新)值(旧)值(新)
RET_FARRET"__RET_FAR__""__RET__"
PUSH_STACKPUSH_RET"__PUSH_STACK__""__PUSH_RET__"
PUSH_AND_GOTOCALL"__PUSH_AND_GOTO__""__CALL__"
INTERRUPT_INTOINT"__INTERRUPT_INTO__""__INT__"
INTERRUPT_RETIRET"__INTERRUPT_RET__""__IRET__"

用枚举成员做比较依然有效;硬编码旧的 tag 字符串则不行。

反汇编助记符 ​

dis() 的输出随名字一同变化:

指令旧助记符新助记符
JMPJMPJMP(不变)
PUSH_RETPUSHPUSH(不变)
RETRET_FARRET
CALLCALL.FAR from -> toCALL to, ret=from
INVOKECALL sym -> [0]INVOKE sym -> [0]
INTINTINTO jmp -> retINT jmp, ret=ret
IRETINTERRUPT_RETIRET
RESETINTRESET
SUSPENDINT.KEEPSUSPEND
PUSH_CONTEXTPUSHCTXPUSHCTX(不变)
ALIASALIAS symALIAS sym(不变)

移除计划 ​

本节所有别名均已弃用,将在 2.0 移除。深层导入路径(如 from amrita_sense.instructions.ret2 import PUSH_STACK)通过各替换位置旁的纯别名继续可用,但这些别名不带静态标记——建议改用顶层 amrita_sense 或 amrita_sense.instructions 导入。

Apache 2.0 许可证约束