在现代AI应用开发中,如何让聊天机器人具备记忆能力和上下文理解是一个核心挑战。传统的无状态对话系统往往无法处理复杂的多轮对话场景,特别是当用户需要提供多种信息来完成特定任务时。

本文就来讨论一下如何利用runnable来编排更有趣的语言模型系统,并理解如何使用运行状态链来管理复杂的对话策略和执行长篇文档的推理。

文章目录

  • 1 保持变量流动
  • 2 运行状态链
  • 3 使用运行状态链实现知识库
  • 4 航空公司客服机器人
  • 5 总结

1 保持变量流动

在之前的示例中,我们通过创建、改变和消费状态,在独立的链中实现了有趣的逻辑。这些状态以带有描述性键和有用值的字典形式传递,这些值被用来为后续的程序提供它们操作所需的信息。回忆一下上一篇文章中的零样本分类示例:

%%time
## ^^ This notebook is timed, which will print out how long it all took
from langchain_core.runnables import RunnableLambda
from langchain_nvidia_ai_endpoints import ChatNVIDIA
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from typing import List, Union
from operator import itemgetter## Zero-shot classification prompt and chain w/ explicit few-shot prompting
sys_msg = ("Choose the most likely topic classification given the sentence as context."" Only one word, no explanation.\n[Options : {options}]"
)zsc_prompt = ChatPromptTemplate.from_template(f"{sys_msg}\n\n""[[The sea is awesome]][/INST]boat</s><s>[INST]""[[{input}]]"
)## Define your simple instruct_model
instruct_chat = ChatNVIDIA(model="mistralai/mistral-7b-instruct-v0.2")
instruct_llm = instruct_chat | StrOutputParser()
one_word_llm = instruct_chat.bind(stop=[" ", "\n"]) | StrOutputParser()zsc_chain = zsc_prompt | one_word_llm## Function that just prints out the first word of the output. With early stopping bind
def zsc_call(input, options=["car", "boat", "airplane", "bike"]):return zsc_chain.invoke({"input" : input, "options" : options}).split()[0]print("-" * 80)
print(zsc_call("Should I take the next exit, or keep going to the next one?"))print("-" * 80)
print(zsc_call("I get seasick, so I think I'll pass on the trip"))print("-" * 80)
print(zsc_call("I'm scared of heights, so flying probably isn't for me"))

输出:

--------------------------------------------------------------------------------
car
--------------------------------------------------------------------------------
boat
--------------------------------------------------------------------------------
air
CPU times: user 23.4 ms, sys: 12.9 ms, total: 36.3 ms
Wall time: 1.29 s

这个链做出的几个设计决策使其非常易于使用,其中最关键的一点是:我们希望它像一个函数一样运作,所以我们只希望它生成输出并返回它。

这使得该链非常自然地可以作为一个模块包含在更大的链系统中。例如,下面的链将接受一个字符串,提取最可能的主题,然后根据该主题生成一个新句子:

%%time
## ^^ 这个笔记被计时,会打印出总共花费的时间
gen_prompt = ChatPromptTemplate.from_template("Make a new sentence about the the following topic: {topic}. Be creative!"
)gen_chain = gen_prompt | instruct_llminput_msg = "I get seasick, so I think I'll pass on the trip"
options = ["car", "boat", "airplane", "bike"]chain = (## -> {"input", "options"}{'topic' : zsc_chain}| PPrint()## -> {**, "topic"}| gen_chain## -> string
)chain.invoke({"input" : input_msg, "options" : options})

输出:

State:
{'topic': 'boat'}CPU times: user 23.6 ms, sys: 4.28 ms, total: 27.8 ms
Wall time: 1.03 s" As the sun began to set, the children's eyes gleamed with excitement as they rowed their makeshift paper boat through the sea of rippling bathwater in the living room, creating waves of laughter and magic."

然而,当您想保持信息流动时,这会有点问题,因为我们在生成响应时丢失了主题和输入变量。在简单的链式结构中,只传递上一步的输出,导致你无法同时访问inputtopic。如果你想让模型同时参考两个变量,需要用运行状态链或状态管理的方式把它们都传过去。如果我们想同时使用输出和输入做些什么,我们需要一种方法来确保两个变量都能传递过去。

我们可以使用映射runnable(即从字典解释或使用手动的RunnableMap)来将两个变量都传递过去,方法是将我们链的输出分配给一个单一的键,并让其他键按需传播。或者,我们也可以使用RunnableAssign来默认将消耗状态的链的输出与输入字典合并。

通过这种方式,我们可以在我们的链系统中传播任何我们想要的东西:

%%time
## ^^ 这个笔记被计时,会打印出总共花费的时间from langchain.schema.runnable import RunnableBranch, RunnablePassthrough
from langchain.schema.runnable.passthrough import RunnableAssign
from functools import partialbig_chain = (PPrint()## 手动映射。有时在分支链内部很有用| {'input' : lambda d: d.get('input'), 'topic' : zsc_chain}| PPrint()## RunnableAssign传递。默认情况下更适合运行状态链| RunnableAssign({'generation' : gen_chain})| PPrint()## 一起使用输入和生成的内容| RunnableAssign({'combination' : (ChatPromptTemplate.from_template("Consider the following passages:""\nP1: {input}""\nP2: {generation}""\n\nCombine the ideas from both sentences into one simple one.")| instruct_llm)})
)output = big_chain.invoke({"input" : "I get seasick, so I think I'll pass on the trip","options" : ["car", "boat", "airplane", "bike", "unknown"]
})
pprint("Final Output: ", output)

输出:

State: 
{'input': "I get seasick, so I think I'll pass on the trip",'options': ['car', 'boat', 'airplane', 'bike', 'unknown']
}
State: 
{'input': "I get seasick, so I think I'll pass on the trip", 'topic': ' boat'}
State: 
{'input': "I get seasick, so I think I'll pass on the trip",'topic': ' boat','generation': " As the sun began to set, the children's eyes gleamed with excitement as they rowed their 
makeshift paper boat through the sea of rippling bathwater in the living room, creating waves of laughter and 
magic."
}
Final Output: 
{'input': "I get seasick, so I think I'll pass on the trip",'topic': ' boat','generation': " As the sun began to set, the children's eyes gleamed with excitement as they rowed their 
makeshift paper boat through the sea of rippling bathwater in the living room, creating waves of laughter and 
magic.",'combination': "Feeling seasick, I'll have to sit this one out, as the children excitedly navigate their paper 
boat in the makeshift ocean of our living room, their laughter filling the air."
}

2 运行状态链

上面只是一个简单例子,如果说有什么作用的话,那就是展示了将许多LLM调用链接在一起进行内部推理的缺点。然而,保持信息在链中流动对于制作能够累积有用状态信息或以多遍方式操作的复杂链来说是无价的。

具体来说,一个非常简单但有效的链是运行状态链,它强制执行以下属性:

  • **“运行状态”**是一个字典,包含系统关心的所有变量。
  • **“分支”**是一个可以引入运行状态并可以将其降级为响应的链。
  • 分支只能在RunnableAssign作用域内运行,并且分支的输入应来自运行状态

在这里插入图片描述

你可以将运行状态链抽象看作是带有状态变量(或属性)和函数(或方法)的Pythonic类的函数式变体。

  • 就像是包装所有功能的抽象类。
  • 运行状态就像是属性(应该总是可访问的)。
  • 分支就像是类方法(可以选择使用哪些属性)。
  • .invoke 或类似的过程就像是按顺序运行分支的__call__方法。

通过在链中强制执行这种范式:

  • 可以保持状态变量在您的链中传播,允许内部组件访问任何必要的东西,并为以后使用累积状态值。
  • 还可以将您的链的输出作为输入传回,允许一个“while-循环”式的链,不断更新和构建您的运行状态。

3 使用运行状态链实现知识库

在理解了运行状态链的基本结构和原理之后,我们可以探索如何将这种方法扩展到管理更复杂的任务,特别是在创建通过交互演变的动态系统中。本节将重点介绍如何实现一个使用json启用的槽位填充累积的知识库:

  • 知识库(Knowledge Base): 一个信息存储库,用于让我们的LLM跟踪相关信息。
  • JSON启用的槽位填充(JSON-Enabled Slot Filling): 要求一个经过指令调优的模型输出一个json风格的格式(可以包括一个字典),其中包含一系列槽位,并依赖LLM用有用和相关的信息来填充这些槽位。

定义我们的知识库

为了构建一个响应迅速且智能的系统,我们需要一种方法,不仅能处理输入,还能在对话流程中保留和更新基本信息。这就是LangChain和Pydantic结合的关键所在。

**Pydantic**是一个流行的Python验证库,在构建和验证数据模型方面起着重要作用。作为其特性之一,Pydantic提供了结构化的模型类,用简化的语法和深度的定制选项来验证对象(数据、类、它们自身等)。这个框架在整个LangChain中被广泛使用,并且在涉及数据转换的用例中成为一个必要的组件。

我们可以先构建一个BaseModel类并定义一些Field变量来创建一个结构化的知识库,如下所示:

from pydantic import BaseModel, Field
from typing import Dict, Union, Optionalinstruct_chat = ChatNVIDIA(model="mistralai/mistral-7b-instruct-v0.2")class KnowledgeBase(BaseModel):## BaseModel的字段,当知识库被构建时将被验证/分配topic: str = Field('general', description="Current conversation topic")user_preferences: Dict[str, Union[str, int]] = Field({}, description="User preferences and choices")session_notes: list = Field([], description="Notes on the ongoing session")unresolved_queries: list = Field([], description="Unresolved user queries")action_items: list = Field([], description="Actionable items identified during the conversation")print(repr(KnowledgeBase(topic = "Travel")))
输出:
KnowledgeBase(topic='Travel', user_preferences={}, session_notes=[], unresolved_queries=[], action_items=[])

这种方法的真正优势在于LangChain提供的额外的以LLM为中心的功能,我们可以将其集成到我们的用例中。其中一个特性是PydanticOutputParser,它增强了Pydantic对象的能力,比如自动生成格式说明。

from langchain.output_parsers import PydanticOutputParserinstruct_string = PydanticOutputParser(pydantic_object=KnowledgeBase).get_format_instructions()
pprint(instruct_string)

输出:

The output should be formatted as a JSON instance that conforms to the JSON schema below.As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": 
"array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": 
["bar", "baz"]}} is not well-formatted.Here is the output schema:
```
{"properties": {"topic": {"default": "general", "description": "Current conversation topic", "title": "Topic", 
"type": "string"}, "user_preferences": {"additionalProperties": {"anyOf": [{"type": "string"}, {"type": 
"integer"}]}, "default": {}, "description": "User preferences and choices", "title": "User Preferences", "type": 
"object"}, "session_notes": {"default": [], "description": "Notes on the ongoing session", "items": {}, "title": 
"Session Notes", "type": "array"}, "unresolved_queries": {"default": [], "description": "Unresolved user queries", 
"items": {}, "title": "Unresolved Queries", "type": "array"}, "action_items": {"default": [], "description": 
"Actionable items identified during the conversation", "items": {}, "title": "Action Items", "type": "array"}}}

这个功能为创建知识库的有效输入生成了指令,这反过来又通过提供一个具体的、期望输出格式的单样本示例来帮助LLM。

可运行的提取模块(Runnable Extraction Module)

我们可以创建一个Runnable,它包装了我们Pydantic类的功能,并简化了知识库的提示、生成和更新过程:

## RExtract的定义
def RExtract(pydantic_class, llm, prompt):'''Runnable提取模块返回一个通过槽位填充提取来填充的知识字典'''parser = PydanticOutputParser(pydantic_object=pydantic_class)instruct_merge = RunnableAssign({'format_instructions' : lambda x: parser.get_format_instructions()})def preparse(string):if '{' not in string: string = '{' + stringif '}' not in string: string = string + '}'string = (string.replace("\\_", "_").replace("\n", " ").replace("\]", "]").replace("\[", "["))# print(string)  ## 适合用于诊断return stringreturn instruct_merge | prompt | llm | preparse | parser## RExtract的实际使用parser_prompt = ChatPromptTemplate.from_template("Update the knowledge base: {format_instructions}. Only use information from the input.""\n\nNEW MESSAGE: {input}"
)extractor = RExtract(KnowledgeBase, instruct_llm, parser_prompt)knowledge = extractor.invoke({'input' : "I love flowers so much! The orchids are amazing! Can you buy me some?"})
pprint(knowledge)

注意,由于LLM预测的模糊性,这个过程可能会失败,特别是对于那些没有针对指令跟随进行优化的模型(还没有被训练成能理解并执行自然语言指令的助手型模型)。对于这个过程,拥有一个强大的指令跟随LLM以及额外的检查和失败处理程序非常重要。

KnowledgeBase(topic='Flowers and orchids',user_preferences={'orchids': 'flowers I love'},session_notes=[],unresolved_queries=[],action_items=[]
)

动态知识库更新

最后,我们可以创建一个在整个对话过程中不断更新知识库的系统。这是通过将知识库的当前状态连同新的用户输入一起反馈到系统中进行持续更新来完成的。

以下是一个示例系统,它既展示了该公式在填充细节方面的强大能力,也展示了假设填充性能会和一般响应性能一样好的局限性:

class KnowledgeBase(BaseModel):firstname: str = Field('unknown', description="Chatting user's first name, unknown if unknown")lastname: str = Field('unknown', description="Chatting user's last name, unknown if unknown")location: str = Field('unknown', description="Where the user is located")summary: str = Field('unknown', description="Running summary of conversation. Update this with new input")response: str = Field('unknown', description="An ideal response to the user based on their new message")parser_prompt = ChatPromptTemplate.from_template("You are chatting with a user. The user just responded ('input'). Please update the knowledge base."" Record your response in the 'response' tag to continue the conversation."" Do not hallucinate any details, and make sure the knowledge base is not redundant."" Update the entries frequently to adapt to the conversation flow.""\n{format_instructions}""\n\nOLD KNOWLEDGE BASE: {know_base}""\n\nNEW MESSAGE: {input}""\n\nNEW KNOWLEDGE BASE:"
)## 切换到一个更强大的基础模型
instruct_llm = ChatNVIDIA(model="mistralai/mixtral-8x22b-instruct-v0.1") | StrOutputParser()extractor = RExtract(KnowledgeBase, instruct_llm, parser_prompt)
info_update = RunnableAssign({'know_base' : extractor})## 初始化知识库,看看你会得到什么
state = {'know_base' : KnowledgeBase()}
state['input'] = "My name is Carmen Sandiego! Guess where I am! Hint: It's somewhere in the United States."
state = info_update.invoke(state)
pprint(state)

输出:

{'know_base': KnowledgeBase(firstname='Carmen',lastname='Sandiego',location='unknown',summary='The user introduced themselves as Carmen Sandiego and asked for a guess on their location within 
the United States, providing a hint.',response="Welcome, Carmen Sandiego! I'm excited to try and guess your location. Since you mentioned it's 
somewhere in the United States, I'll start there. Is it west of the Mississippi River?"),'input': "My name is Carmen Sandiego! Guess where I am! Hint: It's somewhere in the United States."
}

测试:

state['input'] = "I'm in a place considered the birthplace of Jazz."
state = info_update.invoke(state)
pprint(state)state['input'] = "Yeah, I'm in New Orleans... How did you know?"
state = info_update.invoke(state)
pprint(state)

输出:

{'know_base': KnowledgeBase(firstname='Carmen',lastname='Sandiego',location='unknown',summary="The user introduced themselves as Carmen Sandiego and asked for a guess on their location within 
the United States, providing a hint. The user mentioned they're in a place considered the birthplace of Jazz.",response="Interesting hint, Carmen Sandiego! If you're in the birthplace of Jazz, then I can narrow down my
guess to New Orleans, Louisiana."),'input': "I'm in a place considered the birthplace of Jazz."
}{'know_base': KnowledgeBase(firstname='Carmen',lastname='Sandiego',location='New Orleans, Louisiana',summary="The user introduced themselves as Carmen Sandiego and asked for a guess on their location within 
the United States, providing a hint. The user mentioned they're in a place considered the birthplace of Jazz. Upon 
my guess, the user confirmed they're in New Orleans, Louisiana.",response="It's just deductive reasoning, Carmen Sandiego! Now I know your location for sure."),'input': "Yeah, I'm in New Orleans... How did you know?"
}

这个例子演示了如何有效地利用一个运行状态链来管理一个具有不断演变的上下文和需求的对话,使其成为开发复杂交互系统的强大工具。

4 航空公司客服机器人

现在我们根据学到的内容来实现一个简单但有效的对话管理器聊天机器人。对于这个练习,我们将制作一个航空公司支持机器人,帮助客户查询他们的航班信息。

首先创建一个简单的类似数据库的接口,从一个字典中获取一些客户信息。

## 可以被查询信息的函数。实现细节不重要
def get_flight_info(d: dict) -> str:"""一个检索函数的例子,它接受一个字典作为键。类似于SQL数据库查询"""req_keys = ['first_name', 'last_name', 'confirmation']assert all((key in d) for key in req_keys), f"Expected dictionary with keys {req_keys}, got {d}"## 静态数据集。get_key和get_val可以用来操作它,db是你的变量keys = req_keys + ["departure", "destination", "departure_time", "arrival_time", "flight_day"]values = [["Jane", "Doe", 12345, "San Jose", "New Orleans", "12:30 PM", "9:30 PM", "tomorrow"],["John", "Smith", 54321, "New York", "Los Angeles", "8:00 AM", "11:00 AM", "Sunday"],["Alice", "Johnson", 98765, "Chicago", "Miami", "7:00 PM", "11:00 PM", "next week"],["Bob", "Brown", 56789, "Dallas", "Seattle", "1:00 PM", "4:00 PM", "yesterday"],]get_key = lambda d: "|".join([d['first_name'], d['last_name'], str(d['confirmation'])])get_val = lambda l: {k:v for k,v in zip(keys, l)}db = {get_key(get_val(entry)) : get_val(entry) for entry in values}# 搜索匹配的条目data = db.get(get_key(d))if not data:return (f"Based on {req_keys} = {get_key(d)}) from your knowledge base, no info on the user flight was found."" This process happens every time new info is learned. If it's important, ask them to confirm this info.")return (f"{data['first_name']} {data['last_name']}'s flight from {data['departure']} to {data['destination']}"f" departs at {data['departure_time']} {data['flight_day']} and lands at {data['arrival_time']}.")## 使用示例
print(get_flight_info({"first_name" : "Jane", "last_name" : "Doe", "confirmation" : 12345}))
输出:
Jane Doe's flight from San Jose to New Orleans departs at 12:30 PM tomorrow and lands at 9:30 PM.

这是一个非常有用的接口,因为它合理地服务于两个目的:

  • 它可以用来从外部环境(一个数据库)提供关于用户情况的最新信息。
  • 它也可以用作一个硬性的门控机制,以防止未经授权披露敏感信息。

如果我们的网络能够访问这种接口,它将能够代表用户查询和检索这些信息。例如:

external_prompt = ChatPromptTemplate.from_template("You are a SkyFlow chatbot, and you are helping a customer with their issue."" Please help them with their question, remembering that your job is to represent SkyFlow airlines."" Assume SkyFlow uses industry-average practices regarding arrival times, operations, etc."" (This is a trade secret. Do not disclose)."  ## 软性强化" Please keep your discussion short and sweet if possible. Avoid saying hello unless necessary."" The following is some context that may be useful in answering the question.""\n\nContext: {context}""\n\nUser: {input}"
)basic_chain = external_prompt | instruct_llmbasic_chain.invoke({'input' : 'Can you please tell me when I need to get to the airport?','context' : get_flight_info({"first_name" : "Jane", "last_name" : "Doe", "confirmation" : 12345}),
})

输出:

'Jane, your flight departs at 12:30 PM. For domestic flights, we recommend arriving at least 2 hours prior to your scheduled departure time. In this case, please arrive by 10:30 AM to ensure a smooth check-in and security process. Safe travels with SkyFlow Airlines!'

但我们如何真正在实际应用中让这个系统工作起来呢?事实证明,我们可以使用上面提到的KnowledgeBase公式来提供这类信息,就像这样:

from pydantic import BaseModel, Field
from typing import Dict, Unionclass KnowledgeBase(BaseModel):first_name: str = Field('unknown', description="Chatting user's first name, `unknown` if unknown")last_name: str = Field('unknown', description="Chatting user's last name, `unknown` if unknown")confirmation: int = Field(-1, description="Flight Confirmation Number, `-1` if unknown")discussion_summary: str = Field("", description="Summary of discussion so far, including locations, issues, etc.")open_problems: list = Field([], description="Topics that have not been resolved yet")current_goals: list = Field([], description="Current goal for the agent to address")def get_key_fn(base: BaseModel) -> dict:'''给定一个带有知识库的字典,为get_flight_info返回一个键'''return {  ## 更多自动选项是可能的,但这样更明确'first_name' : base.first_name,'last_name' : base.last_name,'confirmation' : base.confirmation,}know_base = KnowledgeBase(first_name = "Jane", last_name = "Doe", confirmation = 12345)get_key = RunnableLambda(get_key_fn)
(get_key | get_flight_info).invoke(know_base)

输出:

"Jane Doe's flight from San Jose to New Orleans departs at 12:30 PM tomorrow and lands at 9:30 PM."

目标:

你希望用户能够在对话交流中自然地调用以下函数:

get_flight_info({"first_name" : "Jane", "last_name" : "Doe", "confirmation" : 12345}) ->"Jane Doe's flight from San Jose to New Orleans departs at 12:30 PM tomorrow and lands at 9:30 PM."

RExtract被提供,以便可以使用以下知识库语法:

known_info = KnowledgeBase()
extractor = RExtract(KnowledgeBase, InstructLLM(), parser_prompt)
results = extractor.invoke({'info_base' : known_info, 'input' : 'My message'})
known_info = results['info_base']

设计一个实现以下功能的聊天机器人:

  • 机器人应该开始时进行一些闲聊,可能会帮助用户处理一些不需要任何私人信息访问的非敏感查询。
  • 当用户开始询问需要访问数据库的信息时(无论是实践上还是法律上),告诉用户他们需要提供相关信息。
  • 当检索成功时,代理将能够谈论数据库中的信息。

这可以通过多种技术来完成,包括以下几种:

  • 提示工程和上下文解析:整体聊天提示大致保持不变,但通过操纵上下文来改变代理行为。例如,失败的数据库检索可以转化为自然语言指令注入,告诉代理如何解决问题,如 "无法使用键 {...} 检索信息。请要求用户澄清或使用已知信息帮助他们。"

  • 提示传递:活动提示作为状态变量传递,并可被监控链覆盖。

  • 分支链:例如**RunnableBranch**或实现条件路由机制的更自定义的解决方案。

    • 对于RunnableBranchswitch语法风格如下:

      from langchain.schema.runnable import RunnableBranch
      RunnableBranch(((lambda x: 1 in x), RPrint("Has 1 (didn't check 2): ")),((lambda x: 2 in x), RPrint("Has 2 (not 1 though): ")),RPrint("Has neither 1 not 2: ")
      ).invoke([2, 1, 3]);  ## -> Has 1 (didn't check 2): [2, 1, 3]
      

下面提供了一些提示和一个Gradio循环,可能有助于这项工作,但目前代理只会产生幻觉。请实现内部链以尝试检索相关信息。在尝试实现之前,请查看模型的默认行为,并注意它可能如何产生幻觉或忘记事情。

from langchain.schema.runnable import (RunnableBranch,RunnableLambda,RunnableMap,       ## 包装一个隐式的“字典”runnableRunnablePassthrough,
)
from langchain.schema.runnable.passthrough import RunnableAssignfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import BaseMessage, SystemMessage, ChatMessage, AIMessage
from typing import Iterable
import gradio as grexternal_prompt = ChatPromptTemplate.from_messages([("system", ("You are a chatbot for SkyFlow Airlines, and you are helping a customer with their issue."" Please chat with them! Stay concise and clear!"" Your running knowledge base is: {know_base}."" This is for you only; Do not mention it!"" \nUsing that, we retrieved the following: {context}\n"" If they provide info and the retrieval fails, ask to confirm their first/last name and confirmation."" Do not ask them any other personal info."" If it's not important to know about their flight, do not ask."" The checking happens automatically; you cannot check manually.")),("assistant", "{output}"),("user", "{input}"),
])## 知识库相关
class KnowledgeBase(BaseModel):first_name: str = Field('unknown', description="Chatting user's first name, `unknown` if unknown")last_name: str = Field('unknown', description="Chatting user's last name, `unknown` if unknown")confirmation: Optional[int] = Field(None, description="Flight Confirmation Number, `-1` if unknown")discussion_summary: str = Field("", description="Summary of discussion so far, including locations, issues, etc.")open_problems: str = Field("", description="Topics that have not been resolved yet")current_goals: str = Field("", description="Current goal for the agent to address")parser_prompt = ChatPromptTemplate.from_template("You are a chat assistant representing the airline SkyFlow, and are trying to track info about the conversation."" You have just received a message from the user. Please fill in the schema based on the chat.""\n\n{format_instructions}""\n\nOLD KNOWLEDGE BASE: {know_base}""\n\nASSISTANT RESPONSE: {output}""\n\nUSER MESSAGE: {input}""\n\nNEW KNOWLEDGE BASE: "
)## 你的目标是通过自然对话调用以下内容
# get_flight_info({"first_name" : "Jane", "last_name" : "Doe", "confirmation" : 12345}) ->
#     "Jane Doe's flight from San Jose to New Orleans departs at 12:30 PM tomorrow and lands at 9:30 PM."chat_llm = ChatNVIDIA(model="meta/llama3-70b-instruct") | StrOutputParser()
instruct_llm = ChatNVIDIA(model="mistralai/mixtral-8x22b-instruct-v0.1") | StrOutputParser()external_chain = external_prompt | chat_llm## TODO: 创建一个链,根据提供的上下文填充你的知识库
knowbase_getter = RExtract(KnowledgeBase, instruct_llm, parser_prompt)## TODO: 创建一个链来拉取d["know_base"]并从数据库中输出检索结果
database_getter = itemgetter('know_base') | get_key | get_flight_info## 这些组件集成在一起构成你的内部链
internal_chain = (RunnableAssign({'know_base' : knowbase_getter})| RunnableAssign({'context' : database_getter})
)state = {'know_base' : KnowledgeBase()}def chat_gen(message, history=[], return_buffer=True):## 引入、更新和打印状态global statestate['input'] = messagestate['history'] = historystate['output'] = "" if not history else history[-1][1]## 从内部链生成新状态state = internal_chain.invoke(state)print("State after chain run:")pprint({k:v for k,v in state.items() if k != "history"})## 流式传输结果buffer = ""for token in external_chain.stream(state):buffer += tokenyield buffer if return_buffer else tokendef queue_fake_streaming_gradio(chat_stream, history = [], max_questions=8):## 模拟gradio的初始化程序,可以打印出一组起始消息for human_msg, agent_msg in history:if human_msg: print("\n[ Human ]:", human_msg)if agent_msg: print("\n[ Agent ]:", agent_msg)## 模拟带有代理初始消息的gradio循环。for _ in range(max_questions):message = input("\n[ Human ]: ")print("\n[ Agent ]: ")history_entry = [message, ""]for token in chat_stream(message, history, return_buffer=False):print(token, end='')history_entry[1] += tokenhistory += [history_entry]print("\n")## history的格式为 [[用户响应 0, 机器人响应 0], ...]
chat_history = [[None, "Hello! I'm your SkyFlow agent! How can I help you?"]]## 模拟流式Gradio接口的排队,使用python输入
queue_fake_streaming_gradio(chat_stream = chat_gen,history = chat_history
)

部分输出如下:

在这里插入图片描述

5 总结

本文系统介绍了如何使用LangChain的Runnable架构构建一个具有状态记忆、上下文推理和外部知识检索能力的对话代理系统,是构建实用AI助手的重要工程范式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90078.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90078.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/90078.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RPA认证考试全攻略:如何高效通过uipath、实在智能等厂商考试

rpa认证考试有什么作用&#xff1f;数字洪流席卷全球&#xff0c;企业效率之争已进入秒级战场。当重复性工作吞噬着创造力&#xff0c;RPA&#xff08;机器人流程自动化&#xff09;技术正以前所未有的速度重塑职场生态。财务对账、报表生成、跨系统数据搬运……这些曾经耗费人…

浅析MySQL事务隔离级别

MySQL 的事务隔离级别定义了多个并发事务在访问和修改相同数据时&#xff0c;彼此之间的可见性和影响程度。它解决了并发事务可能引发的三类核心问题&#xff1a; 脏读&#xff1a; 一个事务读取了另一个未提交事务修改的数据。不可重复读&#xff1a; 一个事务内多次读取同一行…

【Linux系统】基础IO(上)

1. 深入理解"文件"概念1.1 文件的狭义理解狭义上的“文件”主要指存储在磁盘上的数据集合。具体包括&#xff1a;文件在磁盘里&#xff1a;文件是磁盘上以特定结构&#xff08;如FAT、ext4文件系统&#xff09;保存的数据集合&#xff0c;由字节或字符序列构成。磁盘…

构建智能可视化分析系统:RTSP|RTMP播放器与AI行为识别的融合实践

技术背景 随着人工智能向边缘侧、实时化方向加速演进&#xff0c;视频已从传统的“记录媒介”跃升为支撑智能感知与自动决策的关键数据入口。在安防监控、工业安全、交通治理等复杂应用场景中&#xff0c;行为识别系统的准确性和响应效率&#xff0c;越来越依赖于视频源的时效…

AI入门学习-Python 最主流的机器学习库Scikit-learn

一、Scikit-learn 核心定位是什么&#xff1a;Python 最主流的机器学习库&#xff0c;涵盖从数据预处理到模型评估的全流程。 为什么测试工程师必学&#xff1a;✅ 80% 的测试机器学习问题可用它解决✅ 无需深厚数学基础&#xff0c;API 设计极简✅ 与 Pandas/Numpy 无缝集成&a…

apache-doris安装兼datax-web配置

Doris安装 官方快速开始链接 下载2.1.10&#xff0c;解压。我这边个人服务器CPU是J1900&#xff0c;是没有 avx2的&#xff0c;所以选no 配置JAVA_HOME&#xff0c;这里没有配置的要配置下&#xff0c;注意要Oracle的jdk&#xff0c;openjdk没有jps等工具集&#xff0c;后面跑…

问题实例:4G网络下语音呼叫失败

问题描述 测试机 拨号呼出后&#xff0c;一直在4G&#xff0c;超时后自动挂断。 对比机可以呼出成功&#xff0c;呼出时回落3G。 日志分析 测试机和对比机一样发起了CSFB 呼叫。 只是测试机后面没有回落3G。 03:44:40.373264 [0xB0ED] LTE NAS EMM Plain OTA Outgoing Message …

MATLAB 2024b深度学习新特性全面解析与DeepSeek大模型集成开发技术

随着人工智能技术向多学科交叉融合与工程实践领域纵深发展&#xff0c;MATLAB 2024b深度学习工具箱通过架构创新与功能强化&#xff0c;为科研创新和行业应用提供了全栈式解决方案。基于该版本工具链的三大革新方向展开&#xff1a;一是构建覆盖经典模型与前沿架构的体系化&…

Springboot美食分享平台

一、 绪论 1.1 研究意义 当今社会作为一个飞速的发展社会&#xff0c;网络已经完全渗入人们的生活&#xff0c; 网络信息已成为传播的第一大媒介&#xff0c; 可以毫不夸张说网络资源获取已逐步改变了人们以前的生活方式&#xff0c;网络已成为人们日常&#xff0c;休闲主要工…

微信小程序——世界天气小助手

哈喽&#xff0c;大家好&#xff01; 最近小编开发了一个简单的微信小程序——世界天气小助手&#xff0c;希望大家喜欢。 No.1: 为大家介绍下开发者工具下的页面结构。一共有三个界面{主页、搜索页、详情页}No.2&#xff1a; 具体页面展示&#xff1a;当前页面是主页&…

基于单片机的智能家居安防系统设计

摘 要 为了应对目前人们提出的对生活越来越智能的要求&#xff0c;在提高生活品质的同时降低意外事件发生对用户造成的经济损失或其他损失。针对日常生活中经常发生的火灾&#xff0c;失窃&#xff0c;电力资源浪费等生活问题&#xff0c;本设计正是在这种需求背景下展开研究…

腾讯研究院 | AI 浪潮中的中国品牌优势解码:华为、小米、大疆、科大讯飞等品牌从技术破壁到生态领跑的全维突围

当 DeepSeek-R1 模型在 2025 年掀起大众 AI 热潮&#xff0c;当腾讯混元大模型与京东言犀大模型在产业场景中落地生根&#xff0c;中国品牌正在 AI 技术革命的浪潮中完成从追随者到引领者的蜕变。腾讯营销洞察&#xff08;TMI&#xff09;联合京东消费及产业研究院、腾讯研究院…

FreeRTOS学习笔记——空闲任务prvIdleTask

文章目录任务创建任务的内容推荐阅读任务创建 prvIdleTask任务&#xff0c;是由任务调度函数vTaskStartScheduler创建的&#xff0c;任务优先级0&#xff0c;任务堆栈深度由配置选项configMINIMAL_STACK_SIZE定义。 void vTaskStartScheduler(void) {/* 其他代码*//* Add the…

初识卷积神经网络CNN

卷积神经网络CNN 全连接神经网络存在的问题: 输入的形式应该是列向量&#xff0c;但是卷积神经网络中的输入是图像(2D矩阵)&#xff0c;那么就需要对图片进行展平处理&#xff0c;原本图像中蕴含的空间等信息就被打乱了输入的特征多了&#xff0c;那么神经元的参数就会很多&…

高层功能架构详解 - openExo

高层功能架构详解1. 系统整体结构与模块化设计2. 两大核心类&#xff1a;ExoData 与 ExoA. ExoDataB. ExoC. 数据结构的层级关系3. 多微控制器协作与BLE通信4. 主控软件运行流程&#xff08;主循环伪代码&#xff09;5. 架构优点小结6. 与 Code Structure 的关系实用建议1. 系统…

【西北工业大学公开课】导引系统原理(全61讲)周军 -个人笔记版 5000字

【严正声明】此文档为个人笔记&#xff1a;仅供个人与同学参考学习&#xff0c;记录学习过程&#xff0c;严谨商业转载&#xff0c;或商业售卖&#xff01;感谢西北工业大学公开课知识分享&#xff0c;公开资料开源&#xff01;视频链接&#xff1a;【【西北工业大学】导引系统…

《命令行参数与环境变量:从使用到原理的全方位解析》

前言 当我们在终端输入 ls -l /home 查看目录详情&#xff0c;或用 gcc -o hello hello.c 编译代码时&#xff0c;或许很少思考&#xff1a;这些空格分隔的 “指令 选项 路径” 是如何被程序识别的&#xff1f;为什么 PATH 变量能让系统找到可执行文件&#xff0c;而 HOME 变…

C++设计模式:单例模式 (现代C++主流实现方式Meyer‘s Singleton + 使用CRTP模板化)

文章目录单例模式创建单例类饿汉式or懒汉式现代C单例模式的主流实现方式——Meyers Singleton使用 CRTP 模板化单例类单例模式 单例模式是指程序中只需要一个实例化对象&#xff0c;在全局作用域或整个代码架构中&#xff0c;此对象只被实例化一次&#xff0c;就可以达到在整个…

Eureka 和 Nacos

一、基本介绍EurekaEureka 是 Netflix 公司开发的一款基于 REST 风格的服务注册与发现组件&#xff0c;专为分布式系统设计。它遵循 AP 原则&#xff08;可用性、分区容错性优先&#xff09;&#xff0c;强调在网络分区等异常情况下的服务可用性&#xff0c;是 Spring Cloud Ne…

文件IO笔试题

目录前言一、核心概念&#xff1a;二、关键操作步骤&#xff1a;三、为什么需要文件IO&#xff1f;四、常见类型&#xff1a;五、标准IO源码六、笔试真题和练习1.代码实现1代码实现22.代码实现3.代码实现4.代码实现5.代码实现七、总结前言 文件IO&#xff08;文件输入/输出&am…