目录

beagent

一个用 C + Lua 写成的 AI Agent 框架:LLM 调用、工具调度、会话与上下文管理, 配一门自研跨平台 shell bes 作为驱动与编排界面。

零解释器外部依赖:除 Lua 之外不引入 Python / Node / 任何脚本运行时。

文档索引

项目管理三件套(本仓库的”实时状态”以这三份为准):

文件 作用 更新时机
todo.md 实时开发进度与任务板,唯一任务真源 每完成/开始/阻塞一个任务
design.md 开发设计书:目标、分层、接口、分期、构建与测试方案 方向性变化时
mem.md 开发方法与踩坑记录 每踩一个值得记的坑
CHANGELOG.md 发布视角的变更:这一版能做什么、边界在哪 每次发布

格式规则见 docs/CONVENTIONS.md(进度板标记法、MEM 编号规则、提交约定)。

设计与记录之间的分工:

  • design.md 写”这一版要做成什么样”(含未到期的接口与分期表);
  • 每一次”为什么选 A 不选 B”进 docs/decisions/(ADR 0001–0012),由 design.md 以编号引用;
  • “开发中发生了什么意外”进 mem.md;
  • “在哪套工具链上验证过”进 docs/BASELINE.md。

依赖原则

系统的唯一系统级依赖是 xmake。此外:

  • 第三方库全部 vendor 进 libs/(Lua 5.4.8 源码一行不改),来源与 sha256 记在 ADR;
  • 禁止传递构建依赖:任何需要 cmake / meson / autotools / perl / luarocks 才能构建的组件一律否决 (libuv、libcurl 包、readline、busted 因此被拒,见 ADR-0005);
  • 平台自带能力优先:HTTP/TLS 走 WinHTTP / OpenSSL / 系统 libcurl(ADR-0006), 并发走 worker 线程 + 阻塞 IO(ADR-0007)。

clone 下来即可构建,无需联网拉包。

第三方代码来源与许可

libs/lua/ 是入库的第三方源码,不属于本项目的 MIT 授权范围:

项 值
组件 Lua 5.4.8 参考实现
上游 https://www.lua.org/ftp/lua-5.4.8.tar.gz
sha256 4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae
许可 Lua 5.4 license(MIT 风格,与 MIT 兼容),全文见 libs/lua/LICENSE
版权 Copyright © 1994–2025 Lua.org, PUC-Rio
改动 零改动,与上游逐字节一致;libs/lua/src/ 63 个文件入库(34 .c + 27 .h + lua.hpp + 上游 Makefile),其中 32 个 .c 参与构建(lua.c / luac.c 各带 main(),排除在外)

除 Lua 之外当前仓库没有其他第三方代码。计划引入的组件(yyjson、sqlite3 amalgamation、zlib) 及其许可与体积核查见 design.md §5.3 与 ADR-0005; 引入时按 ADR 规则登记来源与哈希。

目录结构

xmake.lua            唯一构建与测试入口(src/**/ 内不放分散构建文件)
design.md  todo.md  mem.md  README.md  LICENSE
docs/                CONVENTIONS / SHELL_SPEC / BASELINE / decisions(ADR 0001-0012)
src/pal/             平台抽象(唯一允许调平台 API 的层):进程、管道、env、路径、which、
                     线程/互斥/事件/单调时钟、控制台码页、入参解码(M1-12/M1-14/M1-8 已落地);
                     PTY 与 http+TLS 属于**这一层但该做未做**(分别在 M4-1 与 M3-7)
src/core/            纯 C 运行时:命令表、worker 池、json、log、error(不含 Lua、不含 shell 语义)
src/lua/             C→Lua 绑定层,注册 beagent 模块
src/shell/           main.c 只做 REPL/CLI 装配;src/shell/bes/ 放词法/语法树/变量/执行/内建(bes_engine 静态库)
src/agent/           llm 适配、tool、memory 的 C 侧(随 M3 建立)
libs/lua/            vendored Lua 5.4.8 源码,一行不改
scripts/             .bes 脚本与 .lua agent 定义
tests/c/             C 单测(自研断言宏,无测试框架)
tests/lua/           自研 Lua harness 与 spec(M2-10,由 test_bes_lua 起真实 bes 跑)
tests/integration/   cases.txt 清单 + 每例一对 .bes / .golden(端到端回归)

构建

需要 xmake(本项目唯一构建与测试管理工具,见 ADR-0003)。

git clone https://gitlink.org.cn/ltree/beagent.git && cd beagent
xmake f -p windows --toolchain=clang -y   # -p 必须显式给,见 mem.md MEM-003
xmake                                     # 构建
xmake test                                # 跑测试(10 个 target)
build/windows/x64/release/bes.exe --version
build/windows/x64/release/bes.exe --help   # 用法 + 退出码速览 + 命令表
build/windows/x64/release/bes.exe run scripts/hello.bes
build/windows/x64/release/bes.exe -c "echo 甲 ; echo 码=$?"

第一次上手最快的三条:bes --help(形态与退出码)、bes run scripts/hello.bes(语法样例)、 bes 进 REPL。踩到的写法坑基本都在 docs/SHELL_SPEC.md: Lua 表达式要用单引号(§5)、$? 取的是刚跑完那条语句的码(§2.2)、 set ? 而不是 set $?(§4)。

Linux 下为 xmake f -p linux && xmake,产物在 build/linux/x64/release/bes。

要一个能直接发给别人用的东西:xmake dist → build/dist/beagent-v<版本>.<平台>.<架构>.tar.gz (含 bin/bes、README、CHANGELOG、LICENSE、SHELL_SPEC、scripts/hello.bes, 打包前会跑一次 staging 自检并打印 sha256)。发布门槛本身定义在 ADR-0012。 M3 的联网能力(LLM 调用)在 Linux 上需要发行版的 libssl-dev,当前阶段还不需要; 各平台实测版本基线见 docs/BASELINE.md。

状态

仓库已公开托管在 GitLink(gitlink.org.cn/ltree/beagent,默认分支 master)。 当前版本 0.1.0,第一个可使用的预览版——能做什么、不承诺什么、怎么装, 一次写在 CHANGELOG.md;发布门槛本身定义在 ADR-0012。 M0 与 M1 均已完成(M1 于 2026-10-29 收口;例外:M1-8 待在非 UTF-8 码页的机器上复验一次), 现在在 M2:语法树已落地(M2-6)、**&&/||/; 已落地(M2-8)、 重定向与管道均已落地(M2-7 完成)、自研 Lua 测试 harness 与 golden 清单回归已落地(M2-10)、bes.run 回调桥已落地(M2-3)、 Lua 错误按两期给码(M2-5)、shell 状态整套线程私有(M2-1 已拍板,ADR-0010 选 C), M2 剩下 beagent 模块扩展(M2-2:session 句柄与日志接口)与 bes.session(M2-9,把”每线程”升格为”每会话”), 详见 todo.md。 src/shell/main.c 只剩 REPL/CLI 装配,词法与内建命令在 src/shell/bes/(独立 bes_engine 静态库,供单测直接链接)。 当前 bes 能跑:REPL、bes run <file>.bes、bes -c "..."、引号/变量展开(含 ${VAR})、 一行多条语句(cmd1 ; cmd2 && cmd3 || cmd4,短路语义同 POSIX,M2-8)、 重定向(> f >> f < f 2> f 2>&1 &> f &>> f,M2-7 前半)、 内嵌 Lua 求值,以及一批内建命令(echo set lua run source cd pwd export env which sleep test [ true false version help exit …)—— 清单与语义以 docs/SHELL_SPEC.md §8 为准,机器可读的那一份是 beagent.commands();README 不复制条数,免得这里成为第二个真源(MEM-035 的同类问题)。 以及外部命令执行(M1-13):命令表未命中 → pal_which(PATH + Windows PATHEXT) → pal_process_spawn → 两路输出分开捕获并透传 → 命令的退出码即 bes 的退出码,找不到才是 127。 bes 的 Lua 表达式需用单引号包住(docs/SHELL_SPEC.md §5),这是目前最容易踩的写法坑。 Lua 那一路的失败按两期给码(M2-5,§5.1):打不开或语法错 = 2(改脚本), 运行期抛错 = 1(改数据),运行期的报错还带 Lua 调用栈;脚本自己 os.exit(n) 原样透传。 入参编码已统一(M1-8):Windows 下 bes 不再用 CRT 按 ANSI 码页转出的 argv, 而是从 GetCommandLineW 自己转 UTF-8,并把控制台码页设为 UTF-8—— CLI 直调、-c、.bes 脚本三条路收到的字节一致(复验条件见 docs/BASELINE.md)。 执行层已从”行式直执行”升级为语法树(M2-6:PROGRAM → PIPELINE → COMMAND, 变量在执行期展开);重定向与管道已落地(M2-7)、bes.session 在 M2-9。 规范要求未实现的语法必须报错,这一纪律已落地(M1-19,M2-6 起落在语法分析层): 引号外的单个 & 与 $( 直接以退出码 2 拒绝,不会把运算符当字面量喂给子进程; 报错消息按运算符分类说清”是没做还是写错了”(后台 & 与 $( → 未排期), 让调用方分得清”我写错了”与”这版还没做”(docs/SHELL_SPEC.md §1、§2)。 &&/||/;、重定向、管道三块都已到期: 短路语义同 POSIX($? 取最后一条实际执行的命令,一行最多 16 条语句,§2.2); 重定向支持 > >> < 2> 2>&1 &> &>>(目标在执行期展开、不占参数位, 内建走 sink、外部走 PAL 文件句柄,§2.3); 管道支持多段(a | b | c,退出码取末段,段数受 worker 池上限约束,§2.4)—— 两处刻意的边界:内建命令暂不参与管道**(显式报错,不静默丢段), 首段 stdin 接空设备(否则读 stdin 的首段会挂住整条管道)。

测试有 10 个 target(core / pal / be_worker / bes_ast / bes_builtin / bes_exec / bes_lua / bes_parse / bes_run / bes_script),Windows 与 Linux 双侧实测全绿。.bes 端到端带 golden 比对: tests/integration/cases.txt 是清单,每行一对 .bes + 期望输出,加用例不用改 C; Lua 里也能反过来跑命令(M2-3 的回调桥):require("bes").run(line, opts) 走的就是 bes_run,返回结构与 agent 工具同源,两态约定见 ADR-0011 与 docs/SHELL_SPEC.md §5.2(on_stdout 回调、ENESTED 递归上限都在那里)。 Lua 侧的断言走自研 harness(tests/lua/,不引 busted),由 test_bes_lua 起真实 bes 进程跑, 并包含一个”故意让套件变红”的反向自检——套件证明自己会失败,绿灯才有意义(design.md §13.1)。 结构化执行入口 bes_run 已定稿(M1-18,ADR-0011): 一次调用交出退出码、两路输出、耗时、截断/超时/取消标记,agent 的 bes.run 工具走它; M2-6 起 REPL 与 bes_run 已经共用同一条”建树 → 走树”路径(bes_parse + bes_exec_node), 多段管道在两条路径上都走 worker 作业(执行体在这里已经是一套); 单命令仍是两条(REPL 实时透传 vs 作业缓冲)——管道落地后这个分叉不再影响正确性, 所以没有继续排期合并。 并发执行机制(一命令一线程、上限 8 超限直接拒绝、片超时、协作取消、进程树 kill、背压截断) 在 src/core/be_worker.h(M1-14)。 bes_run 可以并发调用:输出汇(M2-11)与整套 shell 状态——Lua state、变量表、$?—— 都是线程私有的(M2-1 / ADR-0010)。 同一线程内多次调用看得到彼此写下的变量与 require 缓存,跨线程看不到,这是定义不是缺陷; 还共享的只剩 cwd 与 env(本来就是进程级系统调用),归 M2-9 的 bes.session。 外部命令目前不受策略约束(allow/deny、cwd jail 在 M5-1),交互式程序要等 M4 的 PTY。 License: MIT,见 LICENSE。

关于

C + Lua AI Agent 框架,配自研跨平台 shell bes

1.0 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号