Trae CN 使用体验 - 一言难尽

17,066 阅读11分钟

阅读说明:仅使用国内版 Trae,国际版的Trae 由于没法体验不做比较

总结

Trae 国内版本,在核心能力上跟 Cursor 和 Windsurf 还是有一定的差距,这里面最大的差距应该是模型本身,而非 IDE。

但 Trae 迭代很快,操作体验(非代码能力)也很好,功能上也是越来越丰富,配合字节大量的基建,如果字节整个公司都在使用 Trae(自愿也好,被迫也罢),那么就很像早期的飞书,潜力无限。

写单测

下面以一个真实的测试 case 来体验 Trae 写单测的能力。

测试源码在 simult-interpretation (这是扣子开放平台其中一个 SDK 的源码,可参考项目 README 跑起来以作验证),测试代码放在 simult-interpretation.spec.ts(线上放的是已经没问题的单测代码,测试时,可以先把这个文件清空),这个 case 是有点复杂的,它使用了异步的 Websocket、复杂的上游依赖还有一些浏览器特有的API,测试时,需要 Mock 掉所有的上游依赖,才能比较顺利通过。以下是两个文件的源码(可不看)

import { type WsSimultInterpretationClientOptions } from '../types';
import PcmRecorder from '../recorder/pcm-recorder';
import {
  APIError,
  CozeAPI,
  type CreateSimultInterpretationsWsReq,
  type CreateSimultInterpretationsWsRes,
  type ErrorRes,
  type WebSocketAPI,
  WebsocketsEventType,
  COZE_CN_BASE_WS_URL,
} from '../..';

class BaseWsSimultInterpretationClient {
 //...
}

export default BaseWsSimultInterpretationClient;
import { v4 as uuid } from 'uuid';

import {
  type AIDenoiserProcessorLevel,
  type AIDenoiserProcessorMode,
} from '../recorder/pcm-recorder';
import {
  type SimultInterpretationUpdateEvent,
  WebsocketsEventType,
} from '../..';
import BaseWsSimultInterpretationClient from './base';

class WsSimultInterpretationClient extends BaseWsSimultInterpretationClient {
  // ...
}
export default WsSimultInterpretationClient;

提供的提示语大致如下:

请参考其它单测代码 packages/coze-js/test , 为 packages/coze-js/src/ws-tools/simult-interpretation 写单测,代码放在 packages/coze-js/test/ws-tools/simult-interpretation.spec.ts, 要求:代码规范请参考项目代码,单测覆盖率 90% 以上,你可以在  packages/coze-js目录下支持 npm test 来验证测试结果

Cursor

起初用 Cursor 写,折腾了个把小时,尝试各种思路,就是没搞定,它的错误不是某个单测没通过,而是整个单测都没跑起来,分析了下就是引入了副作用(类似如下截图的错误),经过多次提示,还是没法解决这个问题,通常是解决一个问题引入新的问题,到了最后,实在没精力搞了就放弃了。

Windsurf

隔天,心血来潮,想着要不用 Windsurf 来试一下,同样的提示语与上下文,好家伙,第一次基本就搞定了,三次对话就顺利通过单测,并且单测覆盖率达到惊人的 95%+

Trae CN

第一次尝试(使用 DeepSeek),用上面的提示语,尽管后面补充了要 mock 所有上游依赖,还是没法搞定副作用,而且生成的测试代码仅有 100行,源码都不止 100行,即使通过单测覆盖率也达不到。

第二次尝试,增加了整个 Workspace 上下文,换了一个模型(Gemimi),这次生成的测试代码就丰富多了(700多行),但经过几轮对话,还是没法解决根本性问题,不再继续折腾

第三次,复杂的 case 应付不了,那么尝试一下简单的 case,接下来测试一个纯 utils 函数 utils.ts,经过小修小改就通过了,而且测试 case 的覆盖率还挺全的,生成的测试代码 180 多行,比原来的 90 行多一倍,一些以前没覆盖的场景也都覆盖了,还是挺完美的。

在使用国内版的 Trae,如果是用来写代码,最好还是用国外的模型(如 Gemini)。

编码体验

Tab 能力

修改代码的时候,像 Cursor 会根据上下文做联动修改,只需要按 Tab 键就能快速修复,但是 Trae 这方面能力还有待加强,比如下面,修改 label 标签的内容,理论上下面相关的也能一并修改,但目前不会,而且手动去修改的时候,也无法给出智能提示,全部都要自己复制粘贴

MCP 使用

Figma 还原设计稿

这次使用到了 Figma MCP,安装还是挺简单,用 Figma MCP 还原设计稿,之前使用 Cursor 进行了多次测试,效果不是很完美的,体现两点:

    1. 生成一大堆样式,模型并不太清楚组件的样式,所以生成代码的时候,为了以防唯一,都会给每个 div 加一个 className,这些样式大部分是不需要的,但加了也没啥问题。
    1. 没法像素级别对齐,关键的一些位置信息(如 margin、padding、字体、颜色等)会和 Figma 对不上。看看 Trae 表现如何。

第一次尝试,使用了之前的提示语,发现生成的代码,根本不是按设计稿的来的(又是根据推理得来的),可能是 figma mcp 无法正确读取数据。

第二次尝试,使用截图代替 MCP,就能正确生成出来了,上面提到的两个问题照样存在,其它倒是没啥特别的,中规中矩,基本符合预期。

GitHub MCP

再试一下 github mcp,看看效果如何。

使用 Cursor,提示语:“使用 github mcp,告诉我 coze-js 这个仓库下的, coze/api 这个 SDK如何使用流式请求”,这个要求挺简单的,只要搜索相关的 README 和 示例,就能给出答案

async function streamChat() {
  const stream = await client.chat.stream({
    bot_id: 'your_bot_id', // 必填,要进行会话的 Bot ID
    user_id: 'user123', // 可选,用户ID,若不提供会自动生成
    additional_messages: [{
      role: RoleType.User,
      content: '你好,请介绍一下自己',
      content_type: 'text',
    }],
    // 其他可选参数
    // conversation_id: '已有的会话ID',
    // auto_save_history: true,
    // custom_variables: { key: 'value' },
  });

  // 使用 for await...of 循环处理流式响应
  for await (const part of stream) {
    if (part.event === ChatEventType.CONVERSATION_CHAT_CREATED) {
      console.log('会话开始');
    } else if (part.event === ChatEventType.CONVERSATION_MESSAGE_DELTA) {
      // 实时输出消息片段
      process.stdout.write(part.data.content);
    } else if (part.event === ChatEventType.CONVERSATION_MESSAGE_COMPLETED) {
      // 某条消息完成
      console.log('\n消息完成:', part.data.content);
    } else if (part.event === ChatEventType.CONVERSATION_CHAT_COMPLETED) {
      // 会话完成,此时可以获取 token 使用情况
      console.log('使用情况:', part.data.usage);
    } else if (part.event === ChatEventType.DONE) {
      // 流式响应结束
      console.log('响应结束');
    } else if (part.event === ChatEventType.ERROR) {
      // 处理错误
      console.error('错误:', part.data);
    }
  }
}

上面是 Cursor 给的回复(还有其它内容就不贴了),答案是正确的。

下面是 Trae 给的回复,答案明显是错误的,还是之前的问题,幻觉太多了,模型需要根据已有事实给出答案,而不是存在大量推理成分。

import { CozeAPI, ChatEventType, RoleType } from '@coze/api';

// 假设 client 已经初始化
// const client = new CozeAPI({ pat: 'your_personal_access_token' });

async function streamChat() {
  const stream = await client.chat.stream({
    bot_id: 'your_bot_id', // 替换为你的 Bot ID
    user: 'user_id', // 替换为你的用户 ID
    query: '你好!', // 用户的查询
    stream: true, // 明确启用流式响应
    // 如果需要,可以添加聊天历史
    // chat_history: [
    //   { role: RoleType.User, content: '之前的消息', content_type: 'text' },
    //   { role: RoleType.Assistant, content: '之前的回复', content_type: 'text' },
    // ],
    // 如果需要,可以添加附加消息
    // additional_messages: [{
    //   role: RoleType.User,
    //   content: '附加消息内容',
    //   content_type: 'text',
    // }],
  });

  for await (const part of stream) {
    // 检查事件类型是否为消息增量
    if (part.event === ChatEventType.MESSAGE_DELTA) {
      // part.data.message.content 包含了流式返回的消息片段
      process.stdout.write(part.data.message.content); // 实时打印响应片段
    } else if (part.event === ChatEventType.ERROR) {
      // 处理错误事件
      console.error('Stream error:', part.data);
    }
    // 你可以根据需要处理其他事件类型,例如 ChatEventType.MESSAGE, ChatEventType.DONE 等
  }
  console.log('\nStream finished.');
}

streamChat();

写 SDK 使用文档

在开源项目中,现在大部分的 README 和 SDK 使用文档都是在 AI 的辅助下完成的,接下来看看 Trae 在这方面的能力如何。

在开源项目 Coze-Js 中,有一个 SDK 和配套的 Demo,结合这两份源码生成一份 SDK 使用文档。

它生成的文档如下(可跳过):

 # WsChatClient SDK 文档

`WsChatClient` 是 Coze JS SDK 的一部分,提供通过 WebSocket 与 Coze Bot 进行实时语音和文本聊天的能力。

## 安装

`WsChatClient` 包含在 `@coze/api` 包中。请确保已安装该包:

```bash
npm install @coze/api
# 或者
yarn add @coze/api
```

## 快速上手

以下是一个基本示例,展示如何初始化客户端、连接、发送消息并处理响应:

```typescript
import { WsChatClient, WsChatEventNames, type WsChatEventData } from '@coze/api/ws-tools';
import { WebsocketsEventType, RoleType } from '@coze/api';

async function runChat() {
  let client: WsChatClient | null = null;

  try {
    // 1. 初始化客户端
    client = new WsChatClient({
      botId: 'YOUR_BOT_ID', // 替换为你的 Bot ID
      token: 'YOUR_AUTH_TOKEN', // 替换为你的认证 Token
      // voiceId: 'YOUR_VOICE_ID', // 可选:指定语音合成的音色 ID
      allowPersonalAccessTokenInBrowser: true, // 可选:允许在浏览器环境使用个人访问令牌,默认为 false
      debug: true, // 可选:开启调试日志,默认为 false
    });

    // 2. 监听事件 (示例:监听所有服务端事件)
    client.on(WsChatEventNames.ALL, (eventName: string, event: WsChatEventData) => {
      console.log(`[${eventName}]`, event);

      // 处理收到的消息 (文本或语音)
      if (event.event_type === WebsocketsEventType.CONVERSATION_MESSAGE_DELTA) {
        console.log('收到文本消息片段:', event.data.content);
      }
      if (event.event_type === WebsocketsEventType.CONVERSATION_MESSAGE_COMPLETED) {
        console.log('文本消息接收完成');
      }
      if (event.event_type === WebsocketsEventType.CONVERSATION_AUDIO_DELTA) {
        // SDK 内部会自动处理并播放语音
        console.log('收到语音消息片段');
      }
      if (event.event_type === WebsocketsEventType.CONVERSATION_AUDIO_COMPLETED) {
        console.log('语音消息接收完成');
      }
      if (event.event_type === WebsocketsEventType.ERROR) {
        console.error('发生错误:', event.data.code, event.data.msg);
      }
    });

    // 3. 建立连接 (连接成功后会自动开始录音,除非 audioMutedDefault 为 true)
    await client.connect();
    console.log('连接成功!');

    // 4. 发送用户消息
    client.sendTextMessage('你好,请介绍一下你自己');

    // 5. (可选) 在适当的时候断开连接
    // setTimeout(() => {
    //   client?.disconnect();
    //   console.log('连接已断开');
    // }, 30000); // 30秒后断开

  } catch (error) {
    console.error('发生错误:', error);
    // 断开连接以清理资源
    await client?.disconnect();
  }
}

runChat();
```

## 核心概念

### 初始化 (`new WsChatClient(options)`)

创建 `WsChatClient` 实例时,需要传入配置对象 `WsChatClientOptions`*   `botId` (string, **必需** ): 你的 Coze Bot ID。
*   `token` (string | (() => string | Promise<string>), **必需** ): 用于认证的 Token。可以是字符串,也可以是一个返回 Token 或 Promise<Token> 的函数。
*   `workflowId` (string, 可选): 指定要使用的 Bot 工作流 ID。
*   `voiceId` (string, 可选): 指定语音合成(TTS)使用的音色 ID。如果未提供,可能不会进行语音合成。
*   `allowPersonalAccessTokenInBrowser` (boolean, 可选): 是否允许在浏览器环境中使用个人访问令牌(PAT)。默认为 `false`,出于安全考虑,建议在生产环境的服务端使用 PAT。
*   `debug` (boolean, 可选): 是否在控制台打印详细的调试日志。默认为 `false`*   `baseWsURL` (string, 可选): 指定 WebSocket 连接的基础 URL。默认为中国区的 URL (`wss://api.coze.cn/open-platform/ws/chat/v1`)。
*   `websocketOptions` (object, 可选): 传递给底层 WebSocket 库的额外选项。
*   `audioCaptureConfig` (object, 可选): 音频捕获配置 (详见高级配置)。
*   `aiDenoisingConfig` (object, 可选): AI 降噪配置 (详见高级配置)。
*   `mediaStreamTrack` (MediaStreamTrack, 可选): 使用外部提供的音频轨道,而不是 SDK 内部创建。
*   `wavRecordConfig` (object, 可选): WAV 录制配置 (详见高级配置)。
*   `deviceId` (string, 可选): 指定要使用的音频输入设备 ID。默认为系统默认设备。
*   `audioMutedDefault` (boolean, 可选): 初始化时是否默认静音。默认为 `false` (不静音)。

### 连接 (`connect(options?)`)

调用 `connect()` 方法与 Coze Bot 建立 WebSocket 连接。

*   该方法返回一个 Promise,在连接成功建立并收到 `chat_created` 事件后 resolve。
*   连接成功后,如果 `audioMutedDefault``false` 且未使用外部 `mediaStreamTrack`,SDK 会自动请求麦克风权限并开始录音。
*   可以传入一个可选的 `chatUpdate` 对象来自定义连接时的 `chat.update` 事件内容,例如设置输入/输出音频格式、开启 VAD 等。

```typescript
await client.connect({
  chatUpdate: {
    // 可选的 chat.update 事件数据
    data: {
      // ... 自定义配置
    }
  }
});
```

### 发送消息

*   **`sendTextMessage(text: string)`** : 发送纯文本用户消息。
    ```typescript
    client.sendTextMessage('今天天气怎么样?');
    ```
*   **`sendMessage(data: CreateChatWsReq)`** : 发送原始的 WebSocket 消息对象。这允许发送更复杂的消息类型,例如包含附件或特定指令的消息。
    ```typescript
    import { v4 as uuid } from 'uuid';
    import { WebsocketsEventType, RoleType } from '@coze/api';

    client.sendMessage({
      id: uuid(), // 消息的唯一 ID
      event_type: WebsocketsEventType.CONVERSATION_MESSAGE_CREATE,
      data: {
        role: RoleType.User,
        content_type: 'text',
        content: '这是通过 sendMessage 发送的消息',
      },
    });
    ```

### 接收事件 (`on(event, callback)`)

使用 `on()` 方法监听来自服务端或其他 SDK 内部触发的事件。

*   `event` (string | string[]): 要监听的事件名称或名称数组。
*   `callback` ((eventName: string, event: WsChatEventData) => void): 事件触发时的回调函数。

**常用事件:**

*   `WsChatEventNames.ALL`: 监听所有事件。
*   `WsChatEventNames.CONNECTED`: 连接成功时触发。
*   `WsChatEventNames.DISCONNECTED`: 连接断开时触发。
*   `WsChatEventNames.AUDIO_MUTED`: 调用 `setAudioEnable(false)` 成功后触发。
*   `WsChatEventNames.AUDIO_UNMUTED`: 调用 `setAudioEnable(true)` 成功后触发。
*   `WsChatEventNames.AUDIO_INPUT_DEVICE_CHANGED`: 音频输入设备切换后触发。
*   `WsChatEventNames.AUDIO_INPUT_DUMP`: 当配置了 `wavRecordConfig` 时,录制的 WAV 文件生成后触发。
*   `server.${WebsocketsEventType}`: 监听特定的服务端 WebSocket 事件,例如:
    *   `server.conversation.message.delta`: 收到文本消息片段。
    *   `server.conversation.message.completed`: 文本消息接收完成。
    *   `server.conversation.audio.delta`: 收到语音消息片段 (Base64 编码的 PCM 数据)。SDK 内部会自动解码并使用 `WavStreamPlayer` 播放。
    *   `server.conversation.audio.completed`: 语音消息接收完成。
    *   `server.conversation.chat.requires_action`: 需要外部工具调用。
    *   `server.conversation.chat.canceled`: 对话被取消。
    *   `server.error`: 发生服务端错误。

```typescript
// 监听文本消息片段
client.on(`server.${WebsocketsEventType.CONVERSATION_MESSAGE_DELTA}`, (eventName, event) => {
  if ('content' in event.data) {
    console.log('Text Delta:', event.data.content);
  }
});

// 监听错误
client.on(`server.${WebsocketsEventType.ERROR}`, (eventName, event) => {
  console.error('Server Error:', event.data.code, event.data.msg);
});
```

### 移除监听器 (`off(event, callback)`)

使用 `off()` 方法移除之前添加的事件监听器。

### 断开连接 (`disconnect()`)

调用 `disconnect()` 方法关闭 WebSocket 连接,停止录音和播放,并清理相关资源。

```typescript
await client.disconnect();
```

### 音频控制

*   **`setAudioEnable(enable: boolean)`** : 控制麦克风的开启和关闭。
    *   `enable = true`: 取消静音,开始/恢复录音。
    *   `enable = false`: 静音,暂停录音。
    *   成功切换后会触发 `AUDIO_UNMUTED``AUDIO_MUTED` 事件。
    ```typescript
    await client.setAudioEnable(false); // 静音
    await client.setAudioEnable(true);  // 取消静音
    ```
*   **`setAudioInputDevice(deviceId: string)`** : 切换音频输入设备。
    *   `deviceId`: 要切换到的设备 ID。可以使用 `RealtimeUtils.getAudioDevices()` (来自 `@coze/realtime-api` 包,如果同时使用) 或 `navigator.mediaDevices.enumerateDevices()` 获取设备列表。传入 `'default'` 使用系统默认设备。
    *   切换成功后会触发 `AUDIO_INPUT_DEVICE_CHANGED` 事件。
    ```typescript
    // 假设 newDeviceId 是从设备列表中获取的 ID
    await client.setAudioInputDevice(newDeviceId);
    ```
*   **`setAIDenoising(mode: AIDenoiserProcessorMode, level: AIDenoiserProcessorLevel)`** : 配置 AI 降噪。
    *   `mode`: 降噪模式 (`'balanced'` 或 `'aggressive'`)。
    *   `level`: 降噪级别 (1 到 5)。
    *   需要确保初始化时已配置 `aiDenoisingConfig`。
    ```typescript
    await client.setAIDenoising('aggressive', 3);
    ```

### 打断 (`interrupt()`)

调用 `interrupt()` 方法可以打断当前 Bot 的语音或文本输出。这通常用于用户在 Bot 回答过程中插入新的指令或问题。

```typescript
client.interrupt();
```

## 事件参考

**客户端事件 (`WsChatEventNames`)**

*   `ALL`: 所有事件的通配符。
*   `CONNECTED`: 连接成功。
*   `DISCONNECTED`: 连接断开。
*   `AUDIO_MUTED`: 已静音。
*   `AUDIO_UNMUTED`: 已取消静音。
*   `AUDIO_INPUT_DEVICE_CHANGED`: 音频输入设备已更改。
*   `AUDIO_INPUT_DUMP`: (需要配置) 录制的 WAV 文件已生成。

**服务端事件 (`WebsocketsEventType`)** (通过 `server.${eventType}` 监听)

*   `CHAT_CREATED`: WebSocket 连接建立并初始化成功。
*   `CONVERSATION_MESSAGE_DELTA`: 文本消息增量片段。
*   `CONVERSATION_MESSAGE_COMPLETED`: 文本消息接收完成。
*   `CONVERSATION_AUDIO_DELTA`: 语音消息增量片段 (PCM Base64)。
*   `CONVERSATION_AUDIO_COMPLETED`: 语音消息接收完成。
*   `INPUT_AUDIO_BUFFER_SPEECH_STARTED`: 检测到用户开始说话。
*   `INPUT_AUDIO_BUFFER_SPEECH_ENDED`: 检测到用户停止说话 (VAD)。
*   `CONVERSATION_CHAT_REQUIRES_ACTION`: 需要外部进行工具调用。
*   `CONVERSATION_CHAT_CANCELED`: 对话被取消。
*   `ERROR`: 发生错误。
*   ... (其他可能的事件类型)

## 错误处理

*   **连接错误** : `connect()` 方法可能会 reject 一个 `APIError` 对象,如果 WebSocket 连接建立失败或收到初始的 `error` 事件。
*   **运行时错误** : 在连接期间,服务端可能会发送 `error` 事件,可以通过监听 `server.${WebsocketsEventType.ERROR}` 来捕获。`APIError` 包含 `code``msg` 字段。
*   **SDK 内部错误** : 调用 SDK 方法(如 `setAudioEnable`)时也可能抛出错误,例如设备访问失败。

建议使用 `try...catch` 块包裹 SDK 调用,并监听 `server.error` 事件来处理潜在问题。

## 高级配置

### `audioCaptureConfig`

控制音频捕获的参数:

```typescript
{
  // 音频处理块大小 (samples)
  bufferSize?: number; // default 4096
  // 音频通道数
  channelCount?: number; // default 1
  // 采样率 (Hz)
  sampleRate?: number; // default 16000
  // 是否启用回声消除
  echoCancellation?: boolean; // default true
  // 是否启用自动增益控制
  autoGainControl?: boolean; // default true
  // 是否启用噪声抑制 (浏览器内置)
  noiseSuppression?: boolean; // default true
}
```

### `aiDenoisingConfig`

启用和配置 AI 降噪功能:

```typescript
{
  // 是否启用 AI 降噪
  enabled: boolean; // default false
  // 降噪模式 ('balanced' | 'aggressive')
  mode?: AIDenoiserProcessorMode; // default 'balanced'
  // 降噪级别 (1-5)
  level?: AIDenoiserProcessorLevel; // default 3
  // AI 降噪模型文件的 URL
  modelUrl?: string;
  // WebAssembly 文件的 URL
  wasmUrl?: string;
  // Worklet 脚本文件的 URL
  workletUrl?: string;
}
```

**注意:** 启用 AI 降噪需要提供模型、Wasm 和 Worklet 文件的 URL。

### `wavRecordConfig`

配置录制 WAV 文件:

```typescript
{
  // 是否启用 WAV 录制
  enabled: boolean; // default false
  // 录制模式 ('continuous' | 'vad')
  mode?: 'continuous' | 'vad'; // default 'continuous'
  // 'vad' 模式下,静音检测的阈值 (ms)
  vadSilenceTimeout?: number; // default 1000
  // 'vad' 模式下,语音活动检测的阈值 (0-1)
  vadThreshold?: number; // default 0.5
}
```

启用后,录制的 WAV 文件会通过 `WsChatEventNames.AUDIO_INPUT_DUMP` 事件回调。

### `websocketOptions`

传递给底层 WebSocket 实现的选项,例如自定义 Header:

```typescript
{
  headers: {
    'X-Custom-Header': 'value'
  }
}
```

这份文档,先不谈专业性如何,单看目录结构,没啥问题,但是细看里面的内容,就完全乱套了,里面很多内容都是无中生有的,很明显,模型没有根据事实(源码)整理,而完全是推理出来的,贴几个例子:

截图说明
SDK 没有 setAIDenoising 这个方法,SDK中是拆成两个方法的(setDenoiserLevel 和 setDenoiserMode)
audioCaptureConfig 这个配置项没有那么多的配置(框出来的配置是不存在的),类似的还有 aiDenoisingConfigwavRecordConfig,都不是基于事实写出来的

之前用 Cursor 写,虽然写得也马马虎虎,后续还要一些补充才行,但起码不会无中生有,捏造一些不存在的 API 出来。

最近 DeepWiki 很火,我让 DeepWiki 来写一份 “请帮我总结一份关于 WsChatClient 的使用指南”,结果很惊艳、很全面,条理很清晰,最重要的是没有事实错误,比之前用 Cursor 写得都好。

文案替换

如果项目是国际化的,那么文案替换是一项很常见的工作,以前需要一个一个替换,现在有了 AI ,可以批量将需要替换的文案找出来,然后交给 PM 录入,拿到一批 key后,就可以让 AI 一键替换了,现在试下 Trae 效果如何。

以下也是一个实际业务中的代码,让 Trae 帮忙找出所有的文案:

image.png

一开始使用豆包模型,结果非常差,后面换成 Gemini 效果就好很多了,如下,基本没啥问题。

image.png

Trae 没有自动选择模型的功能,感觉挺有必要的,或者能够给出每个模型适合什么样的场景,不然,一些刚开始使用的用户,很可能就因为模型选择不对导致效果很差,从而弃坑了。