LangChain 基礎

一、LangChain 模塊和體系

LangChain 是一個用于開發由大型語言模型(LLMs)驅動的應用程序的框架。

官方文檔:https://python.langchain.com/docs/introduction/

LangChain 簡化了LLM應用程序生命周期的每個階段:

  • 開發:使用LangChain的開源構建模塊和組件構建您的應用程序。利用第三方集成和模板快速啟動。
  • 生產部署:使用LangSmith檢查、監控和評估您的鏈,以便您可以持續優化并自信地部署。
  • 部署:使用LangServe將任何鏈轉換為API。

在這里插入圖片描述

具體而言,該框架包括以下開源庫:

  • langchain-core:基本抽象和LangChain表達語言。
  • langchain-community:第三方集成。
    • 合作伙伴包(例如 langchain-openailangchain-anthropic 等):一些集成已進一步拆分為僅依賴于 langchain-core 的輕量級包。
  • langchain:構成應用程序認知架構的鏈、代理和檢索策略。
  • langgraph:通過將步驟建模為圖中的邊緣和節點,使用LLMs構建穩健且有狀態的多參與者應用程序。
  • langserve:將LangChain鏈部署為REST API。
  • LangSmith:一個開發平臺,可讓您調試、測試、評估和監控LLM應用程序。

LLM & Chat models PromptTemplates, OutputParses Chains

LLMs

將字符串作為輸入并返回字符串的語言模型。 這些通常是較舊的模型(較新的模型通常是 ChatModels,見上文)。 盡管底層模型是字符串輸入、字符串輸出,LangChain 封裝器還允許這些模型接受消息作為輸入。 這使它們可以與 ChatModels 互換使用。 當消息作為輸入傳入時,它們將在傳遞給底層模型之前在內部格式化為字符串。 LangChain 不提供任何 LLMs,而是依賴于第三方集成。

Messages(消息)

一些語言模型將消息列表作為輸入并返回消息。 有幾種不同類型的消息。 所有消息都有 rolecontentresponse_metadata 屬性。 role 描述了消息的發出者是誰。 LangChain 為不同的角色設計了不同的消息類。 content 屬性描述了消息的內容。 這可以是幾種不同的內容:

  • 一個字符串(大多數模型處理這種類型的內容)
  • 一個字典列表(用于多模態輸入,其中字典包含有關該輸入類型和該輸入位置的信息)
HumanMessage

這代表用戶發送的消息。

AIMessage

這代表模型發送的消息。除了 content 屬性外,這些消息還有: response_metadata response_metadata 屬性包含有關響應的其他元數據。這里的數據通常針對每個模型提供者具體化。 這是存儲對數概率和標記使用等信息的地方。 tool_calls 這些表示語言模型調用工具的決定。它們作為 AIMessage 輸出的一部分包含在內。 可以通過 .tool_calls 屬性從中訪問。 此屬性返回一個字典列表。每個字典具有以下鍵:

  • name:應調用的工具的名稱。
  • arg:該工具的參數。
  • id:該工具調用的 id。
SystemMessage

這代表系統消息,告訴模型如何行為。并非每個模型提供者都支持這一點。

FunctionMessage

這代表函數調用的結果。除了 rolecontent,此消息還有一個 name 參數,傳達了生成此結果所調用的函數的名稱。

ToolMessage

這代表工具調用的結果。這與 FunctionMessage 不同,以匹配 OpenAI 的 functiontool 消息類型。除了 rolecontent,此消息還有一個 tool_call_id 參數,傳達了調用生成此結果的工具的 id。

Prompt templates(提示模板)

提示模板有助于將用戶輸入和參數轉換為語言模型的指令。 這可用于引導模型的響應,幫助其理解上下文并生成相關和連貫的基于語言的輸出。 提示模板以字典作為輸入,其中每個鍵代表要填充的提示模板中的變量。 提示模板輸出一個 PromptValue。這個 PromptValue 可以傳遞給 LLM 或 ChatModel,并且還可以轉換為字符串或消息列表。 存在 PromptValue 的原因是為了方便在字符串和消息之間切換。 有幾種不同類型的提示模板

String PromptTemplates

這些提示模板用于格式化單個字符串,通常用于更簡單的輸入。 例如,構建和使用 PromptTemplate 的常見方法如下:

from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")
prompt_template.invoke({"topic": "cats"})
ChatPromptTemplates

這些提示模板用于格式化消息列表。這些“模板”本身是模板列表。 例如,構建和使用 ChatPromptTemplate 的常見方法如下:

from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate.from_messages([("system", "You are a helpful assistant"),("user", "Tell me a joke about {topic}")
])
prompt_template.invoke({"topic": "cats"})

在上面的示例中,當調用此 ChatPromptTemplate 時,將構建兩條消息。 第一條是系統消息,沒有要格式化的變量。 第二條是 HumanMessage,并將根據用戶傳入的 topic 變量進行格式化。

MessagesPlaceholder

這個提示模板負責在特定位置添加消息列表。 在上面的 ChatPromptTemplate 中,我們看到了如何格式化兩條消息,每條消息都是一個字符串。 但是,如果我們希望用戶傳入一個消息列表,我們將其插入到特定位置,該怎么辦? 這就是您使用 MessagesPlaceholder 的方式。

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!")]})

這將生成兩條消息,第一條是系統消息,第二條是我們傳入的 HumanMessage。 如果我們傳入了 5 條消息,那么總共會生成 6 條消息(系統消息加上傳入的 5 條消息)。 這對于將一系列消息插入到特定位置非常有用。 另一種實現相同效果的替代方法是,不直接使用 MessagesPlaceholder 類,而是:

prompt_template = ChatPromptTemplate.from_messages([("system", "You are a helpful assistant"),("placeholder", "{msgs}") # <-- 這是更改的部分
])

Output parsers(輸出解析器)

這里提到的是將模型的文本輸出進行解析,轉換為更結構化表示的解析器。 越來越多的模型支持函數(或工具)調用,可以自動處理這一過程。 建議使用函數/工具調用,而不是輸出解析。

負責接收模型的輸出并將其轉換為更適合下游任務的格式。 在使用LLMs生成結構化數據或規范化聊天模型和LLMs的輸出時非常有用。 LangChain有許多不同類型的輸出解析器。下表列出了LangChain支持的各種輸出解析器及相關信息:

名稱:輸出解析器的名稱

支持流式處理:輸出解析器是否支持流式處理

具有格式說明:輸出解析器是否具有格式說明。通常是可用的,除非在提示中未指定所需模式,而是在其他參數中指定(如OpenAI函數調用),或者當OutputParser包裝另一個OutputParser時。

調用LLM:此輸出解析器是否調用LLM。通常只有嘗試糾正格式不正確的輸出的輸出解析器才會這樣做。 輸入類型:預期的輸入類型。大多數輸出解析器適用于字符串和消息,但有些(如OpenAI函數)需要具有特定kwargs的消息。

輸出類型:解析器返回的對象的輸出類型。

描述:我們對此輸出解析器的評論以及何時使用它的說明。

示例代碼

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParsermodel = ChatOpenAI(model="gpt-4")messages = [SystemMessage(content="將以下內容從英語翻譯成中文"),HumanMessage(content="It's a nice day today"),
]
parser = StrOutputParser()
result = model.invoke(messages)
#使用parser處理model返回的結果
response = parser.invoke(result)
print(response)
#今天天氣很好

Chains(鏈式調用)

Chains 是 LangChain 中用于將多個步驟組合成一個工作流程的模塊。它們允許你定義一系列操作,并將它們鏈接在一起。比如在這個Chain中,每次都會調用輸出解析器。這個鏈條的輸入類型是語言模型的輸出(字符串或消息列表),輸出類型是輸出解析器的輸出(字符串)。

我們可以使用 | 運算符輕松創建這個Chain。| 運算符在 LangChain 中用于將兩個元素組合在一起。

如果我們現在看一下 LangSmith,我們會發現這個鏈條有兩個步驟:首先調用語言模型,然后將其結果傳遞給輸出解析器。我們可以在 LangSmith 跟蹤 中看到這一點。

https://smith.langchain.com/public/f1bdf656-2739-42f7-ac7f-0f1dd712322f/r

示例代碼

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParsermodel = ChatOpenAI(model="gpt-4")messages = [SystemMessage(content="將以下內容從英語翻譯成中文"),HumanMessage(content="Let's go for a run"),
]
parser = StrOutputParser()# 使用Chains方式調用
chain = model | parser  #等于 model.invoke() + parser.invoke()
response = chain.invoke(messages)
print(response)
#我們去跑步吧

LCEL & Runable interface

LCEL 英文全稱 LangChain Execution Language(LangChain 表達語言) 是一種聲明性的方式來鏈接 LangChain 組件。 LCEL 從第一天起就被設計為支持將原型投入生產,無需更改代碼,從最簡單的“提示 + LLM”鏈到最復雜的鏈(我們已經看到有人成功地在生產中運行了包含數百步的 LCEL 鏈)。以下是您可能想要使用 LCEL 的一些原因的幾個亮點:

一流的流式支持 當您使用 LCEL 構建鏈時,您將獲得可能的最佳時間到第一個標記(直到輸出的第一塊內容出現所經過的時間)。對于某些鏈,這意味著我們直接從 LLM 流式傳輸標記到流式輸出解析器,您將以與 LLM 提供程序輸出原始標記的速率相同的速度獲得解析的增量輸出塊。

異步支持 使用 LCEL 構建的任何鏈都可以使用同步 API(例如,在您的 Jupyter 筆記本中進行原型設計)以及異步 API(例如,在 LangServe 服務器中)進行調用。這使得可以在原型和生產中使用相同的代碼,具有出色的性能,并且能夠在同一服務器中處理許多并發請求。

優化的并行執行 每當您的 LCEL 鏈具有可以并行執行的步驟時(例如,如果您從多個檢索器中獲取文檔),我們會自動執行,無論是在同步接口還是異步接口中,以獲得可能的最小延遲。

重試和回退 為 LCEL 鏈的任何部分配置重試和回退。這是使您的鏈在規模上更可靠的好方法。我們目前正在努力為重試/回退添加流式支持,這樣您就可以獲得額外的可靠性而無需任何延遲成本。

訪問中間結果 對于更復雜的鏈,訪問中間步驟的結果通常非常有用,即使在生成最終輸出之前。這可以用于讓最終用戶知道正在發生的事情,甚至只是用于調試您的鏈。您可以流式傳輸中間結果,并且在每個 LangServe 服務器上都可以使用。

輸入和輸出模式 輸入和輸出模式為每個 LCEL 鏈提供了從鏈的結構推斷出的 Pydantic 和 JSONSchema 模式。這可用于驗證輸入和輸出,并且是 LangServe 的一個組成部分。

無縫 LangSmith 追蹤 隨著您的鏈變得越來越復雜,準確理解每一步發生的事情變得越來越重要。 使用 LCEL,所有步驟都會自動記錄到 LangSmith 中,以實現最大的可觀察性和可調試性。

無縫 LangServe 部署 使用 LCEL 創建的任何鏈都可以輕松地通過 LangServe 部署。

Runable interface(可運行接口)

為了盡可能簡化創建自定義鏈的過程,我們實現了一個 “Runnable” 協議。許多 LangChain 組件都實現了 Runnable 協議,包括聊天模型、LLMs、輸出解析器、檢索器、提示模板等等。此外,還有一些有用的基本組件可用于處理可運行對象,您可以在下面了解更多。 這是一個標準接口,可以輕松定義自定義鏈,并以標準方式調用它們。 標準接口包括:

  • stream: 返回響應的數據塊
  • invoke: 對輸入調用鏈
  • batch: 對輸入列表調用鏈

這些還有相應的異步方法,應該與 asyncio 一起使用 awai 語法以實現并發:

  • astream: 異步返回響應的數據塊
  • ainvoke: 異步對輸入調用鏈
  • abatch: 異步對輸入列表調用鏈
  • astream_log: 異步返回中間步驟,以及最終響應
  • astream_events: beta 流式傳輸鏈中發生的事件(在 langchain-core 0.1.14 中引入)

輸入類型輸出類型 因組件而異:

組件輸入類型輸出類型
提示字典提示值
聊天模型單個字符串、聊天消息列表或提示值聊天消息
LLM單個字符串、聊天消息列表或提示值字符串
輸出解析器LLM 或聊天模型的輸出取決于解析器
檢索器單個字符串文檔列表
工具單個字符串或字典,取決于工具取決于工具

所有可運行對象都公開輸入和輸出 模式 以檢查輸入和輸出:

  • input_schema: 從可運行對象結構自動生成的輸入 Pydantic 模型
  • output_schema: 從可運行對象結構自動生成的輸出 Pydantic 模型

流式運行對于使基于 LLM 的應用程序對最終用戶具有響應性至關重要。 重要的 LangChain 原語,如聊天模型、輸出解析器、提示模板、檢索器和代理都實現了 LangChain Runnable 接口。 該接口提供了兩種通用的流式內容方法:

  1. 同步 stream 和異步 astream:流式傳輸鏈中的最終輸出默認實現
  2. 異步 astream_events 和異步 astream_log:這些方法提供了一種從鏈中流式傳輸中間步驟最終輸出的方式。 讓我們看看這兩種方法,并嘗試理解如何使用它們。

Stream(流)

所有 Runnable 對象都實現了一個名為 stream 的同步方法和一個名為 astream 的異步變體。 這些方法旨在以塊的形式流式傳輸最終輸出,盡快返回每個塊。 只有在程序中的所有步驟都知道如何處理輸入流時,才能進行流式傳輸;即,逐個處理輸入塊,并產生相應的輸出塊。 這種處理的復雜性可以有所不同,從簡單的任務,如發出 LLM 生成的令牌,到更具挑戰性的任務,如在整個 JSON 完成之前流式傳輸 JSON 結果的部分。 開始探索流式傳輸的最佳方法是從 LLM 應用程序中最重要的組件開始——LLM 本身!

LLM 和聊天模型

大型語言模型及其聊天變體是基于 LLM 的應用程序的主要瓶頸。 大型語言模型可能需要幾秒鐘才能對查詢生成完整的響應。這比應用程序對最終用戶具有響應性的約 200-300 毫秒的閾值要慢得多。 使應用程序具有更高的響應性的關鍵策略是顯示中間進度;即,逐個令牌流式傳輸模型的輸出。 我們將展示使用聊天模型進行流式傳輸的示例。從以下選項中選擇一個:

讓我們從同步 stream API 開始:

chunks = []
for chunk in model.stream("天空是什么顏色?"):chunks.append(chunk)print(chunk.content, end="|", flush=True)
天|空|是|什|么|顏|色|?|

或者,如果您在異步環境中工作,可以考慮使用異步 astream API:

chunks = []
async for chunk in model.astream("天空是什么顏色?"):chunks.append(chunk)print(chunk.content, end="|", flush=True)
天|空|是|什|么|顏|色|?|

讓我們檢查其中一個塊:

chunks[1]
AIMessageChunk(content='天', id='run-b36bea64-5511-4d7a-b6a3-a07b3db0c8e7')

我們得到了一個稱為 AIMessageChunk 的東西。該塊表示 AIMessage 的一部分。 消息塊是可疊加的——可以簡單地將它們相加以獲得到目前為止的響應狀態!

chunks[0] + chunks[1] + chunks[2] + chunks[3] + chunks[4]
AIMessageChunk(content='天空是什么顏色', id='run-b36bea64-5511-4d7a-b6a3-a07b3db0c8e7')
Chain(鏈)

幾乎所有的 LLM 應用程序都涉及不止一步的操作,而不僅僅是調用語言模型。 讓我們使用 LangChain 表達式語言 (LCEL) 構建一個簡單的鏈,該鏈結合了一個提示、模型和解析器,并驗證流式傳輸是否正常工作。 我們將使用 StrOutputParser 來解析模型的輸出。這是一個簡單的解析器,從 AIMessageChunk 中提取 content 字段,給出模型返回的 token

LCEL 是一種_聲明式_的方式,通過將不同的 LangChain 原語鏈接在一起來指定一個“程序”。使用 LCEL 創建的鏈可以自動實現 streamastream,從而實現對最終輸出的流式傳輸。事實上,使用 LCEL 創建的鏈實現了整個標準 Runnable 接口。

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("給我講一個關于{topic}的笑話")
parser = StrOutputParser()
chain = prompt | model | parser
async for chunk in chain.astream({"topic": "鸚鵡"}):print(chunk, end="|", flush=True)
|一個|人|去|寵|物|店|買|鸚|鵡|。|店|員|說|:“|這|只|鸚|鵡|會|說|話|。”|
|買|回|家|后|,|那|人|發|現|鸚|鵡|只|會|說|一|句|話|:“|我|是|鸚|鵡|。”|
|那|人|就|去|找|店|員|,|說|:“|你|不|是|說|這|只|鸚|鵡|會|說|話|嗎|?|它|只|會|說|‘|我|是|鸚|鵡|’|。”|
|店|員|回|答|:“|它|確|實|會|說|話|,|你|想|它|怎|么|可能|知|道|自|己|是|鸚|鵡|呢|?”||

請注意,即使我們在上面的鏈條末尾使用了parser,我們仍然可以獲得流式輸出。parser會對每個流式塊進行操作。許多LCEL基元也支持這種轉換式的流式傳遞,這在構建應用程序時非常方便。

自定義函數可以被設計為返回生成器,這樣就能夠操作流。

某些可運行實體,如提示模板和聊天模型,無法處理單個塊,而是聚合所有先前的步驟。這些可運行實體可以中斷流處理。

LangChain表達語言允許您將鏈的構建與使用模式(例如同步/異步、批處理/流式等)分開。如果這與您構建的內容無關,您也可以依賴于標準的命令式編程方法,通過在每個組件上調用invoke、batch或stream,將結果分配給變量,然后根據需要在下游使用它們。

使用輸入流

如果您想要在輸出生成時從中流式傳輸JSON,該怎么辦呢?

如果您依賴json.loads來解析部分JSON,那么解析將失敗,因為部分JSON不會是有效的JSON。

您可能會束手無策,聲稱無法流式傳輸JSON。

事實證明,有一種方法可以做到這一點——解析器需要在輸入流上操作,并嘗試將部分JSON“自動完成”為有效狀態。

讓我們看看這樣一個解析器的運行,以了解這意味著什么。

model = ChatOpenAI(model="gpt-4")
parser = StrOutputParser()
chain = (model | JsonOutputParser()# 由于Langchain舊版本中的一個錯誤,JsonOutputParser未能從某些模型中流式傳輸結果
)
async def async_stream():async for text in chain.astream("以JSON 格式輸出法國、西班牙和日本的國家及其人口列表。"'使用一個帶有“countries”外部鍵的字典,其中包含國家列表。'"每個國家都應該有鍵`name`和`population`"):print(text, flush=True)
{}
{'countries': []}
{'countries': [{}]}
{'countries': [{'name': ''}]}
{'countries': [{'name': 'France'}]}
{'countries': [{'name': 'France', 'population': 670}]}
{'countries': [{'name': 'France', 'population': 670810}]}
{'countries': [{'name': 'France', 'population': 67081000}]}
{'countries': [{'name': 'France', 'population': 67081000}, {}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': ''}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain'}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 467}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 467330}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {'name': ''}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {'name': 'Japan'}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {'name': 'Japan', 'population': 126}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {'name': 'Japan', 'population': 126300}]}
{'countries': [{'name': 'France', 'population': 67081000}, {'name': 'Spain', 'population': 46733038}, {'name': 'Japan', 'population': 126300000}]}

Stream events(事件流)

現在我們已經了解了streamastream的工作原理,讓我們進入事件流的世界。🏞?

事件流是一個beta API。這個API可能會根據反饋略微更改。

本指南演示了V2 API,并且需要 langchain-core >= 0.2。對于與舊版本 LangChain 兼容的V1 API,請參閱這里。

import langchain_core
langchain_core.__version__

為了使astream_events API正常工作:

  • 在代碼中盡可能使用async(例如,異步工具等)
  • 如果定義自定義函數/可運行項,請傳播回調
  • 在沒有 LCEL 的情況下使用可運行項時,請確保在LLMs上調用.astream(而不是.ainvoke以強制LLM流式傳輸令牌
事件參考

下面是一個參考表,顯示各種可運行對象可能發出的一些事件。

當流式傳輸正確實現時,對于可運行項的輸入直到輸入流完全消耗后才會知道。這意味著inputs通常僅包括end事件,而不包括start事件。

事件名稱輸入輸出
on_chat_model_start[模型名稱]{“messages”: [[SystemMessage, HumanMessage]]}
on_chat_model_end[模型名稱]{“messages”: [[SystemMessage, HumanMessage]]}AIMessageChunk(content=“hello world”)
on_llm_start[模型名稱]{‘input’: ‘hello’}
on_llm_stream[模型名稱]‘Hello’
on_llm_end[模型名稱]‘Hello human!’
on_chain_startformat_docs
on_chain_streamformat_docs“hello world!, goodbye world!”
on_chain_endformat_docs[Document(…)]“hello world!, goodbye world!”
on_tool_startsome_tool{“x”: 1, “y”: “2”}
on_tool_endsome_tool{“x”: 1, “y”: “2”}
on_retriever_start[檢索器名稱]{“query”: “hello”}
on_retriever_end[檢索器名稱]{“query”: “hello”}[Document(…), …]
on_prompt_start[模板名稱]{“question”: “hello”}
on_prompt_end[模板名稱]{“question”: “hello”}ChatPromptValue(messages: [SystemMessage, …])
聊天模型

讓我們首先看一下聊天模型產生的事件。

events = []
async for event in model.astream_events("hello", version="v2"):events.append(event)
/home/eugene/src/langchain/libs/core/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: This API is in beta and may change in the future.warn_beta(

嘿,API中那個有趣的version="v2"參數是什么意思?😾 這是一個beta API,我們幾乎肯定會對其進行一些更改(事實上,我們已經做了!) 這個版本參數將允許我們最小化對您代碼的破壞性更改。 簡而言之,我們現在讓您感到煩惱,這樣以后就不必再煩惱了。 v2僅適用于 langchain-core>=0.2.0。

讓我們看一下一些開始事件和一些結束事件。

events[:3]
[{'event': 'on_chat_model_start','data': {'input': 'hello'},'name': 'ChatAnthropic','tags': [],'run_id': 'a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3','metadata': {}},{'event': 'on_chat_model_stream','data': {'chunk': AIMessageChunk(content='Hello', id='run-a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3')},'run_id': 'a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3','name': 'ChatAnthropic','tags': [],'metadata': {}},{'event': 'on_chat_model_stream','data': {'chunk': AIMessageChunk(content='!', id='run-a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3')},'run_id': 'a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3','name': 'ChatAnthropic','tags': [],'metadata': {}}]
events[-2:]
[{'event': 'on_chat_model_stream','data': {'chunk': AIMessageChunk(content='?', id='run-a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3')},'run_id': 'a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3','name': 'ChatAnthropic','tags': [],'metadata': {}},{'event': 'on_chat_model_end','data': {'output': AIMessageChunk(content='Hello! How can I assist you today?', id='run-a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3')},'run_id': 'a81e4c0f-fc36-4d33-93bc-1ac25b9bb2c3','name': 'ChatAnthropic','tags': [],'metadata': {}}]

LLM apps debug: LangSmith Tracing & Verbose, Debug Mode

與構建任何類型的軟件一樣,使用LLM構建時,總會有調試的需求。模型調用可能會失敗,模型輸出可能格式錯誤,或者可能存在一些嵌套的模型調用,不清楚在哪一步出現了錯誤的輸出。 有三種主要的調試方法:

  • 詳細模式(Verbose):為你的鏈中的“重要”事件添加打印語句。
  • 調試模式(Debug):為你的鏈中的所有事件添加日志記錄語句。
  • LangSmith跟蹤:將事件記錄到LangSmith,以便在那里進行可視化。
詳細模式(Verbose Mode)調試模式(Debug Mode)LangSmith跟蹤
免費???
用戶界面???
持久化???
查看所有事件???
查看“重要”事件???
本地運行???

LangSmith Tracing(跟蹤)

使用LangChain構建的許多應用程序將包含多個步驟,其中包含多次LLM調用。 隨著這些應用程序變得越來越復雜,能夠檢查鏈或代理內部發生了什么變得至關重要。 這樣做的最佳方式是使用LangSmith。 在上面的鏈接上注冊后,請確保設置你的環境變量以開始記錄跟蹤:

#windows導入環境變量
setx LANGCHAIN_TRACING_V2 "true"
setx LANGCHAIN_API_KEY "..." #獲取到key
setx TAVILY_API_KEY "..." #獲取到key#mac 導入環境變量
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="..."
export TAVILY_API_KEY="..."

假設我們有一個代理,并且希望可視化它所采取的操作和接收到的工具輸出。在沒有任何調試的情況下,這是我們看到的:

import os
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate
from langchain.globals import set_verbosellm = ChatOpenAI(model="gpt-4o")
tools = [TavilySearchResults(max_results=1)]
prompt = ChatPromptTemplate.from_messages([("system","你是一位得力的助手。",),("placeholder", "{chat_history}"),("human", "{input}"),("placeholder", "{agent_scratchpad}"),]
)
# 構建工具代理
agent = create_tool_calling_agent(llm, tools, prompt)
set_verbose(True)
# 通過傳入代理和工具來創建代理執行器
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "誰執導了2023年的電影《奧本海默》,他多少歲了?"}
)
{'input': '誰執導了2023年的電影《奧本海默》,他多少歲了?', 'output': '克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。截至2023年,他53歲。'}

我們沒有得到太多輸出,但由于我們設置了LangSmith,我們可以輕松地看到發生了什么: https://smith.langchain.com/public/a89ff88f-9ddc-4757-a395-3a1b365655bf/r

Verbose(詳細日志打印)

如果你在Jupyter筆記本中進行原型設計或運行Python腳本,打印出鏈運行的中間步驟可能會有所幫助。 有許多方法可以以不同程度的詳細程度啟用打印。 注意:即使啟用了LangSmith,這些仍然有效,因此你可以同時打開并運行它們。

set_verbose(True)

設置 verbose 標志將以稍微更易讀的格式打印出輸入和輸出,并將跳過記錄某些原始輸出(例如 LLM 調用的令牌使用統計信息),以便您可以專注于應用程序邏輯。

from langchain.globals import set_verbose
set_verbose(True)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "Who directed the 2023 film Oppenheimer and what is their age in days?"}
)
> Entering new AgentExecutor chain...Invoking: `tavily_search_results_json` with `{'query': '2023 movie Oppenheimer director'}`[{'url': 'https://www.imdb.com/title/tt15398776/fullcredits/', 'content': 'Oppenheimer (2023) cast and crew credits, including actors, actresses, directors, writers and more. Menu. ... director of photography: behind-the-scenes Jason Gary ... best boy grip ... film loader Luc Poullain ... aerial coordinator'}]
Invoking: `tavily_search_results_json` with `{'query': 'Christopher Nolan age'}`[{'url': 'https://www.nme.com/news/film/christopher-nolan-fans-are-celebrating-his-54th-birthday-youve-changed-things-forever-3779396', 'content': "Christopher Nolan is 54 Still my fave bit of Nolan trivia: Joey Pantoliano on creating Ralph Cifaretto's look in The Sopranos: 'The wig I had them build as an homage to Chris Nolan, I like ..."}]2023年的電影《奧本海默》由克里斯托弗·諾蘭(Christopher Nolan)執導。他目前54歲。> Finished chain.
{'input': '誰執導了2023年的電影《奧本海默》,他多少歲了?', 'output': '克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。截至2023年,他53歲。'}

Debug(調試日志打印)

set_debug(True)

設置全局的 debug 標志將導致所有具有回調支持的 LangChain 組件(鏈、模型、代理、工具、檢索器)打印它們接收的輸入和生成的輸出。這是最詳細的設置,將完全記錄原始輸入和輸出。

from langchain.globals import set_debug
# 構建工具代理
agent = create_tool_calling_agent(llm, tools, prompt)
#打印調試日志
set_debug(True)
#不輸出詳細日志
set_verbose(False)
# 通過傳入代理和工具來創建代理執行器
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "誰執導了2023年的電影《奧本海默》,他多少歲了?"}
)
[chain/start] [chain:AgentExecutor] Entering Chain run with input:
{"input": "誰執導了2023年的電影《奧本海默》,他多少歲了?"
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] Entering Chain run with input:
{"input": ""
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] [1ms] Exiting Chain run with output:
{"output": []
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] [4ms] Exiting Chain run with output:
{"agent_scratchpad": []
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] [10ms] Exiting Chain run with output:
{"input": "誰執導了2023年的電影《奧本海默》,他多少歲了?","intermediate_steps": [],"agent_scratchpad": []
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] Entering Prompt run with input:
{"input": "誰執導了2023年的電影《奧本海默》,他多少歲了?","intermediate_steps": [],"agent_scratchpad": []
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] Entering LLM run with input:
{"prompts": ["System: 你是一位得力的助手。\nHuman: 誰執導了2023年的電影《奧本海默》,他多少歲了?"]
}
[llm/end] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] [1.81s] Exiting LLM run with output:
{"generations": [[{"text": "","generation_info": {"finish_reason": "tool_calls","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "ChatGenerationChunk","message": {"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessageChunk"],"kwargs": {"content": "","additional_kwargs": {"tool_calls": [{"index": 0,"id": "call_Rhv2KLzFTU0XhJso5F79EiUp","function": {"arguments": "{\"query\":\"2023年電影《奧本海默》導演\"}","name": "tavily_search_results_json"},"type": "function"}]},"response_metadata": {"finish_reason": "tool_calls","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "AIMessageChunk","id": "run-cbeb35e8-b4ee-4c78-b663-e338ef90382d","tool_calls": [{"name": "tavily_search_results_json","args": {"query": "2023年電影《奧本海默》導演"},"id": "call_Rhv2KLzFTU0XhJso5F79EiUp","type": "tool_call"}],"tool_call_chunks": [{"name": "tavily_search_results_json","args": "{\"query\":\"2023年電影《奧本海默》導演\"}","id": "call_Rhv2KLzFTU0XhJso5F79EiUp","index": 0,"type": "tool_call_chunk"}],"invalid_tool_calls": []}}}]],"llm_output": null,"run": null
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] [2ms] Exiting Parser run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence] [1.85s] Exiting Chain run with output:
[outputs]
[tool/start] [chain:AgentExecutor > tool:tavily_search_results_json] Entering Tool run with input:
"{'query': '2023年電影《奧本海默》導演'}"
[tool/end] [chain:AgentExecutor > tool:tavily_search_results_json] [2.06s] Exiting Tool run with output:
"[{'url': 'https://baike.baidu.com/item/奧本海默/58802734', 'content': '《奧本海默》是克里斯托弗·諾蘭自編自導的,由基里安·墨菲主演的傳記電影,該片于2023年7月21日在北美上映,8月30日在中國內地上映,2024年3月29日在日本上映。該片改編自Kai Bird、Martin J. Sherwin的《美國普羅米修斯:奧本海默的勝與悲》,影片《奧本海默》講述了美國"原子彈之父"羅伯特· ...'}]"
[chain/start] [chain:AgentExecutor > chain:RunnableSequence] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] Entering Chain run with input:
{"input": ""
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] [4ms] Exiting Chain run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] [10ms] Exiting Chain run with output:
[outputs]
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] [1ms] Exiting Prompt run with output:
[outputs]
[llm/start] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] Entering LLM run with input:
{"prompts": ["System: 你是一位得力的助手。\nHuman: 誰執導了2023年的電影《奧本海默》,他多少歲了?\nAI: \nTool: [{\"url\": \"https://baike.baidu.com/item/奧本海默/58802734\", \"content\": \"《奧本海默》是克里斯托弗·諾蘭自編自導的,由基里安·墨菲主演的傳記電影,該片于2023年7月21日在北美上映,8月30日在中國內地上映,2024年3月29日在日本上映。該片改編自Kai Bird、Martin J. Sherwin的《美國普羅米修斯:奧本海默的勝與悲》,影片《奧本海默》講述了美國\\\"原子彈之父\\\"羅伯特· ...\"}]"]
}
[llm/end] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] [1.39s] Exiting LLM run with output:
{"generations": [[{"text": "2023年電影《奧本海默》的導演是克里斯托弗·諾蘭。接下來我將查詢他的年齡。","generation_info": {"finish_reason": "tool_calls","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "ChatGenerationChunk","message": {"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessageChunk"],"kwargs": {"content": "2023年電影《奧本海默》的導演是克里斯托弗·諾蘭。接下來我將查詢他的年齡。","additional_kwargs": {"tool_calls": [{"index": 0,"id": "call_QuKQUKd6YLsgTgZeYcWpk2lN","function": {"arguments": "{\"query\":\"克里斯托弗·諾蘭年齡\"}","name": "tavily_search_results_json"},"type": "function"}]},"response_metadata": {"finish_reason": "tool_calls","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "AIMessageChunk","id": "run-b7ee6125-1af5-4073-b81e-076a859755bd","tool_calls": [{"name": "tavily_search_results_json","args": {"query": "克里斯托弗·諾蘭年齡"},"id": "call_QuKQUKd6YLsgTgZeYcWpk2lN","type": "tool_call"}],"tool_call_chunks": [{"name": "tavily_search_results_json","args": "{\"query\":\"克里斯托弗·諾蘭年齡\"}","id": "call_QuKQUKd6YLsgTgZeYcWpk2lN","index": 0,"type": "tool_call_chunk"}],"invalid_tool_calls": []}}}]],"llm_output": null,"run": null
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] [1ms] Exiting Parser run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence] [1.43s] Exiting Chain run with output:
[outputs]
[tool/start] [chain:AgentExecutor > tool:tavily_search_results_json] Entering Tool run with input:
"{'query': '克里斯托弗·諾蘭年齡'}"
[tool/end] [chain:AgentExecutor > tool:tavily_search_results_json] [2.89s] Exiting Tool run with output:
"[{'url': 'https://baike.baidu.com/item/克里斯托弗·諾蘭/5306405', 'content': '克里斯托弗·諾蘭(Christopher Nolan),1970年7月30日出生于英國倫敦,導演、編劇、制片人。1998年4月24日克里斯托弗·諾蘭拍攝的首部故事片《追隨》在舊金山電影節上映。2000年,克里斯托弗·諾蘭憑借著他的《記憶碎片》為他獲得第74屆奧斯卡的提名。2005年,執導《蝙蝠俠》三部曲系列首部電影 ...'}]"
[chain/start] [chain:AgentExecutor > chain:RunnableSequence] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] Entering Chain run with input:
{"input": ""
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] Entering Chain run with input:
{"input": ""
}
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad> > chain:RunnableLambda] [1ms] Exiting Chain run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad> > chain:RunnableParallel<agent_scratchpad>] [4ms] Exiting Chain run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > chain:RunnableAssign<agent_scratchpad>] [9ms] Exiting Chain run with output:
[outputs]
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] Entering Prompt run with input:
[inputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > prompt:ChatPromptTemplate] [2ms] Exiting Prompt run with output:
[outputs]
[llm/start] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] Entering LLM run with input:
{"prompts": ["System: 你是一位得力的助手。\nHuman: 誰執導了2023年的電影《奧本海默》,他多少歲了?\nAI: \nTool: [{\"url\": \"https://baike.baidu.com/item/奧本海默/58802734\", \"content\": \"《奧本海默》是克里斯托弗·諾蘭自編自導的,由基里安·墨菲主演的傳記電影,該片于2023年7月21日在北美上映,8月30日在中國內地上映,2024年3月29日在日本上映。該片改編自Kai Bird、Martin J. Sherwin的《美國普羅米修斯:奧本海默的勝與悲》,影片《奧本海默》講述了美國\\\"原子彈之父\\\"羅伯特· ...\"}]\nAI: 2023年電影《奧本海默》的導演是克里斯托弗·諾蘭。接下來我將查詢他的年齡。\nTool: [{\"url\": \"https://baike.baidu.com/item/克里斯托弗·諾蘭/5306405\", \"content\": \"克里斯托弗·諾蘭(Christopher Nolan),1970年7月30日出生于英國倫敦,導演、編劇、制片人。1998年4月24日克里斯托弗·諾蘭拍攝的首部故事片《追隨》在舊金山電影節上映。2000年,克里斯托弗·諾蘭憑借著他的《記憶碎片》為他獲得第74屆奧斯卡的提名。2005年,執導《蝙蝠俠》三部曲系列首部電影 ...\"}]"]
}
[llm/end] [chain:AgentExecutor > chain:RunnableSequence > llm:ChatOpenAI] [885ms] Exiting LLM run with output:
{"generations": [[{"text": "克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。根據當前時間(2023年),他53歲。","generation_info": {"finish_reason": "stop","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "ChatGenerationChunk","message": {"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessageChunk"],"kwargs": {"content": "克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。根據當前時間(2023年),他53歲。","response_metadata": {"finish_reason": "stop","model_name": "gpt-4o-2024-05-13","system_fingerprint": "fp_4e2b2da518"},"type": "AIMessageChunk","id": "run-0cc2156a-5a9d-41c2-b8bc-ecb2a291f408","tool_calls": [],"invalid_tool_calls": []}}}]],"llm_output": null,"run": null
}
[chain/start] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] Entering Parser run with input:
[inputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence > parser:ToolsAgentOutputParser] [1ms] Exiting Parser run with output:
[outputs]
[chain/end] [chain:AgentExecutor > chain:RunnableSequence] [914ms] Exiting Chain run with output:
[outputs]
[chain/end] [chain:AgentExecutor] [9.25s] Exiting Chain run with output:
{"output": "克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。根據當前時間(2023年),他53歲。"
}
{'input': '誰執導了2023年的電影《奧本海默》,他多少歲了?', 'output': '克里斯托弗·諾蘭(Christopher Nolan)出生于1970年7月30日。根據當前時間(2023年),他53歲。'}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73145.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73145.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73145.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

#echarts#折線圖#餅圖

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>折線圖</title> </head> <body><div id"app" style"width:100%;height:100%;"><div id"chart-c…

Parsing error: Unexpected token, expected “,“

今天在使用Trae AI 編程工具開發大文件切片上傳功能&#xff0c;使用的是VUE3,TS技術棧&#xff0c;開發完成運行時&#xff0c;編譯報錯&#xff08;Parsing error: Unexpected token, expected ","&#xff09;&#xff0c;讓AI自行修復此問題多次后還是沒有解決&a…

NLP高頻面試題(九)——大模型常見的幾種解碼方案

大模型常見的幾種解碼方案 在自然語言生成任務中&#xff0c;如何從模型生成的概率分布中選擇合適的詞匯&#xff0c;是影響文本質量的關鍵問題。常見的解碼方法包括貪心搜索&#xff08;Greedy Search&#xff09;、束搜索&#xff08;Beam Search&#xff09;、隨機采樣&…

農用車一鍵啟動工作原理

移動管家農用車一鍵啟動的工作原理與普通汽車類似&#xff0c;主要依賴于無線射頻識別技術&#xff08;RFID&#xff09;。以下是具體的工作步驟和原理&#xff1a; 智能鑰匙識別&#xff1a; 車主攜帶智能鑰匙靠近車輛時&#xff0c;鑰匙通過發射射頻信號與車輛進行交互。車輛…

Cursor從小白到專家

文章目錄 1&#xff1a;簡單開發一個貪吃蛇游戲規則設置提示詞 cursor開發小工具開發整體步驟創建.cursorrules輸入提示詞composer模式chat模式 執行cursor accept all發布到線上進行分享 cursor開發一個瀏覽器插件創建.cursorrulescursor rules范例集工具 輸入提示詞執行curso…

MAC+PHY 的硬件連接

文章目錄 以太網的 MAC 與 PHY簡介硬件拓撲CPU集成MAC與PHYCPU集成MAC&#xff0c;PHY采用獨立芯片CPU不集成MAC與PHY&#xff0c;MAC與PHY采用集成芯片 在 OSI 分層中的位置MACPHYMAC 與 PHY 數據交互參考 本文為筆者學習以太網對網上資料歸納整理所做的筆記&#xff0c;文末均…

仿函數 VS 函數指針實現回調

前提&#xff1a; 本博客對比 函數指針實現回調 和 仿函數 &#xff0c;突出仿函數的優勢。 目的&#xff1a; 一個類要能夠靈活的調用兩個函數&#xff0c;essfc 和 greaterfc&#xff0c;分別用于比較兩個整數的大小&#xff1a; ①&#xff1a;lessfc&#xff1a;判斷 x …

CH32V208藍牙內部帶運放32位RISC-V工業級微控制器

開發板 CH32V208CBU6立創格式的開發板上述鏈接可下載&#xff0c;官方文件進行了轉換&#xff0c;使用前請仔細核對。 CH32V208CBU6原理圖&#xff0c;上述圖片為芯片部分。已進行DRC。 CH32V208CBU6 PCB三維圖&#xff0c;上述圖片為芯片部分。已進行DRC。 概述 CH32V208C…

整理和總結微信小程序的高頻知識點

前言 近期萌生了一些想法&#xff0c;感覺可以做一個小程序作為產出。 但小程序做得比較少&#xff0c;因此邊做邊復習。整理和總結了一些高頻知識點和大家一起分享。 一、模板和組件 1.1模板&#xff08;Template&#xff09; 優勢 簡單靈活&#xff1a;模板定義和使用都較…

1996-2023年各省公路里程數據(無缺失)

1996-2023年各省公路里程數據&#xff08;無缺失&#xff09; 1、時間&#xff1a;1996-2023年 2、來源&#xff1a;國家統計局、統計年鑒 3、指標&#xff1a;公路里程&#xff08;萬公里&#xff09; 4、范圍&#xff1a;31省 5、指標解釋&#xff1a;公路里程指報告期末…

SEARCH-R1:大型語言模型的多輪搜索推理革命

當AI學會"邊搜索邊思考" 2025年&#xff0c;語言模型領域迎來重大突破——SEARCH-R1框架通過強化學習&#xff08;RL&#xff09;讓大模型實現"動態搜索自主推理"的協同進化。這項技術不僅讓模型在回答"泰坦尼克號沉沒時的船長是誰"時能自動檢索…

Wi-Fi NAN 架構(Wi-Fi Aware Specification v4.0,第2章:2.7~2.9)

1. NAN 介質訪問控制層&#xff08;MAC&#xff09; NAN MAC負責通過參與 NAN同步信標幀&#xff08;NAN Synchronization Beacon frame&#xff09;的傳輸&#xff0c;獲取并維護設備所在的NAN集群的同步。作為同步功能的一部分&#xff0c;NAN MAC運行 TSF 定時器。NAN MAC還…

基于物聯網的便攜式土壤綜合參數檢測儀設計

標題:基于物聯網的便攜式土壤綜合參數檢測儀設計 內容:1.摘要 隨著農業現代化和環境監測需求的不斷增長&#xff0c;對土壤綜合參數的實時、準確檢測變得至關重要。本研究旨在設計一種基于物聯網的便攜式土壤綜合參數檢測儀&#xff0c;以滿足現場快速檢測和數據遠程傳輸的需求…

《Android 13深度定制:手勢攔截技術實現SystemUI狀態欄智能折疊方案》

核心機制解析 在Android 13的SystemUI定制中&#xff0c;狀態欄下拉行為由NotificationPanelViewController控制&#xff0c;其核心邏輯聚焦于手勢事件處理和布局動態調整。當用戶執行下拉操作時&#xff0c;系統通過onQsIntercept方法攔截滑動事件&#xff0c;并調用setQsExp…

《Python實戰進階》No26: CI/CD 流水線:GitHub Actions 與 Jenkins 集成

No26: CI/CD 流水線&#xff1a;GitHub Actions 與 Jenkins 集成 摘要 持續集成&#xff08;CI&#xff09;和持續部署&#xff08;CD&#xff09;是現代軟件開發中不可或缺的實踐&#xff0c;能夠顯著提升開發效率、減少錯誤并加速交付流程。本文將探討如何利用 GitHub Actio…

2025.3.22總結

今天去了光谷書店&#xff0c;看了下&#xff0c;書店里女生比較多&#xff0c;也不知道是不是上班族&#xff0c;發現有本類似馬克思的書籍&#xff0c;也不知道是不是再考研或者考其他證書的。 圖書館很安靜&#xff0c;安靜的讓我的內心也平靜了下來&#xff0c;我也再一旁…

HR人員和組織信息同步AD域服務器實戰方法JAVA

HR人員和組織信息同步AD域服務器 前期準備AD域基礎知識整理HR同步AD的邏輯代碼結構配置文件設置啟動類HR組織的BeanHR人員Bean獲取HR人員和組織信息的類AD中處理組織和人員的類日志配置 POM.xml文件生成EXE文件服務器定時任務異常問題注意事項 前期準備 1、開發語言&#xff1…

修改服務器windows遠程桌面默認端口號

修改服務器windows遠程桌面默認端口號 在Windows服務器上修改遠程桌面協議&#xff08;RDP&#xff09;的默認端口&#xff08;3389&#xff09;可以增強服務器的安全性&#xff0c;減少被惡意掃描和攻擊的風險。以下是修改遠程端口的詳細步驟&#xff1a; 按 Win R 打開運行…

MuJoCo 仿真 Panda 機械臂!末端位置實時追蹤 + 可視化(含縮放交互)

視頻講解&#xff1a; MuJoCo 仿真 Panda 機械臂&#xff01;末端位置實時追蹤 可視化&#xff08;含縮放交互&#xff09; 倉庫地址&#xff1a;GitHub - LitchiCheng/mujoco-learning 本期介紹下&#xff0c;mujoco_py這個庫很老了&#xff0c;最新的版本可以通過mujoco的p…

vue-splice方法

一、代碼解析 語法結構 splice(index, deleteCount, newElement) 是 JavaScript 數組的變異方法&#xff0c;其參數含義為&#xff1a; ? index&#xff1a;操作的起始位置&#xff08;索引&#xff09;。 ? 1&#xff1a;刪除的元素數量&#xff08;此處刪除 1 個元素&#…