Trae-Agent中LLM Client深度分析

引言

在之前的文章,我们分析了Trae Agent整体执行思路和LLM是如何调用内置工具的一些细节。还有一块内容我觉得是有必要了解到,就是Agent对于LLM客户端的封装,毕竟每个供应商提供的LLM使用方式都有些差异,Agent需要将差异性封装起来,使我们使用LLM时使用一个统一的LLM Client对象就可以和LLM进行交互了。

Agent 如何使用 LLMClient

LLMClient在使用上十分的简介,重点的就是初始化、设置轨迹记录器、chat。屏蔽了LLM供应商的细节。

LLMClient下的内容

这里需要用一个类图来展示llm_client.py中的内容了,它是一个工厂模式,根据我们允许命令时指定要使用的LLM供应商,来决定是实例化某一个对象,比如我们执行"trae-cli run "写一个最简单的网页" --provider doubao",会实例化DouBaoClient。可以看到LLMClient下包含了各种供应商Client。

DouBaoClient实现分析

基于 doubao_client.py 的代码,我们来分析一下实现一个 LLM 供应商客户端需要的关键细节。

初始化配置

python 复制代码
def __init__(self, model_parameters: ModelParameters):
    super().__init__(model_parameters)
    
    # 1. API Key 获取(环境变量 + 配置文件)
    if self.api_key == "":
        self.api_key: str = os.getenv("DOUBAO_API_KEY", "")
    
    # 2. Base URL 配置
    if self.base_url is None or self.base_url == "":
        self.base_url: str | None = os.getenv("DOUBAO_API_BASE_URL")
    
    # 3. 创建底层客户端
    self.client: openai.OpenAI = openai.OpenAI(
        base_url=self.base_url, api_key=self.api_key
    )
    
    # 4. 初始化消息历史
    self.message_history: list[ChatCompletionMessageParam] = []

核心实现的方法

ini 复制代码
@override
def chat(
    self,
    messages: list[LLMMessage],
    model_parameters: ModelParameters,
    tools: list[Tool] | None = None,
    reuse_history: bool = True,
) -> LLMResponse:
    # 1. 消息格式转换
    doubao_messages = self.parse_messages(messages)
    
    # 2. 历史消息管理
    if reuse_history:
        self.message_history = self.message_history + doubao_messages
    else:
        self.message_history = doubao_messages
    
    # 3. 工具模式转换
    tool_schemas = None
    if tools:
        tool_schemas = [
            ChatCompletionToolParam(
                function=FunctionDefinition(
                    name=tool.get_name(),
                    description=tool.get_description(),
                    parameters=tool.get_input_schema(),
                ),
                type="function",
            )
            for tool in tools
        ]
    
    # 4. API 调用(带重试机制)
    response = None
    for i in range(model_parameters.max_retries):
        try:
            response = self.client.chat.completions.create(
                model=model_parameters.model,
                messages=self.message_history,
                tools=tool_schemas if tool_schemas else openai.NOT_GIVEN,
                temperature=model_parameters.temperature,
                top_p=model_parameters.top_p,
                max_tokens=model_parameters.max_tokens,
                n=1,
            )
            break
        except Exception as e:
            # 重试逻辑
            time.sleep(random.randint(3, 30))
            continue
    
    # 5. 响应解析和格式转换
    choice = response.choices[0]
    
    # 6. 工具调用解析
    tool_calls: list[ToolCall] | None = None
    if choice.message.tool_calls:
        tool_calls = [
            ToolCall(
                name=tool_call.function.name,
                call_id=tool_call.id,
                arguments=json.loads(tool_call.function.arguments)
            )
            for tool_call in choice.message.tool_calls
        ]
    
    # 7. 构建标准响应
    llm_response = LLMResponse(
        content=choice.message.content or "",
        tool_calls=tool_calls,
        finish_reason=choice.finish_reason,
        model=response.model,
        usage=LLMUsage(
            input_tokens=response.usage.prompt_tokens,
            output_tokens=response.usage.completion_tokens,
        )
    )
    
    # 8. 更新消息历史
    # ... 历史更新逻辑
    
    # 9. 轨迹记录
    if self.trajectory_recorder:
        self.trajectory_recorder.record_llm_interaction(
            messages=messages,
            response=llm_response,
            provider="doubao",
            model=model_parameters.model,
            tools=tools,
        )
    
    return llm_response

细节:豆包客户端使用了OpenAI兼容的API格式,可以直接使用OpenAI 的 Python SDK 作为底层实现。

具体Client之间的差异

每个Client实现类都略微有些差异。比如DoubaoClient和OpenAIClient底层SDK都是用openai Python SDK,AnthropicClient使用原生 anthropic Python SDK。

在消息处理上,DoubaoClient & OpenAIClient都使用相同的 OpenAI 消息格式,AnthropicClient会是用 Anthropic 特有格式。

还有在工具调用、响应处理等方面,不同的Client都会有些差异。

OpenRouterClient分析

我注意到有一个openai_client.py。它的特别指出在于不是一个具体的 LLM 供应商 ,而是一个 LLM 聚合服务平台 。它的作用类似于一个"中介"或"代理",提供统一的 API 接口来访问多个不同的 LLM 提供商。

OpenRouter 的优势

  1. 统一接口 : 通过一个 API 访问多个 LLM 提供商
  2. 模型选择 : 可以在不同模型间轻松切换
  3. 成本优化 : 可以根据价格和性能选择最合适的模型
  4. 简化集成 : 不需要为每个 LLM 提供商单独集成
  5. OpenAI 兼容 : 使用熟悉的 OpenAI API 格式

总结

这篇文章,我们对Agent中LLM Client的封装也有了一定的了解。这让我们学习到一个程序如何调用LLM的诸多细节。接下来会持续关注Trae Agent开源库的发展,会给我们带来什么新的思路和创意。

相关推荐
JouYY2 分钟前
聊一下知识答疑Agent的“层次聚类”流程
架构·llm·agent
L3S13 分钟前
Agent为什么会死循环?
人工智能·agent
云烟成雨TD29 分钟前
LangFlow 1.x 系列【3】入门案例
人工智能·python·agent
墨流藏于库1 小时前
Electron 应用 macOS 自动更新的正确姿势 —— 没有 Apple Developer Program 也能用
agent
新知图书1 小时前
智能体基础架构
人工智能·agent·ai agent·智能体·langgraph
zzz_23683 小时前
从 200 行规则到一条好渠——Agent 工程化的踩坑与解法
人工智能·agent
熊猫钓鱼>_>4 小时前
智能革命的巨浪——AI时代的社会重构与生存之道
大数据·人工智能·重构·架构·llm·agent·ai-native
qcx234 小时前
Agentic RAG不止能回答问题,已经能自动修复真实CVE漏洞了
人工智能·机器学习·ai·llm·脑信号
掉鱼的猫5 小时前
用 ChatModel 构建 LLM 驱动的 Java 应用
java·llm