LangChain和MAF-02]基本编程模式的差异(下篇) 会话保持当目前位置我们演示的Agent都是在一个单一的请求中执行的也就是说我们在调用Agent的RunAsync方法时传入了一个输入消息Agent会根据这个输入消息来执行推理并将最终的输出结果返回给我们。但是在实际应用中Agent往往需要在多个请求之间保持会话状态以便能够在不同的请求中共享上下文信息从而实现更复杂的交互和推理。LangChain通过Thread来实现会话保持而MAF则通过Session来实现会话保持。我们将在下一章中介绍MAF中的Session机制以及它与LangChain中的Thread机制之间的对比。1.1 LangChain如下的示例代码演示了如何在LangChain中实现会话保持。我们首先创建了一个Agent并在第一次调用ainvoke方法时传入一个输入消息来执行推理。然后我们再次调用ainvoke方法并传入一个新的输入消息来继续执行推理。由于我们在第一次调用ainvoke方法时指定了一个Thread IDthread-1所以第二次调用ainvoke方法时只需要指定同样的Thread IDthread-1就可以了这样Agent就会将两次调用关联到同一个Thread中从而实现会话保持。由于LangChain采用基于Checkpoint的状态管理机制所以我们还需要为Agent指定一个Checkpointer来保存和加载Thread的状态。在这个示例中我们使用了MemorySaver作为Checkpointer它会将Thread的状态保存在内存中。from langchain.agents import create_agent from langchain_openai import ChatOpenAI from langchain.tools import tool from langchain_core.runnables import RunnableConfig from langgraph.checkpoint.memory import MemorySaver from dotenv import load_dotenv import asyncio load_dotenv() tool def get_weather(location: str) - str: 根据给定的位置获取当前天气。 return f所在地 {location} 的天气是晴天最高气温25°C。 async def main(): agent create_agent( modelChatOpenAI(modelgpt-5.2-chat), tools[get_weather], checkpointerMemorySaver(), ) config:RunnableConfig { configurable:{ thread_id:thread-1, } } result await agent.ainvoke( input {messages:[{role:user,content:根据当前苏州的天气给我一些穿衣建议。}]}, configconfig) print(result[messages][-1].content) print(f\n{- * 50}\n) result await agent.ainvoke( input {messages:[{role:user,content:偏商务一点}]}, configconfig) print(result[messages][-1].content) asyncio.run(main())对于第二次调用ainvoke方法来说我们指定的问题是偏商务一点这个问题本身是没有上下文信息的Agent无法根据这个问题来生成有意义的回答。但是由于我们在第一次调用ainvoke方法时指定了一个Thread IDthread-1所以第二次调用ainvoke方法时只需要指定同样的Thread IDthread-1就可以了这样Agent就会将两次调用关联到同一个Thread中从而实现会话保持。Agent在执行第二次调用时会将第一次调用的输入消息和输出结果作为上下文信息来推理所以它能够根据第一次调用的结果来理解第二次调用的问题并生成一个有意义的回答。第一轮输出根据当前苏州的天气情况**晴天最高气温约 25°C**给你一些穿衣建议 ### 白天穿搭 - **上衣**短袖T恤、薄衬衫、POLO衫都很合适 - **下装**牛仔裤、休闲裤、薄款长裤或半身裙 - **鞋子**运动鞋、休闲鞋、帆布鞋都很舒适 ### 早晚/室内 - 早晚可能稍微偏凉或室内空调较足 - 可随身带一件 **薄外套 / 针织开衫 / 防晒衣** ### ☀️ 其他建议 - 晴天紫外线可能偏强**可戴帽子、太阳镜** - 户外活动多的话**记得防晒** 如果你是要**通勤、出游、运动**或有特殊场合我也可以帮你搭配得更具体一些。 --- 好的偏**商务风**的话可以这样穿既正式又不会太热 ### 男士商务休闲25°C 适合 - **上衣** - 浅色衬衫白色 / 浅蓝 / 浅灰**长袖可卷袖** - 或薄款商务POLO衫 - **下装** - 修身西裤 / 商务休闲裤面料轻薄、透气 - **外搭**可选 - 薄款西装外套不必全程穿空调房用 - **鞋子** - 皮鞋德比 / 乐福鞋或干净的商务休闲皮鞋 - **配件** - 皮带同色系腕表简约即可 ### 女士商务休闲 - **上衣** - 雪纺或真丝感衬衫浅色系更清爽 - **下装** - 西装裤 / 直筒裤 / 及膝半身裙 - **外搭** - 薄款西装 / 针织小外套 - **鞋子** - 低跟鞋 / 乐福鞋 / 尖头平底鞋 - **配色** - 米白、浅灰、浅蓝 深色下装显得专业又不闷热 ### ✅ 小提醒 - 避免厚西装、深色全套容易闷 - 面料优先选 **棉、羊毛混纺、天丝、亚麻混纺** - 苏州湿度偏高注意透气和抗皱 如果你告诉我**男/女、是否见客户、是否需要穿西装**我可以直接给你一套“今天就能照穿”的完整搭配。第二轮输出好的偏**商务一点**的话在苏州目前**晴天、最高 25°C**的情况下可以这样穿既专业又不闷热 ### 男士商务/商务休闲 - **上装** - 薄款长袖衬衫白色、浅蓝、浅灰 - 如果更正式可选**免烫棉或轻薄西装衬衫** - **外搭** - **薄西装 / 商务休闲西装**无内衬或半衬更舒适 - 不穿外套也可但建议随身备一件应对空调 - **下装** - 轻薄西裤、商务休闲裤 - **鞋子** - 透气皮鞋、乐福鞋Loafer ### 女士商务/商务休闲 - **上装** - 雪纺或真丝衬衫 - 薄针织上衣 衬衫领设计 - **外搭** - **薄西装外套 / 短款西装**浅色系不显闷 - **下装** - 西装裤、直筒裤 - 过膝或及膝的通勤裙 - **鞋子** - 低跟单鞋、穆勒鞋、乐福鞋 ### 颜色与面料建议 - **颜色**白、米色、浅灰、藏蓝、雾蓝清爽又专业 - **面料**棉涤纶混纺、羊毛混纺、天丝**透气不易皱** - **避免**厚西装、深色全套、化纤不透气面料 ### ✅ 小加分项 - 合身剪裁比厚重更显商务感 - 夏天商务更推荐**“轻西装 衬衫”**而非全套正装 如果你告诉我**男/女、是否需要见客户、偏正式还是偏休闲**我可以直接帮你搭一整套。1.2 MAF与LangChain在RunnableConfig中指定Thread ID来实现会话保持不同MAF在创建Agent时就提供了一个CreateSessionAsync方法来创建一个Session对象这个Session对象就代表了一个会话我们可以在调用Agent的RunAsync方法时将这个Session对象作为参数传入这样Agent就会将这个Session对象与当前的推理过程关联起来从而实现会话保持。using Azure.AI.Projects; using Azure.Identity; using dotenv.net; using Microsoft.Extensions.AI; using OpenAI.Chat; using OpenAI.Responses; using System.ComponentModel; DotEnv.Load(); var model Environment.GetEnvironmentVariable(MODEL)!; var apiKey Environment.GetEnvironmentVariable(API_KEY)!; var foundryProjectUrl Environment.GetEnvironmentVariable(FOUNDRY_PROJECT_URL)!; var aiProjectClient new AIProjectClient( endpoint: new Uri(foundryProjectUrl), tokenProvider: new DefaultAzureCredential()); var agent aiProjectClient.AsAIAgent( model: model, instructions: You are a helpful assistant., tools: [AIFunctionFactory.Create(GetWeather)]); var session await agent.CreateSessionAsync(); var result await agent.RunAsync(根据当前苏州的天气给我一些穿衣建议。, session); Console.WriteLine(result); Console.WriteLine(new string(-, 50)); result await agent.RunAsync(偏商务一点, session); Console.WriteLine(result); [Description(获取指定位置的天气信息)] static string GetWeather([Description(天气查询所在的位置)] string location) $ {location}当前添加晴天气温25°C。;我们在第一次调用RunAsync方法时创建了一个Session对象并将其传入到RunAsync方法中来执行推理。然后我们再次调用RunAsync方法并将同一个Session对象传入到RunAsync方法中来继续执行推理。由于我们传入的是同一个Session对象所以两次调用的推理过程就被关联到了同一个会话中从而实现了会话保持。Agent在执行第二次调用时会将第一次调用的输入消息和输出结果作为上下文信息来推理所以它能够根据第一次调用的结果来理解第二次调用的问题并生成一个有意义的回答。第一轮输出根据**当前苏州的天气情况晴天气温约 25°C**给你一些穿衣建议 ### 日常穿搭建议 - **上衣**短袖T恤、薄衬衫、POLO衫都很合适 - **下装**牛仔裤、休闲裤、薄款长裤或半身裙 - **鞋子**运动鞋、休闲鞋、透气的帆布鞋即可 ### 额外小建议 - 早晚可能会稍微有点凉可以**备一件薄外套或防晒衣** - 晴天紫外线较强**外出可戴帽子或墨镜**注意防晒 - 如果需要长时间户外活动选择**透气、吸汗的面料**会更舒适 如果你是通勤、出游还是运动场景我也可以帮你搭配得更具体一些 第二轮输出好的偏**商务/商务休闲**风格在苏州 **25°C 晴天** 的情况下可以这样穿 ### 男士商务建议 - **上衣** - 薄款长袖衬衫白色、浅蓝、浅灰 - 如果环境不太正式可选择高质感短袖衬衫 - **外套** - 薄款西装外套可随时脱下 - **下装** - 轻薄西裤或商务休闲裤 - **鞋履** - 透气的皮鞋、德比鞋或乐福鞋 - **细节** - 可不打领带整体更清爽干练 - 选择透气面料如羊毛混纺、棉弹力 ### 女士商务建议 - **上衣** - 雪纺/真丝衬衫、简约设计的短袖西装上衣 - **外套** - 薄款西装外套空调房很实用 - **下装** - 西装裤、直筒裤或及膝一步裙 - **鞋履** - 中低跟单鞋、乐福鞋 - **配色** - 米白、浅灰、藏蓝、雾蓝等既商务又不闷热 ### ☀️ 天气对应小提醒 - 25°C 偏暖**避免厚重面料** - 户外走动多优先选择**透气、防皱**的商务单品 - 苏州湿度偏高尽量避免全涤、闷热材质 如果你是**非常正式的商务会议 / 日常办公室 / 客户拜访**告诉我场合我可以再帮你精确到“要不要西装外套”的程度。对于LangChain来说我们只需要指定thread_id, Agent会利用它加载存储的Checkpoint来恢复状态。但是对于MAF来说Session对象本身就是一个状态容器但是这个对象的生命周期一般是短暂的而且基于内存的状态无法解决分布式状态一致性的问题所以依然需要一个中心化的状态存储。但是上面的这个例子是唯有问题的因为AIProjectClient默认使用的是有状态的Response接口服务端会自动维护Session状态。如果采用无状态的Completion接口就需要在创建按Agent的时候指定一个ChatHistoryProvider。下面演示程序的Agent是通过调用OpenAIClient的GetChatClient方法来创建的这个方法默认采用Completion接口来调用Chat模型所以它是无状态的我们需要在创建Agent的时候指定一个InMemoryChatHistoryProvider来维护会话状态。由于InMemoryChatHistoryProvider只是在进程内利用内存保存状态所以适合单机或开发环境使用。var model Environment.GetEnvironmentVariable(MODEL)!; var apiKey Environment.GetEnvironmentVariable(API_KEY)!; var openAIUrl Environment.GetEnvironmentVariable(OPENAI_URL)!; var openAIClient new OpenAIClient( credential: new ApiKeyCredential(key: apiKey), options: new OpenAIClientOptions { Endpoint new Uri(openAIUrl) }); var agent openAIClient .GetChatClient(model: model) .AsAIAgent(options: new ChatClientAgentOptions { ChatHistoryProvider new InMemoryChatHistoryProvider(), ChatOptions new ChatOptions { Tools [AIFunctionFactory.Create(GetWeather)] } });2. 流程编排对于一些复杂的任务我们不能完全依赖LLM我们规划更理想的方式我们自行设计整个推理流程。对于LangChain我们可以利用LangGraph构建一个具有任意结构的状态图来定义推理流程对于MAF我们可以利用Workflow来构建一个具有任意结构的流程图来定义推理流程。无论是LangGraph还是Workflow它们本质上都是一样的。2.1 LangChain