展示 HN:Go-Bt:Go 语言的极简行为树
Show HN: Go-Bt: Minimalist Behavior Trees for Go

原始链接: https://github.com/rvitorper/go-bt

## go-bt: 一个 Go 行为树库 go-bt 是一个轻量级的 Go 行为树 (BT) 库,适用于后台工作者、游戏 AI 和自动化。它采用协作式多任务模型,避免使用 `time.Sleep` 等阻塞操作,而是通过节点让出控制权来实现。 主要特性包括 **无状态节点** – 所有状态都驻留在 `BTContext[T]` 中 – 以及简单的返回码系统 (1=成功, 0=运行中, -1=失败)。`BTContext` 集成了 Go 的 `context.Context` 用于取消和超时。 go-bt 提供核心节点,如 `Selector`、`Sequence`、`Condition`、`Action` 和 `Sleep`,允许灵活的逻辑构建。一个突出的特性是 **时间旅行测试**;引擎允许将自定义时钟注入到上下文中,从而可以立即测试依赖于时间的节点,而无需实际延迟。 该库提供了一个并发、panic 安全的 `Supervisor` 来在后台运行树。用户定义一个自定义的 `Blackboard` 结构体来保存应用程序状态,树节点可以访问和修改它。示例代码演示了构建一个连接和处理任务的工作者树,展示了该库的易用性。

黑客新闻 新的 | 过去的 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 展示HN: Go-Bt: Go语言的极简行为树 (github.com/rvitorper) 8点 由 rvitorper 56分钟前 | 隐藏 | 过去的 | 收藏 | 讨论 大家好, 我刚刚发布了go-bt的v0.1.0版本,希望能得到这里Go语言老手们的反馈。 提前感谢! 帮助 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系 搜索:
相关文章

原文

go-bt is a Behavior Tree library for Go.

Designed for background workers, game AI, mundane task automation, and asynchronous logic.

Instead of time.Sleep or infinite while loops, go-bt uses a cooperative multitasking model. Nodes return state instantly, yielding control back to the supervisor.

  1. Stateless Nodes: Nodes (Sequence, Selector, etc.) do not hold runtime state. All temporal memory and execution states live entirely within the generic BTContext[T].
  2. The Magic Numbers: Every node's Run method returns exactly one of three integers:
    • 1 (Success): The task is done and successful.
    • 0 (Running): The task needs more time (e.g., waiting on I/O, or sleeping). It yields the thread.
    • -1 (Failure): The task failed.
  3. First-Class Context: BTContext[T] directly embeds Go's standard context.Context, ensuring your trees natively respect global cancellation tokens and timeouts.
  4. Time-Travel Testing: The engine uses an injected clock directly on the context, allowing you to instantly test a 5-minute Timeout or Sleep node in your unit tests without actually waiting.

go-bt provides a minimalist set of core nodes that can be combined to create any logical control flow:

  • Composites: Selector (Stateless Priority / Fallback), Sequence (Stateless AND gate), MemSequence (Stateful chronological execution)
  • Decorators: Inverter (NOT gate), Optional (Swallows failures), Timeout, Retry, Repeat
  • Leaves: Condition (Instant memory read), Action (Execution), Sleep (Non-blocking wait)

1. Define your Blackboard

Your blackboard is the custom state your tree will read and manipulate. Because the library uses Go Generics, it can be any struct.

type WorkerState struct {
    IsConnected  bool
    PendingTasks int
}

You can use the go-bt nodes to assemble your logic. This example asserts that the worker is connected before attempting to process tasks:

package main

import (
    "time"
    "context"
    "go-bt/core"
    "go-bt/composite"
    "go-bt/leaf"
)

func BuildWorkerTree(cancel context.CancelFunc) core.Command[WorkerState] {
   return composite.NewSelector(
      composite.NewSequence(
         composite.NewSelector(
            leaf.NewCondition(func(blackboard *WorkerState) bool {
               // Assert connected
               // Here you'd check whether the connection is valid, etc.
               return blackboard.IsConnected
            }),
            leaf.NewAction(func(ctx *core.BTContext[WorkerState]) int {
               // Make a network connection
               // Here you'd actually connect to a database
               ctx.Blackboard.IsConnected = true
               return 1
            }),
         ),
         leaf.NewAction(func(ctx *core.BTContext[WorkerState]) int {
            // Process Tasks if connected
            if ctx.Blackboard.PendingTasks <= 0 {
               cancel()
               return 1
            }
            ctx.Blackboard.PendingTasks--
            return 1
         }),
      ),
   )
}

3. Start the Supervisor Daemon

go-bt provides a concurrent, panic-safe Supervisor to drive your tree. It spins up in the background and continuously ticks the logic without blocking your main application thread.

func main() {
   state := &WorkerState{IsConnected: false, PendingTasks: 2}
   
   // Initialize the context with a global cancellation token
   ctx, cancel := context.WithCancel(context.Background())
   btCtx := core.NewBTContext(ctx, state)
   tree := BuildWorkerTree(cancel)
   
   // Create a Supervisor that ticks every 100ms
   supervisor := core.NewSupervisor(tree, 100*time.Millisecond, func(err any) {
   println("Tree panicked, safely recovered:", err)
   })
   
   // Start the daemon in the background
   wg := supervisor.Start(btCtx)
   
   // Wait for the global context to be cancelled
   wg.Wait()
}

You can check examples in the example directory.


Testing & Clock Injection

Testing temporal logic (like Sleep or Timeout) in CI/CD pipelines usually results in slow, flaky tests. go-bt solves this by exposing the Now function directly on the BTContext.

In your tests, simply replace the standard clock with a mock closure. This allows you to artificially advance time by hours or days in a single microsecond, instantly triggering your Timeout or Sleep node's success/failure states to assert structural correctness.

联系我们 contact @ memedata.com