怎样申请网站空间,找产品厂家用哪个app,软件服务网站设计费如何做分录,wordpress sticky深入解析 ‘Tree of Thoughts’ (ToT)#xff1a;利用 LangChain 构建支持回溯与并行路径搜索的思维树尊敬的各位技术同仁#xff1a;欢迎来到今天的讲座。我们将深入探讨一种前沿的、能显著提升大型语言模型#xff08;LLM#xff09;解决复杂问题能力的范式——思维树利用 LangChain 构建支持回溯与并行路径搜索的思维树尊敬的各位技术同仁欢迎来到今天的讲座。我们将深入探讨一种前沿的、能显著提升大型语言模型LLM解决复杂问题能力的范式——思维树Tree of Thoughts, ToT。不同于传统的链式思考Chain of Thought, CoTToT赋予LLM规划、探索和自我修正的能力使其能够更有效地应对需要多步骤推理、决策和评估的任务。我们将聚焦如何利用LangChain这一强大的框架从零开始构建一个支持回溯和并行路径搜索的ToT系统。1. 深入理解思维树 (Tree of Thoughts, ToT)1.1. ToT 的起源与核心思想大型语言模型在处理开放式、多步骤推理任务时常常面临挑战。传统的提示工程技术如零样本Zero-shot或少样本Few-shot提示以及链式思考Chain of Thought, CoT虽然在一定程度上提高了模型的推理能力但它们本质上是线性的。CoT提示模型生成一系列中间推理步骤但这些步骤是顺序生成且不可更改的。一旦模型沿着一个错误的推理路径前进它就很难“回头”修正。思维树Tree of Thoughts, ToT范式正是为了解决这一局限而提出的。ToT的核心思想是将问题解决过程建模为一个树状搜索过程其中每个节点代表一个“思维状态”Thought State每条边代表从一个思维状态到另一个思维状态的“思维动作”Thought Action。LLM不再仅仅是生成下一步的文本而是作为“思维生成器”和“思维评估器”在思维树中进行探索和决策。ToT赋予LLM以下关键能力分解问题: 将复杂问题分解为更小的、可管理的子问题或思维步骤。生成多路径: 为每个思维状态生成多个潜在的下一步思维从而探索不同的解决路径。评估与剪枝: 评估每个思维状态的质量或潜力并根据评估结果剪枝掉不具前景的路径。回溯与修正: 当一条路径被证明是死胡同时能够“回溯”到之前的决策点尝试其他路径。并行探索: 同时探索多条有前景的路径提高搜索效率。1.2. ToT 与 CoT 的对比为了更好地理解ToT的优势我们将其与CoT进行对比特性链式思考 (CoT)思维树 (ToT)结构线性序列一步接一步。树状结构每个节点可有多个子节点思维。探索单一路径深度优先。多路径广度优先或最佳优先可并行探索。纠错困难一旦出错难以回溯。通过评估、剪枝和回溯机制可放弃错误路径尝试新路径。决策局部贪婪每一步选择看似最优的下一步。全局优化通过评估整个路径的潜力来选择。适用场景简单、直接的推理任务或对错误容忍度较高的任务。复杂规划、多步骤推理、创意生成、需要高准确率的任务。LLM角色序列生成器。思维生成器、思维评估器、决策引擎。1.3. ToT 的核心概念在构建ToT系统时我们需要明确以下几个核心概念状态 (State)表示问题解决过程中的一个特定阶段。它包含了当前的所有相关信息例如已生成的思维序列、当前进展、分数或评估结果等。思维 (Thought)从一个状态到另一个状态的转变。它可能是一个具体的动作、一个子问题的解决方案、一个中间推理步骤或一个假设。行动 (Action)在ToT中“思维”通常就是“行动”。它描述了如何从当前状态生成新的思维。评估 (Evaluation)对一个思维状态或一个新生成的思维进行打分或判断其前景。评估函数是ToT成功的关键它指导搜索方向并用于剪枝。搜索策略 (Search Strategy)如何在思维树中探索路径。常见的策略包括深度优先搜索DFS、广度优先搜索BFS、最佳优先搜索Best-First Search、束搜索Beam Search等。我们的重点是支持并行路径搜索束搜索和通过路径管理实现的回溯。2. LangChain 在 ToT 中的战略地位LangChain是一个强大的框架旨在帮助开发者构建由LLM驱动的应用程序。其模块化、可组合和可编排的特性使其成为实现ToT的理想选择。2.1. 为什么选择 LangChainLLM集成: LangChain提供了与各种LLM如OpenAI、Anthropic等的无缝集成简化了API调用和管理。模块化组件: LangChain将LLM应用程序分解为可重用的组件如Prompt Templates、Chains、Agents、Tools、Output Parsers这与ToT的模块化设计高度契合。可编排性: 开发者可以轻松地将不同的组件组合成复杂的逻辑流构建多步骤的推理过程这正是ToT所需要的。输出解析: 提供了强大的输出解析器可以将LLM生成的自由文本结构化为JSON、Pydantic模型等便于程序化处理。灵活性: LangChain允许我们完全控制每个LLM调用的Prompt和参数这对于精细地指导ToT中的思维生成和评估至关重要。2.2. LangChain 组件与 ToT 构建块的映射LangChain的各种组件与ToT的核心概念有着天然的对应关系ToT 构建块LangChain 组件作用思维生成器ChatOpenAI/BaseChatModelPromptTemplateRunnableSequenceJsonOutputParser接收当前状态通过LLM生成多个新的潜在思维。思维评估器ChatOpenAI/BaseChatModelPromptTemplateRunnableSequenceJsonOutputParser接收一个思维状态通过LLM评估其质量、分数和是否为终止状态。状态管理自定义 Python 类如ThoughtState存储和管理每个思维状态的详细信息。搜索控制器自定义 Python 类如ToTSolver协调思维生成、评估、路径管理并行探索、剪枝、回溯。工具集成Tools/Agents(可选)在ToT的某个思维步骤中调用外部工具如计算器、数据库查询来获取信息或执行操作增强LLM的能力。通过这种映射我们可以清晰地看到LangChain如何为我们构建ToT系统提供坚实的基础。3. 构建 ToT 的核心组件现在我们将深入到代码层面构建ToT系统的基本组成部分。3.1. 思维状态 (ThoughtState) 的定义ThoughtState类是ToT系统的核心数据结构它封装了搜索树中每个节点的所有相关信息。import uuid from typing import List, Dict, Any, Optional from pydantic import BaseModel, Field class ThoughtState(BaseModel): 表示思维树中的一个节点思维状态。 thought_id: str Field(default_factorylambda: str(uuid.uuid4()), description当前思维状态的唯一ID。) current_thought: str Field(..., description当前状态的思维内容或采取的行动。) path_history: List[str] Field(default_factorylist, description从根节点到当前状态的思维历史序列。) score: float Field(0.0, description当前思维状态的评估分数。分数越高表示越有前景。) depth: int Field(0, description当前思维状态在树中的深度根节点为0。) is_terminal: bool Field(False, description当前状态是否为终止状态达到目标或死胡同。) parent_id: Optional[str] Field(None, description父思维状态的ID用于回溯。) metadata: Dict[str, Any] Field(default_factorydict, description存储与此状态相关的额外元数据。) def add_thought(self, new_thought: str) - None: 将新思维添加到路径历史中。 self.path_history.append(new_thought) def update_score(self, new_score: float) - None: 更新当前思维状态的评估分数。 self.score new_score def __str__(self) - str: return (fThought ID: {self.thought_id}n f Depth: {self.depth}n f Score: {self.score:.2f}n f Terminal: {self.is_terminal}n f Current Thought: {self.current_thought[:100]}...n f Path History Length: {len(self.path_history)}) def get_full_path(self) - str: 获取从根节点到当前状态的完整思维路径字符串。 return n.join(self.path_history)ThoughtState属性解析:thought_id: 唯一的标识符便于追踪和引用。current_thought: 代表当前节点所采取的行动或产生的具体思维。path_history: 存储从起始状态到当前状态的所有current_thought序列这是实现“回溯”信息追溯的关键。score: LLM评估器给出的分数用于衡量当前路径的潜力指导搜索方向。depth: 树的深度用于限制搜索范围和避免无限循环。is_terminal: 标记当前状态是否已达成目标或已陷入死胡同以便及时结束该路径的探索。parent_id: 指向父节点的ID用于在必要时追溯父节点信息虽然在我们的实现中回溯更多通过并行路径和剪枝实现但这个字段对调试和理解路径构成很有帮助。metadata: 灵活存储额外信息如特定任务的中间结果。3.2. 思维生成器 (ThoughtGenerator)思维生成器的任务是根据当前的思维状态利用LLM生成一系列可能的后续思维或行动。from langchain_core.prompts import PromptTemplate from langchain_core.output_parsers import JsonOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI from langchain_core.language_models import BaseChatModel # 假设已经配置了OPENAI_API_KEY环境变量 # os.environ[OPENAI_API_KEY] YOUR_API_KEY class ThoughtGenerator: 使用LangChain和LLM生成新的思维下一步行动或子问题。 def __init__(self, llm: BaseChatModel): self.llm llm self.output_parser JsonOutputParser() # 定义生成思维的Prompt模板 # 关键在于引导LLM生成多个、多样化的、结构化的思维 self.prompt PromptTemplate( template( 你是一个强大的问题解决助手。你正在解决一个复杂的问题当前路径如下n 历史步骤n{path_history}n 当前思维/进展{current_thought}nn 请根据以上信息提出 {num_thoughts} 个不同的、有前景的下一步思维或子问题。每个思维都应是具体可行的能够推动问题解决进程。n 请以JSON数组格式输出每个元素包含 thought (字符串) 和 reasoning (字符串)。n 示例输出n jsonn [n {{thought: 思考A, reasoning: 为什么思考A}},n {{thought: 思考B, reasoning: 为什么思考B}}n ]n n 请注意n 1. 每个思维都应是新的、未在历史中明确出现的。n 2. 确保思维是推动问题解决的积极步骤而不是重复或死胡同。n 3. 保持输出格式严格为JSON数组。n 输出n ), input_variables[path_history, current_thought, num_thoughts], partial_variables{format_instructions: self.output_parser.get_format_instructions()}, ) # 构建LangChain可运行链 self.chain ( { path_history: RunnablePassthrough(), current_thought: RunnablePassthrough(), num_thoughts: RunnablePassthrough(), } | self.prompt | self.llm | self.output_parser ) def generate_thoughts(self, state: ThoughtState, num_thoughts: int 3) - List[Dict[str, str]]: 根据当前状态生成多个潜在的思维。 try: # 将ThoughtState转换为Prompt所需的字典格式 inputs { path_history: state.get_full_path(), current_thought: state.current_thought, num_thoughts: num_thoughts, } # 调用LangChain链生成思维 generated_thoughts self.chain.invoke(inputs) # 确保输出是列表且每个元素有thought键 if not isinstance(generated_thoughts, list): print(fWarning: Generator returned non-list output: {generated_thoughts}) return [] return [t for t in generated_thoughts if thought in t] except Exception as e: print(fError generating thoughts: {e}) return []ThoughtGenerator关键点:Prompt Engineering: Prompt是核心。它需要清晰地指示LLM理解当前问题背景和历史路径。生成特定数量的、不同且有前景的下一步思维。以结构化JSON格式输出包含思维内容和生成理由。JsonOutputParser: 确保LLM的输出能够被程序正确解析这是自动化流程的关键。RunnableSequence: 将Prompt、LLM和OutputParser串联起来形成一个可调用的逻辑单元。3.3. 思维评估器 (ThoughtEvaluator)思维评估器的作用是评价一个思维状态的质量、潜力以及是否已达到终止条件。这是ToT进行搜索和剪枝的依据。class ThoughtEvaluator: 使用LangChain和LLM评估一个思维状态。 def __init__(self, llm: BaseChatModel): self.llm llm self.output_parser JsonOutputParser() # 定义评估思维的Prompt模板 # 关键在于引导LLM进行客观、全面的评估并给出结构化分数和判断 self.prompt PromptTemplate( template( 你是一个严谨的问题解决过程评估专家。你正在评估一个思维路径的进展和潜力。n 请评估以下思维路径及其当前状态n 历史步骤n{path_history}n 当前思维/进展{current_thought}nn 请根据以下标准进行评估n 1. **进展度 (Progress)**: 当前思维是否有效推动问题解决(0-10分)n 2. **可行性 (Feasibility)**: 当前思维是否实际可行没有明显的逻辑错误或死胡同(0-10分)n 3. **潜力 (Potential)**: 从当前状态继续发展有多大可能最终解决问题(0-10分)n 4. **终止判断 (Is Terminal)**: 当前思维是否已经达到了问题的解决方案或者已经明确陷入死胡同无法继续(True/False)nn 请以JSON格式输出评估结果包含 score (总分0-30), rationale (评估理由), 和 is_terminal (布尔值)。n 示例输出n jsonn {{ score: 25, rationale: 该路径进展良好具有较高潜力但尚未完成。, is_terminal: false }}n n 请注意n 1. score 是 Progress Feasibility Potential 的总和。n 2. 如果 is_terminal 为 True请在 rationale 中明确说明是达到解决方案还是陷入死胡同。n 输出n ), input_variables[path_history, current_thought], partial_variables{format_instructions: self.output_parser.get_format_instructions()}, ) # 构建LangChain可运行链 self.chain ( { path_history: RunnablePassthrough(), current_thought: RunnablePassthrough(), } | self.prompt | self.llm | self.output_parser ) def evaluate_thought(self, state: ThoughtState) - Dict[str, Any]: 评估一个思维状态返回分数和终止判断。 try: inputs { path_history: state.get_full_path(), current_thought: state.current_thought, } evaluation_result self.chain.invoke(inputs) # 确保输出包含必要的键 if not all(k in evaluation_result for k in [score, rationale, is_terminal]): print(fWarning: Evaluator returned incomplete output: {evaluation_result}) return {score: state.score, rationale: 评估失败或不完整。, is_terminal: False} return evaluation_result except Exception as e: print(fError evaluating thought: {e}) return {score: state.score, rationale: f评估异常: {e}, is_terminal: False}ThoughtEvaluator关键点:评估标准: Prompt中明确定义了评估的维度进展度、可行性、潜力指导LLM进行多维度思考。总分与终止判断: 期望LLM返回一个总分以及一个布尔值is_terminal来指示是否达到目标或死胡同。理由 (Rationale): 要求LLM给出评估理由这不仅有助于调试也增强了透明度。鲁棒性: 增加错误处理和默认返回值以应对LLM可能产生的非预期输出。4. ToT 搜索策略回溯与并行探索ToT的强大之处在于其搜索策略它能够有效地在思维树中探索并通过智能地管理路径来避免陷入局部最优或死胡同。4.1. 搜索空间与搜索策略概述ToT的搜索本质是在一个动态生成的树状结构中寻找一条最优路径这条路径由一系列思维构成最终达到问题的解决方案。由于LLM的生成能力这个树的广度和深度可以是巨大的。因此有效的搜索策略至关重要。我们主要关注并行路径探索Beam Search并通过其固有的特性和剪枝Pruning机制来间接实现“回溯”效果。4.1.1. 并行路径探索 (Beam Search)束搜索Beam Search是一种启发式图搜索算法它在每个步骤中扩展有限数量的最佳节点即“束宽”而不是所有可能的节点。这使得它在计算资源有限的情况下能够有效地探索广阔的搜索空间同时保持对最优解的倾向性。在ToT中束搜索的实现方式如下初始化: 从一个初始状态开始将其添加到活动路径列表。迭代扩展: 在每个迭代步骤中从活动路径列表中选择当前分数最高的beam_width条路径。对于每条选定的路径使用ThoughtGenerator生成多个新的候选思维。为每个新的候选思维创建新的ThoughtState对象。使用ThoughtEvaluator评估这些新的ThoughtState并更新它们的分数和终止状态。将所有新生成的、非终止且有前景的状态添加到临时的候选状态列表。排序与剪枝:将所有候选状态包括从上一轮继承下来的未被扩展的路径合并并按照分数从高到低进行排序。保留前beam_width个状态作为新的活动路径列表。将已标记为终止状态且达到目标的路径移到completed_paths列表。将已标记为终止状态但未达到目标死胡同的路径直接丢弃。这种方法允许我们同时探索多个有前景的路径避免过早地提交到一个可能错误的决策。4.1.2. 回溯的实现与理解在传统的算法中回溯意味着当一条路径无法达到目标时程序会撤销之前的选择返回到上一个决策点并尝试该决策点的其他未探索分支。在LLM驱动的ToT中由于LLM的每次生成都是独立的且我们通常不存储所有可能的生成选项因此实现传统意义上的“状态回滚”和“尝试之前未选择的选项”是复杂且效率低下的。然而ToT通过以下机制有效地实现了“回溯”的目的并行探索 (Parallel Exploration)这是最主要的“回溯”机制。由于我们使用束搜索同时维护beam_width条路径如果当前最佳路径在后续步骤中被证明是死胡同得分急剧下降或标记为终止但非目标它将自然地被其他并行探索的、更有前景的路径所取代。我们不需要显式地“回滚”状态只需要关注当前得分最高的几条路径。剪枝 (Pruning)ThoughtEvaluator会标记出is_terminalTrue且未达成目标的路径即死胡同。这些路径会被立即从活动路径列表中移除不再浪费计算资源。这相当于“剪掉”了错误的树枝从而避免了沿着错误路径继续前进这与回溯的目的相同。路径历史 (Path History)ThoughtState中的path_history记录了从根节点到当前状态的所有思维步骤。虽然这不是操作性的回溯但它提供了信息性的回溯。当一条路径失败时我们可以分析其path_history来理解失败的原因从而改进Prompt或搜索策略。因此在我们的LangChain ToT实现中回溯并非传统的程序状态回滚而是通过高效的并行搜索、智能的评估与剪枝来动态地调整搜索方向放弃无望的路径并转向更有潜力的替代路径。4.1.3. 剪枝 (Pruning)剪枝是ToT高效运行的关键。它避免了探索无限的或无意义的路径节省了计算资源并加速了找到解决方案的过程。剪枝的策略包括分数剪枝: 评估分数低于某个阈值的路径会被丢弃。深度剪枝: 超过预设最大深度的路径会被停止探索。重复剪枝: 如果一个思维状态或其核心内容在同一路径中重复出现可能表明陷入循环应予以剪枝。终止剪枝: 如果ThoughtEvaluator将一个状态标记为is_terminalTrue但非目标状态死胡同则该路径被剪枝。5. 整合构建ToT 求解器 (ToTSolver)现在我们将前面定义的组件整合起来构建一个完整的ToTSolver类它将协调思维生成、评估、路径管理和搜索过程。import heapq # 用于优先级队列方便管理beam_width import time class ToTSolver(BaseModel): ToT求解器编排思维生成、评估和搜索过程。 generator: ThoughtGenerator Field(..., description思维生成器实例。) evaluator: ThoughtEvaluator Field(..., description思维评估器实例。) llm: BaseChatModel Field(..., descriptionLangChain ChatModel实例用于生成器和评估器。) max_depth: int Field(5, description思维树的最大探索深度。) beam_width: int Field(3, description每一步保留的最佳路径数量。) max_iterations: int Field(10, description最大搜索迭代次数。) temperature: float Field(0.7, descriptionLLM的生成温度影响思维生成的多样性。) # 内部状态 active_paths: List[ThoughtState] Field(default_factorylist) completed_paths: List[ThoughtState] Field(default_factorylist) all_states: Dict[str, ThoughtState] Field(default_factorydict) # 存储所有已探索的状态用于调试和避免重复 class Config: arbitrary_types_allowed True # 允许非Pydantic模型作为字段类型 def _add_state(self, state: ThoughtState): 将状态添加到所有状态字典中并检查重复。 if state.thought_id in self.all_states: # 可以添加逻辑来处理重复状态例如更新分数或合并路径 pass self.all_states[state.thought_id] state def solve(self, initial_problem: str) - List[ThoughtState]: 启动ToT搜索过程以解决问题。 print(f--- ToT Solver Started for: {initial_problem} ---) start_time time.time() # 1. 初始化创建初始思维状态 initial_state ThoughtState( current_thoughtinitial_problem, path_history[f初始问题: {initial_problem}], score0.0, # 初始分数可以为0或由评估器给出 depth0 ) # 初始状态也需要评估一次以获得基础分数和是否为即时终止的判断 initial_eval self.evaluator.evaluate_thought(initial_state) initial_state.score initial_eval[score] initial_state.is_terminal initial_eval[is_terminal] self._add_state(initial_state) if initial_state.is_terminal: if initial_state.score 0: # 假设正分表示成功解决 self.completed_paths.append(initial_state) print(问题在初始状态即已解决) return self.completed_paths else: print(初始状态为死胡同无法解决。) return [] # 使用一个最小堆来管理active_paths便于快速获取最高分的beam_width个路径 # heapq是最小堆所以存储 (-score, thought_id) self.active_paths [initial_state] # 暂时用列表每次迭代重新排序和选择 for iteration in range(self.max_iterations): print(fn--- Iteration {iteration 1}/{self.max_iterations} (Depth: {self.active_paths[0].depth if self.active_paths else 0}) ---) if not self.active_paths: print(所有活动路径都已耗尽未能找到解决方案。) break # 2. 选择最佳路径进行扩展 (Beam Search的核心) # 对active_paths进行排序并选择前beam_width个 current_beam sorted(self.active_paths, keylambda x: x.score, reverseTrue)[:self.beam_width] # 清空active_paths准备填充新的状态 new_active_paths: List[ThoughtState] [] for i, state_to_expand in enumerate(current_beam): if state_to_expand.is_terminal: if state_to_expand.score 0: # 假设正分表示成功解决 self.completed_paths.append(state_to_expand) continue # 不再扩展已终止的路径 if state_to_expand.depth self.max_depth: print(f Path {state_to_expand.thought_id[:4]}... reached max depth. Pruning.) continue print(f Expanding path {i1} (ID: {state_to_expand.thought_id[:4]}..., Score: {state_to_expand.score:.2f}, Depth: {state_to_expand.depth})) print(f Current thought: {state_to_expand.current_thought[:80]}...) # 3. 生成新的思维 candidate_thoughts_data self.generator.generate_thoughts(state_to_expand, num_thoughtsself.beam_width) for thought_data in candidate_thoughts_data: new_thought_content thought_data.get(thought) if not new_thought_content: continue # 4. 创建新的思维状态 new_path_history state_to_expand.path_history [new_thought_content] new_state ThoughtState( current_thoughtnew_thought_content, path_historynew_path_history, scorestate_to_expand.score, # 初始继承父节点分数待评估器更新 depthstate_to_expand.depth 1, parent_idstate_to_expand.thought_id ) # 5. 评估新的思维状态 eval_result self.evaluator.evaluate_thought(new_state) new_state.score eval_result[score] new_state.is_terminal eval_result[is_terminal] new_state.metadata[evaluation_rationale] eval_result[rationale] self._add_state(new_state) # 记录所有状态 if new_state.is_terminal: if new_state.score 0: # 认为正分是成功解决 self.completed_paths.append(new_state) print(f Found solution path (ID: {new_state.thought_id[:4]}..., Score: {new_state.score:.2f})!) else: print(f Path {new_state.thought_id[:4]}... reached dead end (Score: {new_state.score:.2f}). Pruning.) else: new_active_paths.append(new_state) # 6. 更新活动路径列表 # 将所有新的非终止路径加入并与之前未被扩展的路径合并 self.active_paths new_active_paths # 重新排序并剪枝到beam_width确保我们总是在探索最有前景的路径 self.active_paths sorted(self.active_paths, keylambda x: x.score, reverseTrue)[:self.beam_width] if self.completed_paths: print(fn--- Found {len(self.completed_paths)} solution(s) ---) break # 找到解决方案后可以提前终止 end_time time.time() print(fn--- ToT Solver Finished (Time: {end_time - start_time:.2f}s) ---) return self.completed_paths if self.completed_paths else self.active_paths # 如果没找到解返回最有前景的路径ToTSolver核心逻辑解析:初始化: 创建初始ThoughtState并进行首次评估。主循环: 迭代max_iterations次或直到找到解决方案。束搜索 (Beam Search):在每次迭代开始时从active_paths中选择得分最高的beam_width条路径进行扩展。对于每条选定的路径调用ThoughtGenerator生成多个新的候选思维。为每个新思维创建ThoughtState并调用ThoughtEvaluator进行评估。根据评估结果将新状态添加到completed_paths如果已解决、直接丢弃如果死胡同或添加到new_active_paths。迭代结束后active_paths更新为new_active_paths中得分最高的beam_width条。剪枝: 在每次迭代中深度超过max_depth的路径、被评估为死胡同的路径都会被自动排除或不再扩展。通过beam_width的限制低分的路径也会被自然剪枝。回溯效果: 这里的“回溯”并非显式地撤销操作而是通过维护beam_width条并行路径并根据评估分数动态调整探索方向。如果一条路径变得不佳它会自然地在排序和剪枝过程中被淘汰让位给其他更有前景的路径。这是一种“软回溯”机制。Pydantic集成:ToTSolver本身也作为一个Pydantic模型便于参数管理和实例化。arbitrary_types_allowed True允许我们将ThoughtGenerator和ThoughtEvaluator实例作为字段。6. 案例分析利用 ToT 解决复杂规划问题让我们通过一个具体的例子来演示ToT的运作。问题设定: 为一个家庭聚餐设计一个三道菜的菜单需满足以下条件主菜是烤鸡 (必须)。前菜必须是素食。甜点必须包含水果。总准备时间不能超过2小时 (120分钟)。提供详细的菜品描述和准备时间。这是一个典型的规划问题需要多步骤决策、条件检查和全局评估。# 确保已经导入了所有必要的模块 # from typing import List, Dict, Any, Optional # from pydantic import BaseModel, Field # from langchain_core.prompts import PromptTemplate # from langchain_core.output_parsers import JsonOutputParser # from langchain_core.runnables import RunnablePassthrough, RunnableLambda # from langchain_openai import ChatOpenAI # from langchain_core.language_models import BaseChatModel # import uuid # import heapq # import time # 实例化LLM模型 # 可以选择不同的模型这里使用gpt-4o作为示例因为它在推理和遵循指令方面表现优秀 llm_model ChatOpenAI(modelgpt-4o, temperature0.7) # 实例化生成器和评估器 generator_instance ThoughtGenerator(llmllm_model) evaluator_instance ThoughtEvaluator(llmllm_model) # 实例化ToT求解器 tot_solver ToTSolver( generatorgenerator_instance, evaluatorevaluator_instance, llmllm_model, # 传入llm实例 max_depth4, # 初始问题 前菜 主菜 甜点 最终检查 5层所以max_depth4比较合适 beam_width2, # 每次保留2条最佳路径 max_iterations8 # 最大迭代次数 ) # 定义问题 problem_description ( 为一个家庭聚餐设计一个三道菜的菜单。需要满足以下条件n 1. 主菜是烤鸡。n 2. 前菜必须是素食。n 3. 甜点必须包含水果。n 4. 总准备时间不能超过2小时 (120分钟)。n 请提供详细的菜品描述和准备时间并最终计算总时间。 ) print(fn--- 开始解决菜单设计问题 ---n) solutions tot_solver.solve(initial_problemproblem_description) print(fn--- 解决方案探索完毕 ---) if solutions: print(fn找到 {len(solutions)} 个解决方案:) for i, sol in enumerate(solutions): print(fn--- 解决方案 {i1} (总分: {sol.score:.2f}) ---) print(完整路径) for step_idx, step_thought in enumerate(sol.path_history): print(f 步骤 {step_idx 1}: {step_thought}) # 尝试从最终路径中解析出菜单和时间 final_thought sol.current_thought print(fn最终思维n{final_thought}) # 可以在这里添加更复杂的解析逻辑来提取最终菜单 # 例如使用另一个LLM调用或正则表达式 # 简单的提示如果LLM在最终思维中提供了总时间 if 总准备时间 in final_thought: print(n**尝试提取最终菜单和时间**) print(final_thought) # 打印最终思维假设其中包含了完整菜单 else: print(n**无法从最终思维中自动提取完整菜单和时间。**) print(请检查Path History中的最后几个步骤来理解最终菜单。) else: print(未能找到满足所有条件的菜单解决方案。)运行模拟与输出预期:当上述代码运行时你将看到如下形式的输出具体内容会因LLM的随机性和具体Prompt设计而异--- ToT Solver Started for: 为一个家庭聚餐设计一个三道菜的菜单... --- --- Iteration 1/8 (Depth: 0) --- Expanding path 1 (ID: xxxx..., Score: x.xx, Depth: 0) Current thought: 为一个家庭聚餐设计一个三道菜的菜单。需要满足以下条件... # ThoughtGenerator生成 # 思想1: 确定前菜选项确保是素食且准备时间合理。 # 思想2: 确认主菜烤鸡的具体做法和预计准备时间。 # 思想3: 构思甜点需含水果且准备时间适中。 # Evaluator评估 # New state 1 (ID: yyyy..., Score: 18.00, Depth: 1) # New state 2 (ID: zzzz..., Score: 20.00, Depth: 1) # New state 3 (ID: aaaa..., Score: 19.00, Depth: 1) --- Iteration 2/8 (Depth: 1) --- Expanding path 1 (ID: zzzz..., Score: 20.00, Depth: 1) # 假设这是当前最佳路径 (确认主菜) Current thought: 确认主菜烤鸡的具体做法和预计准备时间。 # Generator生成: # 思想1: 选择经典迷迭香烤鸡预计准备时间90分钟烹饪时间60分钟实际操作时间30分钟。 # 思想2: 选择柠檬香草烤鸡预计准备时间80分钟烹饪时间50分钟实际操作时间30分钟。 # Evaluator评估并创建新状态 # Path yyyy... reached max depth. Pruning. (如果之前的路径深度达到限制) Expanding path 2 (ID: aaaa..., Score: 19.00, Depth: 1) # 假设这是第二最佳路径 (构思甜点) Current thought: 构思甜点需含水果且准备时间适中。 # Generator生成: # 思想1: 设计水果沙拉准备时间20分钟。 # 思想2: 考虑草莓慕斯准备时间45分钟。 # Evaluator评估并创建新状态 ... (后续迭代将继续扩展路径直到找到满足所有条件的完整菜单) --- Found 1 solution(s) --- --- ToT Solver Finished (Time: XX.XXs) --- 找到 1 个解决方案: --- 解决方案 1 (总分: 28.50) --- 完整路径 步骤 1: 初始问题: 为一个家庭聚餐设计一个三道菜的菜单... 步骤 2: 确定前菜、主菜和甜点的初步框架确保满足素食前菜、烤鸡主菜、水果甜点的要求。 步骤 3: 确定具体的前菜为“意式番茄罗勒沙拉”准备时间15分钟。 步骤 4: 确定主菜为“迷迭香蒜香烤鸡”准备时间90分钟。 步骤 5: 确定甜点为“缤纷水果杯”准备时间20分钟。 步骤 6: 检查总准备时间15 90 20 125分钟超出120分钟限制。需要调整。 步骤 7: 重新思考甜点或前菜。若将甜点改为“简易水果拼盘”准备时间10分钟。 步骤 8: 再次检查总准备时间15 90 10 115分钟。符合要求。 最终思维 最终菜单 前菜意式番茄罗勒沙拉 (准备时间15分钟) - 新鲜番茄、罗勒叶、马苏里拉奶酪丁淋上橄榄油和黑醋清爽开胃。 主菜迷迭香蒜香烤鸡 (准备时间90分钟) - 整鸡以迷迭香、大蒜、柠檬腌制后烤制香气扑鼻外酥里嫩。 甜点简易水果拼盘 (准备时间10分钟) - 精选时令水果切块如草莓、蓝莓、哈密瓜、奇异果色彩丰富健康美味。 总准备时间15 90 10 115分钟。满足所有条件。 **尝试提取最终菜单和时间** 最终菜单 前菜意式番茄罗勒沙拉 (准备时间15分钟) - 新鲜番茄、罗勒叶、马苏里拉奶酪丁淋上橄榄油和黑醋清爽开胃。 主菜迷迭香蒜香烤鸡 (准备时间90分钟) - 整鸡以迷迭香、大蒜、柠檬腌制后烤制香气扑鼻外酥里嫩。 甜点简易水果拼盘 (准备时间10分钟) - 精选时令水果切块如草莓、蓝莓、哈密瓜、奇异果色彩丰富健康美味。 总准备时间15 90 10 115分钟。满足所有条件。在这个例子中ToT系统通过多步探索和自我修正最终找到了一个满足所有条件的菜单。其中步骤6展示了ToT的“回溯”效果当发现总时间超限时它并未卡死而是“尝试”了修改甜点方案最终找到了符合要求的解决方案。这正是通过评估、剪枝隐式放弃超时的路径和并行探索同时考虑其他菜单组合共同实现的效果。7. ToT 的进阶考量与最佳实践ToT并非万能其性能高度依赖于精心的设计和调优。7.1. Prompt 工程的艺术清晰的指令: 确保生成器和评估器的Prompt非常具体明确要求输出的格式、内容和评估标准。角色扮演: 让LLM扮演“专家”角色例如“你是一个经验丰富的规划师”“你是一个严谨的评估者”可以引导其更好地执行任务。Few-shot 示例: 提供高质量的输入-输出示例可以显著提高LLM遵循指令的能力和输出质量。在ThoughtGenerator和ThoughtEvaluator的Prompt中加入示例输出JSON结构是强制LLM输出特定格式的有效方法。链式思考增强: 在生成器Prompt中可以要求LLM在提出新思维的同时也给出其“为什么选择这个思维”的理由这本身就是一种CoT有助于生成更合理的思维。7.2. 鲁棒性与错误处理LLM幻觉与不一致: LLM有时会生成不准确或与事实不符的信息。评估器需要能够识别并惩罚这类幻觉。输出格式验证:JsonOutputParser虽然强大但LLM仍可能偶尔输出不符合JSON格式的文本。在代码中增加try-except块和额外的验证逻辑如检查字典中是否存在预期的键是必要的。重试机制: 对于LLM调用失败例如API错误或格式错误可以实现简单的重试机制。7.3. 性能与成本API 调用优化: 每次生成和评估都需要调用LLM API这可能带来显著的延迟和成本。并行调用: 考虑使用asyncio和ainvoke来并行执行多个LLM调用尤其是在生成多个思维或评估多个状态时。缓存: 对于重复的输入可以缓存LLM的响应。模型选择: 较小的、更快的模型如GPT-3.5 Turbo可以在某些步骤中替代更强大的模型以平衡成本和性能。参数调优:max_depth、beam_width、max_iterations等参数需要根据具体问题和可用资源进行仔细调整。过大的参数会导致计算量激增过小则可能无法找到最优解。7.4. 人机协作 (Human-in-the-Loop)在某些关键决策点引入人类的判断可以显著提高ToT的效率和准确性。例如当LLM评估器无法做出明确判断时可以暂停搜索并请求人类输入。7.5. 与其他搜索算法的融合ToT可以与更复杂的搜索算法结合A* 搜索: 如果能够定义一个合适的启发式函数Heuristic Function可以将ToT与A*搜索结合以更智能地引导路径选择。蒙特卡洛树搜索 (MCTS): MCTS在游戏AI中表现出色它通过模拟和回溯来评估节点潜力。将MCTS的探索和利用机制引入ToT可以进一步增强其在复杂决策任务中的能力。8. 思维树的未来展望与应用潜力我们今天构建的这个LangChain ToT系统展示了LLM如何从简单的文本生成器转变为一个具备规划、推理和自我修正能力的智能代理。这种范式在解决需要复杂逻辑、多步骤决策和创造性探索的问题上展现出巨大的潜力。ToT的应用场景极为广泛包括但不限于复杂规划与调度: 如项目管理、供应链优化、机器人路径规划。代码生成与调试: 生成多条代码实现路径评估其正确性和效率甚至辅助调试。科学研究与实验设计: 提出多个实验假设设计验证方案并评估结果。创意写作与内容生成: 构思故事情节、角色发展探索不同的叙事路径。法律与医疗诊断: 基于信息生成多种推理路径评估每种可能性。随着LLM能力的不断增强和ToT等高级推理框架的成熟我们正逐步迈向一个由AI辅助解决更深层次、更复杂问题的时代。LangChain作为连接LLM与应用逻辑的桥梁无疑将在这个过程中扮演核心角色。