- 人工智能
- AI Agent
- 多模态
- 语音
- AI 应用
【免费下载链接】ten-framework
Open-source framework for conversational voice AI agents
本篇指南围绕 TEN Framework 示例扩展simple_http_server_cpp展开:它用 C++ 与 libwebsockets 在本地启动一个 HTTP 服务器,将收到的 HTTP 请求(GET/POST 等)转换为 TEN 运行时命令(cmd)转发给目标扩展,再把命令结果的detail属性作为 JSON 响应返回。读完本文,你将掌握该包的目录结构、manifest.json/property.json配置含义、安装与构建方式,并能从源码层面理解 HTTP 事件回调、跨线程命令桥接与优雅停机的完整实现。
扩展定位与包结构
simple_http_server_cpp是 TEN Framework 官方示例扩展之一,定位为“TEN Framework 的 C++ HTTP 服务器扩展”(Korean 文档见 README.ko-KR.md,同目录还提供英文、日文、简中、繁中版本)。它把 TEN 应用变成可被外部进程/脚本通过 HTTP 调用的服务入口:外部客户端发起一次 HTTP 请求,扩展就构造并发送一条 TEN 命令,命令结果决定 HTTP 响应内容。
从包目录结构看,该扩展是一个标准的 TEN 软件包:
packages/example_extensions/simple_http_server_cpp/ ├── BUILD.gn # GN 构建配置 ├── LICENSE # 许可证 ├── manifest.json # 包元数据与依赖声明 ├── property.json # 扩展属性(默认端口) ├── docs/ # 多语言 README └── src/ └── main.cc # 扩展核心实现manifest.json:包元数据与依赖声明
manifest.json 声明了该包的关键信息:
{ "type": "extension", "name": "simple_http_server_cpp", "version": "0.11.73", "tags": ["cpp"], "dependencies": [ { "type": "system", "name": "ten_runtime", "version": "0.11.73" } ] }type为extension,表明它是可被 TEN 应用编排的扩展包;dependencies中声明了对系统包ten_runtime(版本0.11.73)的依赖,这是该扩展唯一的运行时依赖,即原 Korean 文档中“manifest.json 指定的必需依赖”的具体所指;readme.locales把各语言 README 指到docs/README.*.md,因此本文分析的 Korean 文档就是该包在包管理器界面展示的说明文档。
property.json:默认服务端口
property.json 只有一项配置:
{ "server_port": 8001 }这是扩展启动时 HTTP 服务监听的端口。从 main.cc 的on_start实现看,扩展通过ten_env.get_property_int64("server_port")读取该属性,仅当取值大于 0 时覆盖默认值:
int server_port = DEFAULT_SERVER_PORT; // 8001 auto port = ten_env.get_property_int64("server_port"); if (port > 0) { server_port = static_cast<int>(port); } http_server = create_http_server(DEFAULT_SERVER_URL, server_port);因此:
- 默认监听地址由
DEFAULT_SERVER_URL固定为127.0.0.1,即服务只在本机可达; - 默认端口
DEFAULT_SERVER_PORT为8001(main.cc#L19-L21); - 如需换端口,只需在应用集成时把该扩展实例的
server_port属性改为其他合法端口值即可,不需要改代码。
前提条件与安装
原 Korean 文档的安装部分要求:满足manifest.json中的依赖、按 TEN Framework 包安装指南操作、以 C++ 编译器与tgn构建工具链为前提。结合仓库内文档,完整流程如下:
1. 环境前提
开发/构建 C++ 扩展需要:
- C++ 编译器(gcc 或 clang),可参考 quick-start 指南 中“C++ Development Environment Requirements”一节安装;
- TEN 的 C/C++ 构建工具
tgn(基于 Google GN),可用仓库内脚本安装:bash tools/tgn/install_tgn.sh(脚本见 install_tgn.sh)。
2. 安装扩展包
在 TEN 应用目录下安装该扩展及其ten_runtime依赖:
tman install extension simple_http_server_cpp安装后,TEN 会在应用目录下生成ten_packages/extension/simple_http_server_cpp/安装目录,随后由应用构建流程编译该 C++ 扩展。tman的包安装机制、C++ 扩展的编译与运行流程详见 quick-start.md 的 “Advanced: Developing and Building C++ Extensions” 章节。
BUILD.gn:如何把 libwebsockets 编译进扩展
BUILD.gn 展示了该扩展的完整构建定义,核心是一个ten_package目标:
ten_package("simple_http_server_cpp") { package_kind = "extension" enable_build = true resources = [ "LICENSE", "manifest.json", "property.json", ] # docs/** 下的多语言 README 也会作为资源打包 sources = [ "src/main.cc" ] include_dirs = [ "//core/src", "//core", ] deps = [ ":simple_http_server_cpp_copy_websockets", "//core/src/ten_runtime", "//third_party/libwebsockets", "//third_party/nlohmann_json", ] }要点解读:
package_kind = "extension"与manifest.json的type一致,构建产物会被打包到${root_out_dir}/ten_packages/extension/simple_http_server_cpp/;- 源码只有一个编译单元
src/main.cc;头文件搜索路径指向仓库的//core与//core/src,对应main.cc中#include "include_internal/ten_runtime/binding/cpp/ten.h"这类内部头文件(位于 core/include_internal/ten_runtime/binding/ 下),以及ten_buf_t等底层缓冲工具(buf.h); deps引入三个关键依赖://core/src/ten_runtime:TEN 运行时 C++ 绑定;//third_party/libwebsockets:HTTP/WebSocket 服务端库;//third_party/nlohmann_json:请求体 JSON 解析;
ten_websockets_copy_deps目标会在安装阶段把 libwebsockets 的运行时库拷贝进扩展包的lib/目录,保证运行时链接可用;- 若启用了
ten_enable_ten_manager,还会生成ten_package_publish目标,用于把构建产物上传到 TEN Manager 的包服务器。
核心实现:http_server_extension_t 生命周期
main.cc 约 730 行,整个扩展由一个继承ten::extension_t的类http_server_extension_t驱动,并通过TEN_CPP_REGISTER_ADDON_AS_EXTENSION(simple_http_server_cpp, http_server_extension_t)注册为名为simple_http_server_cpp的扩展插件(main.cc#L730-L731)。
启动:创建服务器、TEN 代理与服务线程
on_start中完成三件事(main.cc#L524-L540):
- 按
server_port属性创建http_server_t(内部创建 libwebsockets 上下文); - 创建
ten::ten_env_proxy_t——这是 libwebsockets 线程回调进 TEN 线程世界的桥梁; - 启动独立 HTTP 服务线程并调用
ten_env.on_start_done()宣告启动完成。
服务器上下文参数在lws_context_new中固定(main.cc#L472-L490):协议表protocols、服务名http_server、监听端口、connect_timeout_secs = 30、keepalive_timeout = 60。服务线程是一个简单循环(main.cc#L506-L518):
while (n >= 0 && (http_server->react_ten_stopping_state < REACT_TEN_STOPPING_COMPLETED)) { n = lws_service(http_server->lws_context, 0); } lws_context_destroy(http_server->lws_context);即:只要 TEN 尚未停机完成,就持续调用lws_service处理事件;停机完成后销毁上下文。
每笔 HTTP 事务的数据结构
每一笔 HTTP 事务(从协议绑定到解绑的生命周期)由http_transaction_data_t描述(main.cc#L42-L75):
| 成员 | 含义 |
|---|---|
req_buf/resp_buf | ten_buf_t缓冲,分别累积请求体与待发送的响应体 |
wsi/lws_context | 当前连接与会话的 libwebsockets 句柄 |
http_server | 反向指回服务器对象,便于跨线程协调 |
method/url | 解析出的 HTTP 方法与请求 URL |
所有进行中的事务保存在http_server_t的all_http_session_data列表中(main.cc#L84-L94),该列表同时承担“停机时是否还有未响应请求”的判定职责。
HTTP 事件回调:一次请求的完整路径
libwebsockets 通过event_callback(main.cc#L304-L458)驱动整个 HTTP 流程,关键事件如下:
LWS_CALLBACK_HTTP_BIND_PROTOCOL:协议绑定时为本次事务分配req_buf/resp_buf(默认容量DEFAULT_BUF_CAPACITY = 512字节,可自动增长)并把会话压入all_http_session_data;LWS_CALLBACK_HTTP:收到请求头。先用parse_http_method从 URI token 判定方法(支持 GET/POST/PUT/PATCH/DELETE/OPTIONS,见 main.cc#L232-L260)。GET/DELETE/OPTIONS 无请求体,立即走“无 body”分支;其余方法等待请求体收齐;LWS_CALLBACK_HTTP_BODY:把分片请求数据追加进req_buf;LWS_CALLBACK_HTTP_BODY_COMPLETION:请求体收齐,走“带 body”分支;LWS_CALLBACK_HTTP_WRITEABLE:TEN 线程写入响应后,此处把响应头 + 响应体发回客户端,然后调用lws_http_transaction_completed结束事务;LWS_CALLBACK_HTTP_DROP_PROTOCOL:清理本事务资源,并在“所有请求已处理且 TEN 正在停机”时推进停机流程;LWS_CALLBACK_EVENT_WAIT_CANCELLED:被lws_cancel_service唤醒时触发,统一触发待发送响应的写出(trigger_lws_write_out_timing),并配合停机判定。
响应写出分两段(main.cc#L97-L164):return_response_header用lws_add_http_common_headers写入固定状态码200 OK、Content-Type: application/json与响应长度,return_response_body以LWS_WRITE_HTTP_FINAL写出响应体。响应体始终是纯 JSON 文本(成功时为命令结果的detail属性,失败时为错误说明字符串)。
HTTP 请求到 TEN 命令的桥接
这是该扩展的核心价值:把 REST 风格请求翻译成 TEN 命令。两条路径分别是send_ten_msg_without_req_body(main.cc#L684-L728)与send_ten_msg_with_req_body(main.cc#L581-L682)。
无请求体:方法名即命令名
GET/DELETE/OPTIONS 请求不携带 JSON body,扩展直接以 HTTP 方法字符串作为命令名:
std::string method = get_http_method_string(http_session_data->method); // 如 "HTTP_GET" auto cmd = ten::cmd_t::create(method.c_str()); cmd->set_property("method", method); cmd->set_property("url", http_session_data->url); ten_env.send_cmd(std::move(cmd), /* callback */);也就是说,应用侧被调用的扩展只要处理HTTP_GET/HTTP_DELETE/HTTP_OPTIONS这三个命令名,即可接入该 HTTP 服务器;命令属性里还附带method与url供业务区分请求来源。
带请求体:JSON 决定命令类型与路由
POST/PUT/PATCH 等请求会把整个 body 按 JSON 解析,并遵循如下约定(main.cc#L600-L647):
- 若 body 含
ten.type == "close_app":构造内置的ten::close_app_cmd_t,把命令发给本地应用,用于从 HTTP 触发应用关闭; - 若 body 含
ten.name:以该值作为自定义命令名创建ten::cmd_t;若还带ten.dest(含app/graph/extension三个字段),则把命令定向到指定的应用/图/扩展; - 若没有
ten字段:默认退化为以 HTTP 方法字符串作为命令名; - 最终请求 JSON 会额外注入
method与url字段,并通过cmd->set_property_from_json整体写入命令属性,随命令一起投递。
命令结果如何变回 HTTP 响应
ten_env.send_cmd的回调运行在 TEN 线程中,处理逻辑统一为(main.cc#L652-L680):
- 命令发送失败(
err != nullptr):响应体为"The command is not supported. err:<错误信息>"; - 发送成功:读取
cmd_result的detail属性并序列化为 JSON 字符串,作为响应体;若取不到detail则回退为"{}"; - 若扩展已进入停机状态(
is_stopping),不再向 libwebsockets 线程推数据,交由停机流程统一收尾。
跨线程协作机制
请求处理横跨两个线程域:libwebsockets 服务线程与 TEN 线程。桥接手段有两个:
ten_env_proxy->notify(lambda):在 lws 线程捕获的回调,被投递到 TEN 线程执行(发送命令、构造响应数据均在 TEN 线程完成);lws_cancel_service与lws_callback_on_writable:TEN 线程写响应完成后调用prepare_response_data_from_ten_world,先更新resp_buf再lws_cancel_service唤醒服务循环,trigger_lws_write_out_timing对每个有待发响应的会话执行lws_callback_on_writable,最终由LWS_CALLBACK_HTTP_WRITEABLE完成实际写出(main.cc#L191-L210)。
优雅停机:状态机与未决请求收尾
停机逻辑由原子状态react_ten_stopping_state驱动,共三态:REACT_TEN_STOPPING_NOT_START → REACT_TEN_STOPPING → REACT_TEN_STOPPING_COMPLETED(main.cc#L77-L81)。流程为:
on_stop被 TEN 运行时调用:置is_stopping = true、状态进入REACT_TEN_STOPPING,并lws_cancel_service唤醒服务循环(main.cc#L562-L568);- 停机期间新请求一律不再处理,
LWS_CALLBACK_HTTP中直接lws_http_transaction_completed拒绝(main.cc#L338-L343); - 对已收到请求但尚未得到 TEN 响应的会话,统一写入默认响应
"TEN is closed."; - 当最后一个会话被清理(
LWS_CALLBACK_HTTP_DROP_PROTOCOL)且无待发响应时(LWS_CALLBACK_EVENT_WAIT_CANCELLED),状态置为COMPLETED,并通过ten_env_proxy->notify回到 TEN 线程执行proceed_to_stop_http_extension:join HTTP 服务线程、释放ten_env_proxy、释放http_server,最后调用ten_env.on_stop_done()通知运行时停机完成(main.cc#L213-L230)。
这套“先排空在途请求、再退出线程、最后释放资源”的顺序保证了停机过程无请求悬挂、无资源泄漏。
使用方式与验证
把该扩展加入某个 TEN 应用的编排后,应用启动时它会在127.0.0.1:8001(或你配置的端口)监听。可用的验证方式:
1. 无 body 请求(GET/DELETE/OPTIONS)
curl -X GET http://127.0.0.1:8001/any/path扩展会构造名为HTTP_GET的命令(属性含method=HTTP_GET、url=/any/path)发往应用。目标扩展处理命令后,若其cmd_result设置了detail属性,HTTP 响应体即该detail的 JSON;否则响应为{}或错误说明文本。
2. 带 body 请求(POST)
发送自定义命令并定向到指定扩展:
curl -X POST http://127.0.0.1:8001/action \ -H 'Content-Type: application/json' \ -d '{ "ten": { "name": "my_custom_cmd", "dest": { "app": "", "graph": "", "extension": "my_extension" } }, "foo": "bar" }'其中ten.name决定命令名(my_custom_cmd),ten.dest决定命令路由目标,其余 JSON 字段(含注入的method/url)整体作为命令属性携带。也可以触发内置关闭命令:
curl -X POST http://127.0.0.1:8001/ \ -d '{"ten": {"type": "close_app"}}'响应统一为 HTTP 200 +application/json,业务语义(成功detail、{}回退、The command is not supported. err:...错误串)需按上述规则解读。
3. 端口调整
修改应用侧该扩展实例的server_port属性即可(默认8001来自 property.json)。若端口被占用,可参考 quick-start 指南 中 “Port 8001 Already in Use” 的排查方法(lsof -i :8001等)。
许可证
该包随附 LICENSE,采用 Apache License 2.0(源文件头部注释亦声明了这一点),是 TEN Framework 项目的组成部分。
小结
simple_http_server_cpp虽然只有一个src/main.cc,却完整演示了 C++ 扩展与外部世界交互的三条关键路径:libwebsockets 事件驱动 HTTP 服务(协议表、事件回调、事务缓冲)、通过ten_env_proxy的跨线程命令桥接(请求 JSON →ten::cmd_t→cmd_result.detail→ JSON 响应)、以及基于原子状态机的优雅停机与资源回收。对希望给 TEN 应用增加本地 HTTP 控制面(外部触发命令、查询结果、关闭应用)的开发者而言,它是可直接安装复用的现成扩展,也是阅读 TEN C++ 扩展开发指南 后理解ten::extension_t生命周期机制的最佳实战样本。
- 人工智能
- AI Agent
- 多模态
- 语音
- AI 应用
【免费下载链接】ten-framework
Open-source framework for conversational voice AI agents
相关推荐
TEN Framework C++ HTTP Server 扩展深度解析:simple_http_server_cpp 从请求接入到命令分发的完整实现
TEN Framework C++ HTTP Server 扩展深度解析:simple_http_server_cpp 从请求接入到命令分发的完整实现 本篇以
人工智能AI Agent多模态语音AI 应用TEN Framework 原生接入 Anthropic Claude:anthropic_llm2_python 扩展实战指南
TEN Framework 原生接入 Anthropic Claude:anthropic_llm2_python 扩展实战指南 anthropic_llm2_
人工智能AI Agent多模态语音AI 应用在 TEN Framework 中集成 NVIDIA Riva TTS:nvidia_riva_tts_python 扩展实现详解
在 TEN Framework 中集成 NVIDIA Riva TTS:nvidia_riva_tts_python 扩展实现详解 本篇技术指南以 TEN Fr
人工智能AI Agent多模态语音AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考