Published on

Langchain 笔记

Authors

Langchain 笔记

langchain_openai 中的 ChatOpenAI

属性 (Attribute)类型 (Type)描述
model / model_namestr(必需) 指定要使用的 OpenAI 模型ID,例如 "gpt-4o", "gpt-4-turbo", 或 "gpt-3.5-turbo"。
api_keystr你的 OpenAI API 密钥。如果未在此处提供,程序会尝试从环境变量 OPENAI_API_KEY 中读取。
temperaturefloat控制生成文本的随机性。值介于 0.0 和 2.0 之间。较高的值(如 0.8)会使输出更随机、更有创意;较低的值(如 0.2)会使其更具确定性。默认为 0.7。
max_tokensint在单次请求中,模型生成的最大 token 数量。这有助于控制内容的长度和成本。
streamingbool是否以流式方式接收响应。如果设置为 True,模型会逐个 token 地返回结果,而不是等待完整响应。这对于实时应用(如聊天机器人)非常有用。默认为 False。
nint为每个输入消息生成多少个聊天完成选项。默认为 1。
presence_penaltyfloat介于 -2.0 和 2.0 之间的数字。正值会惩罚新词元(token)是否已在文本中出现,从而增加模型谈论新主题的可能性。
frequency_penaltyfloat介于 -2.0 和 2.0 之间的数字。正值会根据新词元在文本中的现有频率来惩罚它们,从而降低模型逐字重复同一行的可能性。
timeoutfloatAPI 请求的超时时间(秒)。
max_retriesint在请求失败时进行的最大重试次数。
base_urlstr允许你指定一个自定义的 API 端点,用于连接代理服务器或兼容 OpenAI API 的其他服务。
organizationstr你的 OpenAI 组织 ID,主要用于有多个组织的用户。
方法 (Method)描述
invoke(input, config=None)(最常用) 调用模型处理单个输入,并返回结果。这是 LangChain 表达式语言 (LCEL) 中链式调用的核心方法。输入通常是一个字符串或一个消息列表(List[BaseMessage])。
stream(input, config=None)以流式方式调用模型。它会返回一个迭代器(iterator),你可以遍历这个迭代器来逐块获取生成的响应。适用于 streaming=True 的场景。
batch(inputs, config=None)高效地处理一批输入。它会并发地向模型发送多个请求,并返回一个结果列表。这比循环调用 invoke 更快。
ainvoke(input, config=None)invoke 的异步版本。用于在异步 Python 代码(asyncio)中调用模型。
astream(input, config=None)stream 的异步版本。返回一个异步迭代器。
abatch(inputs, config=None)batch 的异步版本。
bind_tools(...) / bind_functions(...)用于将自定义工具(Tools)或函数(Functions)绑定到模型。这使得模型可以根据需要调用这些工具来获取外部信息或执行操作,是实现 Agent 和 Tool Calling 的关键。

langchain_core.prompts

这个包提供了许多提示词模版,这里只展示几种常用的类

SystemMessagePromptTemplate

  • 指代角色: 系统 (System)

  • 作用: 用于给 AI 模型设定一个高级别的指令、背景或角色。这通常是对话列表中的第一条消息,为整个对话定下基调。系统消息告诉 AI 它应该“扮演”谁,遵循什么样的规则。

  • 示例:

    • "你是一个精通中国古诗词的 AI 助手。"

    • "你是一个代码调试专家,请用简洁、清晰的语言回答问题。"

    • "请始终用中文回答。"

HumanMessagePromptTemplate

  • 指代角色: 用户 (Human)

  • 作用: 代表最终用户的提问或输入。这通常是触发 AI 回答的直接原因。在模板中,它经常包含一个或多个变量(如 {input}{question}),以便在运行时动态地填入用户的具体问题。

  • 示例:

    • "请帮我解释一下李白的《静夜思》。"

    • "我的这段 Python 代码为什么会报错?代码是:{code_snippet}"

    • "{country}的首都是哪里?"

AIMessagePromptTemplate

  • 指代角色: AI / 助手 (Assistant)

  • 作用: 代表 AI 模型的回复。在构建提示时,这个模板主要用于提供“少样本(Few-shot)”示例。通过在提示中提前给出一些“用户提问 -> AI 回答”的范例,你可以引导模型按照特定的格式或风格进行回复。

  • 示例:

    • Human: "苹果公司是谁创立的?"

    • AI: "苹果公司是由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗纳德·韦恩共同创立的。" (这个AI回答就是一个 AIMessage 的例子)

ChatMessagePromptTemplate

  • 指代角色: 任意角色 (Arbitrary Role)

  • 作用: 这是一个更通用的模板,允许你为消息指定任何自定义的角色名称。虽然 System, Human, 和 AI 是最标准、最受模型支持的角色,但在某些特定场景或使用某些特定模型时,你可能需要定义一个不同的角色(例如 ToolFunction 来表示工具的输出)。

  • 示例:

    • 假设一个工具执行了天气查询,返回的结果可以被包装在一个 role="tool" 的消息中,这时就可以使用 ChatMessagePromptTemplate
from langchain_core.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate

# Defining the system prompt (how the AI should act)
system_prompt = SystemMessagePromptTemplate.from_template(
    "You are an AI assistant that helps generate article titles."
)

# the user prompt is provided by the user, in this case however the only dynamic
# input is the article
user_prompt = HumanMessagePromptTemplate.from_template(
    """You are tasked with creating a name for a article.
The article is here for you to examine {article}

The name should be based of the context of the article.

Be creative, but make sure the names are clear, catchy,

and relevant to the theme of the article.

Only output the article name, no other explanation or
text can be provided.""",
    input_variables=["article"]
)
# 这里的 `input_variables` 是一个列表,用来明确声明这个提示模板(Prompt Template)中包含了哪些需要被动态填充的变量

# 使用format方法来对提示词模版中的变量赋值
user_prompt.format(article="TEST STRING")

from langchain_core.prompts import ChatPromptTemplate

# 使用ChatPromptTemplate来组合系统提示词和用户提示词
first_prompt = ChatPromptTemplate.from_messages([system_prompt, user_prompt])

# 同样可以给组合提示词模版中的变量赋值
first_prompt.format(article="TEST STRING")

LangChain Expression Language (LCEL)

chain_one = (
    {"article": lambda x: x["article"]} # 
    | first_prompt
    | creative_llm  # <-- 语言模型输出了一个对象
    | {"article_title": lambda x: x.content} # <-- 这一步处理该对象
)

# 传入定义好的article变量
article_title_msg = chain_one.invoke({"article": article})

{"article": lambda x: x["article"]}创建一个键为"article"的字典 lambda x: 定义了一个函数,它接收一个参数,这里我们叫它 x。在 LCEL 链中,x 就是上一步传来的完整输入。 x["article"] 是函数的执行体。它会从输入的字典 x 中,获取键为 "article" 的值。

可以通过使用with_structured_output方法向ChatOpenAI对象传入一个BaseModel对象来达到结构化输出的目的

from pydantic import BaseModel, Field

class Paragraph(BaseModel):

    original_paragraph: str = Field(description="The original paragraph")

    edited_paragraph: str = Field(description="The improved edited paragraph")

    feedback: str = Field(description=(

        "Constructive feedback on the original paragraph"

    ))

structured_llm = creative_llm.with_structured_output(Paragraph)

chain_three = (
    {"article": lambda x: x["article"]}
    | third_prompt
    | structured_llm
    | {
        "original_paragraph": lambda x: x.original_paragraph,
        "edited_paragraph": lambda x: x.edited_paragraph,
        "feedback": lambda x: x.feedback
    }
)

out = chain_three.invoke({"article": article})

使用Langsmith来追踪agent的运行

初始化

import os

# must enter API key
os.environ["LANGCHAIN_API_KEY"]

# below should not be changed
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"

# you can change this as preferred
os.environ["LANGCHAIN_PROJECT"] = "aurelioai-langchain-course-langsmith-openai"

可以使用@traceable装饰器为函数注解使得函数运行也可以被Langsmith追踪

from langsmith import traceable
import random
import time


@traceable
def generate_random_number():
    return random.randint(0, 100)

@traceable
def generate_string_delay(input_str: str):
    number = random.randint(1, 5)
    time.sleep(number)
    return f"{input_str} ({number})"

@traceable
def random_error():
    number = random.randint(0, 1)
    if number == 0:
        raise ValueError("Random error")
    else:
        return "No error"

![[Pasted image 20250803214859.png]] 可以向装饰器传入参数来为这个追踪增加信息,方便后续的检索

from langsmith import traceable

@traceable(name="Chitchat Maker")
def error_generation_function(question: str):
    delay = random.randint(0, 3)
    time.sleep(delay)
    number = random.randint(0, 1)
    if number == 0:
        raise ValueError("Random error")
    else:
        return "I'm great how are you?"

![[Pasted image 20250803215327.png]]

如何写好prompt?

Basic prompt

[(Rules) For Our Prompt]
Answer the question based on the context below,                 
if you cannot answer the question using the                     
provided information answer with "I don't know"                 

[Context AI has]
Context: Aurelio AI is an AI development studio                 
focused on the fields of Natural Language Processing (NLP)     
and information retrieval using modern tooling                   
such as Large Language Models (LLMs),                           
vector databases, and LangChain.                                

[User Question]
Question: Does Aurelio AI do anything related to LangChain?        

[AI Answer]
Answer:                                                          

示例

prompt = """
Answer the user's query based on the context below.
If you cannot answer the question using the
provided information answer with "I don't know".

Context: {context}
"""

from langchain.prompts import (
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate
)

prompt_template = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(prompt),
    HumanMessagePromptTemplate.from_template("{query}"),
])
# 可以使用prompt_template.messages来获取提示词模版的内容
# 可以使用prompt_template.input_variables来获取提示词模版中的变量
# prompt_template的完整内容如下:
'''
input_variables=['context', 'query'] input_types={} partial_variables={} messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context'], input_types={}, partial_variables={}, template='\nAnswer the user\'s query based on the context below.\nIf you cannot answer the question using the\nprovided information answer with "I don\'t know".\n\nContext: {context}\n'), additional_kwargs={}), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['query'], input_types={}, partial_variables={}, template='{query}'), additional_kwargs={})] 
'''
pipeline = (
    {
        "query": lambda x: x["query"],
        "context": lambda x: x["context"]
    }
    | prompt_template
    | llm
)

context = """Aurelio AI is an AI company developing tooling for AI
engineers. Their focus is on language AI with the team having strong
expertise in building AI agents and a strong background in
information retrieval.

The company is behind several open source frameworks, most notably
Semantic Router and Semantic Chunkers. They also have an AI
Platform providing engineers with tooling to help them build with
AI. Finally, the team also provides development services to other
organizations to help them bring their AI tech to market.

Aurelio AI became LangChain Experts in September 2024 after a long
track record of delivering AI solutions built with the LangChain
ecosystem."""

query = "what does Aurelio AI do?"

pipeline.invoke({"query": query, "context": context})

Few shot prompt

我们可以通过向大模型输入几个问答示例来使得大模型的输出更符合我们的预期

可以使用langchain_core中的FewShotChatMessagePromptTemplate类来创建一个有关fewshot prompt的对象

new_system_prompt = """
Answer the user's query based on the context below.
If you cannot answer the question using the
provided information answer with "I don't know".
  
Always answer in markdown format. When doing so please
provide headers, short summaries, follow with bullet
points, then conclude.

Context: {context}
"""

example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{input}"),
    ("ai", "{output}"),
])

examples = [
    {
        "input": "Can you explain gravity?",
        "output": (
            "## Gravity\n\n"
            "Gravity is one of the fundamental forces in the universe.\n\n"
            "### Discovery\n\n"
            "* Gravity was first discovered by Sir Isaac Newton in the late 17th century.\n"
            "* It was said that Newton theorized about gravity after seeing an apple fall from a tree.\n\n"
            "### In General Relativity\n\n"
            "* Gravity is described as the curvature of spacetime.\n"
            "* The more massive an object is, the more it curves spacetime.\n"
            "* This curvature is what causes objects to fall towards each other.\n\n"
            "### Gravitons\n\n"
            "* Gravitons are hypothetical particles that mediate the force of gravity.\n"
            "* They have not yet been detected.\n\n"
            "**To conclude**, Gravity is a fascinating topic and has been studied extensively since the time of Newton.\n\n"
        )
    },
    {
        "input": "What is the capital of France?",
        "output": (
            "## France\n\n"
            "The capital of France is Paris.\n\n"
            "### Origins\n\n"
            "* The name Paris comes from the Latin word \"Parisini\" which referred to a Celtic people living in the area.\n"
            "* The Romans named the city Lutetia, which means \"the place where the river turns\".\n"
            "* The city was renamed Paris in the 3rd century BC by the Celtic-speaking Parisii tribe.\n\n"
            "**To conclude**, Paris is highly regarded as one of the most beautiful cities in the world and is one of the world's greatest cultural and economic centres.\n\n"
        )
    }
]

from langchain_core.prompts import FewShotChatMessagePromptTemplate

few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)

prompt_template = ChatPromptTemplate.from_messages([
    ("system", new_system_prompt),
    few_shot_prompt,
    ("user", "{query}"),
])

pipeline = prompt_template | llm

context = """Aurelio AI is an AI company developing tooling for AI
engineers. Their focus is on language AI with the team having strong
expertise in building AI agents and a strong background in
information retrieval.

The company is behind several open source frameworks, most notably
Semantic Router and Semantic Chunkers. They also have an AI
Platform providing engineers with tooling to help them build with
AI. Finally, the team also provides development services to other
organizations to help them bring their AI tech to market.

Aurelio AI became LangChain Experts in September 2024 after a long
track record of delivering AI solutions built with the LangChain
ecosystem."""

query = "what does Aurelio AI do?"

out = pipeline.invoke({"query": query, "context": context}).content

Chain of Thought Prompting(COT)

思维链(Chain of Thought)是一种提示词(Prompt)设计技术,它通过引导大型语言模型(LLM)在给出最终答案之前,先输出一系列连贯的、中间的推理步骤,来模仿人类解决复杂问题时的思考过程。

简单来说,你不是直接问模型“答案是什么?”,而是要求它“请一步一步地思考,然后告诉我答案”。这种方式能显著提高模型在需要逻辑推理、数学计算或多步骤规划等复杂任务上的准确性和可靠性。

一般来说我们都在系统提示词设置COT的“咒语”,比如: 无COT: 你是一个乐于助人的助手,回答用户的问题 有COT: 做一个乐于助人的助手,回答用户的问题。 要回答问题,您必须

  • 系统、详细地列出回答问题需要解决的所有子问题。
  • 按顺序单独解决每个子问题。
  • 最后,利用你所做的一切来提供最终答案。

尽管目前的推理模型越来越智能,甚至在某些情况下能够“自发地”进行内部推理,但显式地使用思维链(CoT)提示词仍然是确保复杂任务准确性、可靠性和可控性的最佳实践。

在处理一些高度复杂,作为人类需要想很久的问题时还是建议要使用COT提示词 PS:我们也可以直接通过大模型来生成COT提示词

记忆模块

使用langchain.memory.buffer 中的 ConversationBufferMemory类来实现记忆存储

使用示例:

# return_messages=True使得在输出记忆内容时会附上消息类型,比如HumanMessage
memory = ConversationBufferMemory(return_messages=True)

memory.chat_memory.add_user_message("Hi, my name is James")
memory.chat_memory.add_ai_message("Hey James, what's up? I'm an AI model called Zeta.")
memory.chat_memory.add_user_message("I'm researching the different types of conversational memory.")
memory.chat_memory.add_ai_message("That's interesting, what are some examples?")
memory.chat_memory.add_user_message("I've been looking at ConversationBufferMemory and ConversationBufferWindowMemory.")
memory.chat_memory.add_ai_message("That's interesting, what's the difference?")
memory.chat_memory.add_user_message("Buffer memory just stores the entire conversation, right?")
memory.chat_memory.add_ai_message("That makes sense, what about ConversationBufferWindowMemory?")
memory.chat_memory.add_user_message("Buffer window memory stores the last k messages, dropping the rest.")
memory.chat_memory.add_ai_message("Very cool!")

memory.load_memory_variables({}) # 输出记忆内容

from langchain.chains import ConversationChain

chain = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True
)

chain.invoke({"input": "what is my name again?"})

ConversationBufferMemory with RunnableWithMessageHistory

from langchain.prompts import (
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
    MessagesPlaceholder,
    ChatPromptTemplate
)

system_prompt = "You are a helpful assistant called Zeta."

prompt_template = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(system_prompt),
    MessagesPlaceholder(variable_name="history"),
    # MessagesPlaceholder相当于一个占位符,它可以插入
    # 一系列完整的消息对象(如 `HumanMessage`, `AIMessage`)
    HumanMessagePromptTemplate.from_template("{query}"),
])

pipeline = prompt_template | llm

from langchain_core.chat_history import InMemoryChatMessageHistory

chat_map = {}

def get_chat_history(session_id: str) -> InMemoryChatMessageHistory:
    if session_id not in chat_map:
        # if session ID doesn't exist, create a new chat history
        chat_map[session_id] = InMemoryChatMessageHistory()
    return chat_map[session_id]
    
from langchain_core.runnables.history import RunnableWithMessageHistory

pipeline_with_history = RunnableWithMessageHistory(
    pipeline,
    get_session_history=get_chat_history,
    # 这样langchain就可以使用get_chat_history函数来导入记忆
    input_messages_key="query",
    history_messages_key="history"
    # 这里的"history"对应了上文中的MessagesPlaceholder,它会把记忆中的
    # 内容全部导入到prompt_template中MessagesPlaceholder所占的位置
)

pipeline_with_history.invoke(
    {"query": "Hi, my name is James"},
    config={"session_id": "id_123"}
    # config参数告诉一个RunnableWithMessageHistory对象该如何使用
    # get_session_hitory中的这个可调用对象,相当于get_chat_history("id_123")

)

RunnableWithMessageHistoryget_session_history 属性必须是一个可调用对象,最常见的“可调用对象”有三种:

  1. def 定义的普通函数:这是我们一直在用的,也是最清晰、最推荐的方式。
  2. lambda 匿名函数:适用于逻辑非常简单的单行函数。
  3. 实现了 __call__ 特殊方法的类的实例:适用于需要封装更复杂逻辑或状态的场景。

注意自 LangChain v0.3 版本发布以来, Langchain 社区建议 LangChain 用户利用 LangGraph 持久化功能将 memory 集成到他们的 LangChain 应用程序中。故这里有关langchain的memory模块不再赘述。

Agent

这里做一个简单的大模型调用工具的示例

from langchain_core.tools import tool

# 创建工具时一定要把工具功能写清楚,否则大模型可能不知道这个工具的功能
@tool
def add(x: float, y: float) -> float:
    """Add 'x' and 'y'."""
    return x + y

@tool
def multiply(x: float, y: float) -> float:
    """Multiply 'x' and 'y'."""
    return x * y

@tool
def exponentiate(x: float, y: float) -> float:
    """Raise 'x' to the power of 'y'."""
    return x ** y

@tool
def subtract(x: float, y: float) -> float:
    """Subtract 'x' from 'y'."""
    return y - x

使用工具:

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(
    memory_key="chat_history",  # must align with MessagesPlaceholder variable_name
    return_messages=True  # to return Message objects
)

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "you're a helpful assistant"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

from langchain.agents import create_tool_calling_agent

tools = [add, subtract, multiply, exponentiate]

agent = create_tool_calling_agent(
    llm=llm, tools=tools, prompt=prompt
)

from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True
)

Reference