一文入门 LangChain 开发

2024-05-22

\[Ollama\]

\[N_LangChain\]

\[N_LangGraph\]

LangChain

github langchain 项目页
github langgraph 项目页

官方文档 introduction
官方文档 0.2 introductio
0.2 langserve
官方文档 0.1 Components
Langchain API文档
OpenAI 接口文档

LangChain is a framework for developing applications powered by large language models (LLMs ).

LangChain simplifies (简化每个阶段) every stage of the LLM application lifecycle:

  • Development : Build your applications using LangChain's open-source building blocks, components, and third-party integrations. Use LangGraph to build stateful agents with first-class streaming and human-in-the-loop support.
  • Productionization(产品化?) : Use LangSmith to inspect, monitor and evaluate your chains, so that you can continuously optimize and deploy with confidence.
  • Deployment : Turn your LangGraph applications into production-ready APIs and Assistants with LangGraph Cloud

Concretely, the framework consists of the following open-source libraries:

  • langchain-core: Base abstractions and LangChain Expression Language.
  • langchain-community : Third party integrations.
    • Partner packages (e.g. langchain-openai , langchain-anthropic , etc.): Some integrations have been further split into their own lightweight packages that only depend on langchain-core.
  • langchain: Chains, agents, and retrieval strategies that make up an application's cognitive architecture.
  • LangGraph: Build robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. Integrates smoothly with LangChain, but can be used without it.
  • LangServe: Deploy LangChain chains as REST APIs.
  • LangSmith: A developer platform that lets you debug, test, evaluate, and monitor LLM applications.

in short 它想干的事情是: 将AI大语言模块开发中常用/通用的功能进行 '抽象化' '概念化' 并提供其一些实现的元素组件, 供开发者使用

LangChain 的主要概念

完整见: 官文档-概念指南
组件的具体集成实现

Model I/O

Prompt

OpenAI 的文本生成模型(通常称为生成式预训练转换器,简称 "GPT "模型),如 GPT-4 和 GPT-3.5,经过训练后可以理解自然语言和正式语言。像 GPT-4 这样的模型可以根据输入内容输出文本。这些模型的输入也被称为 "Prompt"。

few-shotting (少量对话示例

https://python.langchain.com/v0.2/docs/how_to/few_shot_examples/

如何创建一个简单的提示模板,在生成时为模型提供示例输入和输出。为LLM提供一些这样的例子被称为"few-shotting",这是一种简单而强大的方法来指导生成,在某些情况下可以大大提高模型性能。
https://python.langchain.com/v0.2/docs/how_to/few_shot_examples_chat/

Prompt templates (提示词模版

https://python.langchain.com/v0.2/docs/concepts/#prompt-templates

These prompt templates are used to format a single string, and generally are used for simpler inputs. For example, a common way to construct and use a PromptTemplate is as follows:

in short 提示词模版

python 复制代码
from langchain_core.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")

prompt_template.invoke({"topic": "cats"})
python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage

prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    MessagesPlaceholder("msgs")
])

prompt_template.invoke({"msgs": [HumanMessage(content="hi!")]})

Chat models

https://python.langchain.com/v0.2/docs/integrations/chat/

使用一系列消息作为输入并返回聊天消息作为输出的语言模型(包括不限于纯文本,纯文本的见下LLMs)。聊天模型支持为对话消息分配不同的角色,有助于将消息与人工智能、用户和指令(如系统消息)区分开来。

in short 表示 多模态模型

Output parsers

负责获取模型的输出,并将其转换为更适合下游任务的格式。当您使用LLM生成结构化数据或规范聊天模型和LLM的输出时,这很有用。

in short '输出解析器' 解析模型的输出为特定的格式

Name Description Supports Streaming Has Format Instructions Calls LLM Input Type Output Type
JSON Returns a JSON object as specified. You can specify a Pydantic model and it will return JSON for that model. Probably the most reliable output parser for getting structured data that does NOT use function calling. str | Message JSON object
XML Returns a dictionary of tags. Use when XML output is needed. Use with models that are good at writing XML (like Anthropic's). str | Message dict
CSV Returns a list of comma separated values. str | Message List[str]
OutputFixing Wraps another output parser. If that output parser errors, then this will pass the error message and the bad output to an LLM and ask it to fix the output. str | Message
RetryWithError Wraps another output parser. If that output parser errors, then this will pass the original inputs, the bad output, and the error message to an LLM and ask it to fix it. Compared to OutputFixingParser, this one also sends the original instructions. str | Message
Pydantic Takes a user defined Pydantic model and returns data in that format. str | Message pydantic.BaseModel
YAML Takes a user defined Pydantic model and returns data in that format. Uses YAML to encode it. str | Message pydantic.BaseModel
PandasDataFrame Useful for doing operations with pandas DataFrames. str | Message dict
Enum Parses response into one of the provided enum values. str | Message Enum
Datetime Parses response into a datetime string. str | Message datetime.datetime
Structured An output parser that returns structured information. It is less powerful than other output parsers since it only allows for fields to be strings. This can be useful when you are working with smaller LLMs. str | Message Dict[str, str]

LLMs models

https://python.langchain.com/v0.2/docs/integrations/llms/

以字符串作为输入并返回字符串的语言模型。这些是传统上较旧的模型

尽管底层模型是字符串输入、字符串输出,但 LangChain 包装器也允许这些模型将消息作为输入。这使得它们可以与ChatModels 互换。当消息作为输入传入时,在传递到底层模型之前,它们将被格式化为隐藏的字符串。

in short 表示 大语言模型 (这里代指支持纯文本的模型)

python 复制代码
import os
from langchain import __version__
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

os.environ["OPENAI_API_KEY"] = 'sk-xxxxxxxxxxxxxxxxxxx'
llm_model = ChatOpenAI(model="qwen-turbo",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

messages = [
    HumanMessage(content="李白有多少首诗"),
]

ret = llm_model.invoke(messages)
print(ret)

结构输出(with_structured_output)

llm.with_structured_output 可以根据给定的 schema 格式化输出
https://python.langchain.com/docs/how_to/structured_output/
https://python.langchain.com/v0.2/docs/how_to/structured_output/

使用 Joke 实体类来接受模型数据

python 复制代码
from typing import Optional
from langchain_core.pydantic_v1 import BaseModel, Field
# from pydantic import BaseModel, Field  for 0.3 version
# Pydantic
class Joke(BaseModel):
    """Joke to tell user."""
    setup: str = Field(description="The setup of the joke")
    punchline: str = Field(description="The punchline to the joke")
    rating: Optional[int] = Field(
        default=None, description="How funny the joke is, from 1 to 10"
    )

# 它这里底层有几种方式可选 method: Literal["function_calling", "json_mode", "json_schema"] = "function_calling",
# 默认用的是 function call 让大模型去填充参数 
structured_llm = llm.with_structured_output(Joke, method="function_calling")
structured_llm.invoke("Tell me a joke about cats")
# out 
# Joke(setup='Why was the cat sitting on the computer?', punchline='Because it wanted to keep an eye on the mouse!', rating=7)

它这里底层有几种方式可选 method: Literal["function_calling", "json_mode", "json_schema"] = "function_calling"

  • function_calling 则是基于 function_calling

  • json_mode 则是添加 'response_format': {'type': 'json_object'} 参数

  • json_schema 则是添加 'response_format': {'type': 'json_schema', 'json_schema': {'schema': {'description': '将执行的计划', 'properties': {'steps': {'description': '', 'items': {'type': 'string'}, 'title': 'Steps', 'type': 'array'}}, 参数

  • function_calling 示例

json 复制代码
ChatOpenAI(model="gpt-4o", temperature=0).with_structured_output(Plan)


// 请求
{"messages": [{"content": "请针对无论简单还是复杂的问题,制定一个简单的逐步计划,并按照 Plan 工具要求调用它。        此计划应按照问题,进行分解为多个步骤,如果正确补充每个步骤的内容,将得出正确答案。        要求: 1.不要添加任何多余的步骤;2.最后一步的结果应该是最终答案;3.确保每个步骤都有所需的所有信息;4.应该按照顺序不要跳过步骤", "role": "system"}, {"content": "对于`荔枝和苹果哪个甜`这个问题, 请按照'Plan'的要求调用它", "role": "user"}], "model": "glm-4-9b-chat-lora", "n": 1, "parallel_tool_calls": false, "stream": false, "temperature": 0.1, "tool_choice": {"type": "function", "function": {"name": "Plan"}}, "tools": [{"type": "function", "function": {"name": "Plan", "description": "将执行的计划", "parameters": {"properties": {"steps": {"description": "应该要遵循的不同步骤, 且应按顺序排列", "items": {"type": "string"}, "type": "array"}}, "required": ["steps"], "type": "object"}}}]}
// 响应
{"model":"glm-4-9b-chat-lora","id":"chatcmpl-rWp3Gqdyrn64H3RAkdRDbfglhB3wz","object":"chat.completion","choices":[{"index":0,"message":{"role":"assistant","content":null,"function_call":null,"tool_calls":[{"index":0,"id":"call_krmI1fFZhKCusMPrba0e8t8O","function":{"name":"Plan","arguments":"{\"steps\": [\"荔枝和苹果哪个甜\"]}"},"type":"function"}]},"finish_reason":"tool_calls"}],"created":1733275396,"system_fingerprint":"fp_GEqAR5k2G","usage":{"prompt_tokens":186,"total_tokens":199,"completion_tokens":13}}

Beyond just the structure of the Pydantic class, the name of the Pydantic class, the docstring, and the names and provided descriptions of parameters are very important. Most of the time with_structured_output is using a model's function/tool calling API, and you can effectively think of all of this information as being added to the model prompt.

  • json
python 复制代码
from typing_extensions import Annotated, TypedDict

# TypedDict
class Joke(TypedDict):
    """Joke to tell user."""
    setup: Annotated[str, ..., "The setup of the joke"]

    # Alternatively, we could have specified setup as:

    # setup: str                    # no default, no description
    # setup: Annotated[str, ...]    # no default, no description
    # setup: Annotated[str, "foo"]  # default, no description

    punchline: Annotated[str, ..., "The punchline of the joke"]
    rating: Annotated[Optional[int], None, "How funny the joke is, from 1 to 10"]


structured_llm = llm.with_structured_output(Joke)

structured_llm.invoke("Tell me a joke about cats")

Messages

语言模型将消息列表作为输入并返回消息。有几种不同类型的消息。所有消息都具有角色、内容和response_metadata 属性。

in short 模型的一个消息表示

消息类型

https://python.langchain.com/docs/how_to/custom_chat_model/#messages

HumanMessage

This represents a message from the user.

用户端的消息

AIMessage

This represents a message from the model. In addition to the content property

AI响应的消息

SystemMessage

This represents a system message, which tells the model how to behave. Not every model provider supports this.

这代表一条系统消息,它告诉模型如何行为。并非每个模型提供者都支持此功能。

FunctionMessage

This represents the result of a function call. In addition to role and content, this message has a name parameter which conveys the name of the function that was called to produce this result.

表示函数调用的结果。除 rolecontent 外,该信息还有一个 name 参数,用于传递产生该结果的函数名称。

ToolMessag

This represents the result of a tool call. This is distinct from a FunctionMessage in order to match OpenAI's function and tool message types.

表示工具调用的结果。它有别于 FunctionMessage,以便与 OpenAI 的 "function "和 "tool "消息类型相匹配

Memory

https://python.langchain.com/v0.2/docs/how_to/message_history/#what-is-the-runnable-you-are-trying-to-wrap
https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html

python 复制代码
from operator import itemgetter
from typing import List

from langchain_openai.chat_models import ChatOpenAI

from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.documents import Document
from langchain_core.messages import BaseMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.runnables import (
    RunnableLambda,
    ConfigurableFieldSpec,
    RunnablePassthrough,
)
from langchain_core.runnables.history import RunnableWithMessageHistory


class InMemoryHistory(BaseChatMessageHistory, BaseModel):
    """In memory implementation of chat message history."""

    messages: List[BaseMessage] = Field(default_factory=list)

    def add_messages(self, messages: List[BaseMessage]) -> None:
        """Add a list of messages to the store"""
        self.messages.extend(messages)

    def clear(self) -> None:
        self.messages = []

# Here we use a global variable to store the chat message history.
# This will make it easier to inspect it to see the underlying results.
store = {}

def get_by_session_id(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryHistory()
    return store[session_id]


history = get_by_session_id("1")
history.add_message(AIMessage(content="hello"))
print(store)  # noqa: T201

RunnableWithMessageHistory (在上下文中运行

Memory 让AI记住聊天记录, 记住上下文, 具有记忆的能力; 分别有两个关键点: 一是如何聊天记录持久化, 二是当聊天记录太长太多不能无限制的记录, 如何将聊天内容提取为概要, 忽略不必要冗余的记忆, 减少对话的token.

https://python.langchain.com/v0.2/docs/integrations/memory/
langchain.memory

MessageHistory (持久化

LangChain 类型 作用
langchain_community.chat_message_histories.ElasticsearchChatMessageHistory 将聊天记录存储到Elasticsearch
langchain_community.chat_message_histories.SQLChatMessageHistory 将聊天记录存储到SQLite

Summary Message (对话概要抽取

LangChain 类型 作用
ConversationBufferMemory 简单粗暴, 将之前的所有对话放到 Prompt 中 (token爆炸)
ConversationSummaryMemory 将之前对话/或按某种策略 Summary 减少token
ConversationBufferWindowMemory 保留特定特点的对话窗口次数
ConversationTokenBufferMemory 将限制保存的token数量。如果字符数量超出指定数目,它会切掉这个对话的早期部分
ConversationSummaryBufferMemory 使用 LLM 对到目前为止历史对话自动总结摘要

How to store and load messages?

在 langchain langchain_core.runnables.history.RunnableWithMessageHistory 可以将模型运行在有聊天上下文中

python 复制代码
from langchain_community.chat_message_histories import SQLChatMessageHistory
# 获取 '聊天记录' 持久化对象
def get_session_history(session_id):
    return SQLChatMessageHistory(session_id, "sqlite:///memory.db")


# 构建 RunnableWithMessageHistory
from langchain_core.messages import HumanMessage
from langchain_core.runnables.history import RunnableWithMessageHistory
runnable_with_history = RunnableWithMessageHistory(
    model,
    get_session_history,
)


runnable_with_history.invoke(
    [HumanMessage(content="hi - im bob!")],
    config={"configurable": {"session_id": "1"}},
)

# 第二次 询问
runnable_with_history.invoke(  
  [HumanMessage(content="whats my name?")],  
  config={"configurable": {"session_id": "1"}},  
)

Data Connection

Document loaders

These classes load Document objects. LangChain has hundreds of integrations with various data sources to load data from: Slack, Notion, Google Drive, etc.

完整支持的表格: https://python.langchain.com/v0.2/docs/integrations/document_loaders/
in short 文档加载器, 支持 PDF, CVS, Slack, Notion, Google Drive 等等

TextLoader

https://python.langchain.com/v0.2/docs/how_to/vectorstore_retriever/

python 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("state_of_the_union.txt")
## loader
documents = loader.load()
## splitter
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
## embedding vectorstore
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(texts, embeddings)
Splitter 分割方式

https://python.langchain.com/v0.2/api_reference/text_splitters/index.html
https://python.langchain.com/v0.2/api_reference/text_splitters/character.html

  • RecursiveCharacterTextSplitter

    LangChain 中推荐用于通用文本的文本分割器。默认的 separator 是一个列表 ["\n\n", "\n", " ", ""]

    通过不同的符号递归地分割文档,从 "\n\n" 开始,然后是 "\n",再然后是 " ",最后是空字符串 ""

  • CharacterTextSplitter

    CharacterTextSplitter 的 separator 是一个字符串, 默认是 "\n\n"

WebBaseLoader

Load HTML pages using urllib and parse them with `BeautifulSoup'.

python 复制代码
## 文档加载从网络
urls = [
	"https://lilianweng.github.io/posts/2023-06-23-agent/",
	"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/",
	"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/",
]
docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]

## 分割
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
	chunk_size=100, chunk_overlap=50
)
doc_splits = text_splitter.split_documents(docs_list)

Embedding models

Open AI embeddings文档
https://python.langchain.com/v0.2/docs/how_to/#embedding-models
https://python.langchain.com/v0.2/docs/integrations/text_embedding/

The Embeddings class is a class designed for interfacing with text embedding models. There are many different embedding model providers (OpenAI, Cohere, Hugging Face, etc) and local models, and this class is designed to provide a standard interface for all of them.

Embeddings 类是一个用于连接文本嵌入模型的类。有许多不同的嵌入模型提供商(OpenAI、Cohere、Hugging Face 等)和本地模型,该类旨在为它们提供一个标准接口。

The base Embeddings class in LangChain provides two methods: one for embedding documents and one for embedding a query. The former takes as input multiple texts, while the latter takes a single text. The reason for having these as two separate methods is that some embedding providers have different embedding methods for documents (to be searched over) vs queries (the search query itself).

LangChain 中的基础 Embeddings 类提供了两种方法:一种用于嵌入文档,另一种用于嵌入查询。前者将多个文本作为输入,而后者将单个文本作为输入。之所以将这两种方法分开,是因为有些Embedding 0提供商对文档(搜索)和查询(搜索查询本身)有不同的 Embedding 方法。

Hugging Face

pip install langchain-huggingface

https://python.langchain.com/v0.2/docs/how_to/embed_text/
API Reference: HuggingFaceEmbeddings

python 复制代码
from langchain_huggingface import HuggingFaceEmbeddings
embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
embed_documents
python 复制代码
from langchain_huggingface import HuggingFaceEmbeddings
## embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
embeddings_model = HuggingFaceEmbeddings()
embeddings = embeddings_model.embed_documents(  
	[  
	"Hi there!",  
	"Oh, hello!",  
	"What's your name?",  
	"My friends call me World",  
	"Hello World!"  
	]  
)  
len(embeddings), len(embeddings[0])
embed_query

Use .embed_documents to embed a list of strings, recovering a list of embeddings:

python 复制代码
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")  
embedded_query[:5]
embedding txt

注意: 默认的 sentence-transformers/all-mpnet-base-v2 模型对中文支持很差

目前下载最多的是BAAI/bge-large-zh-v1.5 模型

这里可以看全部的embedding模型 Sentence Transformers model

python 复制代码
def embeddingsTest():
    loader = TextLoader("E:\\content-for-work\\2024-05大模型\\wenku.baidu\\荔枝种植\\荔枝百科.txt")
    ## loader
    documents = loader.load()
    ## splitter
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
    texts = text_splitter.split_documents(documents)
    ## print( "splitter[0] = ", texts[0].page_content )
    ## print( "texts len = ", len(texts) )
    
    page_contents = [item.page_content for item in texts]
    print( "page_contents len = ", len(page_contents) )
    #embeddings_model = HuggingFaceEmbeddings()

    ## embedding
    ## embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
    embeddings_model = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
    embeddings = embeddings_model.embed_documents(page_contents)
    print("embeddings len = ", len(embeddings), len(embeddings[0]) )
    
    ## embed_query 查询返回的是 embedding 向量
    query = embeddings_model.embed_query("荔枝")
    print("embed_query ", query )
    similarity_search = embeddings_model.similarity_search("荔枝")
	

Vector stores

https://python.langchain.com/v0.1/docs/modules/data_connection/vectorstores/

Chroma

python 复制代码
def vectorStoreLoadTest():
    loader = TextLoader("E:\\content-for-work\\2024-05大模型\\wenku.baidu\\荔枝种植\\荔枝百科.txt")
    ## loader
    documents = loader.load()
    ## splitter
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0,separators=["\n","。",";","!"])
    texts = text_splitter.split_documents(documents)
    ## embedding 向量存储 Add to vectorDB
    embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
    vectorstore = Chroma.from_documents(documents=texts, embedding=embeddings_model)

    ## 相关性检索
    question = "荔枝是什么?"
    docs = vectorstore.similarity_search(question)
    print("result: ", docs)
    
    ## 向量检索 (没啥区别)
    #question_verctor = embeddings_model.embed_documents( "荔枝是什么?")
    #result = vectorstore.similarity_search_by_vector(question_verctor)
    #print("question_verctor result ", result)
持久化
python 复制代码
## save to disk
db2 = Chroma.from_documents(docs, embedding_function, persist_directory="./chroma_db")
docs = db2.similarity_search(query)

## load from disk
db3 = Chroma(persist_directory="./chroma_db", embedding_function=embedding_function)
docs = db3.similarity_search(query)
print(docs[0].page_content)
实例

索引文档, 本地存储

python 复制代码
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma

from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_text_splitters import RecursiveCharacterTextSplitter
import os
def recursive_walk(directory):
    file_paths = []
    for root, dirs, files in os.walk(directory):
        for name in files:
            if name.endswith(".txt"):
                file_path = os.path.join(root, name)
                ## print(os.path.join(root, name))
                file_paths.append(file_path)
    return file_paths   
def getVectorStore():
    embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
    vectorstore = Chroma(persist_directory="./chroma_lizhi.dat", collection_name="lizhi",embedding_function=embeddings_model)
    return vectorstore

def initVectorStore(rootDir):
    file_paths = recursive_walk(rootDir)
    vectorstore = getVectorStore()
    for path in file_paths:
        loader = TextLoader(path)
        ## loader
        documents = loader.load()
        ## splitter
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0,separators=["\n","。",";"])
        texts = text_splitter.split_documents(documents)
        print(f"索引: {path}, 有 {len(texts)} 个文档")
        ## add to vector store
        ret = vectorstore.add_documents(texts)
        print(f"索引: 结果 {ret}")

if __name__ == '__main__':
	dir = 'E:\\content-for-work\\2024-05大模型\\wenku.baidu\\'
	initVectorStore(dir)
	pass

Retrievers

A retriever is an interface that returns documents given an unstructured query. It is more general than a vector store. A retriever does not need to be able to store documents, only to return (or retrieve) them. Vector stores can be used as the backbone of a retriever, but there are other types of retrievers as well.

https://python.langchain.com/v0.1/docs/modules/data_connection/retrievers/

Chain

https://api.python.langchain.com/en/latest/langchain_api_reference.html#module-langchain.chains

更复杂的应用需要将LLM串联起来--要么相互串联,要么与其他组件串联。这时就需要用到 链(Chain) 组件

Chain 允许我们将多个组件结合在一起,创建一个单一的、连贯的应用程序。例如,我们可以创建一个链,接受用户输入,用PromptTemplate格式化,然后将格式化的响应传递给LLM。我们可以通过将多个链组合在一起,或将链与其他组件组合在一起,建立更复杂的链。

LLMChain 就是最基本的构建块链。它接受一个提示模板,用用户输入的格式化它,并从LLM返回响应。

我们首先要创建一个提示模板:

python 复制代码
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate

llm = OpenAI(temperature=0.9)
prompt = PromptTemplate(
    input_variables=["product"],
    template="What is a good name for a company that makes {product}?",
)

然后,创建一个非常简单的链,它将接受用户的输入,用它来格式化提示,然后将其发送到LLM。

python 复制代码
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)

# Run the chain only specifying the input variable.
print(chain.invoke("colorful socks"))


#out// Colorful Toes Co.
    
'''
如果有多个变量,可以用一个字典一次输入它们。
'''
prompt = PromptTemplate(
    input_variables=["company", "product"],
    template="What is a good name for {company} that makes {product}?",
)
chain = LLMChain(llm=llm, prompt=prompt)
print(chain.invoke({
    'company': "ABC Startup",
    'product': "colorful socks"
    }))

#out// Socktopia Colourful Creations.

官方的一些 chain

LangChain类型 描述
chains.llm_math.base.LLMMathChain 适合解决数学方面的 chain
chains.sequential.SequentialChain 按顺序串行执行chain 的chain
create_sql_query_chain 接受问题并回答SQL(结构化回答)的chain

in short 类似 tensorflow 1 的数据流图, 可以理解 包含固定流程执行的'逻辑段' , 硬编码

Agents & Tools

https://python.langchain.com/v0.2/docs/concepts/#tools
https://api.python.langchain.com/en/latest/langchain_api_reference.html#module-langchain.agents
Open AI文档 - Function Call

Tools

Tools 是 agentchainChat models/LLM可以用来与世界交互的接口。

给定一个可用工具列表和提示,大型语言模型(LLM)可以请求调用一个或多个工具,并附带适当的参数。 重要的是要记住以下几点:

  1. 经过 '工具调用' 微调的聊天模型, 在工具调用方面会比未经过微调的模型表现会更好
  2. 未经微调的模型可能根本无法使用工具,尤其是当工具较为复杂或需要多次工具调用时
  3. 如果工具的名称、描述和JSON模式选择得当,模型的性能会更好
  4. 一般来说,简单的工具比复杂工具更容易被模型使用, so 设计工具应当尽可能的简单, 高度封装

Agent

https://blog.langchain.dev/what-is-an-agent/

"What is an agent?"

I get asked this question almost daily. At LangChain, we build tools to help developers build LLM applications, especially those that act as a reasoning engines and interact with external sources of data and computation. This includes systems that are commonly referred to as "agents".

在 Agents 中,语言模型被用作推理引擎,以确定要采取哪些操作以及按哪个顺序执行, 比Chain更上一级的抽象, Agent 是一个历史遗留的概念, 它会被 LangGraph 逐步替代.

This is a dataclass that represents the action an agent should take. It has a tool property (which is the name of the tool that should be invoked) and a tool_input property (the input to that tool)

  • Agents 可以理解为精心设置好 prompt 模版流程(或者说chain), 引导AI让它提供下一步的操作, so 给 'Agent' 的 'Tools' 的 name 和 description 就非常重要了
  • Tools 可以理解为 '函数', 'Agents' 可以理解为实际(引导)调用 '函数' 的逻辑, 它决定是否tools, 使用哪个tools, 以什么顺序使用tools
  • 这两者的包含关系是: agent 中包含 tools 和 llm(大模型), 使用llm驱动引导使用 tools

Agent 的类型

https://python.langchain.com/v0.1/docs/modules/agents/agent_types/
https://api.python.langchain.com/en/latest/agents/langchain.agents.agent_types.AgentType.html

ReAct agents

https://python.langchain.com/v0.1/docs/modules/agents/agent_types/react/
https://python.langchain.com/v0.2/docs/concepts/#react-agents

langchain.agents.create_react_agent

这种 Agent使用 ReAct(Retrieve-and-Act)框架,该框架通过理解工具的描述来选择最合适的工具执行任务。Zero-shot 意味着Agent不需要针对特定任务进行训练,而是可以基于工具的描述直接进行推断。

One popular architecture for building agents is ReAct. ReAct combines reasoning and acting in an iterative process - in fact the name "ReAct" stands for "Reason" and "Act".

The general flow looks like this:

  • The model will "think" about what step to take in response to an input and any previous observations. (模型将 "思考 "应采取什么步骤来响应输入和之前的观察结果)
  • The model will then choose an action from available tools (or choose to respond to the user).(然后,模型将从可用工具中选择一项操作(或选择对用户做出响应)
  • The model will generate arguments to that tool.(模型将为该工具生成参数)
  • The agent runtime (executor) will parse out the chosen tool and call it with the generated arguments.(代理运行时(执行器)将解析所选工具,并使用生成的参数调用该工具)
  • The executor will return the results of the tool call back to the model as an observation.(执行器会将工具调用的结果作为观察结果返回给模型)
  • This process repeats until the agent chooses to respond.(这一过程不断重复,直到代理选择做出响应)

ReAct 的论文!! https://react-lm.github.io/ ?

Chain 与 Agent

  1. 链 (chain) :围绕LLM编写的程序,用于执行任务,例如自动SQL编写或NER提取链等。请注意,链不能用于任何其他任务(甚至不能用于一般用例),如果尝试这样做,可能会损坏链。**链中遵循的步骤是预定义的,不可灵活调整。
  2. 代理 (Agent):**链的更加灵活版本,代理通常是启用第三方工具(例如谷歌搜索、YouTube)的LLM,由LLM本身决定下一步如何解决给定的查询。
    当处理现实世界的问题时,即不能链(chain)那样硬编码,但也不能像代理(agent)那样完全由LLM驱动

如何优化 Agent 的能力 ?

一个Agent可能无法解决一些问题,可以考虑以下方法:

  • 重新理解问题: 如果效果不佳,可能是模型对提示词的理解与您的期望有偏差。可以让模型解释其理解,然后优化提示词。如果模型对中文理解有歧义,考虑使用英文提示词,有时会有意想不到的效果。
  • 更换模型: 如果即使优化提示词,模型仍无法达到满意效果,可能是模型能力不足。此时可以考虑使用更强大的模型或者专门针对任务进行优化的模型。
  • 多Agent协作: 复杂任务可以分解成多个子任务 "分而治之",每个子任务由一个Agent处理,然后多个Agent协作完成整个任务。例如,一篇文章可以由一个Agent写草稿,另一个Agent润色,第三个Agent起一个吸引人的标题,还可以有专门配图的Agent。
  • 人机协作: 当任务过于复杂,甚至多个Agent协作也不足以完成时,需要人类参与。可以拆分业务成多个步骤,让Agent完成部分,人工进行优化或审核。例如,Agent提供初稿,人工审核、修改等等。

LangGraph

https://python.langchain.com/v0.2/docs/concepts/#agents

\[N_LangGraph\]

By themselves, language models can't take actions - they just output text. A big use case for LangChain is creating agents . Agents are systems that use an LLM as a reasoning engine to determine which actions to take and what the inputs to those actions should be. The results of those actions can then be fed back into the agent and it determine whether more actions are needed, or whether it is okay to finish.

语言模型本身无法采取任何实际动作,只能输出文本。LangChain 的一个重要用例就是创建 agents。agents是使用 LLM 作为推理引擎的系统,用于确定要采取的行动以及这些行动的输入。这些行动的结果可以反馈给代理,由它决定是否需要采取更多行动,或者是否可以结束。
There is a legacy agent concept in LangChain that we are moving towards deprecating

Agent 是一个历史遗留的概念, 它会被 LangGraph 逐步替代.

LangGraph 是LangChain的扩展,专门旨在创建高度可控和可定制的代理。

Tutorials - Get Start

how to guides
官文档-教程

New to LangChain or to LLM app development in general? Read this material to quickly get up and running.

环境准备 (依赖)

安装依赖
https://python.langchain.com/v0.2/docs/contributing/integrations/

sh 复制代码
# langchain 
pip install -U langchain langchain_openai langsmith pandas langchain_experimental matplotlib langgraph langchain_core
pip install -U langsmith

# 第三方, 社区扩展集成
pip install langchain-community
# openai api
pip install langchain-openai
# or
conda install langchain-openai

# anaconda 
conda create -n langchain
activate langchain
# deactivate langchain
# conda remove -n langchain --all
  • langchain-core

    This package contains base abstractions of different components and ways to compose them together. The interfaces for core components like LLMs, vector stores, retrievers and more are defined here. No third party integrations are defined here. The dependencies are kept purposefully very lightweight.

  • Partner packages

    While the long tail of integrations are in langchain-community, we split popular integrations into their own packages (e.g. langchain-openai, langchain-anthropic, etc). This was done in order to improve support for these important integrations.

  • langchain

    The main langchain package contains chains, agents, and retrieval strategies that make up an application's cognitive architecture. These are NOT third party integrations. All chains, agents, and retrieval strategies here are NOT specific to any one integration, but rather generic across all integrations.

  • langchain-community

    This package contains third party integrations that are maintained(维护) by the LangChain community. Key partner packages are separated out (see below). This contains all integrations for various components (LLMs, vector stores, retrievers). All dependencies in this package are optional to keep the package as lightweight as possible.

  • langgraph
    langgraph is an extension of langchain aimed at building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph.

    LangGraph exposes high level interfaces for creating common types of agents, as well as a low-level API for composing custom flows.

  • langserve

    A package to deploy LangChain chains as REST APIs. Makes it easy to get a production ready API up and running.

  • LangSmith

    A developer platform that lets you debug, test, evaluate, and monitor LLM applications.

Simple LLM + Ollama

Ollama 见 [[Ollama]]
代码, 待测

python 复制代码
from langchain_community.llms import Ollama  

def entry(): 
   llava = Ollama(
            base_url='http://172.16.21.155:11434/',
            # api_key='ollama', # 必须但是会忽略
            model="llava:7b" # http://172.16.21.155:11434/v1/models/
       )
   llava.invoke("李白是谁")
   pass

if __name__ == '__main__':
    print(__version__)
    entry()

Simple LLM + QWEN

OpenAI的接口兼容

https://help.aliyun.com/zh/dashscope/developer-reference/compatibility-of-openai-with-dashscope/?spm=a2c4g.11186623.0.i1

http 复制代码
https://dashscope.aliyuncs.com/compatible-mode/v1
模型分类 模型名称
通义千问 qwen-turbo qwen-plus qwen-max qwen-max-0428 qwen-max-0403 qwen-max-0107 qwen-max-longcontext
代码
python 复制代码
import os
from langchain import __version__
# 需安装 pip install langchain-openai
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

def entry(): 
   os.environ["OPENAI_API_KEY"] = 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
   llm_model = ChatOpenAI(model="qwen-turbo",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
   
   prompt = PromptTemplate(
      input_variables=["name"],
      template="{name} 是谁?",
   )
   chain = LLMChain(llm=llm_model, prompt=prompt)

   print(chain.invoke("李白"))
   pass

if __name__ == '__main__':
    entry()

最终发送请求 \openai\_base_client.py

Simple Chat Model + QWEN

langchain-bailian 模块

shell 复制代码
pip install langchain-bailian
pip install broadscope_bailian
代码
python 复制代码
import os

from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain_bailian import Bailian


def test_bailian_llm():
    """ 测试基础llm功能 """
    access_key_id = os.environ.get("ACCESS_KEY_ID")
    access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
    agent_key = os.environ.get("AGENT_KEY")
    app_id = os.environ.get("APP_ID")



    llm = Bailian(access_key_id=access_key_id,
                  access_key_secret=access_key_secret,
                  agent_key=agent_key,
                  app_id=app_id)
    out = llm("1+1=?")
    print(out)


def test_conversation_chain():
    """ 测试包含memory的chain """

    access_key_id = os.environ.get("ACCESS_KEY_ID")
    access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
    agent_key = os.environ.get("AGENT_KEY")
    app_id = os.environ.get("APP_ID")

    llm = Bailian(access_key_id=access_key_id,
                  access_key_secret=access_key_secret,
                  agent_key=agent_key,
                  app_id=app_id)

    memory = ConversationBufferMemory()
    conversant_chain = ConversationChain(memory=memory, llm=llm,
                                         verbose=True)

    conversant_chain.run("我想明天去北京")
    out = conversant_chain.run("那边有什么旅游景点吗")

    print(out)




if __name__ == '__main__':
    # os.environ["ACCESS_KEY_ID"] = 'LTXXXXXXXXXXXXXxjp'
    # os.environ["ACCESS_KEY_SECRET"] = 'LTXXXXXXXXXXXXXXXXXXXXXXXXxjp'
    os.environ["ACCESS_KEY_ID"] = 'LTAI5t6Dxxxxxxxxxxxxxx'
    os.environ["ACCESS_KEY_SECRET"] = '0uTF9xxxxxxxxxxxx'
    os.environ["AGENT_KEY"] = '0xxxxxxxxxxxxxxx_p_efm'
    os.environ["APP_ID"] = 'cb5113a7xxxxxxxxxxxxxxxxxxxxx'
    # test_bailian_llm()
    test_conversant_memory()

Simple Graph + QWEN

https://langchain-ai.github.io/langgraph/tutorials/customer-support/customer-support/#prerequisites
https://langchain-ai.github.io/langgraph/#example

python 复制代码
'''
Author: yangfh
Date: 2024-07-13 13
LastEditors: yangfh
LastEditTime: 2024-07-23 14
Description: 


参考官方教程文档
https://langchain-ai.github.io/langgraph/#step-by-step-breakdown

通义千问
https://help.aliyun.com/zh/dashscope/developer-reference/compatibility-of-openai-with-dashscope/?spm=a2c4g.11186623.0.i1
'''

import os
from langchain_openai import ChatOpenAI

from typing import Annotated, Literal, TypedDict

from langchain_anthropic import ChatAnthropic
from langchain_core.tools import tool
# from langgraph.checkpoint import MemorySaver 版本变更
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode

from langchain_core.messages import (
    BaseMessage,
    HumanMessage,SystemMessage,AIMessage, ToolMessage,
)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder


############################################### LLM 模型 定义 #######################################################
# LLM 模型 qwen-turbo 模型
def get_llm(): 
   os.environ["OPENAI_API_KEY"] = 'sk-f54d63230ee14b8d958eb2ab172f8367'
   llm_model = ChatOpenAI(model="qwen-turbo",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
   return llm_model

############################################### tools 定义 #######################################################

from Tools_Basic import tool_get_list
from langgraph.prebuilt import ToolNode

def tool_nodes():
    tools = [tool_get_list]
    tool_node = ToolNode(tools)
    return tool_node

############################################### Agent 定义 #######################################################
"""
通用创建 agent 方法.
"""
def create_agent(llm, tools, system_message: str):
    """Create an agent."""
    prompt = ChatPromptTemplate.from_messages(
        [
            (
               "system",
               ""XXX云平台"是一个以云为基础、AI为核心,构建开放、立体感知、全域协同、精确判断和持续进化的综合服务平台。它具有以下功能:"
                  "1. `基地管理` 基地数据包括:基地ID,基地名称,基地地址"
               "如果您无法完全回答, 没关系,您可以使用以下工具:{tool_names}。\n{system_message},尽您所能的配合取得结果。",
            ),
            MessagesPlaceholder(variable_name="messages"),
        ]
    )
    # 填充 PromptTemplate 参数
    prompt = prompt.partial(system_message=system_message)
    prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools]))
    return prompt | llm.bind_tools(tools)

def create_baisc_expert_agent():
    llm = get_llm()
    tools_ = [tool_get_list]
    return create_agent(llm, tools_, "")

g_baisc_expert_agent = create_baisc_expert_agent()
def call_baisc_expert_agent(state: MessagesState):
    messages = state['messages']
    response = g_baisc_expert_agent.invoke(messages)
    # We return a list, because this will get added to the existing list
    return {"messages": [response]}



############################################### Graph 定义 #######################################################
# `Graph` 状态更新函数 (路由)
# Define the function that determines whether to continue or not
def should_continue(state: MessagesState) -> Literal["tools", "__end__"]:
    messages = state['messages']
    # 拿到最新一条消息
    last_message = messages[-1]
    # 如果是 tools 消息则返回 "tools", 给 tools 节点执行
    if last_message.tool_calls:
        return "tools"
    # Otherwise, we stop (reply to the user)
    return END


###############################################  Graph 的节点

# agent 节点
# call_baisc_expert_agent 见上 初始化

# tool 节点
tool_nodes_ = tool_nodes()

# 创建图 `Graph`
workflow = StateGraph(MessagesState)

# `Graph` 图中的所有循环节点
# Define the two nodes we will cycle between
workflow.add_node("agent", call_baisc_expert_agent)
workflow.add_node("tools",tool_nodes_ )

# 设置入口点: 这个节点会第一个调用
workflow.set_entry_point("agent")

###############################################  Graph 的边缘条件

# 添加边缘条件?  意思是在"agent" 节点执行完之后, 调用 "should_continue" 函数, 它将确定调用下一个的哪个'节点'; 
# 比如 Agent调用tool后, 把结果返回给对应的Agent; 还可以加一个入参, 映射字典表, 见官方API
# We now add a conditional edge
workflow.add_conditional_edges(
    # First, we define the start node. We use `agent`.
    # This means these are the edges taken after the `agent` node is called.
    "agent",
    # Next, we pass in the function that will determine which node is called next.
    should_continue,
)

#  调用 `tools` 节点 后会调用`agent`节点;
#  这里是因为只有两个节点,可以硬编码. 意思是 `tools` 节点后再调用回`agent`
workflow.add_edge("tools", 'agent')


###############################################  Graph 的持久化策略 
# 持久化策略: 保存到内存
# Initialize memory to persist state between graph runs
checkpointer = MemorySaver()


# 编译 `graph`
app = workflow.compile(checkpointer=checkpointer)


############################################### Runnable  #######################################################
# Use the Runnable 
final_state = app.invoke(
    {"messages": [HumanMessage(content="我有多少个基地?")]},
    config={"configurable": {"thread_id": 44}}# 相当于会话ID; 会保存 graph 的 状态
)

print("graph final_state = ",final_state)
final_state["messages"][-1].content # graph 结束后, 拿到最后一条消息

request 参数解释

参数名 推荐值 简介 定义
temperature 0.95 控制生成内容的随机性,值越大越随机,多样性越好 这个参数控制着生成的随机性。较高的温度值(如 1.2)会增加文本的多样性和创造性,但可能会牺牲一些准确性或连贯性。具体地,temperature 会调整概率输出的softmax概率分布,如果 temperature 的值为1,则没有任何调整;如果其值比1大,则会生成更加随机的文本;如果其值比1小,则生成的文本更加保守。
top_p 0.95 单步累计采用阈值,越大越多token会被考虑 如果累计概率已经超过0.95,剩下的token不会被考虑例如有下面的token及其概率,a:0.9,b:0.03,c:0.03,d:0.015,e... 。则只会采用用abc,因为已经是0.96超过了0.95
top_k 50 单步采用token的数量,越大采用token会越多 单步中最多考虑的token数量
max_length 512 最大采样长度 模型生成的文本最大长度,超过的话会做截断,512是参考值,这个依赖于实际情况自己设置
num_beams 1 beam搜索数量,越大文本质量越高 想象一棵树,这个树在每一层的叶子节点数量都是num_beams个,正常模型推理时设置成1就行啦;num_beams=20 表示在每一步时,模型会保留20个最有可能的候选序列,保留方式是累计概率乘积。这有助于生成更加精确和高质量的文本。
do_sample False 是否概率采样token得到结果 当设置为 False 时,模型在生成文本时不会随机采样,而是选择最可能的下一个词。这使得生成的文本更加确定和一致。
num_beam_groups 1 分成num_beam_groups组进行搜索 这个参数与束搜索相关。它将搜索的束分为不同的组,每个组内部进行搜索。这可以增加文本的多样性。num_beam_groups包含num_beams
num_return_sequences 1 有多少条返回的结果 推理的话设成1就好了
output_scores True 调试实验时用到 设为True时模型在生成文本的每一步都会输出每个词的分数(或概率),这有助于了解模型是如何在不同选项中做出选择的。
repetition_penalty 1 重复惩罚值,越大越不会生成重复token 默认值为1.0,其中较高的值意味着更强的惩罚,生成的文本中将出现更少的重复。如果取值为0,则没有惩罚,生成的文本可能包含大量重复的内容。
max_new_tokens 256 模型生成的最大新词数 在这里设置为256,意味着每次生成的文本最多包含256个新词。
diversity_penalty 1.5 当使用多束搜索时,这个参数惩罚那些在不同束中过于相似的词,以提高生成文本的多样性。 设置为1.5意味着对相似性施加较大的惩罚。如果在同一个step中某个beam生成的词和其他beam有相同的,那么就减去这个值作为惩罚,仅在 num_beam_groups 启用时这个值才有效
length_penalty 1 beam search分数会受到生成序列长度的惩罚 length_penalty=0.0:无惩罚、length_penalty<0.0:鼓励模型生成长句子、length_penalty>0.0:鼓励模型生成短句子
eos_token_id - 指定搜索时的结束token 有时可以提升模型性能,例如同时指定和为结束符可以让模型在出现时也结束,防止模型停不下来
bad_words_ids - 禁止生成的token 帮助解决伦理安全、种族歧视等问题
prefix_allowed_tokens_fn - 约束模型只能在给定的tokens里生成token 帮助特定功能的模型提升性能

开发实例

https://python.langchain.com/v0.2/docs/concepts/

解决复杂的任务 (多 Agent 协作)

参考 Basic Multi-agent Collaboration(LangGraph)

依赖的package

sh 复制代码
pip install -U langchain langchain_openai langsmith pandas langchain_experimental matplotlib langgraph langchain_core

拆分任务 分而治之

相对于目前的AI水平。。 人类自然语言表达出来的问题绝大部分都是属于复杂任务。。将复杂的任务分解成多个子任务,每个子任务可以由一个专门的智能体处理 "分而治之", 多个智能体可以并行处理 , 单独调教

This notebook (inspired by(受到启发) the paper AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation, by Wu, et. al.) shows one way to do this using LangGraph.
通过"分而治之"的方法:为每个任务或领域创建一个专门的代理,并将任务路由"Router" 到正确的"专家"。

逻辑图: 两个Agent 相互配合工作

  1. 用户的输入内容, 来到 "Researcher" 角色(Agent)
  2. "Researcher" LLM 响应消息, 将消息给到 'Router'
  3. 'Router' 解析(包括不限于"Researcher")的消息内容, 具体去执行 'call_tool' 或者 'code eval'(可以再给Agent)
  4. 'Router' 类似一个状态机循环, 如果遇到最终消息 (FINAL ANSWER) 则退出 返回最终结果

自定义 tool (custom_tools)

https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/

When constructing your own agent, you will need to provide it with a list of Tools that it can use. Besides the actual function that is called, the Tool consists of several components:

  • name (str), is required and must be unique within a set of tools provided to an agent
  • description (str), is optional but recommended, as it is used by an agent to determine tool use
  • args_schema (Pydantic BaseModel), is optional but recommended, can be used to provide more information (e.g., few-shot examples) or validation for expected parameters.
python 复制代码
# Import things that are needed generically
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from typing import Optional, Type

@Tool 装饰器

https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/#tool-decorator

pydantic 库的用法: https://docs.pydantic.dev/latest/why/#type-hints

这个 langchain_core.tools#tool 装饰器做了好多工作

python 复制代码
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two numbers."""
    return a * b
    
print(multiply.name)  
print(multiply.description)  
print(multiply.args)
json 复制代码
multiply
multiply(a: int, b: int) -> int - Multiply two numbers.
{'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}

You can also customize the tool name and JSON args by passing them into the tool decorator.

python 复制代码
class SearchInput(BaseModel):
    query: str = Field(description="should be a search query")


@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
    """Look up things online."""
    return "LangChain"

print(search.name)
print(search.description)
print(search.args)
print(search.return_direct)
json 复制代码
search-tool
search-tool(query: str) -> str - Look up things online.
{'query': {'title': 'Query', 'description': 'should be a search query', 'type': 'string'}}
True

Subclass BaseTool

https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/#subclass-basetool

您还可以通过 继承 BaseTool 类来显式定义自定义工具。这提供了对工具定义的最大控制

python 复制代码
# Import things that are needed generically
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool


from typing import Optional, Type

from langchain.callbacks.manager import (
    AsyncCallbackManagerForToolRun,
    CallbackManagerForToolRun,
)


class SearchInput(BaseModel):
    query: str = Field(description="should be a search query")


class CalculatorInput(BaseModel):
    a: int = Field(description="first number")
    b: int = Field(description="second number")


class CustomSearchTool(BaseTool):
    name = "custom_search"
    description = "useful for when you need to answer questions about current events"
    args_schema: Type[BaseModel] = SearchInput

    def _run(
        self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        """Use the tool."""
        return "LangChain"

    async def _arun(
        self, query: str, run_manager: Optional[AsyncCallbackManagerForToolRun] = None
    ) -> str:
        """Use the tool asynchronously."""
        raise NotImplementedError("custom_search does not support async")


class CustomCalculatorTool(BaseTool):
    name = "Calculator"
    description = "useful for when you need to answer questions about math"
    args_schema: Type[BaseModel] = CalculatorInput
    return_direct: bool = True

    def _run(
        self, a: int, b: int, run_manager: Optional[CallbackManagerForToolRun] = None
    ) -> str:
        """Use the tool."""
        return a * b

    async def _arun(
        self,
        a: int,
        b: int,
        run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
    ) -> str:
        """Use the tool asynchronously."""
        raise NotImplementedError("Calculator does not support async")
python 复制代码
search = CustomSearchTool()
print(search.name)
print(search.description)
print(search.args)
json 复制代码
custom_search
useful for when you need to answer questions about current events
{'query': {'title': 'Query', 'description': 'should be a search query', 'type': 'string'}}
python 复制代码
multiply = CustomCalculatorTool()
print(multiply.name)
print(multiply.description)
print(multiply.args)
print(multiply.return_direct)
json 复制代码
Calculator
useful for when you need to answer questions about math
{'a': {'title': 'A', 'description': 'first number', 'type': 'integer'}, 'b': {'title': 'B', 'description': 'second number', 'type': 'integer'}}
True

StructuredTool dataclass

https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/#structuredtool-dataclass

python 复制代码
def search_function(query: str):
"""
这个一个web搜索工具
"""

    return "LangChain"

class search_arg(query: str):
	Field(name="query" )

search = StructuredTool.from_function(
    func=search_function, search_arg
    name="Search",
    description="useful for when you need to answer questions about current events",
    # coroutine= ... <- you can specify an async method if desired as well
)

print(search.name)  
print(search.description)  
print(search.args)
json 复制代码
Search
Search(query: str) - useful for when you need to answer questions about current events
{'query': {'title': 'Query', 'type': 'string'}}

Create tool from a given function.

A classmethod that helps to create a tool from a function.

Args:

func: The function from which to create a tool

coroutine: The async function from which to create a tool

name: The name of the tool. Defaults to the function name

description: The description of the tool. Defaults to the function docstring

return_direct: Whether to return the result directly or as a callback

args_schema: The schema of the tool's input arguments

infer_schema: Whether to infer the schema from the function's signature

response_format: The tool response format. If "content" then the output of

the tool is interpreted as the contents of a ToolMessage. If

"content_and_artifact" then the output is expected to be a two-tuple

corresponding to the (content, artifact) of a ToolMessage.

parse_docstring: if infer_schema and parse_docstring, will attempt

to parse parameter descriptions from Google Style function docstrings.

error_on_invalid_docstring: if parse_docstring is provided, configures

whether to raise ValueError on invalid Google Style docstrings.

**kwargs: Additional arguments to pass to the tool

Returns:

The tool

Examples:

python 复制代码
def add(a: int, b: int) -> int:
	\"\"\"Add two numbers\"\"\"
	return a + b
tool = StructuredTool.from_function(add)
tool.run(1, 2) # 3

ToolNode

https://github.com/webup/notebooks/blob/main/langgraph-tool-node.ipynb

使用 ToolNode 调用工具

ToolNode 是一个 LangChain Runnable 对象,它以图的状态(包含消息列表)作为输入,并输出带有工具调用结果的更新状态。

它设计的目的是与 LangGraph 预建的 ReAct Agent 良好配合,但也可以与任何 StateGraph 配合工作,只要其状态有一个带有适当的 messages 键(见 MessagesState )。

错误处理 (Handling Tool Errors)

python 复制代码
from langchain_core.tools import ToolException

def search_tool1(s: str):
    raise ToolException("The search tool1 is not available.")

LangSmith

\[N_LangChain\]

\[N_LangGraph\]

https://docs.smith.langchain.com/

Get started with LangSmith

1. Install LangSmith

pip install -U langsmith

2. Create an API key

To create an API key head to the Settings page. Then click Create API Key.

3. 代码

python 复制代码
import os
from langchain_openai import ChatOpenAI

from typing import Annotated, Literal, Sequence, TypedDict

from langchain_core.tools import tool
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.prebuilt import ToolNode

from langchain import hub
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langgraph.prebuilt import tools_condition

from langchain.chains.llm import LLMChain

# langsmith
from langsmith.wrappers import wrap_openai
from langsmith import traceable


# langsmith 配置环境 (在线且收费的)
os.environ["LANGCHAIN_TRACING_V2"]="true"
os.environ["LANGCHAIN_API_KEY"]="lsv2_pt_xxxxxxxxxxxxxxxxxxxxxxx_xxxxxxx"



def get_llm(): 
   os.environ["OPENAI_API_KEY"] = 'sk-xxxxxxxxxxxxxxxxxxxxxxx'
   llm_model = ChatOpenAI(model="qwen-turbo",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", streaming=True)
# 本地
#    os.environ["OPENAI_API_KEY"] = 'EMPTY'
#    llm_model = ChatOpenAI(model="chatglm3-6b",base_url="http://172.16.21.155:8000/v1/chat/completions", streaming=False)
   return llm_model


llm_model = get_llm()
prompt = PromptTemplate(
  input_variables=["name"],
  template="{name}是谁?"
)
chain = LLMChain(llm=llm_model, prompt=prompt)
output = chain.invoke("李白")
print(output)

需要配置key, 在线还且收费..

踩坑指南

设置全局日志级别

python 复制代码
import logging
logging.basicConfig(level=logging.DEBUG)

调试底层请求入口点

C:\Users\yang\AppData\Local\Programs\Python\Python312\Lib\site-packages\openai\_base_client.py

python 复制代码
formatted_headers = []
for header in request.headers.raw:
    header_key, header_value = header
    formatted_header = f"{header_key.decode('utf-8').lower()}: {header_value.decode('utf-8')}"
    formatted_headers.append(formatted_header)
    print(formatted_header)
    
    
request.content.decode('utf-8')

新版本特点

LangChain v0.2

The following features have been added during the development of 0.1.x:

LangChain v0.3

https://python.langchain.com/docs/versions/v0_3/

The following features have been added during the development of 0.2.x:

  • Moved more integrations from langchain-community to their own langchain-x packages. This is a non-breaking change, as the legacy implementations are left in langchain-community and marked as deprecated. This allows us to better manage the dependencies of, test, and version these integrations. You can see all the latest integration packages in the API reference.
  • Simplified tool definition and usage. Read more here.
  • Added utilities for interacting with chat models: universal model constructor, rate limiter, message utilities,
  • Added the ability to dispatch custom events.
  • Revamped integration docs and API reference. Read more here.
  • Marked as deprecated a number of legacy chains and added migration guides for all of them. These are slated for removal in langchain 1.0.0. See the deprecated chains and associated migration guides here.
out 复制代码
pip install --upgrade

pip install -U langchain langchain_openai langsmith pandas langchain_experimental matplotlib langgraph langchain_core --proxy=""

# 第三方, 社区扩展集成
pip install langchain-community

# openai
pip install langchain-openai==0.2.0

# langserver
pip install "langserve[all]"

pip freeze > requirements.txt
相关推荐
JJ1M81 小时前
用 Python 快速搭建一个支持 HTTPS、CORS 和断点续传的文件服务器
服务器·python·https
汤姆yu2 小时前
基于python大数据的小说数据可视化及预测系统
大数据·python·信息可视化
x***J3482 小时前
Python多线程爬虫
开发语言·爬虫·python
m***D2862 小时前
Python网络爬虫实战案例
开发语言·爬虫·python
ID_180079054732 小时前
基于 Python 的淘宝商品详情数据结构化解析:SKU、价格与库存字段提取
开发语言·数据结构·python
Laughtin3 小时前
终端Python环境的选择与切换
开发语言·python
JHC0000003 小时前
Python PDF 相关操作
开发语言·python·pdf
databook3 小时前
Manim进阶:用背景图片让你的数学视频脱颖而出
python·动效
温轻舟3 小时前
Python自动办公工具01-Excel文件编辑器
开发语言·python·编辑器·excel·温轻舟