docs: 记录发布执行与全新克隆复跑,todo/ADR 的门槛现状对齐远程 master 已推到 33418d3,tag v0.1.0-preview 经授权改写并重推(fb903be→0509945, –force-with-lease 指定旧 tag 对象,ls-remote 复核后远程 tag 剥开指向 33418d3 = 远程 master)。 发布动作之后从公开远程全新克隆复跑了一遍 Linux 矩阵:clone → describe 得 tag → xmake(零告警)→ xmake test 10/10 → bes –version 基线一致 → hello.bes 正常。 ADR-0012 的那条门槛快照不改(它写的是当时的现场),另起一条写最终现状: 1–5 全过,只剩把两个 tar.gz 挂到 gitlink release/附件——需要登录的网页动作, 在此之前”下载即用”只对拿到文件的人成立,从远程只能走源码安装(已验证可用)。 本次提交未推送:上一轮授权只覆盖那一次发布动作。
docs: 记录发布执行与全新克隆复跑,todo/ADR 的门槛现状对齐远程
master 已推到 33418d3,tag v0.1.0-preview 经授权改写并重推(fb903be→0509945, –force-with-lease 指定旧 tag 对象,ls-remote 复核后远程 tag 剥开指向 33418d3 = 远程 master)。 发布动作之后从公开远程全新克隆复跑了一遍 Linux 矩阵:clone → describe 得 tag → xmake(零告警)→ xmake test 10/10 → bes –version 基线一致 → hello.bes 正常。
ADR-0012 的那条门槛快照不改(它写的是当时的现场),另起一条写最终现状: 1–5 全过,只剩把两个 tar.gz 挂到 gitlink release/附件——需要登录的网页动作, 在此之前”下载即用”只对拿到文件的人成立,从远程只能走源码安装(已验证可用)。 本次提交未推送:上一轮授权只覆盖那一次发布动作。
一个用 C + Lua 写成的 AI Agent 框架:LLM 调用、工具调度、会话与上下文管理, 配一门自研跨平台 shell bes 作为驱动与编排界面。
bes
零解释器外部依赖:除 Lua 之外不引入 Python / Node / 任何脚本运行时。
项目管理三件套(本仓库的”实时状态”以这三份为准):
格式规则见 docs/CONVENTIONS.md(进度板标记法、MEM 编号规则、提交约定)。
设计与记录之间的分工:
docs/decisions/
系统的唯一系统级依赖是 xmake。此外:
libs/
clone 下来即可构建,无需联网拉包。
libs/lua/ 是入库的第三方源码,不属于本项目的 MIT 授权范围:
libs/lua/
https://www.lua.org/ftp/lua-5.4.8.tar.gz
4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629ae
libs/lua/src/
.c
.h
lua.hpp
Makefile
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)。
bes --help
bes run scripts/hello.bes
$?
set ?
set $?
Linux 下为 xmake f -p linux && xmake,产物在 build/linux/x64/release/bes。
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。
xmake dist
build/dist/beagent-v<版本>.<平台>.<架构>.tar.gz
bin/bes
scripts/hello.bes
libssl-dev
仓库已公开托管在 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 的首段会挂住整条管道)。
gitlink.org.cn/ltree/beagent
master
0.1.0
&&
||
;
bes.run
bes.session
src/shell/main.c
src/shell/bes/
bes_engine
bes run <file>.bes
bes -c "..."
${VAR}
cmd1 ; cmd2 && cmd3 || cmd4
> f
>> f
< f
2> f
2>&1
&> f
&>> f
echo
set
lua
run
source
cd
pwd
export
env
which
sleep
test
[
true
false
version
help
exit
beagent.commands()
pal_which
pal_process_spawn
os.exit(n)
argv
GetCommandLineW
-c
.bes
PROGRAM → PIPELINE → COMMAND
&
$(
>
>>
<
2>
&>
&>>
a | b | c
测试有 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。
tests/integration/cases.txt
require("bes").run(line, opts)
bes_run
on_stdout
ENESTED
tests/lua/
test_bes_lua
bes_parse
bes_exec_node
src/core/be_worker.h
require
C + Lua AI Agent 框架,配自研跨平台 shell bes
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802047560号
beagent
一个用 C + Lua 写成的 AI Agent 框架:LLM 调用、工具调度、会话与上下文管理, 配一门自研跨平台 shell
bes作为驱动与编排界面。零解释器外部依赖:除 Lua 之外不引入 Python / Node / 任何脚本运行时。
文档索引
项目管理三件套(本仓库的”实时状态”以这三份为准):
格式规则见 docs/CONVENTIONS.md(进度板标记法、MEM 编号规则、提交约定)。
设计与记录之间的分工:
docs/decisions/(ADR 0001–0012),由 design.md 以编号引用;依赖原则
系统的唯一系统级依赖是 xmake。此外:
libs/(Lua 5.4.8 源码一行不改),来源与 sha256 记在 ADR;clone 下来即可构建,无需联网拉包。
第三方代码来源与许可
libs/lua/是入库的第三方源码,不属于本项目的 MIT 授权范围:https://www.lua.org/ftp/lua-5.4.8.tar.gz4f18ddae154e793e46eeab727c59ef1c0c0c2b744e7b94219710d76f530629aelibs/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(本项目唯一构建与测试管理工具,见 ADR-0003)。
第一次上手最快的三条:
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< f2> f2>&1&> f&>> f,M2-7 前半)、 内嵌 Lua 求值,以及一批内建命令(echosetluarunsourcecdpwdexportenvwhichsleeptest[truefalseversionhelpexit…)—— 清单与语义以 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。