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开源库的发展,会给我们带来什么新的思路和创意。

相关推荐
听到微笑7 小时前
LLM 只会生成文本?用 ReAct 模式手搓一个简易 Claude Code Agent
人工智能·langchain·llm
chaofa用代码打点酱油1 天前
RAG 进化之路:传统 RAG 到工具与强化学习双轮驱动的 Agentic RAG
算法·llm
许泽宇的技术分享1 天前
微软Agent框架深度解析:重新定义AI应用开发的革命性架构
microsoft·agent
山顶夕景1 天前
【MLLM】Qwen3-Omni全模态模型源码解读
大模型·llm·多模态·mllm
_t1 天前
关于AI Agent处理大量第三方集成的思路
openai·agent·mcp
阿福Chris1 天前
Dify本地初始化后安装模型供应商瞬间失败控制台报错401
大模型·llm·dify·大模型工具
镰刀韭菜2 天前
【AI4S】DrugChat:迈向在药物分子图上实现类似ChatGPT的功能
llm·图神经网络·预训练·gnn·ai4s·drugchat·药物分子图谱
山顶夕景3 天前
【LLM-RL】GSPO算法Group Sequence Policy Optimization
llm·强化学习·rlhf·gspo
DevYK3 天前
企业级Agent开发教程(三)基于LangGraph开发低代码 AI Agent 轻量级开发框架
人工智能·agent
余衫马3 天前
llama.cpp:本地大模型推理的高性能 C++ 框架
c++·人工智能·llm·llama·大模型部署