Underthesea:越南语NLP与AI智能体一体化工具箱实战指南

2026-05-10 17:19:53261 阅读量

1. 项目概述:一个为越南语量身定制的智能体化AI工具箱

如果你正在寻找一个能同时处理越南语自然语言处理任务和构建AI智能体的Python工具箱,那么 underthesea 绝对值得你花时间深入了解。这个项目最初以越南语NLP工具包闻名,如今已进化成一个功能全面的“智能体化AI工具箱”。简单来说,它让你能用一套简洁的API,既完成对越南语文本的分词、词性标注、命名实体识别等传统NLP任务,又能快速搭建起一个能调用工具、支持多轮会话、具备完整追踪能力的AI智能体。最吸引人的一点是,它的智能体模块设计极其“干净”,除了Python标准库外没有任何外部依赖,这意味着你无需陷入管理 openai 、 anthropic 等众多SDK版本冲突的烦恼。对于需要处理越南语场景,同时又想引入AI智能体能力的开发者、研究者或是创业者, underthesea 提供了一个高度集成且轻量化的解决方案。

相关服务:越南服务器

2. 核心架构与设计哲学

2.1 双引擎驱动:NLP基础能力与智能体框架的融合

underthesea 的设计核心在于“双引擎”。一个引擎是深耕多年的越南语NLP处理管道,这构成了它的基石能力。另一个引擎则是从v9.3.0版本开始引入的智能体框架。这两者并非简单捆绑,而是有机结合的。智能体可以无缝调用NLP模块的处理结果作为其感知或决策的一部分,例如,一个客服智能体可以先对用户的越南语查询进行情感分析,再根据情感倾向决定回复策略。

这种融合解决了特定语言场景下AI应用开发的一个痛点:通常你需要用一个库处理语言,再用另一个框架构建智能体,中间存在大量的数据转换和适配工作。 underthesea 试图提供一站式的体验,尤其针对越南语这一资源相对较少的语言,其价值更为凸显。

2.2 智能体模块的“零外部依赖”哲学

这是 underthesea 智能体部分最激进也最实用的设计选择。其 agent 模块与大型语言模型API的通信,完全依赖于Python标准库中的 urllib 和 json ,彻底摒弃了对 openai 、 anthropic 、 google-genai 等官方SDK的依赖。

为什么这么做? 在实际生产环境中,依赖管理是个头疼的问题。不同SDK版本升级可能带来不兼容的API变更,多个SDK并存可能引发依赖冲突。 underthesea 选择用最朴素的方式直接调用RESTful API,将复杂性封装在内部,给开发者提供一个极其稳定的接口。这意味着你的项目依赖列表会非常干净,部署和复现环境也更容易。当然,这也对 underthesea 团队提出了更高要求,他们需要自行维护与各家厂商API的兼容性,但这对使用者而言无疑是福音。

架构清晰度 :从提供的架构图可以看出,模块组织非常清晰。 providers 目录下是各家模型供应商的客户端实现, trace 目录负责可观测性,核心的 Agent 、 Session 、 Tool 等类各司其职。这种结构让代码易于阅读和扩展,如果你想支持一个新的LLM提供商,只需在 providers 下添加一个遵循相同模式的类即可。

Underthesea:越南语NLP与AI智能体一体化工具箱实战指南

3. 智能体功能深度解析与实操要点

3.1 多提供商支持与无缝切换

underthesea 目前支持OpenAI、Azure OpenAI、Anthropic Claude和Google Gemini四大主流提供商。它的设计巧妙之处在于提供了一个统一的 LLM() 类,能根据环境变量自动检测并初始化对应的提供商。

实操示例与配置细节 :

from underthesea.agent import Agent, LLM, OpenAI, AzureOpenAI

# 方式一:自动检测(最省心)
import os
os.environ[“OPENAI_API_KEY”] = “sk-...” # 或设置其他提供商的环境变量
agent = Agent(name=“my_agent”, provider=LLM()) # LLM()会自动读取环境变量

# 方式二:显式指定(更可控)
# 使用原生OpenAI
agent_openai = Agent(name=“bot”, provider=OpenAI(api_key=“sk-...”, model=“gpt-4o”))

# 使用Azure OpenAI(注意参数差异)
agent_azure = Agent(name=“bot”, provider=AzureOpenAI(
    api_key=“your-azure-key”,
    endpoint=“https://your-resource.openai.azure.com/”, # 必须包含协议和域名
    deployment=“your-deployment-name”, # Azure门户中部署的名称,非模型名
    api_version=“2024-02-15-preview” # 建议指定,避免默认版本变更
))

注意 :Azure OpenAI的 endpoint 参数容易填错。它不是模型的base URL,而是你Azure资源的终结点,格式通常为 https://[your-resource-name].openai.azure.com/ 。 deployment 参数是你在该资源下创建的部署名称,而不是“gpt-4”这样的模型名。

3.2 工具调用:从函数到智能体能力的转化

工具调用是智能体与现实世界交互的桥梁。 underthesea 的 Tool 类将一个普通的Python函数包装成智能体可以理解和调用的工具。

定义与集成工具 :

from underthesea.agent import Agent, Tool, OpenAI
from typing import Dict, Any
import requests

def search_products(query: str, category: str = None) -> Dict[str, Any]:
    “”“根据查询词和可选类别搜索产品列表。
    
    Args:
        query: 搜索关键词。
        category: 产品类别,如‘electronics’,‘books’。
    
    Returns:
        包含产品列表的字典。
    ”“”
    # 这里模拟一个内部API调用
    params = {“q”: query}
    if category:
        params[“category”] = category
    # 假设调用某个搜索接口
    # response = requests.get(“https://api.example.com/products”, params=params)
    # return response.json()
    return {
        “products”: [
            {“name”: “Sample Product 1”, “price”: 99.99},
            {“name”: “Sample Product 2”, “price”: 149.99},
        ]
    }

# 将函数转化为工具
product_search_tool = Tool(search_products)

# 创建带有自定义工具的智能体
agent = Agent(
    name=“shopping_assistant”,
    provider=OpenAI(api_key=“sk-...”),
    tools=[product_search_tool], # 传入工具列表
    instruction=“你是一个购物助手,帮助用户查找商品信息。请优先使用提供的工具来获取准确数据。”
)

response = agent(“我想找一些预算在100元左右的电子产品”)
print(response)

关键点解析 :

  1. 函数文档字符串至关重要 :LLM依赖函数的 docstring 来理解工具的功能、参数和返回值。描述必须清晰、准确。
  2. 类型提示(Type Hints) :像 query: str 这样的类型提示能帮助 underthesea 和LLM更好地理解参数格式,提升工具调用的准确率。
  3. 工具指令(Instruction) :在创建 Agent 时提供的 instruction 参数,是引导智能体行为的关键。你可以在这里设定角色、规定工具使用优先级、说明输出格式等。

3.3 内置工具库:开箱即用的强大能力

underthesea 提供了12个内置工具,覆盖了常见需求,能极大提升开发效率。

from underthesea.agent import Agent, default_tools, LLM

# default_tools 包含了所有内置工具
agent = Agent(name=“全能助手”, provider=LLM(), tools=default_tools)

# 现在这个智能体可以:
# - 计算数学表达式(calculator)
# - 获取当前日期时间(datetime)
# - 进行网络搜索(web_search,需额外配置API Key)
# - 查询维基百科(wikipedia)
# - 读写本地文件(file_read, file_write)
# - 执行简单的Shell命令(shell)
# - 执行Python代码(python_exec)

使用内置工具的注意事项 :

  • 安全风险 : shell 和 python_exec 工具功能强大但极其危险。在生产环境中使用务必谨慎,最好通过设置 instruction 严格限制其使用场景,或考虑移除此类工具。在沙箱或受控环境中测试是必须的。
  • 网络工具配置 : web_search 工具通常需要接入如Serper、Exa等搜索服务的API。你需要查阅 underthesea 文档,设置相应的环境变量(如 SERPER_API_KEY )才能启用。
  • 选择性导入 :如果你不需要所有工具,可以从 underthesea.agent.default_tools 中按需导入,例如 from underthesea.agent import calculator_tool, datetime_tool 。

3.4 会话管理与长任务编排

对于需要多步执行、状态保持的复杂任务, Session 类提供了优雅的解决方案。它借鉴了Anthropic关于长周期智能体设计的思路,支持任务分解、进度持久化和会话间的结构化交接。

实战:使用Session处理研究任务

from underthesea.agent import Agent, Session, AzureOpenAI
import os

# 配置Azure OpenAI
os.environ[“AZURE_OPENAI_API_KEY”] = “...”
os.environ[“AZURE_OPENAI_ENDPOINT”] = “https://my-resource.openai.azure.com/”

agent = Agent(
    name=“research_agent”,
    provider=AzureOpenAI(deployment=“gpt-4”),
    tools=[...], # 可以配备文件读取、网络搜索等工具
    instruction=“你是一个严谨的研究员,擅长分解任务、收集信息并撰写报告。”
)

# 创建一个会话,并指定进度保存文件
session = Session(agent, progress_file=“./research_progress.json”)

# 定义一项复杂任务及其子步骤
session.create_task(
    name=“分析关于‘越南可再生能源发展’的三份文档”,
    steps=[
        “阅读并总结文档A的核心观点”,
        “阅读并总结文档B的政策建议”,
        “阅读并总结文档C的市场数据”,
        “对比三份文档的异同点”,
        “撰写一份综合性的分析报告摘要”
    ]
)

# 运行会话,最多迭代5轮(即5次智能体调用)
try:
    result = session.run_until_complete(max_sessions=5)
    print(f“任务完成,最终结果:{result}”)
except Exception as e:
    print(f“任务执行出错:{e}”)
    # 进度已自动保存到‘research_progress.json’,下次可以从断点恢复

Session机制的优势 :

  1. 进度持久化 : progress_file 参数使得任务状态可以保存到磁盘。即使程序中断,重启后可以加载进度继续执行,非常适合处理耗时长的任务。
  2. 任务分解 : create_task 方法鼓励你将大任务拆解为清晰的子步骤。智能体在每一步都能获得具体的上下文,避免在长对话中迷失方向。
  3. 可控的执行 : max_sessions 参数限制了最大交互轮数,防止智能体陷入无意义的循环,也便于控制API调用成本。

4. 可观测性与追踪:洞察智能体每一步

缺乏可见性是调试智能体的主要难点。 underthesea 内置了追踪功能,能自动记录每次智能体调用的详细过程。

4.1 本地追踪:零配置上手

这是默认开启的功能,无需任何设置。每次调用 agent() 或相关方法,都会在 ~/.underthesea/traces/ 目录下生成一个JSON文件。

from underthesea.agent import Agent, calculator_tool, LLM

agent = Agent(name=“demo_bot”, provider=LLM(), tools=[calculator_tool])
response = agent(“请计算(15 + 27) * 3 等于多少?”)

# 此时,在 ~/.underthesea/traces/ 下会生成一个类似‘20250411_trace_abc123.json’的文件。

你可以打开这个JSON文件查看,里面会包含时间戳、使用的模型、输入输出消息、工具调用的详情(包括参数和返回值)、token消耗量以及各步骤耗时。这对于复盘智能体的推理过程、排查工具调用错误、进行性能分析至关重要。

提示 :如果你在无头环境(如服务器)或需要禁用追踪,只需设置环境变量 UNDERTHESEA_TRACE_DISABLED=1 。

4.2 集成Langfuse:专业级的可观测性

对于团队协作或需要更强大分析、监控功能的项目, underthesea 支持与开源的可观测性平台Langfuse集成。

# 首先安装langfuse
# pip install langfuse

from underthesea.agent import Agent, LangfuseTracer, LLM
import os

# 配置Langfuse凭证(通常来自环境变量)
os.environ[“LANGFUSE_SECRET_KEY”] = “sk-lf-...”
os.environ[“LANGFUSE_PUBLIC_KEY”] = “pk-lf-...”
os.environ[“LANGFUSE_HOST”] = “https://cloud.langfuse.com” # 或你的自托管地址

agent = Agent(
    name=“production_agent”,
    provider=LLM(),
    tools=[...],
    tracer=LangfuseTracer() # 指定使用Langfuse追踪器
)

agent(“处理用户订单查询...”)

集成后,所有追踪数据会发送到Langfuse服务器。你可以在Langfuse的仪表板中可视化整个调用链、分析延迟和成本、设置警报,甚至进行基于追踪数据的再训练。

4.3 使用@trace装饰器进行细粒度追踪

对于复杂的应用,你可能希望将自定义函数也纳入追踪体系,形成嵌套的调用链。 @trace 装饰器可以实现这一点。

from underthesea.agent import Agent, LLM
from underthesea.agent.trace import trace, LocalTracer

# 创建一个本地追踪器实例
local_tracer = LocalTracer()

@trace(local_tracer) # 装饰你的业务函数
def customer_service_pipeline(user_query: str) -> str:
    “”“一个简单的客服流水线:先分类,再根据类别调用不同的智能体。”“”
    # 这里可以做一些预处理,比如用underthesea的NLP功能进行意图分类
    # category = classify_intent(user_query) 
    category = “billing” # 假设分类结果
    
    if category == “billing”:
        agent = Agent(name=“billing_specialist”, provider=LLM(), instruction=“处理账单问题”)
        return agent(user_query)
    else:
        agent = Agent(name=“general_support”, provider=LLM(), instruction=“处理一般咨询”)
        return agent(user_query)

# 调用被装饰的函数,其内部所有的agent调用都会作为子span记录在同一个trace中
result = customer_service_pipeline(“我的上月账单有疑问”)

这样,在追踪视图中, customer_service_pipeline 是一个根span,其内部智能体的调用是它的子span,整个业务逻辑的脉络一目了然。

5. 越南语NLP管道实战应用

underthesea 的NLP功能是其传统强项,提供了覆盖文本处理全流程的管道。

5.1 基础文本处理流程

让我们从一个完整的例子开始,看看如何将一段原始越南语新闻文本处理成结构化信息。

from underthesea import sent_tokenize, word_tokenize, pos_tag, ner, sentiment

# 示例:一段越南语科技新闻
news_text = ““”
Công ty công nghệ Vintech vừa công bố một đột phá trong lĩnh vực xử lý ngôn ngữ tự nhiên (NLP) tại thành phố Hồ Chí Minh. Giám đốc điều hành Nguyễn Văn A cho biết, sản phẩm mới có độ chính xác cao và phản hồi tích cực từ người dùng.
““”

# 1. 句子分割
sentences = sent_tokenize(news_text)
print(“句子分割:”, sentences)
# 输出: [‘Công ty công nghệ Vintech... tại thành phố Hồ Chí Minh.’, ‘Giám đốc điều hành... từ người dùng.’]

# 2. 对第一句进行深入分析
first_sentence = sentences[0]

# 3. 词语分割(分词)
words = word_tokenize(first_sentence)
print(“\n词语分割:”, words)
# 输出: [‘Công ty’, ‘công nghệ’, ‘Vintech’, ‘vừa’, ‘công bố’, …]

# 4. 词性标注
pos_tags = pos_tag(first_sentence)
print(“\n词性标注:”)
for word, tag in pos_tags:
    print(f”  {word}: {tag}“) # 例如:Công ty: N, công nghệ: N, Vintech: Np

# 5. 命名实体识别
ner_results = ner(first_sentence)
print(“\n命名实体识别:”)
for entity in ner_results:
    # entity格式: (词语, 词性, 组块标签, 实体标签)
    if entity[3] != ‘O’: # ‘O’表示非实体
        print(f”  实体‘{entity[0]}’ -> 类型: {entity[3]}“) 
        # 例如:实体‘Vintech’ -> 类型: B-ORG, 实体‘Hồ Chí Minh’ -> 类型: B-LOC

# 6. 情感分析(对第二句)
second_sentence = sentences[1]
sentiment_result = sentiment(second_sentence)
print(f”\n情感分析(‘{second_sentence}’): {sentiment_result}“)
# 输出: 情感分析(‘Giám đốc điều hành...’): positive

5.2 高级功能与集成应用

除了基础管道, underthesea 还提供了一些高级功能,可以直接服务于应用。

依存句法分析 :理解句子中词语间的语法关系(如主谓宾)。

from underthesea import dependency_parse

sentence = “Tôi thích học lập trình Python.”
result = dependency_parse(sentence)
for token in result:
    # token包含:索引、词语、词根索引、依存关系
    print(f”{token[1]} -> {token[3]}({result[token[2]-1][1] if token[2]>0 else ‘ROOT’})“)
# 输出有助于理解句子结构,例如:Tôi -> nsubj(thích), thích -> ROOT, học -> xcomp(thích)...

文本分类 :将文本归入预定义的类别。

from underthesea import classify

# 假设有新闻分类模型
news_title = “Giá vàng hôm nay tăng mạnh”
category = classify(news_title, model=“news”) # 需要指定或加载特定分类模型
print(f”‘{news_title}’ 被分类为: {category}“)
# 注意:classify函数可能需要额外的模型参数或配置,请查阅最新文档。

将NLP能力融入智能体 :这是 underthesea 的威力所在。

from underthesea import sentiment, word_tokenize
from underthesea.agent import Agent, Tool, LLM

def analyze_vietnamese_text(text: str) -> dict:
    “““分析越南语文本,返回情感倾向和关键词。”””
    sentiment_score = sentiment(text)
    # 简单的关键词提取:这里取名词性词语作为示例
    words = word_tokenize(text)
    # 在实际应用中,这里可以集成更复杂的关键词提取或实体链接逻辑
    keywords = [w for w in words if len(w) > 3][:5] # 简单过滤
    return {
        “sentiment”: sentiment_score,
        “keywords”: keywords,
        “word_count”: len(words)
    }

nlp_tool = Tool(analyze_vietnamese_text)

agent = Agent(
    name=“vietnamese_analyst”,
    provider=LLM(),
    tools=[nlp_tool],
    instruction=“你是一个越南语内容分析师。当用户提供越南语文本时,使用工具分析其情感和关键词,然后基于分析结果给出内容优化建议。”
)

user_feedback = “Sản phẩm tốt, giao hàng nhanh nhưng giá hơi cao.”
response = agent(f”请分析以下用户反馈,并给出回复建议:{user_feedback}“)
print(response)
# 智能体会先调用analyze_vietnamese_text工具获取情感(应为positive)和关键词,
# 然后生成如“反馈整体积极,提到了‘产品好’、‘送货快’,但对‘价格高’有所顾虑。建议在回复中感谢好评,并对价格问题做出解释或提供优惠信息。”的回答。

6. 常见问题、排查技巧与性能优化

在实际使用 underthesea 构建应用时,你可能会遇到一些典型问题。以下是我在项目中积累的一些排查经验和优化建议。

6.1 智能体相关问题

问题1:智能体不调用工具,而是直接回答问题。

  • 可能原因 : instruction 不够明确,或者工具的函数描述( docstring )不够清晰。
  • 排查步骤 :
    1. 检查 instruction :是否明确要求智能体“必须使用工具”、“请优先调用工具”?角色设定(如“数据分析师”、“查询助手”)也有影响。
    2. 检查工具描述:确保 docstring 用简洁的语言说明了工具的功能、每个参数的意义和类型、返回值的格式。LLM依赖这些信息做决策。
    3. 启用追踪:查看生成的trace文件,观察LLM在收到用户请求后生成的“思考”内容。它可能显示了为什么决定不调用工具。
  • 解决方案 :强化 instruction ,例如:“你是一个严格遵循流程的助手。对于任何数据查询或计算请求,你必须且只能使用我提供的工具来获取信息,不得自行编造答案。”同时,优化工具函数的 docstring 。

问题2:调用Azure OpenAI时出现认证或终结点错误。

  • 错误信息 :可能包含 401 、 404 或 InvalidRequestError 。
  • 排查清单 :
    • api_key :确认是Azure门户中对应资源的密钥,而不是OpenAI平台的密钥。
    • endpoint :格式必须是 https://[your-resource-name].openai.azure.com/ ,确保没有多余路径(如 /openai/deployments )。
    • deployment :确认该名称的部署已在Azure AI Studio中创建且状态为“成功”。注意区分“部署名”和“模型名”。
    • api_version :建议在 AzureOpenAI 初始化时显式指定一个稳定的版本号,例如 api_version=“2024-02-15-preview” ,避免使用默认版本可能带来的变化。

问题3:流式响应(Streaming)不工作或显示异常。

  • 现象 :调用 agent.stream() 时没有输出,或者输出是乱码。
  • 排查 :
    1. 确认提供商支持流式响应。所有主流提供商通常都支持。
    2. 检查循环打印代码:确保使用了 print(chunk, end=“”, flush=True) 。 flush=True 是关键,它强制立即输出缓冲区内容。
    3. 网络问题:在某些环境(如某些代理后)下,流式响应可能被缓冲或中断。尝试在简单网络环境下测试。
    4. 查看Trace:流式响应也会被记录,可以查看trace中接收到的chunk序列。

6.2 NLP模块相关问题

问题1:分词( word_tokenize )结果不符合预期。

  • 现象 :专有名词被拆散,或复合词处理不当。
  • 原因 :越南语分词本身具有挑战性, underthesea 的模型在通用文本上表现良好,但对于特定领域(如医学、法律)或新词可能效果下降。
  • 应对策略 :
    • 后处理 :编写简单的规则对分词结果进行修正。例如,如果知道“Hồ Chí Minh”总是一个整体,可以在分词后将其合并。
    • 自定义词典 :查阅 underthesea 文档,看是否支持加载用户自定义词典来提升特定领域词汇的分词准确率。
    • 理解误差范围 :对于非关键应用,接受一定误差;对于关键应用,考虑结合规则和人工校验。

问题2: sentiment 分析结果中性居多,不够敏感。

  • 分析 :情感分析模型通常在平衡的数据集上训练,对于非常强烈或非常微妙的情感可能区分度不够。
  • 建议 :
    1. 领域微调 :如果可能,收集你业务领域的标注数据,对 underthesea 的情感模型进行微调。
    2. 结合规则 :可以定义一些领域内的强情感词(如“tuyệt vời”-极好,“tệ hại”-糟糕)列表,当文本中出现这些词时,覆盖或加权模型的结果。
    3. 使用更细粒度方法 :不要只依赖最终的 positive / negative / neutral 标签。可以尝试用 underthesea 的 text_normalize 和 word_tokenize 后,结合情感词典进行更细致的分析。

6.3 性能与最佳实践

  1. 智能体调用优化 :

    • 设置超时和重试 : underthesea.agent 的Provider类通常支持 timeout 参数。为生产环境配置合理的超时和重试逻辑,避免因网络波动导致整个流程卡死。
    • 缓存结果 :对于频繁出现的、结果确定的查询(如“今天的日期”),可以在调用工具的函数内部实现简单的缓存机制,避免重复调用LLM。
    • 精简上下文 :在 Session 或长对话中,注意上下文窗口会不断增长。定期总结或清除早期不重要的历史消息,以节省token并保持模型关注重点。
  2. NLP处理批量化 :如果需要处理大量文本,避免在循环中单条调用 word_tokenize 等函数。 underthesea 的某些函数可能支持批处理输入,请查阅最新文档。如果不支持,可以考虑使用 concurrent.futures 进行并行处理以提高吞吐量。

  3. 模型选择 :在智能体模块中,不同的LLM提供商和模型在成本、速度、能力上差异很大。对于简单的工具调用和格式化任务,使用更小、更快的模型(如GPT-3.5-turbo, Claude Haiku)可能更具性价比。对于需要复杂推理的任务,再切换到更强大的模型。

  4. 错误处理与降级 :在生产系统中,务必对 agent() 调用和工具函数调用进行完善的 try-except 包装。当主要LLM服务不可用时,应有降级方案,例如切换到备用提供商,或返回一个预设的友好错误提示。

经过多个项目的实践,我发现 underthesea 最大的优势在于它将越南语NLP的专门化能力与现代化AI智能体框架结合在了一起,并且通过“零外部依赖”的设计大幅降低了运维复杂度。对于目标市场包含越南语用户的团队来说,它能够显著缩短从想法到原型再到产品的路径。当然,如同任何开源库,深入使用时需要仔细阅读文档、关注社区更新,并根据自身业务场景进行适配和优化。

本文地址:https://www.limyvps.com/news/26_7723.html