mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-18 09:24:03 +00:00

* add new doc (#5175) Co-authored-by: dreamer6680 <146868355@qq.com> * Test docs (#5235) * fix: change the page of doc * chore: add new dependencies, update global styles/layout, optimize docs, add Feishu & GitHub icons, update API examples * fix: docs/index 404 not found * Update environment variable names, optimize styles, add new API routes, fix component styles, adjust documentation, and update GitHub and Feishu icons * update readme * feat: add a linkfastgpt compontent * feat: update new doc * fix:remove unuse page and redirect homepage to docs (#5288) * fix:remove some unuse doc * fix: redirect homepage to doc * git ignore * fix:navbar to index (#5295) * sidbar * fix: navtab unlight (#5298) * doc --------- Co-authored-by: dreamer6680 <1468683855@qq.com> Co-authored-by: dreamer6680 <146868355@qq.com>
312 lines
11 KiB
Plaintext
312 lines
11 KiB
Plaintext
---
|
||
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 对源文本块进行初始翻译(翻译的提示词在源项目中都有)
|
||
|
||

|
||
|
||
通过`文本拼接`模块引用 源语言、目标语言、源文本这三个参数,生成提示词,传给 LLM,让它给出第一版的翻译
|
||
|
||
## 反思
|
||
|
||
然后让 LLM 对第一步生成的初始翻译给出修改建议,称之为 反思
|
||
|
||

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

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

|
||
|
||
## 运行效果
|
||
|
||
由于考虑之后对这个反思翻译的复用,所以创建了一个插件,那么在下面我直接调用这个插件就能使用反思翻译,效果如下
|
||
|
||
随机挑选了一段哈利波特的文段
|
||
|
||

|
||
|
||

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

|
||
|
||
# 长文反思翻译
|
||
|
||
在掌握了对短文本块的反思翻译后,我们能轻松的通过分片和循环,实现对长文本也即多文本块的反思翻译
|
||
|
||
整体的逻辑是,首先对传入文本的 tokens数量做判断,如果不超过设置的 tokens 限制,那么直接调用单文本块反思翻译,如果超过设置的 tokens限制,那么切割为合理的大小,再分别进行对应的反思翻译处理
|
||
|
||
## 计算 tokens
|
||
|
||

|
||
|
||
首先,我使用了 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 数量
|
||
|
||
## 计算单文本块大小
|
||
|
||

|
||
|
||
由于不涉及第三方包,只是一些数据处理,所以直接使用 代码运行 模块处理即可
|
||
|
||
```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限制的合理单文本块大小是多少了
|
||
|
||
## 获得切分后源文本块
|
||
|
||

|
||
|
||
通过单文本块大小和源文本,我们再编写一个函数调用 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
|
||
}
|
||
}
|
||
```
|
||
|
||
这样我们就获得了切分好的文本,接下去的操作就类似单文本块反思翻译
|
||
|
||
## 多文本块翻译
|
||
|
||
这里应该还是不能直接调用前面的单文本块反思翻译,因为提示词中会涉及一些上下文的处理(或者可以修改下前面写好的插件,多传点参数进去)
|
||
|
||
详细的和前面类似,就是提示词进行一些替换,以及需要做一些很简单的数据处理,整体效果如下
|
||
|
||
### 多文本块初始翻译
|
||
|
||

|
||
|
||
### 多文本块反思
|
||
|
||

|
||
|
||
### 多文本块提升翻译
|
||
|
||

|
||
|
||
## 批量运行
|
||
|
||
长文反思翻译比较关键的一个部分,就是对多个文本块进行循环反思翻译
|
||
|
||
Fastgpt 提供了工作流线路可以返回去执行的功能,所以我们可以写一个很简单的判断函数,来判断结束或是接着执行
|
||
|
||

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

|
||
|
||
## 运行效果
|
||
|
||
首先输入全局设置
|
||
|
||

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

|
||
|
||
实际运行效果如下
|
||
|
||

|
||
|
||
可以看到还是能满足阅读需求的
|
||
|
||
# 进一步调优
|
||
|
||
## 提示词调优
|
||
|
||
在源项目中,给 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}}的灵活性,用不同的表述方式展现同一内容,提高译文的可读性
|
||
```
|
||
|
||
从而返回更准确更高质量的初始翻译,后续的反思和提升翻译也可以修改更准确的提示词,如下
|
||
|
||

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

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

|
||
|
||
从“让你的猫头鹰给我写信”这样有失偏颇的翻译,变成“给我写信,你的猫头鹰会知道怎么找到我”这样较为准确的翻译
|
||
|
||
## 其他调优
|
||
|
||
比如限定词调优,源项目中已经做了示范,就是加上国家地区这个限定词,实测确实会有不少提升
|
||
|
||
出于 LLM 的卓越能力,我们能够通过设置不同的prompt来获取不同的翻译结果,也就是可以很轻松地通过设置特殊的限定词,来实现特定的,更精确的翻译
|
||
|
||
而对于一些超出 LLM 理解的术语等,也可以利用 Fastgpt 的知识库功能进行相应扩展,进一步完善翻译机器人的功能
|