---
title: 多轮翻译机器人
description: 如何使用 FastGPT 构建一个多轮翻译机器人，实现连续的对话翻译功能
---

吴恩达老师提出了一种反思翻译的大语言模型(LLM)翻译工作流程——[GitHub - andrewyng/translation-agent](https://github.com/andrewyng/translation-agent)，具体工作流程如下：

1. 提示一个 LLM 将文本从 `source_language` 翻译到 `target_language`；
2. 让 LLM 反思翻译结果并提出建设性的改进建议；
3. 使用这些建议来改进翻译。

这个翻译流程应该是目前比较新的一种翻译方式，利用 LLM 对自己的翻译结果进行改进来获得较好的翻译效果

项目中展示了可以利用对长文本进行分片，然后分别进行反思翻译处理，以突破 LLM 对 tokens 数量的限制，真正实现长文本一键高效率高质量翻译。

项目还通过给大模型限定国家地区，已实现更精确的翻译，如美式英语、英式英语之分；同时提出一些可能能带来更好效果的优化，如对于一些 LLM 未曾训练到的术语（或有多种翻译方式的术语）建立术语表，进一步提升翻译的精确度等等

而这一切都能通过 Fastgpt 工作流轻松实现，本文将手把手教你如何复刻吴恩达老师的 translation-agent

# 单文本块反思翻译

先从简单的开始，即不超出 LLM tokens 数量限制的单文本块翻译

## 初始翻译

第一步先让 LLM 对源文本块进行初始翻译（翻译的提示词在源项目中都有）

![](/imgs/translate1.png)

通过`文本拼接`模块引用 源语言、目标语言、源文本这三个参数，生成提示词，传给 LLM，让它给出第一版的翻译

## 反思

然后让 LLM 对第一步生成的初始翻译给出修改建议，称之为 反思

![](/imgs/translate2.png)

这时的提示词接收 5 个参数，源文本、初始翻译、源语言、目标语言 以及限定词地区国家，这样 LLM 会对前面生成的翻译提出相当多的修改建议，为后续的提升翻译作准备

## 提升翻译

![](/imgs/translate3.png)

在前文生成了初始翻译以及相应的反思后，将这二者输入给第三次 LLM 翻译，这样我们就能获得一个比较高质量的翻译结果

完整的工作流如下

![](/imgs/translate4.png)

## 运行效果

由于考虑之后对这个反思翻译的复用，所以创建了一个插件，那么在下面我直接调用这个插件就能使用反思翻译，效果如下

随机挑选了一段哈利波特的文段

![](/imgs/translate5.png)

![](/imgs/translate61.png)

可以看到反思翻译后的效果还是好上不少的，其中反思的输出如下

![](/imgs/translate7.png)

# 长文反思翻译

在掌握了对短文本块的反思翻译后，我们能轻松的通过分片和循环，实现对长文本也即多文本块的反思翻译

整体的逻辑是，首先对传入文本的 tokens数量做判断，如果不超过设置的 tokens 限制，那么直接调用单文本块反思翻译，如果超过设置的 tokens限制，那么切割为合理的大小，再分别进行对应的反思翻译处理

## 计算 tokens

![](/imgs/translate8.png)

首先，我使用了 Laf函数 模块来实现对输入文本的 tokens 的计算

laf函数的使用相当简单，即开即用，只需要在 laf 创建个应用，然后安装 tiktoken 依赖，导入如下代码即可

```typescript
const { Tiktoken } = require("tiktoken/lite");
const cl100k_base = require("tiktoken/encoders/cl100k_base.json");

interface IRequestBody {
  str: string
}

interface RequestProps extends IRequestBody {
  systemParams: {
    appId: string,
    variables: string,
    histories: string,
    cTime: string,
    chatId: string,
    responseChatItemId: string
  }
}

interface IResponse {
  message: string;
  tokens: number;
}

export default async function (ctx: FunctionContext): Promise<IResponse> {
  const { str = "" }: RequestProps = ctx.body
  
  const encoding = new Tiktoken(
    cl100k_base.bpe_ranks,
    cl100k_base.special_tokens,
    cl100k_base.pat_str
  );
  const tokens = encoding.encode(str);
  encoding.free();
  
  return {
    message: 'ok',
    tokens: tokens.length
  };
}
```

再回到 Fastgpt，点击“同步参数”，再连线将源文本传入，即可计算 tokens 数量

## 计算单文本块大小

![](/imgs/translate9.png)

由于不涉及第三方包，只是一些数据处理，所以直接使用 代码运行 模块处理即可

```typescript
function main({tokenCount, tokenLimit}){
  const numChunks = Math.ceil(tokenCount / tokenLimit);
  let chunkSize = Math.floor(tokenCount / numChunks);

  const remainingTokens = tokenCount % tokenLimit;
  if (remainingTokens > 0) {
    chunkSize += Math.floor(remainingTokens / numChunks);
  }

  return {chunkSize};
}
```

通过上面的代码，我们就能算出不超过 token限制的合理单文本块大小是多少了

## 获得切分后源文本块

![](/imgs/translate10.png)

通过单文本块大小和源文本，我们再编写一个函数调用 langchain 的 textsplitters 包来实现文本分片，具体代码如下

```typescript
import cloud from '@lafjs/cloud'
import { TokenTextSplitter } from "@langchain/textsplitters";

interface IRequestBody { 
  text: string 
  chunkSize: number
}

interface RequestProps extends IRequestBody {
  systemParams: {
    appId: string,
    variables: string,
    histories: string,
    cTime: string,
    chatId: string,
    responseChatItemId: string
  }
}

interface IResponse {
  output: string[];
}

export default async function (ctx: FunctionContext): Promise<IResponse>{
  const { text = '', chunkSize=1000 }: RequestProps = ctx.body;

  const splitter = new TokenTextSplitter({
    encodingName:"gpt2",
    chunkSize: Number(chunkSize),
    chunkOverlap: 0,
  });

  const output = await splitter.splitText(text);

  return { 
    output
   }
}
```

这样我们就获得了切分好的文本，接下去的操作就类似单文本块反思翻译

## 多文本块翻译

这里应该还是不能直接调用前面的单文本块反思翻译，因为提示词中会涉及一些上下文的处理（或者可以修改下前面写好的插件，多传点参数进去）

详细的和前面类似，就是提示词进行一些替换，以及需要做一些很简单的数据处理，整体效果如下

### 多文本块初始翻译

![](/imgs/translate11.png)

### 多文本块反思

![](/imgs/translate12.png)

### 多文本块提升翻译

![](/imgs/translate13.png)

## 批量运行

长文反思翻译比较关键的一个部分，就是对多个文本块进行循环反思翻译

Fastgpt 提供了工作流线路可以返回去执行的功能，所以我们可以写一个很简单的判断函数，来判断结束或是接着执行

![](/imgs/translate14.png)

也就是通过判断当前处理的这个文本块，是否是最后一个文本块，从而判断是否需要继续执行，就这样，我们实现了长文反思翻译的效果

完整工作流如下

![](/imgs/translate15.png)

## 运行效果

首先输入全局设置

![](/imgs/translate16.png)

然后输入需要翻译的文本，这里我选择了一章哈利波特的英文原文来做翻译，其文本长度通过 openai 对 tokens 数量的判断如下

![](/imgs/translate17.png)

实际运行效果如下

![](/imgs/translate18.png)

可以看到还是能满足阅读需求的

# 进一步调优

## 提示词调优

在源项目中，给 AI 的系统提示词还是比较的简略的，我们可以通过比较完善的提示词，来督促 LLM 返回更合适的翻译，进一步提升翻译的质量

比如初始翻译中，

```typescript
# Role: 资深翻译专家

## Background:
你是一位经验丰富的翻译专家,精通{{source_lang}}和{{target_lang}}互译,尤其擅长将{{source_lang}}文章译成流畅易懂的{{target_lang}}。你曾多次带领团队完成大型翻译项目,译文广受好评。

## Attention:
- 翻译过程中要始终坚持"信、达、雅"的原则,但"达"尤为重要
- 译文要符合{{target_lang}}的表达习惯,通俗易懂,连贯流畅 
- 避免使用过于文绉绉的表达和晦涩难懂的典故引用

## Constraints:
- 必须严格遵循四轮翻译流程:直译、意译、校审、定稿  
- 译文要忠实原文,准确无误,不能遗漏或曲解原意

## Goals:
- 通过四轮翻译流程,将{{source_lang}}原文译成高质量的{{target_lang}}译文  
- 译文要准确传达原文意思,语言表达力求浅显易懂,朗朗上口
- 适度使用一些熟语俗语、流行网络用语等,增强译文的亲和力
- 在直译的基础上,提供至少2个不同风格的意译版本供选择

## Skills:
- 精通{{source_lang}} {{target_lang}}两种语言,具有扎实的语言功底和丰富的翻译经验
- 擅长将{{source_lang}}表达习惯转换为地道自然的{{target_lang}}
- 对当代{{target_lang}}语言的发展变化有敏锐洞察,善于把握语言流行趋势

## Workflow:
1. 第一轮直译:逐字逐句忠实原文,不遗漏任何信息
2. 第二轮意译:在直译的基础上用通俗流畅的{{target_lang}}意译原文,至少提供2个不同风格的版本
3. 第三轮校审:仔细审视译文,消除偏差和欠缺,使译文更加地道易懂 
4. 第四轮定稿:择优选取,反复修改润色,最终定稿出一个简洁畅达、符合大众阅读习惯的译文

## OutputFormat: 
- 只需要输出第四轮定稿的回答

## Suggestions:
- 直译时力求忠实原文,但不要过于拘泥逐字逐句
- 意译时在准确表达原意的基础上,用最朴实无华的{{target_lang}}来表达 
- 校审环节重点关注译文是否符合{{target_lang}}表达习惯,是否通俗易懂
- 定稿时适度采用一些熟语谚语、网络流行语等,使译文更接地气- 善于利用{{target_lang}}的灵活性,用不同的表述方式展现同一内容,提高译文的可读性
```

从而返回更准确更高质量的初始翻译，后续的反思和提升翻译也可以修改更准确的提示词，如下

![](/imgs/translate19.png)

然后再让我们来看看运行效果

![](/imgs/translate20.png)

给了和之前相同的一段文本进行测试，测试效果还是比较显著的，就比如红框部分，之前的翻译如下

![](/imgs/translate21.png)

从“让你的猫头鹰给我写信”这样有失偏颇的翻译，变成“给我写信，你的猫头鹰会知道怎么找到我”这样较为准确的翻译

## 其他调优

比如限定词调优，源项目中已经做了示范，就是加上国家地区这个限定词，实测确实会有不少提升

出于 LLM 的卓越能力，我们能够通过设置不同的prompt来获取不同的翻译结果，也就是可以很轻松地通过设置特殊的限定词，来实现特定的，更精确的翻译

而对于一些超出 LLM 理解的术语等，也可以利用 Fastgpt 的知识库功能进行相应扩展，进一步完善翻译机器人的功能
