持久化02 —— 跨会话持久化 2.1 Checkpoint 的局限性2.1.1 问题1跨会话信息会丢失LangGraph 的 Checkpoint 机制提供了强大的短期记忆能力它能够自动保存工作流每个步骤的状态快照维持单次对话的完整上下文隔离不同线程(Thread)的执行状态简单示例(仅调用了下LLM)# 定义状态 class MessagesState(TypedDict): messages: Annotated[list[AnyMessage], operator.add] # 定义模型节点 model init_chat_model(gpt-4o-mini, temperature0) def llm_call(state: dict): LLM调用 return { messages: [ model.invoke([SystemMessage(content你是一个乐于助人的助手。)] state[messages]) ] } # 构建图 builder StateGraph(MessagesState) builder.add_node(llm_call, llm_call) builder.add_edge(START, llm_call) builder.add_edge(llm_call, END) # 编译图挂载内存检查点thread对话记忆 graph builder.compile(checkpointerInMemorySaver())检查点可以完美处理会话内记忆config1 {configurable: {thread_id: 1}} # 第一次对话 result1 graph.invoke({messages: [HumanMessage(content我爱吃汉堡推荐一家餐厅)]}, config1) # AI记得你爱吃汉堡 result2 graph.invoke({messages: [HumanMessage(content我爱吃什么?)]}, config1) result2[messages][-1].pretty_print() # 输出你提到你爱吃汉堡所以可以推测你喜欢美味的快餐和丰富的口味组合。如果你有其他喜欢的食物或菜系也可以告诉我我可以为你推荐更多相关的美食或餐厅想象一个多会话的AI助手场景:星期一用户首次对话星期二用户开启一个新对话问题就会出现AI不记得用户喜欢汉堡! 每次对话都要重新认识。# 星期一用户首次对话 config1 {configurable: {thread_id: day_1}} result1 graph.invoke({messages: [HumanMessage(content我爱吃汉堡推荐一家餐厅)]}, config1) 星期二用户开启一个新对话 config2 {configurable: {thread_id: day_2}} result2 graph.invoke({messages: [HumanMessage(content我爱吃什么?)]}, config2) result2[messages][-1].pretty_print() 输出我不知道你具体喜欢吃什么但可以根据一些常见的食物类型来猜测。比如有些人喜欢甜食如蛋糕和冰淇淋有些人喜欢咸食如薯条和披萨还有些人喜欢健康的食物如沙拉和水果。你可以告诉我你喜欢的食物类型我可以给你一些推荐2.1.2 现实世界的需求从单次对话”到终身服务例如一个智能客服系统具有以下实际业务需求识别VIP客户优先服务避免重复询问相同问题基于历史投诉优化服务仅是检查点无法满足这些需求graph builder.compile(checkpointerInMemorySaver()) config1 {configurable: {thread_id: query_1}} result1 graph.invoke({messages: [HumanMessage(content我的账户被冻结了)]}, config1) # 用户第一次投诉账户问题已解决。智能客服了如下过程 # - 搜集用户信息 # - 了解用户问题与需求 # - 处理问题 config2 {configurable: {thread_id: query_2}} result2 graph.invoke({messages: [HumanMessage(content我的账户又被冻结了)]}, config2) # 10天后用户第二次投诉账户问题。 # 由于智能客服不知道用户历史无法准确解决问题。还需再次了解前因后果如何做到【共享状态】的需求模型如精准识别客户、保留VIP客户关键历史记录是智能客服系统的关键。2.2 解决方案引入StoreStore 就像是一个长期记忆仓库支持在我们在执行过程中保存用户信息、偏好设置等长期数据以实现不同对话间信息的持久化共享。2.2.1 存储 vs 检查点检查点保存状态变化历史(时间线)存储保存结构化知识(数据库)实际上使用 Checkpoint Store 模式才能够真正实现理想中的AI 记忆系统2.2.2 AI应用架构的范式转变Store 的引入不是简单的功能增加而是 AI应用架构的范式转变:Store的引入真正能做到从关注单次交互 一 到关注用户生命周期从处理当前请求 一 到利用历史数据从通用回复 一 到深度个性化这种转变让AI从工具进化为伙伴真正实现智能服务的核心理念在正确的时间以正确的方式为正确的人提供正确的价值。2.3 跨会话持久化使用姿势要想使用store我们需要创建一个存储实例其也有【内存级存储】与相关【存储库存储】两种方式。例如内存级存储:from langgraph.store.memory import InMemoryStore store InMemoryStore()接着只需像以前一样使用 Checkpoints 和 Store 变量编译图表即可。graph builder.compile(checkpointercheckpointer,storestore)2.3.1 方式1内存存储2.3.1.1 Store 基本用法Store 本身是通过Namespace区分不同数据如下所示在LangGraph中其提供了一个简单的内存实现 InMemoryStore。想要进行存储需要1、先定义命名空间为了区分不同用户的记忆需要一个“命名空间”。这就像在数据库里为每个用户创建一个独立的文件夹。命名空间用于组织记忆通常按业务逻辑划分。一般用元组来定义命名空间。如:# 使用元组 - 层次清晰易于扩展 namespace1 (user_123, preferences, food) # 用户食物偏好 namespace2 (user_123, preferences, music) # 用户音乐偏好 namespace3 (user_123, conversations, 2025-05) # 用户某天的对话历史当在对话中获取到用户的重要信息时使用store.put()方法将内存保存到存储中的命名空间。该方法参数包含:namespace决定这个记忆属于谁以及是什么类型。memory_id是这个记忆条目的唯一键。memory_content是记忆的具体内容一个字典。from langgraph.store.memory import InMemoryStore store InMemoryStore() store.put(namespace, memory_id, memory_content)完整代码# 1. 导入并创建存储 store InMemoryStore() # 2. 定义命名空间Namespace # 命名空间用于组织记忆通常按业务逻辑划分例如按用户。 # 这里我们用一个元组用户ID记忆类型 user_id user_123 namespace (user_id, preferences) # 用户 user_123 的偏好记忆 # 3. 存入一条记忆Memory # 每条记忆需要一个唯一的 memory_id 和一个 value通常是字典 memory_id str(uuid.uuid4()) # 生成唯一ID如 abc-123-def-456 memory_value {favorite_food: 汉堡, allergy: 花粉} store.put(namespace, memory_id, memory_value) print(记忆已存入!) # 4. 读取记忆 # 可以搜索某个命名空间下的所有记忆 all_memories store.search(namespace) for mem in all_memories: print(mem.dict()) # 记忆对象转成字典查看 # 输出 记忆已存入! { namespace: [ user_123, preferences ], key: db826e33-c68c-4669-a79a-3579bff02ff1, value: { favorite_food: 汉堡, allergy: 花粉 }, created_at: 2025-12-03T08:16:14.13456800:00, updated_at: 2025-12-03T08:16:14.13457600:00, score: None }因使用元组作为命名空间故同样支持下面的搜索方式:all_memories store.search((user_id, ))2.3.1.2 在 LangGraph 中使用 Store内存存储适用于开发和测试程序重启后存储的数据会丢失。由于要加入Store需要在合适的地方加入与存储关键信息相关的代码。如我们可以在每次调用LLM前先进行信息收集然后带着收集到的共享信息进行LLM调用。因此流程变成了:这样两部分信息将会被收集一是用户发的消息二是通过工具调用返回的结果信息也会被采集。在编译图时直接添加编译参数store现在在任何一个节点的函数中都可以通过注入 store 参数来访问这个全局存储。1、新增提取用户信息节点在这个节点中我们需要根据【用户发的消息】和【工具调用返回的结果】来采集需要收集的信息。收集的信息需要使用 Store 进行存储。关键设计如下1.任何节点函数如果需要访问 Store可以通过在参数中声明storeBaseStore和configRunnableConfig来获取.2.在这里可以通过LLM提取用户信息因此定义结构化返回是很有必要的。# 定义结构化输出 class Person(BaseModel): 一个人的信息。 # 注意 # 1. 每个字段都是 Optional 可选的 — 允许 LLM 在不知道答案时输出 None。 # 2. 每个字段都有一个 description 描述 — LLM使用这个描述。 name: Optional[str] Field(defaultNone, description这个人的名字) height_in_meters: Optional[str] Field(defaultNone, description以米为单位的高度) favourite_food: Optional[list[str]] Field(defaultNone, description最喜欢的食物列表) model_with_structured model.with_structured_output(Person)读取对话历史 → 调用 LLM 结构化输出提取用户信息 → 存入前面讲的store记忆存储# 提取用户信息节点 def get_person_by_llm(state: MessagesState, config: RunnableConfig, *, store: BaseStore): 通过 LLM 提取用户信息 # 1. 先提取模型输出的结构化people_info people_info model_with_structured.invoke( [ SystemMessage( content你是一个提取信息的专家只从文本中提取我的相关信息不能提取别人的信息。如果你不知道要提取的属性的值属性值返回null。 ) ] state[messages][-3:] # 只查看state里最近3条消息 ) # 2. 再获取用户 ID划分命名空间 # 从运行配置拿到当前用户 id用来隔离记忆。 user_id config[configurable][user_id] # 保存用户基本信息-命名空间姓名、身高 namespace1 (user_id, info) # 保存用户偏好-命名空间爱吃的食物 namespace2 (user_id, preferences) # 3. 每次put前应判断是否存在再更新。否则会有多条记录被记录。这里简写 store.put( namespace1, str(uuid.uuid4()), { name: people_info.name, height: people_info.height_in_meters } ) store.put( namespace2, str(uuid.uuid4()), { favourite_food: people_info.favourite_food } ) return { llm_calls: state.get(llm_calls, 0) 1 }*在函数参数列表里代表后面所有参数必须用关键字传参keyword-only argument仅关键字参数不能按位置传。state: MessagesState——按位置传config: RunnableConfig——按位置传*——分隔符不是变量不接收任何值store: BaseStore—— 放在*之后只能用关键字方式传入不能按位置传问题每次执行这个节点都会新建一条记忆而不是更新旧记录正确逻辑注释里提到的先store.search()读取 namespace 里旧记忆 → 判断是否已有记录有就用同一个 key 做 put 覆盖更新没有才新增。这里先简写整体执行流程节点触发 → 读取 state 最近 3 词messageLLM 结构化抽取用户信息姓名 / 身高 / 爱吃食物从 config 拿到 user_id构建两个命名空间namespace将提取出来的信息存入 store基础信息放 info偏好放 preferences更新状态LLM 调用计数 1节点结束2、更新模型调用节点添加共享信息到提示词和上一个get_person_by_llm节点分工不同get_person_by_llm提取对话里的用户信息存入 store写记忆llm_call读取 store 里保存好的用户记忆交给 LLM 用来回答用户问题读记忆 LLM 生成回答新 llm_call 节点作用拿到以前用户保存的记忆基础信息 偏好把记忆放进系统提示词交给带工具能力的 LLM 生成回复LLM 可以自主判断要不要调用工具。def llm_call(state: MessagesState, config: RunnableConfig, *, store: BaseStore): LLM决定是否调用工具 # 1. 用和写入记忆 完全相同的命名空间 定位到该用户的两类记忆。 user_id config[configurable][user_id] namespace1 (user_id, info) namespace2 (user_id, preferences) # 2. 读取记忆 info_result store.search(namespace1) pref_result store.search(namespace2) # 3. return { messages: [ model_with_tools.invoke( [ SystemMessage( contentf你是一个乐于助人的助手支持调用工具进行搜索。 f查询 LLM 前可参考以下信息 f1. 用户基本情况: {info_result[0].value} f2. 用户偏好情况: {pref_result[0].value} ) ] state[messages] ) ], llm_calls: state.get(llm_calls, 0) 1 } #LangGraph 节点返回字典更新图 state 1. messages: [xxx]把 LLM 输出的消息追加进状态的消息列表 2. llm_callsLLM 调用次数计数器每进这个节点一次计数 1state.get(...,0)没有该字段默认初始值 0SystemMessage系统提示词把从 store 读出的用户历史记忆注入到 prompt 里。LLM 回答问题的时候就能 “看到” 用户的姓名、身高、喜好食物 state[messages]把系统提示 完整对话历史用户和助手之前的对话一起传给 LLM完整执行流程进入节点从 config 拿到user_id通过两个命名空间调用store.search读取用户基础信息、偏好记忆将读取到的记忆拼入 System 提示词把【系统提示 全部对话历史】传给带工具的 LLMLLM 根据问题自主选择直接回答 / 调用工具LLM 生成的消息写入 state 的 messagesLLM 调用计数 1节点结束3、构件图时加入新节点与调整边根据下图完成调整:agent_builder StateGraph(MessagesState) agent_builder.add_node(llm_call) agent_builder.add_node(tool_node) # 新增 信息获取节点 agent_builder.add_node(get_person_by_llm) # 调整边先拿到信息在去llm agent_builder.add_edge(START, get_person_by_llm) agent_builder.add_edge(get_person_by_llm, llm_call) agent_builder.add_conditional_edges( llm_call, should_continue, [tool_node, END] ) agent_builder.add_edge(tool_node, get_person_by_llm)到此代码已经改造完成完整代码如下# 步骤 1: 定义工具和模型 search TavilySearch(max_results4) tools [search] # 绑定工具 model init_chat_model(gpt-4o-mini, temperature0) model_with_tools model.bind_tools(tools) # 步骤 2: 定义状态 class MessagesState(TypedDict): messages: Annotated[list[AnyMessage], operator.add] llm_calls: int # 定义结构化输出 class Person(BaseModel): 一个人的信息。 name: Optional[str] Field(defaultNone, description这个人的名字) height_in_meters: Optional[str] Field(defaultNone, description以米为单位的高度) favourite_food: Optional[list[str]] Field(defaultNone, description最喜欢的食物列表) model_with_structured model.with_structured_output(Person) # 步骤 3: 新增提取信息节点 def get_person_by_llm(state: MessagesState, config: RunnableConfig, *, store: BaseStore): 通过 LLM 提取用户信息 # 1. 先提取 people_info model_with_structured.invoke( [ SystemMessage( content你是一个提取信息的专家只从文本中提取我的相关信息不能提取别人的信息。如果你不知道要提取的属性的值属性值返回null。 ) ] state[messages][-3:] # 只查看最近3条消息 ) # 2. 再保存 user_id config[configurable][user_id] # 保存用户基本信息 namespace1 (user_id, info) # 每次put前应判断是否存在再更新。否则会有多条记录被记录。这里简写 store.put( namespace1, str(uuid.uuid4()), { name: people_info.name, height: people_info.height_in_meters } ) # 保存用户偏好 namespace2 (user_id, preferences) store.put( namespace2, str(uuid.uuid4()), {favourite_food: people_info.favourite_food} # 省略追加逻辑: 先搜再更新 ) return { llm_calls: state.get(llm_calls, 0) 1 } # 步骤 4: 更新模型调用节点: 添加共享用户信息到提示词 def llm_call(state: MessagesState, config: RunnableConfig, *, store: BaseStore): LLM决定是否调用工具 # 搜索用户信息 user_id config[configurable][user_id] namespace1 (user_id, info) namespace2 (user_id, preferences) info_result store.search(namespace1) pref_result store.search(namespace2) return { messages: [ model_with_tools.invoke( [ SystemMessage( contentf你是一个乐于助人的助手支持调用工具进行搜索。 f查询 LLM 前可参考以下信息: f1. 用户基本情况: {info_result[0].value} f2. 用户偏好情况: {pref_result[0].value} ) ] state[messages] ) ], llm_calls: state.get(llm_calls, 0) 1 } # 步骤 5: 定义工具节点 tools_by_name {tool.name: tool for tool in tools} def tool_node(state: dict): 执行工具调用 result [] for tool_call in state[messages][-1].tool_calls: tool tools_by_name[tool_call[name]] observation tool.invoke(tool_call[args]) result.append(ToolMessage(contentobservation, tool_call_idtool_call[id])) return {messages: result} # 步骤 6: 构件图 def should_continue(state: MessagesState): 根据LLM是否调用工具来决定是应该继续循环路由到工具节点还是停止循环END messages state[messages] last_message messages[-1] # 如果LLM调用工具则执行操作 if last_message.tool_calls: return tool_node return END # 加入新节点并修改边 agent_builder StateGraph(MessagesState) agent_builder.add_node(llm_call) agent_builder.add_node(tool_node) agent_builder.add_node(get_person_by_llm) agent_builder.add_edge(START, get_person_by_llm) agent_builder.add_edge(get_person_by_llm, llm_call) agent_builder.add_conditional_edges( llm_call, should_continue, [tool_node, END] ) agent_builder.add_edge(tool_node, get_person_by_llm) # 编译图 agent agent_builder.compile(checkpointerInMemorySaver(), storeInMemoryStore())运行与验证同一用户但不同会话的请求# 第一次聊天 config1 {configurable: {thread_id: 1, user_id: 1}} result1 agent.invoke( {messages: [HumanMessage(content我叫李华我最爱吃汉堡。我的朋友叫小明他爱吃披萨)]}, config1 ) print(f\n调用 LLM 总次数: {result1[llm_calls]}次) for m in result1[messages]: m.pretty_print() # 过了几天新开对话线程同一个用户 config2 {configurable: {thread_id: 2, user_id: 1}} result2 agent.invoke( {messages: [HumanMessage(content给我推荐下餐厅)]}, config2 ) print(f\n调用 LLM 总次数: {result2[llm_calls]}次) for m in result2[messages]: m.pretty_print() # 输出 调用 LLM 总次数: 2次 Human Message 我叫李华我最爱吃汉堡。我的朋友叫小明他爱吃披萨 Ai Message 你好李华很高兴认识你。汉堡和披萨都是很受欢迎的美食。你和小明有没有一起去过什么好吃的地方呢或者你们有没有想尝试的新餐厅? 调用 LLM 总次数: 4次 Human Message 给我推荐下餐厅 Ai Message Tool Calls: tavily_search (call_btsV05aeqldqjaZNVgdlYRe9) Call ID: call_btsV05aeqldqjaZNVgdlYRe9 Args: query: 推荐汉堡餐厅 Tool Message {省略……} Ai Message 以下是一些推荐的汉堡餐厅: 1. **[Burger She Wrote](https://www.novacircle.com/zh-CN/spots/north-america/united-states/california/los-angeles-county/los-angeles/burger-she-wrote-9ea267)** - 位于洛杉矶这是一家小而温馨的餐厅以其美味的和牛汉堡而闻名。 2. **[Tripadvisor 上洛杉矶的最佳汉堡](https://cn.tripadvisor.com/Restaurants-g32655-zfd10907-zfn7231034-Los_Angeles_California-Hamburger.html)** - 包含多家受欢迎的汉堡餐厅如Bottega Louie和Eggslut后者以其鸡蛋汉堡而著称。 希望这些推荐能帮助你找到美味的汉堡!2.3.1.3 语义搜索Store 的强大之处在于它支持语义搜索而不仅仅是精确匹配。这意味着我们可以用自然语言问题来查找相关记忆。首先我们需要配置带嵌入模型的 Storestore InMemoryStore( index{ embed: init_embeddings(openai:text-embedding-3-small), # 使用OpenAI嵌入模型 dims: 1536, # 嵌入向量的维度 fields: [$], # 对value中的所有字段进行嵌入 } )[$] → 把food和allergy全部内容一起向量化 # 如果你只想单独对food字段做向量可以写fields : [$.food]在节点中可以这样搜索user_id config[configurable][user_id] # 前缀搜索匹配所有命名空间以user_id开头的记忆 namespace (user_id, ) # 在Store中进行语义搜索找出最相关的2个记忆 info_result store.search(namespace, query用户基本信息, limit2) pref_result store.search(namespace, query用户偏好信息, limit2)query用户基本信息语义查询文本不是关键词匹配底层会把用户基本信息转为向量和 store 内已经向量化好的记忆向量做相似度计算。会自动找出语义上最接近用户基本资料的记忆条目比如姓名、身高那条记忆limit2最多返回相似度最高的 2 条记忆2.3.2 方式2Postgres存储库Postgres 存储库适用于生产环境或需要状态持久化的场景。由于之前已经启动过PostgresSQL这里可以直接连接到数据库作为PostgresStore使用。只需在编译时设置store即可。【注意第一次使用 Postgres store 时需要调用 store.setup()】修改【内存存储】部分的代码将内存存储方式修改为 Postgres 存储库。DB_URI postgresql://postgres:bit192.168.100.233:5432/postgres with ( PostgresSaver.from_conn_string(DB_URI) as checkpointer, PostgresStore.from_conn_string(DB_URI) as store, ): # 第一次使用 Postgres 检查点时需要调用 checkpointer.setup() checkpointer.setup() # 第一次使用 Postgres store 时需要调用 store.setup() store.setup() # 编译图 agent agent_builder.compile(checkpointercheckpointer, storestore) # ...后续调用...模拟第一次聊天DB_URI postgresql://postgres:bit192.168.100.233:5432/postgres with ( PostgresSaver.from_conn_string(DB_URI) as checkpointer, PostgresStore.from_conn_string(DB_URI) as store, ): # 第一次使用 Postgres 检查点时需要调用 checkpointer.setup() checkpointer.setup() # 第一次使用 Postgres store 时需要调用 store.setup() store.setup() # 编译图 agent agent_builder.compile(checkpointercheckpointer, storestore) # 第一次聊天 config1 {configurable: {thread_id: 1, user_id: 1}} result1 agent.invoke( {messages: [HumanMessage(content我叫李华我最爱吃汉堡。我的朋友叫小明他爱吃披萨)]}, config1 ) print(f\n调用 LLM 总次数: {result1[llm_calls]}次) for m in result1[messages]: m.pretty_print()运行系统后可以看到postgres 中新增 store 相关表其中存放了用户基本的信息:再次验证同一用户但不同会话的请求DB_URI postgresql://postgres:bit192.168.100.233:5432/postgres with ( PostgresSaver.from_conn_string(DB_URI) as checkpointer, PostgresStore.from_conn_string(DB_URI) as store, ): # 第一次使用 Postgres 检查点时需要调用 checkpointer.setup() checkpointer.setup() # 第一次使用 Postgres store 时需要调用 store.setup() store.setup() # 编译图 agent agent_builder.compile(checkpointercheckpointer, storestore) # 第一次聊天 config1 {configurable: {thread_id: 1, user_id: 1}} result1 agent.invoke( {messages: [HumanMessage(content我叫李华我最爱吃汉堡。我的朋友叫小明他爱吃披萨)]}, config1 ) print(f\n调用 LLM 总次数: {result1[llm_calls]}次) for m in result1[messages]: m.pretty_print()注意执行前将以下代码注掉因为在存入store前并没有编写不存在存入存在更新的代码逻辑(只是演示)因此会将空的用户信息误存导致LLM调用前查出来空的。def get_person_by_llm(state: MessagesState, config: RunnableConfig, *, store: BaseStore): ... # 每次put前应判断是否存在再更新。否则会有多条记录被记录。这里简写 # store.put( # namespace1, # str(uuid.uuid4()), # { # name: people_info.name, # height: people_info.height_in_meters # } # ) # 保存用户偏好 # store.put( # namespace2, # str(uuid.uuid4()), # {favourite_food: people_info.favourite_food} # 省略追加逻辑: 先搜再更新 # ) ...执行结果调用 LLM 总次数: 3次 Human Message 给我推荐下餐厅 Ai Message Tool Calls: tavily_search (call_PkIzsQRuCS6KZM15g3noHgBU) Call ID: call_PkIzsQRuCS6KZM15g3noHgBU Args: query: 推荐汉堡餐厅 Tool Message {query: 推荐汉堡餐厅, follow_up_questions: None, answer: None, images: [], results: [{url: https://www.reddit.com/r/AskNYC/comments/1470o9z/best_burger_spot_in_nyc/?tlzh-hans, title: 纽约最好吃的汉堡店是哪家 : r/AskNYC, content: The Thompson 的Burger Joint 和Minetta Tavern 等被推荐。汉堡餐厅推荐来自1个月前。Smashed 和Korzo 等被推荐。纽约/布鲁克林最好的汉堡 以及 ...Read more, score: 0.7336813, raw_content: None}, {url: https://www.cosmopolitan.com/tw/lifestyle/food-and-drink/g44382807/hamburger-20230629/, title: 美國旅遊必吃7大人氣漢堡店IN-N-OUT最強勁敵, content: 1.The Habit Burger Grill · 2.Cheeseburger in Paradise · 3.Five Guys · 4.IN-N-OUT · 5.Shake Shack · 6.SmashburgerRead more, score: 0.61972505, raw_content: None}, {url: https://cn.tripadvisor.com/Restaurants-g60763-zfd10907-zfn7102345-New_York_City_New_York-Hamburger.html, title: 纽约市最佳汉堡, content: Ellens Stardust Diner · (23,569 条点评). 美式烹饪, 晚餐 ; Virgils Real BBQ - NYC · (4,968 条点评). 美式烹饪, 烧烤 ; 1. SMAC · (488 条点评). 快餐小吃, 美式烹饪 ; 2.Read more, score: 0.56719416, raw_content: None}, {url: https://mliesl.edu/contents/ch/%E7%BE%8E%E5%9B%BD%E6%9C%80%E4%BD%B3%E6%B1%89%E5%A0%A1%E8%BF%9E%E9%94%81%E6%8E%92%E5%90%8D/, title: 美国最佳汉堡连锁排名, content: 最受好评的汉堡连锁店之一- Five Guys - 被康涅狄格州、乔治亚州、蒙大拿州、内布拉斯加州、俄勒冈州、南卡罗来纳州、佛蒙特州和西弗吉尼亚州评为第一名。那是8个州同意。...Read more, score: 0.56690645, raw_content: None}], response_time: 0.92, request_id: 0ee8ee23-bbd3-48e2-b4c2-ca6d1c1dfe7a} Ai Message 以下是一些推荐的汉堡餐厅: 1. **[纽约最好吃的汉堡店](https://www.reddit.com/r/AskNYC/comments/1470o9z/best_burger_spot_in_nyc/?tlzh-hans)** - 推荐的汉堡店包括 The Thompson 的 Burger Joint 和 Minetta Tavern 等。 2. **[美国旅遊必吃7大人氣漢堡店](https://www.cosmopolitan.com/tw/lifestyle/food-and-drink/g44382807/hamburger-20230629)** - 包括 The Habit Burger Grill、Cheeseburger in Paradise、Five Guys、IN-N-OUT、Shake Shack 和 Smashburger。 3. **[纽约市最佳汉堡](https://cn.tripadvisor.com/Restaurants-g60763-zfd10907-zfn7102345-New_York_City_New_York-Hamburger.html)** - 推荐的餐厅有 Ellens Stardust Diner 和 Virgils Real BBQ - NYC。 4. **[美国最佳汉堡连锁排名](https://mliesl.edu/contents/ch/%E7%BE%8E%E5%9B%BD%E6%9C%80%E4%BD%B3%E6%B1%89%E5%A0%A1%E8%BF%9E%E9%94%81%E6%8E%92%E5%90%8D/)** - Five Guys 是最受好评的汉堡连锁店之一。 希望这些推荐能帮助到您! 如果您有特定的城市或地区需求,请告诉我。