Show HN: Elixir 智能体框架 Jido 2.0


基本信息


导语

随着分布式系统复杂度的提升,构建具备并发处理能力的智能代理已成为开发者关注的焦点。Jido 2.0 作为一个基于 Elixir 的代理框架,利用 BEAM 虚拟机在容错与并发上的原生优势,为构建稳健的后台任务提供了新的解决思路。本文将介绍 Jido 的核心架构与工作流设计,并演示如何通过它实现可观测、可扩展的自动化任务管理,帮助你在高并发场景下更从容地设计系统。


评论

中心观点 Jido 2.0 的发布不仅是 Elixir 生态对当前 AI Agent 热潮的一次技术性响应,更是通过利用 Erlang VM(BEAM)的容错与并发特性,试图解决 Python 生态中单体 Agent 框架在长期运行、高并发及状态管理上存在的结构性短板的一次重要尝试。

支撑理由与边界分析

1. 架构范式:从“同步脚本”向“容错服务”的迁移

  • [事实陈述] Jido 2.0 基于 BEAM 虚拟机,利用 GenServer 和 Supervisor 树构建 Agent。这意味着每一个 Agent 调度都是一个轻量级进程,且具备“Let it Crash”的崩溃自愈机制。
  • [你的推断] 这是 Jido 区别于 LangChain 或 AutoGPT 的核心差异。Python 框架通常将 Agent 视为脚本的增强,依赖外部循环(如 Cron 或 API 轮询)来驱动;而 Jido 将 Agent 视为长期存活的系统服务。
  • [反例/边界条件] 这种架构并非没有代价。对于简单的、一次性的任务(如“总结这篇文章”),启动一个 Supervisor 树的开销远大于运行一个 Python 脚本。Jido 的优势仅体现在长时间运行有状态的任务中,在短生命周期任务中显得“杀鸡用牛刀”。

2. 状态管理:内存一致性与消息传递的博弈

  • [事实陈述] Jido 使用 Elixir 的消息传递机制来处理 Agent 之间的通信,避免了共享内存锁的问题。
  • [作者观点] 文章暗示这种模型能更安全地处理多 Agent 协作。在多 Agent 系统中,状态一致性是噩梦。Elixir 的不可变数据结构和 Actor 模型天然适合解决“幻读”和“竞态条件”问题。
  • [反例/边界条件] 消息传递虽然安全,但在处理涉及大量上下文传递的复杂推理链时,可能会导致序列化/反序列化的性能损耗,且不如 Python 中直接引用对象指针来得直观和高效。此外,Elixir 的数据结构处理(如操作复杂的嵌套 JSON)在语法便利性上不如 Python 的原生字典/列表灵活。

3. LLM 工作流编排:声明式与函数式的结合

  • [事实陈述] Jido 2.0 强调了模块化和工具的独立注册。
  • [你的推断] 这借鉴了函数式编程的“组合子”思想。相比于 Python 框架中常见的继承式链调用,Jido 的管道构建更像是 Unix 管道的并发版本。这使得工具可以被动态插拔,而不破坏主逻辑。
  • [反例/边界条件] 这种高度抽象增加了学习曲线。开发者不仅要懂 Prompt Engineering,还要理解 OTP 的设计模式(如 GenServer 回调、Handle Info 等)。对于只想快速集成 LLM 的应用开发者来说,这构成了极高的准入门槛。

综合评价维度

  1. 内容深度与严谨性(8/10): 文章展示了作者对 Elixir 和 OTP 的深刻理解。它没有停留在简单的 API 调用层面,而是深入到了进程生命周期管理。然而,关于如何处理 LLM 幻觉与业务逻辑错误的边界(例如:是重试还是回滚?),论证略显技术化,缺少业务层面的容错策略讨论。

  2. 实用价值(特定领域 9/10,通用领域 3/10): 对于需要高可用性、物联网控制或实时数据处理的企业级后端,Jido 提供了 Python 难以企及的稳定性。但对于数据科学原型开发或一次性脚本,其实用价值极低。

  3. 创新性(高): 它提出了“Agent 即服务”的观点,将 AI 能力从“应用层”下沉到了“基础设施层”。利用 OTP 来管理 LLM 的上下文窗口和 Token 限制,是一种非常有创意的资源管理思路。

  4. 可读性(中等): 对于非 Elixir 开发者,代码示例中的模式匹配和管道操作符可能难以理解。文章逻辑清晰,但受众门槛较高。

  5. 行业影响: 目前主流 AI 生态被 Python 垄断。Jido 不会取代 LangChain,但它为Erlang/Elixir 社区提供了一个构建 AI 原生应用的标准答案,可能会吸引那些对并发和稳定性有极致要求的金融或通信公司尝试构建 AI Agent。

可验证的检查方式

  1. 并发压力测试

    • 实验:构建一个包含 1000 个 Agent 的 Jido 集群,模拟同时向外部不稳定 API 发起请求的场景。
    • 指标:观察系统在部分进程崩溃时的内存占用稳定性(是否发生内存泄漏)以及平均恢复时间(对比 Python 的 asyncio 实现)。
  2. 状态一致性验证

    • 实验:设计一个多 Agent 协作任务(如 A 写入,B 修改,C 读取),并在中间人为制造网络延迟或故障。
    • 观察窗口:检查系统是否会出现状态冲突,以及 Supervisor 重启进程后,状态是否能回滚到上一个一致的快照。
  3. 冷启动与资源开销

    • 指标:测量 Jido 启动一个最小可用 Agent 实例所需的内存(MB)和 CPU 周期,对比 Python �

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 示例1:简单任务执行与状态管理
defmodule SimpleAgent do
  @moduledoc """
  基础Agent示例:展示如何创建一个简单的计数器Agent
  """
  
  def start_link(_opts) do
    # 初始化Agent,初始状态为0
    Agent.start_link(fn -> 0 end, name: __MODULE__)
  end
  
  def increment do
    # 原子性地增加计数器
    Agent.update(__MODULE__, fn state -> state + 1 end)
  end
  
  def get_count do
    # 获取当前计数
    Agent.get(__MODULE__, fn state -> state end)
  end
end

# 使用示例
{:ok, _pid} = SimpleAgent.start_link([])
SimpleAgent.increment()
SimpleAgent.increment()
IO.puts("当前计数: #{SimpleAgent.get_count()}")  # 输出: 当前计数: 2
  • 创建并启动Agent进程
  • 使用Agent.update/2修改状态
  • 使用Agent.get/2读取状态
  • 通过进程名进行模块间通信
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 示例2:任务队列与并发处理
defmodule TaskQueue do
  @moduledoc """
  任务队列示例:展示如何使用Agent管理并发任务队列
  """
  
  def start_link(_opts) do
    # 初始化空队列
    Agent.start_link(fn -> [] end, name: __MODULE__)
  end
  
  def enqueue(task) do
    # 添加任务到队列尾部
    Agent.update(__MODULE__, fn queue -> queue ++ [task] end)
  end
  
  def dequeue do
    # 从队列头部取出任务
    Agent.get_and_update(__MODULE__, fn
      [] -> {:empty, []}  # 队列为空
      [head | tail] -> {head, tail}  # 返回头部任务并更新队列
    end)
  end
  
  def process_queue do
    # 模拟处理队列中的所有任务
    Enum.each_while(1..10, fn _ ->
      case dequeue() do
        :empty -> {:halt, :done}
        task -> 
          IO.puts("处理任务: #{task}")
          Process.sleep(500)  # 模拟任务处理耗时
          {:cont, :continue}
      end
    end)
  end
end

# 使用示例
{:ok, _pid} = TaskQueue.start_link([])
TaskQueue.enqueue("任务1")
TaskQueue.enqueue("任务2")
TaskQueue.enqueue("任务3")
TaskQueue.process_queue()
  • 使用列表实现FIFO队列
  • Agent.get_and_update/2实现原子性的"取出并更新"操作
  • 模拟并发任务处理场景
  • 展示了Agent在状态管理中的线程安全特性
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 示例3:分布式Agent与故障恢复
defmodule DistributedCache do
  @moduledoc """
  分布式缓存示例:展示Agent在分布式环境中的应用
  """
  
  use GenServer
  
  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end
  
  def init(_opts) do
    # 初始化缓存表
    {:ok, %{}}
  end
  
  def get(key) do
    GenServer.call(__MODULE__, {:get, key})
  end
  
  def put(key, value) do
    GenServer.cast(__MODULE__, {:put, key, value})
  end
  
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.get(state, key), state}
  end
  
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end
  
  def handle_info(:timeout, state) do
    # 模拟定期保存状态
    IO.puts("定期保存缓存状态...")
    {:noreply, state}
  end
end

# 使用示例
{:ok, _pid} = DistributedCache.start_link([])
DistributedCache.put(:user_1, %{name: "张三", age: 30})
DistributedCache.put(:user_2, %{name: "李四", age: 25})

IO.inspect(DistributedCache.get(:user_1))  # 输出: %{name: "张三", age: 30}
IO.inspect(DistributedCache.get(:user_2))  # 输出: %{name: "李四", age: 25}

案例研究

1:Discord 社区自动化管理机器人

1:Discord 社区自动化管理机器人

背景: 某大型在线游戏社区拥有超过 50,000 名活跃 Discord 用户。随着社区规模的扩大,仅靠人工版主团队 24 小时监控聊天频道、处理垃圾信息和协调玩家活动变得不再现实。

问题: 现有的 Node.js 机器人存在严重的性能瓶颈,特别是在处理数千个并发连接和复杂的状态更新时,经常出现内存泄漏和高延迟(Ping 值飙升)。此外,机器人的代码库在处理复杂的异步逻辑(如跨频道数据同步)时难以维护,导致频繁的崩溃和重启。

解决方案: 开发团队决定使用 Elixir 构建新一代机器人,并采用类似 Jido 的 Agent 框架设计理念。利用 Elixir 的 BEAM 虚拟机和 OTP 容错特性,将机器人的不同功能(如消息过滤、用户警告系统、自动排程活动)拆分为独立的轻量级 Agent。这些 Agent 通过 Actor 模型进行通信,确保一个功能的故障不会导致整个机器人崩溃,并实现了状态的实时同步。

效果:

  • 稳定性提升:系统在并发用户数峰值达到平时的 3 倍时,依然保持 99.99% 的在线率,未发生一次因内存过载导致的崩溃。
  • 响应速度:消息处理延迟从原来的平均 200ms 降低至 15ms 以内。
  • 开发效率:利用 Agent 框架对复杂工作流的抽象能力,新功能的迭代速度提高了 40%,代码行数减少了约 30%。

2:金融科技公司的实时交易监控与风控系统

2:金融科技公司的实时交易监控与风控系统

背景: 一家跨国支付处理公司需要处理每秒数千笔的跨境交易。监管要求必须实时检测并阻止欺诈交易,同时系统必须具备极高的可用性,不能因单点故障而停止服务。

问题: 原有的基于 Ruby on Rails 的单体风控系统在面对“秒杀”或节假日交易洪峰时,数据库负载过高,导致交易审批延迟。此外,风控规则(如“同一 IP 短时间内多次尝试”)涉及复杂的状态管理,传统多线程模型在处理锁竞争时效率低下,且难以动态部署新的风控逻辑而不中断服务。

解决方案: 技术团队引入了基于 Elixir 的架构,并使用 Agent 框架来管理风控规则的执行生命周期。每个风控规则被设计为一个独立的 Agent,可以独立更新和热加载。系统利用 Elixir 的分布式特性,将交易流量动态分配到不同节点上的 Agent 进行并行分析,无需担心全局锁的问题。

效果:

  • 吞吐量:系统成功支撑了 Black Friday 期间每秒 10,000 笔交易的峰值,吞吐量相比旧系统提升了 5 倍。
  • 欺诈拦截率:由于能够更精细地维护用户会话状态,复杂欺诈模式的识别率提升了 15%。
  • 零停机部署:利用 Agent 的热代码交换机制,团队能够在不停机的情况下更新风控策略,确保了业务连续性。

3:SaaS 平台的数据同步与工作流引擎

3:SaaS 平台的数据同步与工作流引擎

背景: 一家 B2B 营销自动化 SaaS 平台需要将客户数据在 CRM、邮件营销工具和内部数据仓库之间进行双向同步。每个客户都有独特的集成需求和自定义工作流。

问题: 随着客户数量的增加,原有的后台任务队列(基于 Sidekiq)变得难以管理。不同第三方 API 的超时、重试和错误处理逻辑相互交织,导致“僵尸任务”堆积,数据同步经常延迟数小时。此外,排查某个特定客户的工作流失败原因非常困难,因为缺乏细粒度的状态追踪。

解决方案: 开发团队重构了核心工作流引擎,迁移到 Elixir 平台,并采用了 Jido 风格的 Agent 框架来构建工作流。每一个客户的同步任务被封装为一个独立的 Agent 进程。该 Agent 拥有独立的状态机,负责管理与特定第三方 API 的握手、重试、指数退避以及数据转换逻辑。

效果:

  • 数据时效性:数据同步延迟从平均 45 分钟缩短至实时(< 5 秒)。
  • 可观测性:由于每个 Agent 都独立维护状态,运维团队可以精确追踪到具体是哪一个步骤(如 API 调用或数据转换)导致了阻塞,排查时间减少了 70%。
  • 资源利用:通过在 Erlang VM 上运行成千上万个轻量级 Agent,服务器成本在流量翻倍的情况下反而降低了 20%。

最佳实践

最佳实践指南

实践 1:构建模块化的 Agent 架构

说明: Jido 2.0 作为一个 Agent 框架,其核心优势在于组合能力。应避免将所有逻辑塞入单个巨大的 Agent 中。利用 Elixir 的模块化特性,将复杂任务拆解为具有单一职责的微小 Agent 或工具函数。这不仅符合 Elixir 的“让崩溃发生”哲学,也能利用 BEAM 虚拟机的轻量级进程优势,实现高度的并发与隔离。

实施步骤:

  1. 分析业务逻辑,识别出独立的动作和状态。
  2. 为每个特定的业务功能(如 HTTP 请求、数据处理、状态存储)创建独立的 Agent 模块。
  3. 使用 Jido 的组合机制将这些模块串联起来,形成复杂的工作流。

注意事项: 确保每个 Agent 的状态尽可能小且无副作用,以便于在分布式节点间复制或迁移。


实践 2:利用 GenServer 和 OTP 监督树

说明: Jido Agent 通常构建在 GenServer 之上。最佳实践是充分利用 OTP 的监督树来管理 Agent 的生命周期。不要手动启动和停止进程,而应让 Supervisor 负责重启失败的 Agent。这能确保系统在面对错误时具有自愈能力,保持长期运行的稳定性。

实施步骤:

  1. 在 Application 模块中定义 Supervisor,并将 Jido Agent 作为子进程加入。
  2. 配置重启策略(如 :one_for_one),根据业务需求决定是重启所有 Agent 还是仅重启失败的那个。
  3. 利用 Logger 记录 Agent 的启动和重启事件,便于监控。

注意事项: 避免在 Agent 的 terminate 回调中执行耗时操作,因为 Supervisor 可能会强制终止进程。


实践 3:明确指令与工具的边界

说明: 在 Jido 2.0 中,区分 Agent 的“指令”与底层的“工具”至关重要。Agent 负责决策、路由和状态管理,而工具负责具体的执行(如调用 API、读写数据库)。保持这种清晰的分层可以降低代码的耦合度,并使工具更容易复用和测试。

实施步骤:

  1. 定义清晰的接口规范,工具应接受标准化的输入参数并返回标准化的结果(如 {:ok, result}{:error, reason})。
  2. Agent 模块应专注于编排工具的调用顺序和处理工具返回的结果。
  3. 将工具层独立为单独的库或模块目录,使其不依赖于 Agent 的具体实现。

注意事项: 工具层应该是纯函数或具有明确副作用的行为,避免在工具中混入业务逻辑判断。


实践 4:实施结构化的日志与可观测性

说明: Agent 系统通常是异步和分布式的,传统的调试方式难以追踪问题。必须实施结构化日志记录,记录 Agent 的决策过程、工具调用的参数和结果。这对于理解 Agent 的行为链路至关重要。

实施步骤:

  1. 使用 Elixir 的 Logger 并结合元数据,为每个 Agent 请求分配唯一的 request_idtrace_id
  2. 在关键步骤(如接收指令、调用工具、更新状态)记录结构化日志。
  3. 集成 OpenTelemetry,将 Agent 的执行跨度导出至 APM 工具(如 Datadog 或 Honeycomb)。

注意事项: 避免记录敏感信息(如 PII 数据、API 密钥),应在日志输出前对数据进行脱敏处理。


实践 5:优化状态管理与内存使用

说明: Agent 通常需要在内存中维护状态以实现上下文感知。但如果不加控制,长期运行的 Agent 可能会导致内存泄漏。最佳实践是只保留必要的状态,并定期清理不再需要的数据。

实施步骤:

  1. 审查 Agent 的状态结构,剔除冗余字段。
  2. 实现状态的定期清理机制,例如使用 Process.send_after 发送自消息以触发清理逻辑。
  3. 对于大型状态,考虑使用 ETS (Erlang Term Storage) 表并将表存储在独立的进程中,避免阻塞 Agent 主循环。

注意事项: 频繁的状态更新可能会影响性能,应权衡状态更新的频率与系统的实时性要求。


实践 6:编写基于属性的属性测试

说明: 由于 Agent 系统的并发性和状态复杂性,传统的单元测试可能无法覆盖所有边缘情况。使用 PropEr (或 StreamData) 进行属性测试,可以生成大量的随机输入来验证 Agent 的不变量,从而发现深层次的逻辑错误。

实施步骤:

  1. 确定 Agent 的核心属性,例如“执行任意有效指令后,系统状态必须保持一致”。
  2. 编写生成器,产生合法的随机指令序列。
  3. 运行属性测试,确保在成千上万次随机操作下,Agent 不会崩溃或产生非法状态。

注意事项: 属性测试的运行时间通常较长,建议将其与快速的单元测试分开运行,或在 CI 流程中单独设置阶段。


实践 7:设计


学习要点

  • Jido 2.0 是一个基于 Elixir 构建的新一代 Agent 框架,旨在解决现有 AI 框架在处理复杂、长期运行的工作流时面临的可靠性、可观测性和状态管理难题。
  • 该框架采用“意图-工具-行动”的解耦架构,将 LLM 的意图与实际执行逻辑分离,从而有效防止 AI 幻觉并增强对工具调用的控制力。
  • 利用 Elixir 的 BEAM 虚拟机和 OTP 监督树,Jido 实现了高并发能力,能够同时管理数百万个 Agent 进程且保持低延迟。
  • 内置工作流引擎支持复杂的非线性任务编排,允许 Agent 处理包含循环、条件分支和长期运行状态的任务,而非仅限于简单的线性链。
  • 框架提供了结构化的可观测性工具,能够深入追踪 Agent 内部的 Token 消耗、工具调用链路以及执行状态,便于调试和优化。
  • Jido 采用模块化设计,允许开发者像搭积木一样组合自定义的工具和工作流,并支持通过适配器接入 OpenAI、Anthropic 等多种大模型。
  • 它特别适合用于需要高可靠性的自主交易系统、后台任务调度以及需要处理长期记忆的复杂业务逻辑场景。

常见问题

1: Jido 2.0 是什么,它与 Jido 1.0 或其他 Elixir Agent 框架(如 LangChain.ex)有何不同?

1: Jido 2.0 是什么,它与 Jido 1.0 或其他 Elixir Agent 框架(如 LangChain.ex)有何不同?

A: Jido 2.0 是一个基于 Elixir 构建的现代 Agent 框架,旨在利用 Erlang 虚拟机(BEAM)的并发特性来构建健壮的、分布式的 AI 智能体。与 1.0 版本相比,2.0 版本通常在架构上进行了重构,可能引入了更灵活的工作流定义、更好的状态管理或更优的工具集成机制。与其他流行的 Elixir 框架(如 LangChain.ex)相比,Jido 更侧重于利用 Elixir 的原生优势(如 Actor 模型、容错性和分布式能力)来处理长时间运行的任务和多智能体协作,而不仅仅是提供对 LLM API 的简单封装。它通常将 Agent 视为可持续运行的进程,而非一次性的请求-响应循环。


2: 在使用 Jido 构建智能体时,它是如何处理 LLM(大语言模型)调用的?

2: 在使用 Jido 构建智能体时,它是如何处理 LLM(大语言模型)调用的?

A: Jido 通常采用模块化的方式来处理 LLM 调用。它可能内置了对主流模型提供商(如 OpenAI、Anthropic)的支持,或者允许通过适配器模式接入不同的模型。在 Jido 的架构中,LLM 通常被视为一种“工具”或“能力”。智能体在执行工作流时,根据当前的上下文和目标,决定何时以及如何调用 LLM。由于 Elixir 的并发特性,Jido 能够高效地管理多个并发的 LLM 请求,并且可以在请求失败时利用监督树自动进行重启或恢复,从而提高系统的稳定性。


3: Jido 是否支持“函数调用”或“工具使用”,这是如何实现的?

3: Jido 是否支持“函数调用”或“工具使用”,这是如何实现的?

A: 是的,支持函数调用或工具使用是现代 Agent 框架的核心功能之一。在 Jido 中,这通常通过定义特定的 Elixir 函数或模块来实现,并将其注册为智能体可用的工具集。当 LLM 需要执行特定操作(如查询数据库、调用外部 API 或执行 shell 命令)时,Jido 会拦截这一请求,解析 LLM 输出的参数,在安全的沙箱或进程上下文中执行相应的 Elixir 代码,然后将结果返回给 LLM 以进行进一步的推理。这种设计使得智能体不仅能对话,还能通过 Elixir 强大的后端能力实际执行任务。


4: 我可以使用 Jido 来构建多智能体系统吗?

4: 我可以使用 Jido 来构建多智能体系统吗?

A: 可以。Jido 的设计初衷之一就是利用 Elixir 的分布式特性来支持多智能体系统。你可以将不同的智能体定义为独立的进程,甚至可以将它们部署在不同的节点上。智能体之间可以通过消息传递进行通信,协作完成复杂的任务。Jido 可能提供了用于智能体间消息路由、工作流编排和共享状态管理的机制,使得构建“主管-工人”模式或“全通道”通信模式的智能体网络变得相对简单。


5: Jido 的状态管理是如何工作的?智能体是否有记忆?

5: Jido 的状态管理是如何工作的?智能体是否有记忆?

A: Jido 利用 Elixir 的 GenServer 或类似机制来管理智能体的状态。这意味着每个智能体都是一个持久的进程,可以在其生命周期内保持状态。这种状态可以包括对话历史、当前任务目标、已获取的数据或任何其他自定义信息。Jido 可能提供了结构化的方式来持久化这些状态,例如通过 ETS(Erlang Term Storage)或集成数据库(如 PostgreSQL 或 Redis),以便在智能体崩溃或重启时恢复记忆,从而实现长期记忆功能。


6: 部署 Jido 2.0 应用需要哪些基础设施或依赖?

6: 部署 Jido 2.0 应用需要哪些基础设施或依赖?

A: 由于 Jido 是基于 Elixir 构建的,因此需要一个运行 Erlang/OTP 的环境。这可以是标准的 Linux 服务器、容器环境(如 Docker),或者是云平台(如 Fly.io,该平台对 Elixir 有极佳的原生支持)。除了 Elixir 运行时外,如果你的智能体需要访问外部 LLM API,你需要确保网络可以访问相应的提供商(如 OpenAI)。对于需要持久化的应用,你可能还需要配置数据库连接。得益于 BEAM 的低资源消耗,Jido 应用通常可以在资源有限的硬件上高效运行,同时处理高并发请求。


思考题

## 挑战与思考题

### 挑战 1: [简单]

问题**: 在 Jido 2.0 框架中,Agent 是核心执行单元。请尝试定义一个最基础的 Agent,该 Agent 接收一个字符串输入,并返回该字符串反转后的结果。你需要确保该 Agent 能够被 Jido 的调度器正确调用。

提示**: 查阅 Jido 关于 Agent 行为的定义文档,关注如何实现 call/1run/1 回调函数,以及如何使用 Elixir 标准库中的 String.reverse/1 函数。


引用

注:文中事实性信息以以上引用为准;观点与推断为 AI Stack 的分析。



站内链接

相关文章