掌握Agent的部署,以及如何开发应用与部署的Agent交互,需要对几个基本的概念有清晰的理解。这些概念包括我们在上面提及的Graph,还包括Assistant、Thread、Run、Cron Job、Store等。当我们制定部署Agent的URL调用get_client函数时,后者会根据传入的URL创建一个httpx.AsyncClient对象,并利用此对象构建一个LangGraphClient对象。如下面的代码片段所示,LangGraphClient在初始化过程中会创建一个HttpClient对象来封装这个httpx.AsyncClient对象。我们在LangGraphClient上的绝大部分调用都会转换成针对Agent Server RESTful API的HTTP请求,背后的功臣就是一个HttpClient对象。
classLangGraphClient:def__init__(self,client:httpx.AsyncClient)->None:self.http=HttpClient(client)self.assistants=AssistantsClient(self.http)self.threads=ThreadsClient(self.http)self.runs=RunsClient(self.http)self.crons=CronClient(self.http)self.store=StoreClient(self.http)asyncdef__aenter__(self)->LangGraphClientasyncdef__aexit__(self,exc_type:type[BaseException]|None,exc_val:BaseException|None,exc_tb:TracebackType|None,)->None:asyncdefaclose(self)->None在__init__方法中,我们不仅将传入的httpx.AsyncClient对象封装成一个HttpClient对象,还利用这个HttpClient对象创建了多个子客户端,包括AssistantsClient、ThreadsClient、RunsClient、CronClient和StoreClient等,它们用来管理上述的Assistant、Thread、Run、Cron Job和Store。
1. Graph
当使用Agent Server部署Graph时,实际上是在部署Assistant的蓝图,具有两种部署形式:
- 已编译的Graph:直接使用编译好的
CompiledStateGraph对象,这样服务器在容器启动时加载一次,并在每次运行中重复使用,这是最高效也是推荐的部署形式; - 提供一个工厂函数:服务器每次需要该Graph时都会调用该函数。仅当需要针对每次运行进行Graph自定义时才使用此函数(例如,根据Assitant配置选择不同的模型或工具)。由于工厂函数每次调用都会运行,因此应尽量保持轻量级。
2. Assistant
在上面的例子中,我们调用LangGraphClient.runs.wait方法时指定了thread_id和assistant_id两个参数。顾名思义,这两个参数分别代表Thread和Assistant的标识,Thread自然代表一次对话的上下文,那么Assistant又是什么呢?
Assistant是Agent Server的一个概念,它允许我们将配置(例如提示词、LLM 选择、工具)与Graph的核心逻辑分开管理。这使我们可以创建针对同一Graph架构的多个专用版本,这些版本在运行时具有不同的行为。同一个Graph可以使用在不同的推理任务中,我们可以为不同的任务定义针对性的配置以提供更优的性能和用户体验。这些绑定了配置的Graph实例就被称为Assistant。
假设有一个基于通用Graph的用来进行写作的Agent。虽有由Graph体现的整体执行流程都差不多,但不同的写作风格(例如博客文章和推文)需要定制配置才能优化性能。为了支持这些变化,可以创建多个Assistant(一个用于博客,另一个用于推文),它们共享底层Graph,但在模型选择和系统提示方面有所不同。
Assistant具有如下几个关键特性:
- 通过API和UI进行管理:使用Agent Server/LangGraph SDK或LangSmith UI创建、列出、更新、版本控制和获取Assistant;
- 一个Graph,多个Assistant:单个已部署的Graph可以支持多个Assistant,每个Assistant可以拥有不同的配置(例如,提示、模型、工具);
- 版本化配置:每个Assistant都通过版本控制维护自己的配置历史记录。编辑Assistant会创建一个新版本,可以升级或回滚到任何版本;
- 无需更改Graph即可进行配置更新:通过辅助配置更新提示、模型选择和其他设置,无需修改或重新部署Graph代码即可实现快速迭代。
3. Thread
Thread是一个持久化的对话容器,它在多次运行中保持状态。Thread通过在针对多个Agent调用之间保留对话历史和上下文来实现有状态交互。如果没有Thread,每次运行都将是无状态的,不会记住之前的交互。Thread在以下情况下尤其有用:
- 多轮对话中,Assistant需要记住讨论过的内容;
- 需要跨多个步骤维护上下文的长时间运行任务;
- 用户专属状态管理,每个用户都有自己的对话历史记录。
下图展示了Thread如何在两次运行之间维护状态。第二次运行可以访问第一次运行的消息,从而使Assitant能够理解**明天怎么样?**的上下文指的是第一次运行中的天气查询。
- Thread维护着一段持久的对话,并拥有唯一的Thread ID;
- 每次运行都会将Assitant的配置应用到Graph的执行中;
- 状态在每次运行后都会更新,并保留到后续运行中;
- 后续运行可以访问完整的对话历史记录。
4. Run
Run是指针对Assitant的一次调用。执行一个Run对象时,需要指定要使用的Assitant。可以通过Graph ID指定默认Assitant,也可以通过assistant_id指定一个绑定了特定配置的Assitant。Run利用RunsClient来管理,它提供了创建、等待、检索和列出Run的功能。
上图展示了运行如何将Assitant与Thread结合起来执行图表:
- Graph(蓝色):包含Agent逻辑的已部署代码;
- Assitant(浅蓝色):配置选项(模型、提示、工具);
- Thread(橙色):用于存储对话历史记录的状态容器;
- Run(绿色):Assitant与Thread配对的执行操作。
执行一个Run对象时:
- 每次运行都可以有自己的输入、配置覆盖和元数据;
- Run可以是无状态的(不绑定Thread),也可以是有状态的(在Thread上执行以实现对话持久化);
- 多次运行可以使用相同的Assitant配置;
- Assitant的配置会影响底层Graph的执行方式。
5. Cron Job
在许多情况下,定时调用Assistant程序非常有用。假设您正在构建一个每日运行并发送当日新闻摘要邮件的助手程序。您可以使用Cron Job每天晚上 8:00 运行该Assistant程序。利用Cron Job可以按照用户定义的时间表运行。用户需要指定时间表、Assistant程序以及一些输入。之后服务器会在指定的时间表执行以下操作:
- 创建一个包含指定输入的新Thread;
- 输入预先制定的输入调用Assistant程序。
LangSmith部署API提供了多个用于创建和管理定时任务的端点,但是这项功能仅限于以云平台部署。如果采用本地部署的方式,需要购买企业级订阅才能使用定时任务功能。
5. Store
我们知道Agent使用BaseStore作为长期存储,而StoreClient的作用在于利用提供的API远程调用注册的BaseStore罢了。StoreClient提供了CRUD方法来管理存储的内容。通过StoreClient,我们可以在Graph的执行过程中存储和检索数据,从而实现更复杂的状态管理和数据持久化。例如,在一个购物助手程序中,我们可以使用Store来保存用户的购物车内容。每当用户添加或删除商品时,Graph都可以更新Store中的购物车状态。当用户准备结账时,Graph可以从Store中检索购物车内容并生成订单摘要。
对于上述得到的6个核心概念,我们对它们进行一句话总结:
- Graph是Agent的蓝图,定义了Agent的核心逻辑和结构;
- Assistant是Graph的配置化实例,允许我们为同一Graph创建多个具有不同行为的版本;
- Thread是一个持久化的对话容器,用于在多次运行中保持状态;
- Run是针对Assitant的一次调用,执行时需要指定要使用的Assitant;
- Cron Job允许我们按照用户定义的时间表运行Assistant程序;
- StoreClient提供了远程调用注册的BaseStore的方法,用于在Graph执行过程中存储和检索数据。
由于Cron Job对订阅级别有限制,本系列文章后续对除此之外得五个对象进行深入介绍。