网易首页 > 网易号 > 正文 申请入驻

这门国产编程语言,悄悄推出了新功能,碾压Node.js

0
分享至

近日,国产编程语言MoonBit补全了关键语言特性的最后一块拼图:异步编程库moonbitlang/async。

本次发布时间距离MoonBit Beta Release相距仅仅半年,足见MoonBit团队对异步编程的重视。

moonbitlang/async吸收了现有语言的经验和教训,语法更加简洁,基于结构化并发理念,帮助用户写出更健壮、安全的异步程序。未来很可能「占领」包括云服务、AI agent 等重度依赖异步编程的领域。

0 1

什么是异步编程?

你开了一家饭店,雇佣了5个店小二来招待顾客,但是这几个店小二的干活儿的模式一模一样:

客人来到饭店,马上有个店小二殷勤迎上去,带着找座位,点菜,给后厨下单。

由于后厨做菜需要很长时间,店小二就在客人的旁边等着。

后厨一摇铃铛,大喊一声:上菜,店小二马上端到客人面前, 然后站在一边等着客人吃完。

客人说:结账,小二收钱,找钱,送客, 迎接下一位。

由于只有5个店小二,你饭店同时只能招待5个顾客。

很快,你的饭店倒闭了。

倒闭的核心原因就是店小二采用的是“同步模式”,即使有耗时的工作(厨师做菜,顾客吃饭),他也会干等着,非常浪费。

你接受了教训,开了一家新饭店,这次只雇佣了一个店小二,他的工作方式和之前大相径庭:

客人来到饭店,唯一的店小二殷勤迎上去,带着找座位,点菜,给后厨下单

由于后厨做菜需要很长时间,店小二闪电般的离开,去干别的活了,可能是迎客,点菜,找座等,总之是那些不用等待,迅速干完的活。

后厨大喊一声:上菜,这个小二马上端到客人面前,然后离开,干其他活。

客人说:结账,小二收钱,找钱,然后还是迅速闪人,干其他活。

这一次,店小二采用的是“异步模式”,即对于耗时的操作,店小二会暂时离开,做其他事儿,等到操作完成以后再回来接着干。

对应到计算机世界,耗时的操作就是访问文件/数据库/网络,网络服务器的线程遇到了这些I/O操作,坚决不能等待,因为服务器收到的请求可不是几十个几百个,而是成千上万个,所以一定要采用异步编程。

但是对程序员来说,异步编程很麻烦,为了支持任务的中断和切换,程序的逻辑会被分散到程序的不同部分,使得开发效率和程序的可维护性极大下降。

所以各种编程语言Go/Rust/Python/JS都在语言层面直接支持异步编程,降低程序员的负担,MoonBit也不例外。

0 2

MoonBit 异步性能优势

MoonBit 的异步运行时在底层基于线程池并结合epoll/kqueue实现,支持 Linux 与 macOS 的 native 后端。其设计思路与 Node.js 类似:采用单线程、多任务模型。

在这一模式下,异步程序中的同步部分始终在同一线程上执行。对开发者而言,这带来显著的简化效果:程序的行为与单线程应用一致,无需额外加锁,也不必担心竞争条件等并发错误。

虽然仍处于早期阶段,这一运行时已经展现出出色的性能表现。

为了检验 MoonBit 异步运行时的性能,我们搭建了一个简单的 TCP 服务器:它会把收到的数据原样返回给客户端。这个测试几乎没有计算成分,因此能够直接反映运行时在高并发场景下的处理能力。

在测试中,我们同时维持多个连接,不断收发数据,并记录吞吐量和响应延迟。结果显示,MoonBit 在并发连接数不断增加的情况下,依然保持了优异的吞吐表现和极低的响应延迟,充分体现了其运行时系统的高效性和稳定性。

对比的对象是 Node.js 和 Go 语言。

性能测试的结果如下:

测试结果显示,MoonBit 在 200 到 1000 个并发连接下始终保持最高吞吐量,在高并发场景中明显优于 Node.js 和 Go。这表明其异步运行时具备出色的扩展性。

在高并发场景下,MoonBit 的平均延迟始终保持在个位数毫秒,即便在 1000 个连接时也只有 4.43ms;相比之下,Node.js 延迟超过 116ms。这意味着 MoonBit 的异步运行时能够在大规模连接下依然保持快速响应。

下面是一个 HTTP 服务器的例子,相比 TCP 服务器,HTTP 例子需要进行 HTTP 协议的解析,有更多的计算成分,不是单纯的 I/O。

这个测试会使用 (github.com/wg/wrk) 工具,通过多个连接不断向 HTTP 服务器发送 GET / HTTP/1.1 的请求,服务器应当返回一个空的回复。测试会记录服务器每秒处理的请求数以及每个请求的平均延迟。测试的结果如下:

可以看到,得益于 MoonBit 语言本身的优秀性能,在这个测试中 MoonBit 依然表现良好。

MoonBit 在所有并发连接数下的请求处理效率和延迟都稳定高于 Node.js 和单线程的 Go。

0 3

构建简单代码智能体的示例

MoonBit 不只是写服务器更方便,它甚至能直接驱动 AI 智能体。下面,我们就用它构建一个最小可运行的代码智能体(Code Agent)。

这个代码智能体除了可以调用大模型,还支持工具调用(如读取本地文件,执行ls命令等)。

例如,用户的请求是:请读取本地文件 /home/user/data.txt 并告诉我里面的内容。

代码智能体会把这个消息发给大模型,并且告诉大模型,我这里有两个工具可以调用,工具的名称,参数也给你发过去了。

大模型看到看到“请读取本地文件......”,它当然不会直接读取文件,而是看看根据智能体都发来了什么样的工具,然后发挥自己的强项,选择对应的工具,生成调用请求:

{
  "tool_calls": [
    {
      "function": {
        "name": "read_file"
      },
      "arguments": {
        "path": "/home/user/data.txt"
      }
    }
  ]
}

智能体收到大模型发回的工具调用请求,执行真正的工具调用,读取 /home/user/data.txt,假设结果是:MoonBit is the future programming language!

智能体会将结果包装成消息,发送给大模型模型,大模型收到工具返回的内容后,会判断:“我已经得到了文件内容,不需要再调用工具了,我可以生成最终回答”

最终响应可能是这样的:

{
  "role": "assistant",
  "content": "我已经读取了文件 /home/user/data.txt,里面的内容是:\nMoonBit is the future programming language!"
}

在这个代码智能体中,需要处理网络调用,文件读取,命令执行,会使用MoonBit的这些异步操作:

1. @http.post 发送消息到 LLM 接口。

2. @fs.read_file 从文件读取内容。

3. @process.collect_output_merged 来执行外部程序并收集其输出。

值得注意的是,在MoonBit中所有异步函数调用默认会被隐式 await,并且异步调用实现了结构化并发(Structured Concurrency) ,这意味着MoonBit 的异步程序几乎不可能产生僵尸后台任务,并且程序员能够更加容易地理解并分析异步代码的行为。

1、向 LLM 接口发起请求

MoonBit 异步网络库提供了 @http.post 用于发送 HTTP POST 请求。我们可以简单地将其包装一下,用来更方便地发送消息到 LLM:

///|
async fn generate(request : Request) -> Response {
  let (response, body) = @http.post(
    "\{base_url}/chat/completions",
    request.to_json(),
    headers={
      "Authorization": "Bearer \{api_key}",
      "Content-Type": "application/json",
      "Connection": "close",
    },
  )
  guard response.code is (200..=299) else {
    fail("HTTP request failed: \{response.code} \{response.reason}")
  }
  body.json() |> @json.from_json()
}

接下来,我们将展示如何让 LLM 使用工具。

2、定义工具

为了让代码智能体更有用,我们需要通过工具扩展它与外部世界交互的能力。

请求体中的 "tools" 字段描述了我们向 LLM 提供的工具。一个典型的工具描述包含以下字段:

  • name:工具名称,将在工具调用中使用。

  • description:对工具的简短描述。

  • parameters:描述工具参数的 JSON Schema。本示例中为简化处理,我们只使用 type、properties 和 required 字段。

例如,下面的 JSON 描述了一个名为 read_file 的工具:

{
  "name": "read_file",
  "description": "Read a file from local disk",
  "parameters": {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "The path of the file to read"
      }
    },
    "required": ["path"]
  }
}

我们在 MoonBit 中将该工具描述建模为如下结构:

///|
struct Tool {
  name : String
  description : String
  parameters : Json
  /// 执行工具的函数
  execute : async (String) -> String
}

在本演示中,我们将定义两个简单工具:

read_file:从本地磁盘读取文件。

execute_command:执行一个外部程序。

3、read_file 工具

使用 moonbitlang/async 与文件系统交互非常简单。可以直接使用 @fs.read_file/@fs.write_file 来进行对文件的读取/写入。对于更加灵活的需求,moonbitlang/async 也提供了 @fs.open ,用户可以传入自定义选项,并在后续调用 read / write 方法进行 I/O 操作。

我们可以将 read_file 工具实现为:

///|
let read_file_tool : Tool = {
  name: "read_file",
  description: "Read a file from local disk",
  parameters: {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "The path of the file to read",
      },
    },
    "required": ["path"],
  },
  execute: args => {
    guard @json.parse(args) is { "path": String(path), .. } else {
      fail("Invalid arguments for read_file, expected {\"path\": String}")
    }
    @moonbitlang/async/fs.read_file(path).text()
  },
}

4、execute_command 工具

在 moonbitlang/async 中实现 execute_command 工具也非常简单。我们可以使用 @process.collect_output_merged 来执行一个外部程序,并收集其 stdout 和 stderr 输出。

对于更高级的需求,我们可以使用 @process.run 来启动一个进程,并通过管道(pipe)与其交互。

execute_command 工具实现如下:

///|
let execute_command_tool : Tool = {
  name: "execute_command",
  description: "Execute an external program",
  parameters: {
    "type": "object",
    "properties": {
      "command": { "type": "string", "description": "The command to execute" },
      "arguments": {
        "type": "array",
        "items": { "type": "string" },
        "description": "The arguments to pass to the command",
      },
    },
    "required": ["command", "arguments"],
  },
  execute: arguments => {
    guard @json.parse(arguments)
      is { "command": String(command), "arguments": arguments, .. } else {
      fail(
        "Invalid arguments for execute_command, expected {\"command\": String, \"args\": Array[String]}",
      )
    }
    let arguments : Array[String] = @json.from_json(arguments)
    let (status, output) = @process.collect_output_merged(
      command,
      arguments.map(argument => argument),
    )
    let output = output.text()
    (
      $|Exit status: \{status}
      $|Output:
      $|\{output}
    )
  },
}

5、处理工具调用与智能体主循环

得到大模型发回的工具调用请求以后,代码智能体需要进行处理,使用的是这个异步函数:

///|
async fn handle_tool_call(
  tools : Map[String, Tool],
  tool_call : ToolCall,
) -> Json {
  guard tools.get(tool_call.function.name) is Some(tool) else {
    return {
      "role": "tool",
      "content": "Tool not found: \{tool_call.function.name}",
      "tool_call_id": tool_call.id,
    }
  }
  return {
    "role": "tool",
    "content": (tool.execute)(tool_call.function.arguments),
    "tool_call_id": tool_call.id,
  } catch {
    error =>
      {
        "role": "user",
        "content": "Error executing tool \{tool_call.function.name}: \{error}",
      }
  }
}

有了处理工具调用的能力后,我们就可以实现智能体的主循环了。我们定义了一个 Agent 结构来保存智能体状态,包括工具集合、对话历史和消息队列:

///|
struct Agent {
  tools : Map[String, Tool]
  conversation : Array[Json]
  mut message_queue : Array[Json]
}

然后我们为 Agent 实现 run 方法,持续处理消息队列中的消息,直到队列为空:

///|
async fn Agent::run(self : Agent) -> Unit {
  while !self.message_queue.is_empty() {
    // Take all messages from the message queue
    let messages = self.message_queue
    self.message_queue = []
    // Send the messages to LLM endpoint
    let response = generate({
      model,
      messages: [..self.conversation, ..messages],
      tools: self.tools.values().collect(),
    })
    let response = response.choices[0].message
    // Save the response to the conversation history
    self.conversation.push(response)
    if response is { "content": String(content), .. } {
      // Print the assistant's response
      println("Assistant: \{content}")
    }
    let tool_calls : Array[ToolCall] = if response
      is { "tool_calls": tool_calls, .. } {
      @json.from_json(tool_calls)
    } else {
      []
    }
    // Handle tool calls
    for tool_call in tool_calls {
      let message = handle_tool_call(self.tools, tool_call)
      self.message_queue.push(message)
      println("Tool: \{tool_call.function.name}")
      println("Response: \{message.stringify(indent=2)}")
    }
  }
}

大功告成,接下来测试一下。

让这个智能体获取当前时间,并把结果告诉我们:

///|
async test "agent/current-time" {
  let agent = Agent::{
    tools: {
      "read_file": read_file_tool,
      "execute_command": execute_command_tool,
    },
    conversation: [],
    message_queue: [],
  }
  agent.message_queue.push({
    "role": "user",
    "content": "Can you please tell me what time is it now?",
  })
  agent.run()
}

0 4

结论

在这篇文章中,我们展示了如何使用 moonbitlang/async 构建一个简单的代码智能体。该智能体可以通过调用工具从本地磁盘读取文件并执行外部程序。当然,这只是一个基础示例,市面上的智能体通常会更加复杂,例如会添加更多工具、更优雅地处理错误、实现更复杂的对话流程等。

如果你想了解 moonbitlang/async 的更多信息,请参阅其文档。你也可以查看 maria 项目源码,了解我们是如何基于 moonbitlang/async 构建代码智能体的。

(1) MoonBit 再添异步能力,实现 AI Agent 高效与稳定开发:

https://mp.weixin.qq.com/s/t5k9bUmuE-rs3qaGB0yLVw

(2) AI Agent 案例完整代码:

https://gist.github.com/tonyfettes/2953d5bef1610fce12cca05ea20655e2

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
夺冠,全红婵还能回国家队?复出首秀夺冠,谁注意她体型变化

夺冠,全红婵还能回国家队?复出首秀夺冠,谁注意她体型变化

乐聊球
2025-11-02 21:45:47
CBA新赛季唯一一支全华班?曝昔日总冠军不找外援,或面临降级

CBA新赛季唯一一支全华班?曝昔日总冠军不找外援,或面临降级

老叶评球
2025-11-02 17:16:06
“良心坏透”的5个生活用品,很多人每天用,却不知其中危害

“良心坏透”的5个生活用品,很多人每天用,却不知其中危害

家居美少女
2025-10-23 15:34:22
乌军投降了,红军城大鱼落网,俄凌晨击落6架敌机,基辅封锁消息

乌军投降了,红军城大鱼落网,俄凌晨击落6架敌机,基辅封锁消息

影孖看世界
2025-11-02 23:36:09
镇江一小区楼栋发生沉降居民撤离,墙体严重变形露出砖块,附近商户:小区有二三十年历史

镇江一小区楼栋发生沉降居民撤离,墙体严重变形露出砖块,附近商户:小区有二三十年历史

极目新闻
2025-11-02 17:55:34
这是我见过的五官最精致的女性,一脸福相,以后会贵不可言

这是我见过的五官最精致的女性,一脸福相,以后会贵不可言

手工制作阿歼
2025-10-28 14:03:28
马筱梅一家在成都,两姐弟露脸菻菻抗拒张兰,重男轻女评论区翻车

马筱梅一家在成都,两姐弟露脸菻菻抗拒张兰,重男轻女评论区翻车

冷紫葉
2025-11-02 21:41:08
新王登场!新机官宣:搭载骁龙8 Elite Gen5+小直屏!

新王登场!新机官宣:搭载骁龙8 Elite Gen5+小直屏!

科技堡垒
2025-11-02 11:17:56
27分大胜!火箭击退凯尔特人3连胜,杜兰特26分申京16+10+9

27分大胜!火箭击退凯尔特人3连胜,杜兰特26分申京16+10+9

湖人崛起
2025-11-02 10:18:53
司晓迪硬刚李云迪:睡我、拉黑、玩消失!

司晓迪硬刚李云迪:睡我、拉黑、玩消失!

TVB的四小花
2025-11-01 08:37:26
皇马,再见!阿隆索拍板,5000万“嫡系”离队!9000万节拍器来投

皇马,再见!阿隆索拍板,5000万“嫡系”离队!9000万节拍器来投

头狼追球
2025-11-02 16:33:56
知名男演员自曝,被亲戚骗光所有积蓄

知名男演员自曝,被亲戚骗光所有积蓄

极目新闻
2025-11-02 10:28:03
7项第一+3项第二!火箭蜕变太快还补PG吗?阿门奥科吉均有大贡献

7项第一+3项第二!火箭蜕变太快还补PG吗?阿门奥科吉均有大贡献

颜小白的篮球梦
2025-11-03 06:19:12
运动可能影响寿命!医生提醒:65岁以后,牢记运动“6不要”

运动可能影响寿命!医生提醒:65岁以后,牢记运动“6不要”

观星赏月
2025-10-31 14:18:54
反人类设计!东莞一公园长石凳上安装金属扶手,为驱赶流浪汉躺卧

反人类设计!东莞一公园长石凳上安装金属扶手,为驱赶流浪汉躺卧

火山诗话
2025-11-02 10:11:42
黄金又出大事了,目前已经有个人想要出售黄金,但是金店拒绝回收

黄金又出大事了,目前已经有个人想要出售黄金,但是金店拒绝回收

流苏晚晴
2025-11-02 16:32:42
NBA战报:雷霆137-106轻取鹈鹕取NBA7连胜,亚历山大30+2+7

NBA战报:雷霆137-106轻取鹈鹕取NBA7连胜,亚历山大30+2+7

懂球帝
2025-11-03 06:57:06
高中时期你经历过哪些炸裂事迹?网友:大家的青春都这么污的吗

高中时期你经历过哪些炸裂事迹?网友:大家的青春都这么污的吗

带你感受人间冷暖
2025-10-03 00:20:08
莫雷4-0击败日本神童,踢台桌扔球,松岛崩盘吃警告!

莫雷4-0击败日本神童,踢台桌扔球,松岛崩盘吃警告!

阿籫你好
2025-11-03 02:27:31
一场3-1让曼城超越利物浦,哈兰德踢疯了:13场狂轰17球

一场3-1让曼城超越利物浦,哈兰德踢疯了:13场狂轰17球

足球狗说
2025-11-03 06:58:35
2025-11-03 07:16:49
码农翻身 incentive-icons
码农翻身
有趣且硬核的技术文章
191文章数 555关注度
往期回顾 全部

科技要闻

10月零跑突破7万辆,小鹏、蔚来超4万辆

头条要闻

马来西亚首富之子买上海大平层 449平米1.17亿元

头条要闻

马来西亚首富之子买上海大平层 449平米1.17亿元

体育要闻

这个日本人,凭啥值3.25亿美元?

娱乐要闻

陈道明被王家卫说他是阴阳同体的极品

财经要闻

段永平捐了1500万元茅台股票!本人回应

汽车要闻

神龙汽车推出“发动机终身质保”政策

态度原创

房产
旅游
亲子
健康
教育

房产要闻

中粮(三亚)国贸中心ITC首期自贸港政策沙龙圆满举行

旅游要闻

这道金黄的绝美秋景里藏着古韵京华

亲子要闻

我发现一个带娃永远不生气的理论

核磁VS肌骨超声,谁更胜一筹?

教育要闻

背诵很多遍依旧写不出来,要让自己根据真题练习模拟写作

无障碍浏览 进入关怀版