LangChain 基本使用

140 阅读6分钟

常用模版特性和使用场景对比

子类适用模型输入类型主要用途
PromptTemplate文本补全模型单字符串生成单轮文本任务的提示
ChatPromptTemplate聊天模型多消息列表模拟多轮对话或角色扮演
FewShotPromptTemplate所有模型包含示例的模版通过示例引导模型完成复杂任务

LangChain 调用大模型

安装 langchain_openai 包:

uv add langchain_openai

大模型选择:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
  model = "qwen-max-latest",
  base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1",
  api_key= SecretStr("xxx"),
  streaming=True,
)

调用 invoke 或者 stream 方法,传入即可

respInvoke = llm.invoke("100+100=?")
print(respInvoke.content)

respStream = llm.stream("100+100=?")
for chunk in respStream:
    print(chunk.content, end='')

invokestream 的区别:

  • invoke:是同步调用并一次性返回完整结果
  • stream:是异步流式返回结果,逐个输出 token

SecretStr

ChatOpenAI 传入的参数 api_key 需要使用 SecretStr 进行包装

SecretStrpydantic 提供

from pydantic import SecretStr

api_key = SecretStr("xxx")

SecretStr 的作用:

  • 安全性:防止敏感信息在日志或错误消息中暴露,会显示 ***** 而不是实际值
  • 一致性:LangChain 框架的统一设置

重用提示词

当提示词比较复杂时,希望可以重复使就需要使用提示词模版功能

PromptTemplate

用于文本补全模型,输入纯文本

from langchain_core.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("今天{something}真不错")
print(prompt_template)

打印 prompt_template 结果,可以看到模版的详细信息:

input_variables=['something'] input_types={} partial_variables={} template='今天{something}真不错'

打印 prompt,可以看到模版的内容,something 替换成天气

prompt = prompt_template.format(something="天气")
print(prompt)

然后将 prompt 传入大模型,大模型就会根据提示词进行回答

resp = llm.stream(prompt)
for chunk in resp:
    print(chunk.content, end='')

源码

ChatPromptTemplate

基于对话模型,适用于多轮对话

根据一组消息来创建提示词模版,这个提示词模版一上来就具备多轮对话的能力

form_messages 方法接收一个元组列表,元组中每一个元b表示一轮对话

from langchain_core.prompts import ChatPromptTemplate

chat_prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个{role}专家、擅长回答{domain}领域的问题"),
        ("user", "用户问题:{question}"),
    ]
)

prompt = chat_prompt_template.format_messages(
    role="编程",
    domain="web开发",
    question="如何构建一个 vue 应用",
)
print(prompt)

打印 prompt 我们可以看到占位符被替换成了实际的值

[SystemMessage(content='你是一个编程专家、擅长回答web开发领域的问题', additional_kwargs={}, response_metadata={}), HumanMessage(content='用户问题:如何构建一个 vue 应用', additional_kwargs={}, response_metadata={})]
resp = llm.stream(prompt)
for chunk in resp:
    print(chunk.content,end="")

源码

ChatMessagePromptTemplate

它的作是用来抽象提示词模版某一条消息

from langchain_core.prompts import ChatPromptTemplate, ChatMessagePromptTemplate

system_message_template = ChatMessagePromptTemplate.from_template(
    template="你是一个{role}专家、擅长回答{domain}领域的问题", role="system"
)

human_message_template = ChatMessagePromptTemplate.from_template(
    template="用户问题:{question}", role="user"
)

chat_prompt_template = ChatPromptTemplate.from_messages(
    [system_message_template, human_message_template]
)

prompt = chat_prompt_template.format_messages(
    role="编程",
    domain="web开发",
    question="如何构建一个 vue 应用",
)

源码

FewShotPromptTemplate

少样本提示模版,这个意思是给大模提供一些样本,让大模型更好地理解任务

from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

example_template = "输入:{input}\n输出: {output}"
examples = [
    {"input": "将 'hello' 翻译成中文", "output": "你好"},
    {"input": "将 'goodbye' 翻译成中文", "output": "再见"},
]

few_shot_prompt_template = FewShotPromptTemplate(
    examples=examples,
    example_prompt=PromptTemplate.from_template(example_template),
    prefix="请将以下英文翻译成中文: ",
    suffix="输出:{text}\n",
    input_variables=["text"],
)

prompt = few_shot_prompt_template.format(text="Thank you")
print(prompt)

输入 prompt 结果:

请将以下英文翻译成中文:
20
输入:将 'hello' 翻译成中文
输出: 你好

输入:将 'goodbye' 翻译成中文
输出: 再见

输出:Thank you

FewShotPromptTemplate 生成的 prompt 传入大模型,就会输出 thank you 的中文翻译

resp = llm.stream(prompt)
for chunk in resp:
    print(chunk.content, end="")

# 谢谢您

源码

langChain 链式调用

resp = llm.stream(prompt) 这种调用方式比较原始,是拿到提示词后在调用大模型,langChain@0.3 版本提了了供了链式调用的方式

example_template = "输入:{input}\n输出: {output}"
examples = [
    {"input": "将 'hello' 翻译成中文", "output": "你好"},
    {"input": "将 'goodbye' 翻译成中文", "output": "再见"},
]

few_shot_prompt_template = FewShotPromptTemplate(
    examples=examples,
    example_prompt=PromptTemplate.from_template(example_template),
    prefix="请将以下英文翻译成中文: ",
    suffix="输出:{text}\n",
    input_variables=["text"],
)

chain = few_shot_prompt_template | llm

调用的时候输入一个对象,传入 text 字段即可

resp = chain.stream(input={"text": "I love programming"})

for chunk in resp:
    print(chunk.content, end="")

python 是如何实现 | 操作符的

在大部分语言中 | 是按位或操作符,Python 中可以通过重载 __or__ 方法来实现自定义的 | 操作符

下面的例子就是实现了一个链式调用的功能的 demo

class Chainable:
    def __or__(self, other):
        return Chain([self, other])

    def process(self, data):
        raise NotImplementedError("子类必须实现 process 方法")

    def __call__(self, data):
        return self.process(data)


class Chain(Chainable):
    def __init__(self, steps):
        self.steps = steps

    def process(self, data):
        result = data
        for step in self.steps:
            result = step.process(result)
        return result

    def __or__(self, other):
        return Chain(self.steps + [other])


class AddNumber(Chainable):
    def __init__(self, number):
        self.number = number

    def process(self, data):
        return data + self.number


class MultiplyNumber(Chainable):
    def __init__(self, number):
        self.number = number

    def process(self, data):
        return data * self.number


chain = AddNumber(5) | MultiplyNumber(2) | MultiplyNumber(2)
print("链式调用结果:", chain(10))

result = MultiplyNumber(2).process(
    MultiplyNumber(2).process((AddNumber(5).process(10)))
)
print("普通调用结果:", result)

源码

调用自定义工具

开发一个自定义工具,所以需要先定义一个函数 add

def add(a, b):
    return a + b

将工具函数转为 langchain.Tool 对象

from langchain_core.tools import Tool

add_tool = Tool.from_function(
    func=add, # 函数对象
    name="add", # 工具名称
    description="两个数字相加", # 工具描述
)

将大模型和工具函数绑定起来

llm_with_tools = llm.bind_tools([add_tool])

调用大模型


chain = chat_prompt_template | llm_with_tools

resp = chain.invoke(
    input={
        "role": "计算",
        "domain": "数学计算",
        "question": "使用工具计算:100 + 200 = ?",
    }
)

print(resp)

大模型不会给我们直接去执行工具函数 add,而是会返回一个 tool_calls 的结果,告诉我们需要调用哪个工具函数

具体的调用过程是需要自己

content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_952988c47cfb4c478f50f8', 'function': {'arguments': '{"__arg1": "100", "__arg2": "200"}', 'name': 'add'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max-latest'} id='run--04d91d73-e91e-4d34-8fa1-989eeb2f42e3-0' tool_calls=[{'name': 'add', 'args': {'__arg1': '100', '__arg2': '200'}, 'id': 'call_952988c47cfb4c478f50f8', 'type': 'tool_call'}]
tool_dict = {"add": add}

tool_calls = getattr(resp, "tool_calls", None)
if tool_calls:
    for tool_call in tool_calls:
        args = tool_call["args"]
        func_name = tool_call["name"]

        tool_func = tool_dict[func_name]
        tool_content = tool_func(int(args["__arg1"]), int(args["__arg2"]))
        print("工具函数计算结果:", tool_content)

源码

使用装饰器改造

上面的调用过程比较繁琐,可以使用装饰器来简化调用

from langchain_core.agents import tool

class AddInputArgs(BaseModel):
    a: str = Field(..., description="第一个加数")
    b: str = Field(..., description="第二个加数")


@tool(
    description="两个数相加",
    args_schema=AddInputArgs,
)
def add(a, b):
    return a + b


llm_with_tools = llm.bind_tools([add])

tool_calls = getattr(resp, "tool_calls", None)
if tool_calls:
    for tool_call in tool_calls:
        args = tool_call["args"]
        func_name = tool_call["name"]
        tool_func = tool_dict[func_name]
        tool_content = tool_func.invoke(args)
        print("工具函数计算结果:", tool_content)

装饰器 @tool 会自动帮我们生成 langchain.Tool 对象,并且会根据 args_schema 自动帮我们做参数校验

ab 都是 str 类型,就会按照字符串类型传入,如果是 int 类型就会转换成整数类型

# 字符串类型相加
class AddInputArgs(BaseModel):
    a: str = Field(description="第一个加数")
    b: str = Field(description="第二个加数")


# 数字类型相加
class AddInputArgs(BaseModel):
    a: int = Field(description="第一个加数")
    b: int = Field(description="第二个加数")

源码

内置工具调用