langchain 中,我们可以将一个任务拆分为多个更简单的子任务,不同的子任务使用不同的 LLM 来处理。

正如在软件工程中,将复杂系统分解为一组模块化组件是一种良好实践一样,对于提交给 GPT 的任务也是如此。 复杂任务的错误率往往高于简单任务。此外,复杂任务通常可以重新定义为简单任务的工作流程, 其中较早任务的输出用于构建较晚任务的输入。

再有一种场景是,有些简单的任务,我们可以交给一些效率更高、更廉价的 LLM 进行处理。 而对于复杂一点的任务,我们可以交给一些能力更强的 LLM 进行处理(同时可能更加昂贵)。

这就等同于一个团队中,有些任务可以交给实习生,有些任务可以交给初级工程师,有些任务可以交给高级工程师。 如果一些简单的任务交给高级工程师,可能会浪费资源,而一些复杂的任务交给实习生,可能会导致任务无法完成。

又或者,不同的 LLM 的能力是不一样的(比如一些模型能处理图像,一些模型只能处理文本),我们拆分任务,让不同的 LLM 分别处理,最后将结果整合。

实例一

在下面这个例子中,我们使用了两个 LLM:零一万物的 yi-large 和 OpenAI 的 gpt-3.5-turbo

要处理的任务是,根据用户输入的内容:

  1. 总结其内容。
  2. 将总结的内容翻译成英文。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 使用 OpenAI 的 LLM 处理总结的任务
openai_llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0,
max_tokens=200,
api_key="your key",
base_url="https://api.openai-hk.com/v1",
)
summarizing_prompt_template = """
总结以下文本为一个 20 字以内的句子:
---
{content}
"""
prompt = PromptTemplate.from_template(summarizing_prompt_template)
summarizing_chain = prompt | openai_llm | StrOutputParser()

# 使用零一万物的 LLM 处理翻译的任务
yi_llm = ChatOpenAI(
model_name="yi-large",
temperature=0,
max_tokens=200,
api_key="your key",
base_url="https://api.lingyiwanwu.com/v1",
)
translating_prompt_template = """将{summary}翻译成英文"""
prompt = PromptTemplate.from_template(translating_prompt_template)
translating_chain = prompt | yi_llm | StrOutputParser()

overall_chain = summarizing_chain | translating_chain

response = overall_chain.invoke({"content": "这是一个测试。"})

print(response)

输出:

1
This is a test.

在这个例子中,我们依然是使用了管道操作的方式,将两个 LLM 连接在一起,最终得到了我们想要的结果。

实例二

在上面例子的基础上,再调用一个新的模型,并且显示 langchain 的实际处理过程。

下面使用了 LLMChain,因为上面的 prompt | openai_llm 返回的结果并不能作为 SequentialChain 的参数。 同时也加上了 verbose=True 参数,以便查看处理过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from langchain.chains.llm import LLMChain
from langchain.chains.sequential import SequentialChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

# 使用 OpenAI 的 LLM 处理总结的任务
openai_llm = ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0,
max_tokens=200,
api_key="your key",
base_url="https://api.openai-hk.com/v1",
)
summarizing_prompt_template = """
总结以下文本为一个 20 字以内的句子:
---
{content}
"""
prompt = PromptTemplate.from_template(summarizing_prompt_template)
summarizing_chain = LLMChain(llm=openai_llm, prompt=prompt, output_key="summary", verbose=True)

# 使用零一万物的 LLM 处理翻译的任务
yi_llm = ChatOpenAI(
model_name="yi-large",
temperature=0,
max_tokens=200,
api_key="your key",
base_url="https://api.lingyiwanwu.com/v1",
)
translating_prompt_template = """将{summary}翻译成英文"""
prompt = PromptTemplate.from_template(translating_prompt_template)
translating_chain = LLMChain(llm=yi_llm, prompt=prompt, output_key="translated", verbose=True)

# 智谱清言 LLM 统计翻译后句子的长度
zhipu_llm = ChatOpenAI(
model_name="glm-4",
temperature=0,
max_tokens=200,
api_key="your key",
base_url="https://open.bigmodel.cn/api/paas/v4/",
)
translating_prompt_template = """统计{translated}的长度"""
prompt = PromptTemplate.from_template(translating_prompt_template)
stat_chain = LLMChain(llm=zhipu_llm, prompt=prompt, output_key="result", verbose=True)

overall_chain = SequentialChain(
chains=[summarizing_chain, translating_chain, stat_chain],
input_variables=["content"],
output_variables=["result"],
verbose=True
)

response = overall_chain.invoke({"content": "这是一个测试。"})

print(response)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
> Entering new SequentialChain chain...

> Entering new LLMChain chain...
Prompt after formatting:

总结以下文本为一个 20 字以内的句子:
---
这是一个测试。

> Finished chain.

> Entering new LLMChain chain...
Prompt after formatting:
将这是一个测试。翻译成英文

> Finished chain.

> Entering new LLMChain chain...
Prompt after formatting:
统计This is a test.的长度

> Finished chain.

> Finished chain.
{'content': '这是一个测试。', 'result': '字符串"This is a test."的长度是14个字符。这里包括了空格和句号。'}

输出的模式为:每使用一个 chain,都会输出一行 Entering new LLMChain chain...,在处理完成后,会输出处理结果,接着输出 Finished chain.

总结

langchain 中,我们可以将多个 LLM 连接在一起,形成一个链式请求,以便处理更复杂的任务。 将不同的任务交给不同的 LLM 处理,可以提高效率,降低成本。

LangChain 是一个用于语言模型和应用程序开发的框架,它提供了一系列工具和组件, 帮助开发者更轻松地构建基于大型语言模型(LLMs,如 OpenAI 的 GPT 系列)的应用程序。 LangChain 的目标是降低构建复杂语言应用程序的难度,通过提供一个模块化的框架,使得开发者能够快速集成和定制语言模型。

LangChain 可以很方便地连接大语言模型和各种应用。

主要特性

以下是 LangChain 的主要特性:

  • 模块化设计:LangChain 提供了一系列模块,包括模型、消息、角色、API 等,开发者可以根据自己的需求选择合适的模块进行组合。
  • 多种语言模型支持:LangChain 支持多种语言模型,包括 OpenAI 的 GPT 系列、清言的 GLM 系列等。
  • 链式调用:开发者可以创建 “链”(chains),这些链是一系列可以顺序执行的语言模型调用和其他操作,用于构建复杂的交互式应用程序。
  • 数据集成:LangChain 提供了与多种数据源集成的能力,如文件、数据库、搜索引擎等,使得语言模型能够访问和利用这些数据。
  • 可定制性:开发者可以根据特定任务对语言模型的行为进行微调,包括设置提示(prompts)、添加额外的上下文信息等。

为什么需要 LangChain?

大模型的智能效果令人振奋,可是当开发人员试图将大模型这颗“聪明脑”放入应用程序时,却发现了前所未有的挑战。

  • prompt 的结构如何标准化?(PromptTemplate
  • 如果我想中途随时切换大模型,怎样简单方便地操作?
  • LLM 的输出是非结构化的,它如何与结构化的程序接口相互交互?
  • 预训练模型的知识落后,如何让它知道最新的信息?
  • 如何让这颗大脑拥有记忆?(Memory
  • 如何给这颗 “聪明脑” 装上 “五官”,让它能够感知环境输入?
  • 怎样给这颗 “聪明脑” 装上 “手” 和 “脚”,让它帮我执行具体的任务?(Agent

LangChain 尝试解决的,就是这些问题。 LangChain 框架背后的核心思想是将自然语言处理序列分解为各个部分,允许开发人员根据自己的需要高效地定制工作流程

LangChain 的核心模块

Langchain 有6大核心模块:

  • Models:模型,是各种类型的模型和模型集成。
  • Prompts:提示,包括提示管理、提示优化和提示序列化。
  • Memory:记忆,用来保存和模型交互时的上下文状态。
  • Indexes:索引,用来结构化文档,以便和模型交互。包括文档加载程序、向量存储器、文本分割器和检索器等。
  • Agents:代理,决定模型采取哪些行动,执行并且观察流程,直到完成为止。
  • Chains:链,一系列对各种组件的调用。

LangChain 通常被用作「粘合剂」,将构建 LLM 应用所需的各个模块连接在一起。 使用 Langchain 中不同组件的特性和能力,可以构建不同场景下的应用,如聊天机器人、基于文档的问答、知识管理、个人助理、Agent 智能体等等。

如果需在本地或离线环境下使用大模型,需要首先将所需的模型下载至本地,通常开源 LLM 与 Embedding 模型可以从 HuggingFace 下载。

再比如,使用 Index 部分涉及的 RAG 技术,可以将特定领域的资料存在向量数据库里,在调用大模型时找出相关的部分作为“参考资料”传给大模型,以得到更为符合业务要求的回答。

在上一篇文章中,我们知道了,ChatOpenAI 对象调用 invoke 方法返回的的信息中, 包含了输入的 token 数量以及输出的 token 数量。

那么它到底是怎么计算的呢?

titoken

tiktoken 是 OpenAI 开发的开源的快速 token 切分器。

跟人类不一样,GPT 都是以 token 的形式来阅读文本的。而不同数量的 token,消耗的资源是不一样的,同样的,花费的 RMB 也是不一样的。

另一方面,我们也可以通过计算输入的 token 数量来了解是否太长而超出了模型处理能力。

使用 tiktoken 可以快速的计算出文本的 token 数量:

1
2
3
4
5
6
7
8
9
10
11
12
import tiktoken

encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
chinese = """LangChain为各种组件提供了标准的、可扩展的接口和外部集成,可用于构建LLMs。"""

tokens = encoding.encode(chinese)

print(tokens)

num_of_token_in_chinese = len(encoding.encode(chinese))

print(f'chinese: {chinese}, num of token: {num_of_token_in_chinese}')

输出:

1
2
[27317, 19368, 18184, 7305, 226, 87502, 41127, 14558, 29172, 84844, 35287, 31944, 12870, 228, 9554, 5486, 31540, 15355, 102, 77413, 9554, 30177, 40526, 34208, 48915, 34048, 43167, 13153, 3922, 31540, 11883, 35304, 78935, 26892, 4178, 22365, 1811]
chinese: LangChain为各种组件提供了标准的、可扩展的接口和外部集成,可用于构建LLMs。, num of token: 37

不同模型的上下文长度及价格

参考文档:

  1. 零一万物:https://platform.lingyiwanwu.com/docs
  2. OpenAI:https://platform.openai.com/docs/models
  3. 智谱清言:https://open.bigmodel.cn/dev/howuse/model

使用过 OpenAI 或者其他 LLM 的人应该都知道,有时候 LLM 会回答得不那么准确。 这是因为,LLM 可能并不知道你问题的背景是什么,所以只能从它大量学习到的数据中,找到一个最接近的答案, 但可能这个答案跟我们实际想要的答案相去甚远。

指定角色

如果我们为 LLM 指定一个角色,那么就等于给了 LLM 一个明确的指示,为它提供了一个上下文框架,这样它就能使用相关的知识来回答问题。

下面是一些例子:

指定为翻译

[翻译]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="yi-large",
temperature=0.3,
max_tokens=200,
api_key='your key',
base_url="https://api.lingyiwanwu.com/v1",
)

messages = [
SystemMessage(content="你是一名翻译,把用户的输入翻译为英语"),
HumanMessage(content="今天天气真好"),
]

response = chat.invoke(messages)

print(response.content)

指定为 程序员

[程序员]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="yi-large",
temperature=0.3,
max_tokens=200,
api_key='your key',
base_url="https://api.lingyiwanwu.com/v1",
)

messages = [
# 如果设定的是翻译角色,那么 LLM 只是翻译 ruby 是什么
# SystemMessage(content="你是一名翻译"),
SystemMessage(content="你是一名程序员"),
HumanMessage(content="ruby是什么"),
]

response = chat.invoke(messages)

print(response.content)

在上面的例子中,我们可以看到,在给 LLM 设定的角色是翻译的时候,我问他它 ruby 是什么,它直接翻译成中文给我。 但是当我给它设定的角色是程序员的时候,它就给我回答了 ruby 是一种编程语言。

指定上下文

下面这个例子中,我们为 LLM 提供了一个上下文,这样 LLM 就能更好地回答我们的问题。

如果我们直接问 LLM 我想查询所有年龄大于 18 岁的用户,应该怎么写 SQL 语句? 的话,它可能会回答得不那么准确,因为它并不知道我们的表结构是怎样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="yi-large",
temperature=0.3,
max_tokens=200,
api_key='your key',
base_url="https://api.lingyiwanwu.com/v1",
)

messages = [
SystemMessage(content="你是一名 MySQL DBA"),
HumanMessage(content=""""
假设我有一个 user 表,里面有 id, name, age 三个字段,我想查询所有年龄大于 18 岁的用户,应该怎么写 SQL 语句?
"""),
]

response = chat.invoke(messages)

print(response.content)

总结

如果我们想从 LLM 那里得到更加精确、高质量的答案,我们就需要为它指定一个角色,或者给他提供更多跟我们问题相关的内容。

当然,我们没办法给他提供太多的上下文,因为这样会让 LLM 处理很久,需要更昂贵的价格,同时,每个 LLM 的最大输入长度也是有限制的。

在开始之前,我们需要先准备一个可以调用 OpenAPI 的 key,又或者是其他 LLM 的 key。

因为墙的原因,所以没有办法直接使用 OpenAI 的 key 来调用,但是我们可以使用一些替代品或者代理,可选的有:

依赖

我们可以使用 openai 的 SDK 来调用它们的接口,同时也可以使用 langchain 这个库来调用它们的接口。

不过本系列文章都会基于 langchain 来进行实现。langchain 是一个基于 OpenAPI 的封装,可以方便的调用 OpenAPI 的接口。

同时很多国内的大模型也支持通过 langchain 的 API 来使用,非常方便。

通过 OpenAI SDK 使用

目前市面上大多数的 LLM 都支持 OpenAI 的接口,所以我们可以通过 OpenAI 的 SDK 来调用这些接口。

需要注意的是:

  1. 模型:不同的 LLM 有不同的模型,比如零一万物的模型是 yi-large,OpenAI 的模型是 gpt-3.5-turbo,智谱清言的模型是 glm-4
  2. base_url:不同的 LLM 有不同的 base_url,需要根据不同的 LLM 来设置。
  3. api_key:不同的 LLM 有不同的 api_key

下面是一个使用 OpenAI SDK 的例子:

依赖安装:

1
pip install openai

零一万物

[零一万物]
1
2
3
4
5
6
7
8
9
10
11
12
from openai import OpenAI

client = OpenAI(
api_key='your key',
base_url='https://api.lingyiwanwu.com/v1'
)
completion = client.chat.completions.create(
model="yi-large",
messages=[{"role": "user", "content": "Hi, who are you?"}]
)

print(completion.choices[0].message.content)

OpenAI HK

[OpenAI HK]
1
2
3
4
5
6
7
8
9
10
11
12
from openai import OpenAI

client = OpenAI(
api_key='your key',
base_url="https://api.openai-hk.com/v1"
)
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hi, who are you?"}]
)

print(completion.choices[0].message.content)

智谱清言

[智谱清言]
1
2
3
4
5
6
7
8
9
10
11
12
from openai import OpenAI

client = OpenAI(
api_key='your key',
base_url='https://open.bigmodel.cn/api/paas/v4/'
)
completion = client.chat.completions.create(
model="glm-4",
messages=[{"role": "user", "content": "Hi, who are you?"}]
)

print(completion.choices[0].message.content)

通过 langchain 使用

依赖安装:

1
pip install langchain_openai

跟上面的例子差不多,修改一下 base_urlmodel 以及 api_key 即可。

零一万物

[零一万物]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="yi-large",
temperature=0.3,
max_tokens=200,
api_key='your key',
base_url="https://api.lingyiwanwu.com/v1"
)

messages = [
SystemMessage(content="你是一名精通了 golang 的专家"),
HumanMessage(content="写一个 golang 的 hello world 程序"),
]

response = chat.invoke(messages)

print(response.content)

OpenAI HK

[OpenAI HK]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.3,
max_tokens=200,
api_key="your key",
base_url="https://api.openai-hk.com/v1"
)

messages = [
SystemMessage(content="你是一名精通了 golang 的专家"),
HumanMessage(content="写一个 golang 的 hello world 程序"),
]

response = chat.invoke(messages)

print(response.content)

智谱清言

[智谱清言]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="glm-4",
temperature=0.3,
max_tokens=200,
api_key="your key",
base_url='https://open.bigmodel.cn/api/paas/v4/'
)

messages = [
SystemMessage(content="你是一名精通了 golang 的专家"),
HumanMessage(content="写一个 golang 的 hello world 程序"),
]

response = chat.invoke(messages)

print(response.content)

环境变量指定 API KEY 以及 BASE URL

我们可以通过环境变量来指定 api_key 以及 base_url,这样我们就不需要在代码中指定了。

1
2
export OPENAI_API_KEY="your key"
export OPENAI_BASE_URL="https://api.lingyiwanwu.com/v1"

这样我们就可以直接使用 langchain 来调用了,当然,传递给 ChatOpenAImodel 参数还是得指定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

chat = ChatOpenAI(
model="yi-large",
temperature=0.3,
max_tokens=200
)

messages = [
SystemMessage(content="你是一名精通了 golang 的专家"),
HumanMessage(content="写一个 golang 的 hello world 程序"),
]

response = chat.invoke(messages)

print(response.content)

注意事项

  1. 截止到目前为止(2024-07-15),langchain 还在一个很频繁的更新阶段,所以可能会有一些 API 的变动,所以本文的一些 langchain 示例代码可能在未来会不可用。
0%