TEN Framework 入坑记
TL;DR
TEN Framework 最初叫 Astra,后改为 TEN,即 Transformative Extensions Network。
我第一次见到 TEN (那时还叫 Astra)是在今年的 6 月份的极客公园 AGI Playground 大会 RTE OpenDay 的活动上。当时展区现场人声嘈杂,但对话效果已经很不错。当时我们在展示用 XSwitch 支持多模态对话,我们已经可以接入各种视频会议系统,以及各种 AI 提供商的 API,但还没有接入 TEN。
XSwitch 是一个多协议的音视频和 AI 连接器(Connector),致力于接入所有音视频和 AI 相关的平台和服务。因此,TEN 也是一个我们需要接入的框架和协议。
TEN 框架写得其实非常好,Docker 容器也很容易跑起来,但在接入过程中,我们还是遇到了一些问题,有些已经解决,有些还在解决中。在踩坑的过程中,也深入理解了这个框架,遇到一些问题,也发现了它的好。
前端的问题及优化
在官方网站上试用的时候,有时候发现前端不响应。点击 Join 按钮没有任何反应。后来发现,服务器可能在国外,首页加载看起来倒很快,但后台可能还在“懒”加载一些 JS 等内容,在加载完成前,Join 按钮是没有反应的,看起来就像是卡住了。
我们做了一些优化并提了一个 PR,在加载过程中显示“Loading …”,并不允许点击,这样,至少用户可以理解成是页面由于等待网络等原因没有加载完成,而不是个 Bug。
Docker 相关的问题
TEN 是使用 C、Rust 和 Go 混合开发的,因此会用到 Cgo。底层组件有一些没有适配 ARM,因此只有 x86-64 镜像,这对于 Apple Silicon 的用户来说不够友好,不过,按官方文档中的指导,也可以顺利跑起来。
编译却很令人头疼。由于我使用了 OrbStack,TEN 开发人员可能都没有用过,因此在编译时有一个检查没有通过。后来几经探索,找到一个编译脚本,把里面的检查去掉,算是绕过去了。后来反馈到微信群里,TEN 的开发人员也确认了这个问题,现在已经修好了。
编译遇到的问题
TEN 提供了一键编译的脚本,但 TEN 包含了 Python、Go 等多种语言和多种依赖,在编译期间会自动下载一些依赖,有时候下载时间很长,有时候下载失败,再加上那段时间正好碰上国内无法下载 Docker 镜像的问题,总之一键编译并不能一键完成。
这也算是生活在国内程序员的日常了,如果使用这么复杂的系统,就必须具备一定的上网技能。我有一个 socks5 代理,但是有些 Python 环境不支持 socks5,我只好禁用掉代码,在开发镜像中安装了以下内容:
pip install pysocks
pip install httpx[socks]
然后,再开启 HTTPS 代理:
export HTTPS_PROXY=socks5://host.orb.internal:8888
其中,host.orb.internal:8888
是我 socks5 代理的地址。这是在 OrbStack 在 Docker 容器中访问宿主机 IP 的方法。这样,我拿着笔记本满世界乱跑时就不需要每次都修改 IP 地址之类的。
为了让 OpenAI 能用上代理,需要在 .env
中配置以下内容:
OPENAI_PROXY_URL=socks5://host.orb.internal:8888
好在编译脚本基本上是幂等的,多执行几次,总会成功。
Graph Designer 相关问题
TEN 的 Graph Designer 是一个很好的工具,可以用来设计对话流程。但最初的版本功能很弱(现在也不是很强,还是 Beta 版)。Graph Designer 连接到后台一个 property.json
文件,修改了以后刷新页面不生效。需要重启所有 Docker 容器。但我在开发过程中,重启所有 Docker 容器就意味着要重新编译安装很多东西,而这个过程很慢,又不是每次能成功。在微信群里问,开发人员支持很积极,但是没有给出有效的解决方案,直到后来我自己找到了原因。
ten_graph_designer
容器只是个前端,它连接到 astra_agents_dev
里的一个 tman
服务进程上,默认端口是 49483
,由 make run-gd-server
命令启动。因此,只需要重启这个 tman
服务进程就可以了,而不需要重启整个 Docker 容器。
但事情没有那么简单,make run-gd-server
是随着 Docker 容器一起启动的,kill 掉 tman
以后整个容器也会退出!所以,我只好修改了 docker-compose.yml
,增加了一个entrypoint
,让它启动 bash
而不是 tman
。
entrypoint: ["/usr/bin/bash"]
启动 Docker 后,通过docker exec -it astra_agents_dev bash
再进入容器,手工使用 make run-gd-server
启动 tman
服务进程。这样,每次修改 property.json
以后,只需要在容器中重启 tman
服务进程就可以了。
作为一个新手,在 Graph Designer 做了修改并 Save 以后,习惯性地看一看 git diff
。发现 property.json
格式变化很大,无法有效知道到底是改了什么。这个如果能保证一个一致的格式就好了。好像有一个 PR 已经在跟进这个问题了。
其实这个问题也不难发现,只是我前期急于写框架,反正全面重启下又不是不能用。现在看来,“磨刀不误砍柴工”,如果早点搞定这个问题应该能多节约一点时间。
缓存优化
astra_agents_dev
容器在安装各种 Python 或 Go 依赖包时,会有很多缓存到/root/.cache
目录下。在docker-compose.yml
中做一个持久化,映射到宿主机的.cache
目录下,可以加快后续的编译速度。当然,这个要重启容器。
volumes:
- ./:/app
- ./.cache:/root/.cache
编译过程优化
整个编译过程基本都是靠install_deps_and_build.sh
脚本完成的,它会编译所有内容。这个编译非常耗时。目前,我只开发了 Go Extension,没有改 Python,就没有必要每次都检查并下载 Python 的依赖。好在这个脚本写得非常清楚,我只是简单的把 Go 不相关的行全部注释掉了,这样编译速度就快了很多。
echo "install dependencies..."
#tman install
# build extensions and app
echo "build_cxx_extensions..."
#build_cxx_extensions $APP_HOME
echo "build_go_app..."
build_go_app $APP_HOME
echo "install_python_requirements..."
#install_python_requirements $APP_HOME
echo "post installation..."
#post_install $APP_HOME
当然,如果你只是尝试在本地运行 TEN Framework,也没必要像我这样折腾,因为你一般只需要编译一次就好了。而我需要写插件。
写 Hello World 插件
按官方文档用 Go 写了个 Hello World 插件,比较顺利,但也不是一点都不需要改。用 tman
生成代码后,要修改hello_world/default_extension.go
中 init
函数中的内容,有一个字符串要改成hello_world
,否则会提示找不到插件。
// Register addon
ten.RegisterAddonAsExtension(
"hello_world",
ten.NewDefaultExtensionAddon(newDefaultExtension),
)
在使用 tman
生成插件以后,使用 git diff
可以看到有一个 manifest
文件有变化,但是编译完成后,变化又还原了一部分,开发人员说可能是个 Bug,但是我测试了好像也不影响运行。
一点建议
Graph Designer 虽然还是 Beta 版,但功能已经很不错了。不过,也还有一些可改进之处。
首先是它看起来很酷,但我花了很长时间才搞明白它是怎么用的。文档也不是很全,尤其是 flush
根本没有任何解释,只好去啃源代码。
Graph 看起来很酷,表示数据流的虚线也是动的,看起来就像是数据在流动。但是一旦图一复杂,就容易花眼。我感觉这个虚线应使用样条曲线(spline)而不应该使用直线,另外,也可以使用不同的颜色表示不同的数据类型。大家可以对比以下两个图看哪个更容易理解。
后面的图是我使用 Graphviz 生成的,我其实对 Python 并不熟,但有了 ChatGPT,我也会写 Python 代码了,我使用了如下提示词。
write a python script, convert json into graphviz digraph
graph nodes take from nodes
nodes label use html syntax
graph edge take from connections
node has ports like data and text_data, make them in a sub table, in ports at left, out ports and right
an example json follows
... 后面是简化后的 property.json 内容 ...
当然生成的代码直接编译会有错误,我修了一下,并做了很多改进,最后提了一个 PR。
我们还做了什么
我们其实研究了很多。TEN 框架包括前端和后端,在我们还没有搞明白后端的时候,我们就山寨了一个前端,然后把后端接入到了 TEN(当时还叫 Astra)官方的后端上给客户演示,薅了一把 TEN 的流量,直到有一天我们发现被限流。不过我们只是自己测试也没有对外宣传,遇到限流应该就是 TEN 的试用者本身比较多,而不是我们的锅 😂。
当然,这个方法是个秘密,我们不说。
另外,我们直接在 XSwitch 中打通了 TEN。XSwitch 是一个软交换平台,支持 SIP、H323、WebRTC 等多种协议。我们是最早跟 Agora 打通的平台。TEN 的 RTC 功能现在是由 agora_rtc
组件支持的,而且信令也不复杂,只有 start
、ping
、stop
三个 API。我们直接在 XSwitch 中写了一个 Lua 脚本就打通了,使用任何 PSTN 或 SIP 话机,拨打一个电话号码就可以直接跟 TEN 聊天。
最近几天的成果,我学会写插件了。我直接在 TEN 中写了一个 xswitch
插件,现在,也可以在 Graph Designer 中使用 xswitch 了。但是 TEN Store 还没有做好,我们还没有对外发布。我们也只是 Alpha 阶段,有些流程还没有调好。
在国庆放假前的一天,我改了一下 agents
框架,可以从 TEN 框架中直接启动 XSwitch 插件,甚至不需要 agora_rtc
。但我不跟别人说 😂 。
再说 TEN
TEN 框架其实有三部分组成,底层叫 TEN Framework,由 C、Rust 和 Go 写成,我看了一下源代码,但还没有学会怎么编译。如果你不开发底层,好像也不需要编译。
大部分人其实不需要改 TEN Framework,我这些天主要折腾的也都是 TEN Agent,这也是第二部分,大家可以在这里面写插件。TEN 的神奇之处是你可以使用 Python、Go、C++ 等语言写插件,并通过 Graph Designer(或 JSON)把这些插件串联起来,组成一个图(Graph),然后不同语言写得插件甚至可以在同一个进程中运行!
第三部分就是上面说的 Graph Designer。日后,更多的开发者其实只需要会 Graph Designer 就好了,然后就可以组合出不同的对话流程。如果 Graph Designer 成熟了,TEN Agent 里面的插件丰富了,也许就不需要我们这些开发者了。
话说回来,TEN 框架其实写得很好,读源代码也会发现其作者功力很深。虽然现在文档不多,但还是包括了很多概念和框架设计的基本内容,对开发者也比较友好,甚至包括 tman
工具,以及如何设置 VS Code,如何 Debug 等。但也是正因为文档不多,很多东西还需要自己摸索。微信群里支持很积极,但总不能什么都问吧,哈哈。
更多期待
期待 TEN 会越来越好。另外,如何更好地组织源代码,把自己开发的 Extension 放到独立的仓库中(不管开源还是不开源),而不是跟现有的 Agent 代码混在一起,也是一个很令人期待的事情。
小结
国庆第一天没出门,还是折腾了一下 TEN,顺便写了这篇文章。
不得不说,入坑还是很有门槛的。
- 首先 Docker 就挡住了不少人。
- 其次,虽然官方提供了 Docker,但还是需要自己编译,因为有些依赖不能流畅下载的缘故,这个门槛很高。
- 再次,框架支持很多语言,这是个好事,但对开发者要求也很高。不过,如果看了我的书《大道至简》,应该就不怕了。
- 最后,TEN 还很年轻,文档不全,仅有的文档也大多是英文的。
当然,这其实都不是什么问题。很多人都说,有 AI 了,就不需要程序员了。但我做为一个会很多语言的程序员,在 RTC 这一行混了这么多年,折腾起 TEN 框架来还是花了不少时间。未来不是不需要程序员了,是未来的程序员要懂很多语言。光一个 TEN 框架就包含了 C/C++、Rust、Go、Python、Shell、Javascript 等这么多语言,如果一点都不了解的话,折腾起来还是很费劲的。这也是我写《大道至简》 的初衷——希望大家从根本上系统地了解编程开发的本质。
时间仓促,简单记录一下,希望对大家有帮助。TEN 框架是一个很有前途的框架,如果后面我有什么新发现,再跟大家分享吧。
更新
就在写完这篇文章的第二天,发生了一件大事:OpenAI 宣布了 Realtime API,以及与 Agora 的合作,Agora 股票(股票代码是 API
,这个一看就很高级)应声大涨,我有点后悔没有美股的账户了。相关链接如下:
- https://openai.com/index/introducing-the-realtime-api/
- https://www.agora.io/en/blog/agora-and-openai-enabling-natural-real-time-conversational-ai/
不知道 TEN 框架对此有多少助力,但看起来,一切皆是因果。
另:我视频号上也有 XSwitch 跟 TEN 对接的相关视频,但是视频号视频好像又没有直接的链接,感兴趣的朋友可以自行找一下。