網易首頁 > 網易號 > 正文 申請入駐

這門國產編程語言,悄悄推出了新功能,碾壓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.

相關推薦
熱點推薦
丟人!假空姐大鬧亞航后續:賬號被扒正臉曝光,十級美顏淪為笑話

丟人!假空姐大鬧亞航后續:賬號被扒正臉曝光,十級美顏淪為笑話

壹月情感
2026-04-25 13:23:50
NBA季后賽明天4月26日賽程:雷霆沖擊賽點,掘金PK狼隊避免3連敗

NBA季后賽明天4月26日賽程:雷霆沖擊賽點,掘金PK狼隊避免3連敗

薇說體育
2026-04-25 16:52:59
下課倒計時!火箭3連敗+烏杜卡遭完爆,拒絕哈登加盟,你后悔嗎?

下課倒計時!火箭3連敗+烏杜卡遭完爆,拒絕哈登加盟,你后悔嗎?

兵哥籃球故事
2026-04-25 15:57:34
全世界會發現,伊朗戰爭打完后,世界只剩下一個超級大國了

全世界會發現,伊朗戰爭打完后,世界只剩下一個超級大國了

深析古今
2026-04-14 00:35:58
老人1500元賣掉自家大狗,小孫女舍不得哭得撕心裂肺,狗販心軟不收了,只要求退回1300元,“那200塊就當補償小姑娘吧”

老人1500元賣掉自家大狗,小孫女舍不得哭得撕心裂肺,狗販心軟不收了,只要求退回1300元,“那200塊就當補償小姑娘吧”

觀威海
2026-04-24 10:56:04
用了這么多年Mac才發現,我天天拔插充電線的樣子像個傻子…

用了這么多年Mac才發現,我天天拔插充電線的樣子像個傻子…

小柱解說游戲
2026-04-22 11:19:09
出乎意料!2032奧運主辦地確定了,不知名小城市以72:5碾壓當選

出乎意料!2032奧運主辦地確定了,不知名小城市以72:5碾壓當選

安珈使者啊
2026-04-24 11:44:45
猛降14℃!降溫、降雨、雷暴大風即將抵達長沙!另外,“五一”假期長沙天氣最新預測→

猛降14℃!降溫、降雨、雷暴大風即將抵達長沙!另外,“五一”假期長沙天氣最新預測→

芒果都市
2026-04-25 15:31:14
鄭愷苗苗合體露面被偶遇,手挽手同行超甜蜜,真夫妻就是好嗑

鄭愷苗苗合體露面被偶遇,手挽手同行超甜蜜,真夫妻就是好嗑

扒蝦侃娛
2026-04-24 16:32:53
斯馬特加時賽拿下8分,97年以來湖人球員僅次于科比

斯馬特加時賽拿下8分,97年以來湖人球員僅次于科比

懂球帝
2026-04-25 12:50:45
她為兒子息影,卻在丈夫肝癌去世后,從主持人到演員,靠演技出圈

她為兒子息影,卻在丈夫肝癌去世后,從主持人到演員,靠演技出圈

以茶帶書
2026-04-25 15:05:16
昔日福建地產首富黃其森卸任泰禾集團董事長,年初剛解除留置,身家曾達200億

昔日福建地產首富黃其森卸任泰禾集團董事長,年初剛解除留置,身家曾達200億

紅星新聞
2026-04-25 14:28:17
全球最疲憊總統宣布退場

全球最疲憊總統宣布退場

劉瀾昌
2026-04-25 08:43:52
TVB老戲骨忍無可忍含淚揭家丑,每月接濟兒子五萬,走了向太老路

TVB老戲骨忍無可忍含淚揭家丑,每月接濟兒子五萬,走了向太老路

翰飛觀事
2026-04-22 19:35:28
萬萬沒想到,打敗彩禮居然是社保

萬萬沒想到,打敗彩禮居然是社保

放牛娃的遐想
2026-03-03 08:34:32
廣東珠海一女神好漂亮, 身高179cm,體重50kg 美的讓人移不開眼

廣東珠海一女神好漂亮, 身高179cm,體重50kg 美的讓人移不開眼

今日搞笑分享
2026-04-06 17:55:16
黃一鳴回應:沒結婚一天找8個男的也沒關系,承認跟40歲大叔交往過

黃一鳴回應:沒結婚一天找8個男的也沒關系,承認跟40歲大叔交往過

林雁飛
2026-04-25 17:07:43
局勢惡化!日本開出危險一槍,中國紅線遭踩踏,東部戰區行動了

局勢惡化!日本開出危險一槍,中國紅線遭踩踏,東部戰區行動了

云舟史策
2026-04-23 07:32:35
我勒個去!25歲還是當家老大,26歲成魚腩替補,他怎么混這么差

我勒個去!25歲還是當家老大,26歲成魚腩替補,他怎么混這么差

球毛鬼胎
2026-04-25 16:13:02
成都一居民小區凌晨起火致5死2傷,記者現場探訪:起火樓棟入口被熏黑,傷者無生命危險

成都一居民小區凌晨起火致5死2傷,記者現場探訪:起火樓棟入口被熏黑,傷者無生命危險

極目新聞
2026-04-25 16:02:45
2026-04-25 17:56:49
碼農翻身 incentive-icons
碼農翻身
有趣且硬核的技術文章
257文章數 650關注度
往期回顧 全部

科技要聞

DeepSeek V4發布!黃仁勛預言的"災難"降臨

頭條要聞

伊朗最高領袖一條腿截肢面部嚴重燒傷 通信方式披露

頭條要聞

伊朗最高領袖一條腿截肢面部嚴重燒傷 通信方式披露

體育要聞

火箭0-3觸發百分百出局定律:本季加時賽9戰8敗

娛樂要聞

鄧超最大的幸運,就是遇見孫儷

財經要聞

90%訂單消失,中東旺季沒了

汽車要聞

2026款樂道L90亮相北京車展 樂道L80正式官宣

態度原創

旅游
家居
親子
藝術
公開課

旅游要聞

繁花未盡 音浪已起——新鄉何以常來常“新”?

家居要聞

自然肌理 溫潤美學

親子要聞

孩子上幼兒園第一天又哭又鬧?幾個原因,家長提前了解

藝術要聞

美國發現一部《十七帖》!這字還原真實的王羲之筆法,放大10倍仍無瑕疵

公開課

李玫瑾:為什么性格比能力更重要?

無障礙瀏覽 進入關懷版