underthesea:越南语 NLP 工具链如何把 Agent 运行时塞进标准库
Underthesea - AI Assistant
秒懂
- 它是什么?
- 这个项目从越南语分词起家,v9.3.0 之后叠加了一层只用 urllib 和 json 实现的 Agent 运行时。它的取舍很清楚:不引入任何 LLM SDK,代价是你要自己认领协议细节。
- 适合谁用?
- 如果你要处理越南语文本,同时希望 Agent 层不把 openai 或 anthropic SDK 拖进依赖树,underthesea 值得先跑一遍 pip install underthesea 加一段 Agent(name="bot", provider=LLM()) 的最小脚本,确认 provider 自动探测在你的环境变量下能命中。反过来,如果你的技术栈已经深度绑定某个厂商 SDK 的异步客户端、重试策略和结构化输出,这层薄封装会让你失去这些能力,不如直接用官方 SDK。
- 能商用吗?
- 可以。Apache-2.0 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 1 天前。
- 用什么语言写的?
- 主要是 Python(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
从越南语分词器长出来的 Agent 运行时
underthesea 最早是一个越南语自然语言处理库,README 把它描述为提供「Vietnamese Natural Language Processing」的一组 Python 模块。越南语的分词不是空格切分那么简单,词与词之间用空格分隔但复合词内部同样用空格,所以分词器本身要处理歧义。这个背景决定了项目的用户画像:需要处理越南语语料的工程团队。
v9.3.0 之后项目定位改成「Open-source Agentic AI Toolkit」,在原有 NLP 能力之上加了一层 Agent 抽象。这不是把 NLP 模块包成工具那么简单,而是把 Agent 运行时作为独立卖点。README 的标题行写的是「An Agentic AI Toolkit」,NLP 能力被降级为「built-in」的附带项。
对读者的实际含义是:你可以在同一个依赖里拿到分词、词性标注这类越南语处理函数,以及一个能调用 LLM 的 Agent 对象。这两部分耦合度不高,可以只用其中一半。
零依赖是怎么做到的:urllib 加 json 直连 HTTP
README 对这一点说得很直白:Agent 模块「Communicates with LLM APIs using only Python stdlib (urllib + json)」,不需要 openai、anthropic 或 google-genai 包。四个 provider 各自实现为一个类,OpenAI、AzureOpenAI、Anthropic、Gemini,命名和构造方式参照 Anthropic SDK 的模式。
这意味着 HTTP 请求、请求体序列化、SSE 流式解析、错误映射都由项目自己维护。好处是依赖树干净,不会因为某个 SDK 的大版本升级被迫改代码,也不会出现两个包同时钉住 httpx 版本的情况。代价是每家 provider 的 API 变更都要等这个项目跟进,而官方 SDK 通常跟进更快。
LLM() 这个无参构造是自动探测入口,从环境变量推断用哪家。README 列出的变量名分别是 OPENAI_API_KEY、AZURE_OPENAI_API_KEY 加 AZURE_OPENAI_ENDPOINT、ANTHROPIC_API_KEY、GOOGLE_API_KEY。注意 Azure 需要两个变量同时存在,只有 key 没有 endpoint 时探测会失败。
流式输出走 agent.stream(),返回可迭代对象,README 的示例是逐 chunk 打印。这个接口是同步的,没有给出 async 版本。
工具调用、默认工具与多会话状态机
工具通过 Tool() 包装一个普通 Python 函数,函数的 docstring 被用作工具描述,类型注解被用作参数 schema,README 里 get_weather(location: str) -> dict 的例子说明了这个约定。
default_tools 提供 12 个内置工具,README 列举的类别是 calculator、datetime、web search、wikipedia、file I/O、shell、python exec。这里需要警惕:shell 和 python exec 意味着 Agent 可以直接在宿主上执行命令和任意代码。默认工具集适合本地实验,不适合暴露给不可信输入。
多会话部分针对长任务。Session(agent, progress_file="progress.json") 把进度落到文件,create_task() 接收任务名和一个步骤列表,run_until_complete(max_sessions=5) 控制最多开几轮会话。README 说明这个设计参照 Anthropic 关于长任务 harness 的工程文章,核心是在上下文窗口耗尽时重置上下文,用结构化交接把已完成的工作传下去。
限制在于 max_sessions 是硬上限,达到之后任务是否完成需要调用方自己判断,README 没有给出返回值语义。progress.json 的格式也没有在 README 里定义,如果你要读取它做外部监控,得先看源码。
追踪默认开启,写到主目录下的隐藏目录
每次 Agent 调用都会自动写入 ~/.underthesea/traces/,这是默认行为,不是可选项。关闭方式是设置环境变量 UNDERTHESEA_TRACE_DISABLED=1。
README 给出的 trace 输出形态包含 trace id、每次 generation 的模型名、耗时和 token 数(形如 100->18 tokens),以及工具调用的耗时。文件按时间戳命名,例如 20260411_trace_a1b2c3.json。
这个设计对调试友好,但有两个实际问题。第一,写入位置固定在用户主目录,容器环境或只读文件系统下可能直接失败,或者悄悄累积大量 JSON 文件。第二,trace 内容包含 prompt 和响应,如果业务数据敏感,默认开启意味着数据落盘,需要主动用环境变量关掉。
除本地追踪外,还提供 LangfuseTracer,需要额外 pip install langfuse。@trace 装饰器可以把普通函数变成 span,README 说明嵌套调用会自动继承 trace 上下文。
A2A 服务端:裸 ASGI,不带 Web 框架
v9.5.0 加了 A2A Agent Server。serve(agent, port=8000, path="/a2a/math", ui=True) 会暴露三个端点:GET 的 /ui 是内置聊天界面,GET 的 /.well-known/agent-card.json 是 AgentCard 发现文档,POST 的同一路径处理 JSON-RPC 的 message/stream,走 HTTP+SSE。
基础安装里不含 Web 框架依赖,服务端是一个裸 ASGI 可调用对象。想省事可以装 underthesea[agent-server] 这个 extra,它带 uvicorn、starlette、httpx。想接自己的服务器就用 make_app(agent, path="/a2a/math"),README 提到 uvicorn 和 hypercorn 都能挂。
这个选择有明确后果。裸 ASGI 意味着中间件、认证、限流、CORS 都要你自己在 ASGI 层写,项目不提供。ui=True 打开的内置聊天界面也没有提到任何访问控制。如果你的 Agent 挂了 default_tools 里的 shell 工具,又把这个端点暴露到网络上,风险由部署方承担。README 没有讨论鉴权,这一点需要在上线前自己确认。
什么时候不该用它
最明显的一条:如果你的需求只是调用 LLM,不需要越南语处理,也不需要零依赖,那么这个项目的 Agent 层相比官方 SDK 是功能子集。官方 SDK 通常提供异步客户端、自动重试、结构化输出、批量接口、token 计数工具,这些在 README 里都没有出现。
第二条与语言有关。项目名字和主题标签都指向越南语,NLP 模块的价值集中在越南语上。用它处理英文或中文语料,你只用到那层薄薄的 Agent 封装,性价比不高。
第三条关于 provider 覆盖。README 只列了 OpenAI、Azure OpenAI、Anthropic、Gemini 四家。自建推理服务、本地模型、其他云厂商的兼容端点都不在列表里。虽然很多服务兼容 OpenAI 协议,理论上可以借用 OpenAI 类改 base_url,但 README 没有说明是否支持这个参数,需要看源码确认。
第四条关于版本节奏。从发布记录看,v9.3.0 在 2026-04-11,v9.4.0 在同一天,v9.5.0 在 2026-05-17。Agent 相关功能在两个月内连推三个版本,接口稳定性需要你自己评估。
与直接用厂商 SDK 的差别在哪
拿 Anthropic 官方 Python SDK 作对比。官方 SDK 提供 Anthropic() 客户端、messages.create()、流式事件类型、工具调用的结构化解析、以及内置的重试和超时控制。underthesea 的 Anthropic 类只覆盖最基础的对话和工具调用路径,构造方式刻意模仿官方 SDK 的写法以便迁移。
区别在维护责任。用官方 SDK,API 变更由厂商维护,你升级包版本即可。用 underthesea 的封装,厂商改了请求体字段或流式事件格式,要等这个项目发版。对于把 LLM 调用放在关键路径上的系统,这是实打实的风险。
反过来,如果你的部署环境对依赖数量有硬性要求,比如要打进一个受限的运行时,或者你的供应链策略不允许引入某家厂商的 SDK,那么这个只用 urllib 的实现就有实际价值。这不是性能优势,是依赖治理上的优势。
另一个差别是工具生态。官方 SDK 通常与厂商的 Agent 框架、评测工具、可观测性平台打通。underthesea 这边是自建的 Tool 抽象加自建的 trace 格式,Langfuse 是唯一提到的外部集成。
许可、维护成本与升级前该确认的事
许可证是 Apache-2.0,允许商用和修改,附带专利授权条款,要求保留版权声明和变更说明。具体的合规判断请咨询法务,这里只陈述许可标识。
维护成本主要来自三处。第一是 provider 协议跟进,四家 API 里任何一家改字段都可能需要升级。第二是 trace 文件的清理,默认写入 ~/.underthesea/traces/ 且没有提到轮转或上限,长期运行需要自己加清理逻辑或设置 UNDERTHESEA_TRACE_DISABLED=1。第三是 A2A 服务端的自建部分,认证和限流不在项目范围内。
升级前建议确认:你的 Python 版本是否在 3.10 到 3.14 区间内,README 的 badge 标注了这个范围;你依赖的 provider 是否在四家之内;default_tools 里哪些工具会被真正暴露。
项目主页是 undertheseanlp.com,文档在 undertheseanlp.github.io/underthesea,README 还提供了 Colab 链接。仓库未归档,最近一次推送时间在 2026-09-09。
编辑结论
如果你要处理越南语文本,同时希望 Agent 层不把 openai 或 anthropic SDK 拖进依赖树,underthesea 值得先跑一遍 pip install underthesea 加一段 Agent(name="bot", provider=LLM()) 的最小脚本,确认 provider 自动探测在你的环境变量下能命中。反过来,如果你的技术栈已经深度绑定某个厂商 SDK 的异步客户端、重试策略和结构化输出,这层薄封装会让你失去这些能力,不如直接用官方 SDK。上线前至少验证三件事:默认写入 ~/.underthesea/traces/ 的追踪文件是否落在可写且容量可控的路径上,default_tools 里的 shell 与 python exec 是否被暴露给外部输入,以及 A2A 服务端在 serve() 之后是否真的挂在你的 ASGI 服务器上而不是仅监听本地。
社区笔记