[{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/","section":"Nary's Blog","summary":"","title":"Nary's Blog","type":"page"},{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/series/neovim/","section":"Series","summary":"","title":"Neovim","type":"series"},{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/tags/tools/","section":"Tags","summary":"","title":"Tools","type":"tags"},{"content":"身為用 Neovim 多年的使用者, 我一直在找有沒有最方便用 Neovim review code 的方式, 畢竟自己配置的 Neovim 用得真的太順手了。這篇文章的目的是把我現在的使用方式分享給大家, 希望有更多人加入 Neovim 的行列！\n我目前的配置是:\nWezterm with tmux Neovim (用 codecompanion 插件 review) worktrunk: worktree CLI 配置歷程 # 我現在的 workflow 是受 orca 的啟發, 他是個 ADE (Agent Development Environment), 有以下幾個功能是我非常喜歡的:\n快速開多個 worktrees orca 中我覺得非常實用的功能就是可以快速的打開並管理多個 worktree: 只要貼上 PR link, 他就會自己 create 一個 git worktree 並開成獨立的 workspace。一個 workspace 就很像一個 terminal session, 可以開 terminal tabs / claude tabs, 也可以 split window。這對於要 review 多個 PRs 的 maintainer 來說非常有幫助, 可以同時開好幾個 PR 並行跑, 還可以快速接換。\n貼上 PR link 就會自動 create worktree 並開成獨立 workspace 看 PR 的 diff 其實就是看現在這個 branch 跟 main / master 之間的 diff。有這個功能我們就不用來回切換 github 來看 diff, 直接在一個視窗看就好。\nOrca 的 PR diff 頁面: 左邊 diff, 右邊是變更的檔案列表 在 diff page 上面留 comments for AI 我們可以直接打開 diff 頁面, 並留下一些要問 AI 的 comments, 之後可以單個送出到當前的 AI chat / new chat, 或是全部 comments 一起送出。\n在 diff 上直接留 comment, 之後可單個或全部送給 AI 當然 orca 還是有些不足的地方: 現在好像沒有 editor 支援, 沒辦法 go to definition (github 上看到有 support vscode 之類的 issues). 我現在是開一個 terminal tab \u0026amp; 開 nvim -\u0026gt; 用 diffview 同步 review, 需要問 AI 的再跑到對應的 orca diff 中問 (有點麻煩)。\n現在的配置 # 以下講一下我的配置怎麼做到上面說的三個 orca 的功能:\n1. 快速開多個 worktrees # 這個是用 worktrunk 實現的, 他的用法非常簡單, 假設我要切換到 PR 1234 的 branch 上面 review 的話, 我就用:\nwt switch pr:1234 他就會自動 pull PR + create worktree + 切換到那個 worktree, review 完要把 worktree remove 掉只要在同一個資料夾下面 wt remove 就行, 非常方便。同時也可以用 wt list 看現在所有的 worktrees:\nwt list 列出目前所有的 worktrees 上面 worktrunk 只解決快速切換 worktree, 那要怎麼一次同時開多個 worktree 呢? 這是後就要用 tmux 的 sessions! tmux sessions 功能我就不詳細介紹了, 你可以想成開好幾個 terminal workspaces, 每個 workspace 有自己的 tabs。對同時需要 review 多個 PRs 並同時寫 code 的我來說非常有用。\n我 tmux 裡面有設定快捷鍵 \u0026lt;leader\u0026gt; + N 可以快速的新建 session:\nbind-key N command-prompt -p \u0026#34;new session:\u0026#34; \u0026#34;new-session -s \u0026#39;%%\u0026#39;\u0026#34; 然後 \u0026lt;leader\u0026gt; + s 會打開 sessions list:\nbind-key s choose-tree -Zs -O name 2. Nvim 裡面看 PR 的 diff # 這我覺得對 reviewer 來說是個非常實用的功能, 可以快速的看 diff 並跳到檔案。在 nvim 裡面, 我是用 Diffview.nvim, 他可以做到:\n看本地改動的 diff 看兩個 commit 間的 diff 處理 merge conflicts (有像 JetBrain 的三行式我覺得很讚) 看一個檔案的 history commits 跟 diff 我們要看一個 PR 的 diff 用的就是上面提到的第 2 點, 但每次還要先複製現在的 commit 然後再貼到指令上太花時間的, 所以我加一個 keymap \u0026lt;leader\u0026gt; + gd (config) 可以快速的打開 PR diff。\nYour browser cannot play this video. Download video.\nDiffview.nvim 看 PR diff Note: 這個 plugin 最適配的 color theme 是 tokyonight, 可以看一下這邊的討論去配置: (issue comment), 也可以看一下我的 config。\n3. Nvim 裡面使用 AI # 這部分不局限於上面說的 \u0026ldquo;在 diff page 上面留 comments for AI\u0026rdquo;, 而是我怎麼在 nvim 裡面使用 AI 的 (當然留 comment 也會 cover 到 😄)。\n我用的工具是 codecompanion.nvim, 他可以在 nvim 裡面去 toggle 一個 virtical split window 當作跟 AI 溝通的窗口。這個窗口其實就是個 markdown file, 所以我們可以在上面用 vim 操作。為了方便選取一個區域來問 AI, 我在 config 裡面加了一個 \u0026lt;leader\u0026gt; + cs (config) 的 keymap, 會把我選擇的段落以 @filename:line_number 的格式傳到 code companion 對話框裡面, 讓整個操作更絲滑。\nYour browser cannot play this video. Download video.\ncode companion 簡易操作 除了上面說的功能外, code companion 在最近加入了 code review 的功能, 這個功能跟 Diffview.nvim 一起用非常方便。因為 diff view 一次會有左右兩個視窗, 再開一個 AI chat 的窗口就會比較擁擠, 這時候我們就可以用 code review 功能留下一些 comments, 再一次送出問。 這時候我們就可以用 code review 功能留下一些 comments, 一次送出問 AI 或請 AI 改。\nYour browser cannot play this video. Download video.\n在 diff view 用 code review comment 如果覺得側邊小視窗太小的話, 也可以把它移動到一個新的 tab 上面, 用的是 nvim 內建的 Ctrl-w + T。當然他們是同一個 buffer, 所以彼此的內容是連動的。\nYour browser cannot play this video. Download video.\n在新的 tab 打開 AI chat Code companion 我還有一些其他設定, 像是在 quick fix 中 list 所有的 comments 等等, 可以參考我的 config。\n結尾 # 這就是一些粗淺的介紹, 歡迎參考我的 nvim 跟 tmux 配置。\nbtw code companion 最新的 release 有 herdr support, 說不定我會跳槽過去用看看 😄\n","date":"2026年8月6日","externalUrl":null,"permalink":"/zh-tw/posts/nvim/review-code-with-nvim/","section":"Posts","summary":"分享我用 Neovim review code 的 workflow: worktrunk、tmux sessions、Diffview.nvim 和 codecompanion.nvim","title":"用 Neovim review code","type":"posts"},{"content":" Issue link: https://github.com/ray-project/ray/issues/58213 My PR: https://github.com/ray-project/ray/pull/58914 Issue Description # Originally, cancelling threaded actor task is not supported. This issue is requesting adding the support for cancel threaded actor with an is_canceled flag to detect the cancellation.\nWe are expecting the usage like so:\n@ray.remote class SyncActor: def __init__(self): self.is_canceled = False def long_running_method(self): \u0026#34;\u0026#34;\u0026#34;A sync actor method that checks for cancellation periodically.\u0026#34;\u0026#34;\u0026#34; for i in range(100): # For sync actor tasks, is_canceled() can be checked in the task body if ray.get_runtime_context().is_canceled(): self.is_canceled = True print(\u0026#34;Actor task canceled, cleaning up...\u0026#34;) return \u0026#34;canceled\u0026#34; time.sleep(0.1) return \u0026#34;completed\u0026#34; How to cancel actor # Actor is a stateful worker that keeps the state. When calling actor method, it can access and mutate the worker\u0026rsquo;s state. While it\u0026rsquo;s stateful, it\u0026rsquo;s not safe for us to directly interrupt it like what we did for normal task, as we will lose the current state. The better way is to enable the graceful termination option so that we can clean up before termination, like what we have for async actor.\nFor async actor, it\u0026rsquo;s running in the asyncio.Task, where asyncio is the standard Python library (link). When canceling the async actor, Ray will cancel the asyncio.Task, which will raise an asyncio.CancelledError. The official document recommends us to use try/finally block to do the clean up even if we cancel the task (link)\nHere’s the example of how to start and cancel an actor. We use async actor as example here:\nimport ray import asyncio import time @ray.remote class Actor: async def f(self): try: await asyncio.sleep(5) except asyncio.CancelledError: print(\u0026#34;Actor task canceled.\u0026#34;) actor = Actor.remote() ref = actor.f.remote() # Wait until task is scheduled. time.sleep(1) # Cancel the actor with ray.cancel ray.cancel(ref) # When doing ray.get on the canceled object, the TaskCancelledError will be raised try: ray.get(ref) except ray.exceptions.TaskCancelledError: print(\u0026#34;Object reference was cancelled.\u0026#34;) Below is the brief code path on calling the ray.cancel(). Note that the place we call ray.cancel() is in the “Submitter” node, and this will be send to the “Executor” node through RPC.\nSubmitter side ray.cancel(ref) → worker.core_worker.cancel_task() (python/ray/_private/worker.py) → CCoreWorkerProcess.GetCoreWorker().CancelTask (python/ray/_raylet.pyx) → CoreWorker::CancelTask (src/ray/core_worker/core_worker.cc) → ActorTaskSubmitter::CancelTask(task_spec, recursive) If the task is in the actor’s submitter queue, can be canceled directly; otherwise send RPC: client-\u0026gt;CancelTask Threaded actor task - Executor side CoreWorker::HandleCancelTask() → CancelActorTaskOnExecutor() → TaskReceiver::CancelQueuedActorTask() → If queued, remove from queue → If running, cannot cancel (return success to avoid retry) Async actor task - Executor side CoreWorker::HandleCancelTask() → CancelActorTaskOnExecutor() → task_execution_service_.post() (into the actor event loop) If running: cancel_async_actor_task() → fut.cancel() → Python async task receives asyncio.CancelledError If queued: TaskReceiver::CancelQueuedActorTask() Originally, when the threaded actor is running, Ray will ignore the cancel signal and keep running the actor. This is what we want to solve in this issue. My solution is described in the next section.\nMy Solution # To tackle this, we need following components:\nPython API ray.get_runtime_context().is_canceled() to show that the actor is canceled\nRecord the cancellation state\nRaise TaskCanceled error when ray.get() on the cancelled task\nPython API # I started from the Python API to know where the cancellation state will be read from. The is_canceled API that users will use is defined in python/ray/runtime_context.py (code)\ndef is_canceled(self) -\u0026gt; bool: ... return self.worker.is_canceled Here, calling is_canceled from the self.worker will follow the code path (1 → 2 → 3) and eventually goes into CoreWorker::IsTaskCanceled. This is the place that we will check the task cancellation status.\nRecord the cancellation state # The cancel state can be put in either Python side or Cpp side, which is better?\nMy first proposal is to add a task context class on the Python side to record the task cancellation state (see here). However, this introduces a thread-safety issue: when multiple threads (e.g., one executing a callback and another running the actual actor task) access the shared cancellation flag simultaneously, a race condition can occur. To ensure thread safety and better performance, we should store the cancellation state in C++, utilizing C++ mutex or atomic variables (see here).\nThere are also different places that we can add is_canceled flag in C++ part:\nDirectly use is_canceled_ flag in TaskManager.\nthis cannot work as TaskManager runs in submitter side, and we want to get the is canceled status in the executor side Put in WorkerThreadContext(worker_context_) in HandleCancelTask (code)\nThis cannot work either. In executor, the place receiving RPC request and executing the task are in separate thread Execute task (main thread): code RPC request (spawned new thread): code Record the canceled task ID in the canceled_tasks_ hash set that\u0026rsquo;s shared between threads in the executor (code).\nThis is what we eventually do. When calling CoreWorker::CancelTaskOnExecutor, we will put the task ID into the set (code) When calling CoreWorker::IsTaskCanceled, we will check if the task ID is present in the canceled_tasks_ set (code) Raise TaskCanceled error # When async actor is canceled, it will raise asyncio.CancelledError, which is treated as an application error. In this case, when calling CompletePendingTask, it will raise TASK_CANCELLED error if it receives is_application_error to be true.\nHowever, when cancelling threaded actor, we will not raise any error, so the is_application_error will be false. In this case, we need to manually fail the actor task and raise the TASK_CANCELLED (as mentioned here).\nSee further information here: https://github.com/ray-project/ray/pull/58914/files#r2564220449\nSummary # This is my first Ray PR working on a feature and digging into the C++ side. It has been a pretty exciting experience, and I plan to keep contributing to Ray with more cool PRs ahead!\n","date":"27 December 2025","externalUrl":null,"permalink":"/posts/ray/ray-cancel-threaded-actor/","section":"Posts","summary":"Issue deep dive on how to support cancellation for threaded actor task","title":"Add support for cancelling threaded actor task","type":"posts"},{"content":"","date":"27 December 2025","externalUrl":null,"permalink":"/tags/ray/","section":"Tags","summary":"","title":"Ray","type":"tags"},{"content":"","date":"27 December 2025","externalUrl":null,"permalink":"/series/ray-issues/","section":"Series","summary":"","title":"Ray Issues","type":"series"},{"content":"","date":"2025年8月23日","externalUrl":null,"permalink":"/zh-tw/tags/flyte/","section":"Tags","summary":"","title":"Flyte","type":"tags"},{"content":"這篇文章將深入解析 Flyte 的核心架構，探討各個組件的職責以及如何共同運作構成 scalable + production ready 的平台，用以執行 Python workflow。\n概覽 # Flyte 的架構可以分為三層，分別是用戶、控制、以及資料層。詳細資訊請參考官方文件。\n簡化的 Flyte 架構 用戶層: 與 Flyte 互動的介面 FlyteKit: Python SDK，用於定義 workflow 及 task FlyteConsole: 網頁 UI，用於視覺化 workflow 和監控執行狀況 FlyteCTL: CLI 工具，從終端機與 Flyte 叢集互動 控制層: Flyte 的大腦，處理用戶請求並管理 workflow 和生命週期 FlyteAdmin: 中央 API 伺服器，驗證輸入、將 workflow 編譯成可執行的格式、以及與資料層溝通 資料層: 在 Kubernetes 上執行 workflow 並向控制層回報狀態 FlytePropeller: Kubernetes 控制器，負責任務調和（確保實際狀態與期望狀態一致）並調用合適的 FlytePlugin 執行任務 FlytePlugin: 可擴展的外掛，透過建立 pod 或調用 Kubernetes operator 來處理不同類型的任務 Flyte 有許多串接各種運算引擎和雲端服務的 plugins，包括 Spark、Ray、PyTorch、Dask、AWS Batch、BigQuery、Snowflake 等等。 官方文件中有列出詳細的 plugin list 以及設定方式。 以下是這些組件如何相互搭配來執行 workflow:\n用戶透過 FlyteKit、FlyteConsole 或 FlyteCTL 建立 workflow FlyteAdmin 驗證輸入、編譯 workflow，並將其轉發給 FlytePropeller FlytePropeller 選擇適當的 FlytePlugin 來執行任務，並監控進度直到完成 現在讓我們深入了解執行 workflow 的每個步驟中發生了什麼。\n執行 workflow # 深入探討 Flyte workflow 怎麼被執行前，讓我們先來簡單介紹 3 個 Flyte 的核心概念: launch plan、workflow、以及 task。\nlaunch plan: 定義 workflow 輸入的模板 workflow: 由多個 task 組合成的完整流水線 task: 個別的計算單元（例如資料轉換、模型訓練等） 這些是核心組件的簡要說明 - 之後會有更詳細的介紹文章。\n註冊並執行 workflow 以下的步驟描述 Flyte 從 user request 到執行 workflow 的流程:\n客戶端發送 launch plan 請求: 客戶端向 FlyteAdmin 發送取得 launch plan 的請求 (getLaunchPlan) 如果沒有明確設定 launch plan，會建立一個與 workflow 同名的預設 launch plan FlyteAdmin 回傳 launch plan: FlyteAdmin 回傳客戶端要的 launch plan 客戶端驗證輸入: 客戶端檢查 launch plan 是否提供了所有 workflow 需要的 input 提交執行請求: 客戶端向 FlyteAdmin 發送 workflow 執行請求 FlyteAdmin 處理請求: FlyteAdmin 驗證輸入並編譯 workflow 和 task 中繼資料儲存: 編譯後的 workflow 上傳到 Flyte 的 metadata storage（如果之前已編譯過，則直接從 storage 中拿取） workflow 轉換: 編譯後的 workflow 會與輸入結合，轉換為 custom resource (CR) flyteworkflow CustomResourceDefinitions (CRDs) 讓你可以透過定義自己的資源類型來擴展 Kubernetes， 像是 Pod 和 Service 都是一個 CRD。 Flyte 透過 flyteworkflow CRD 來讓 Kubernetes 能夠理解和管理 workflow。 FlytePropeller 執行: FlytePropeller 存取 flyteworkflow CR，調用適當的 FlytePlugin 來執行 workflow，並監控執行狀態 狀態更新: FlytePropeller 持續向 FlyteAdmin 回報 workflow 狀態 總結 # Flyte 的三層架構區分出明確的職責:\n客戶端工具 (FlyteKit、FlyteConsole、FlyteCTL): 定義、提交和監控 workflow 的介面 FlyteAdmin: 驗證、編譯和協調 workflow 執行的中央控制器 FlytePropeller: Kubernetes 控制器，負責調用合適的工具來執行 workflow 並管理其生命週期 這樣的架構設計讓 Flyte 在生產環境中能夠穩定運行、輕鬆擴展，同時保持良好的維護性。\n","date":"2025年8月23日","externalUrl":null,"permalink":"/zh-tw/posts/flyte/flyte-architecture/","section":"Posts","summary":"深入了解 Flyte 的三層架構，探討用戶層、控制層和資料層如何協作，讓 workflow 在大規模環境中穩定運行","title":"Flyte 架構: 執行 workflow 背後的祕密","type":"posts"},{"content":"","date":"2025年8月23日","externalUrl":null,"permalink":"/zh-tw/series/how-to-flyte/","section":"Series","summary":"","title":"How to Flyte","type":"series"},{"content":"","externalUrl":null,"permalink":"/zh-tw/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"}]