XAgent 数据结构详解:TaskSearchTree 任务搜索树的实现原理与实战
发布时间:2026/9/25 7:12:42来源:尧图网络
AI Agent大模型后端任务调度【免费下载链接】XAgentAn Autonomous LLM Agent for Complex Task Solving项目地址https://gitcode.com/gh_mirrors/xa/XAgent点击查看免费下载TaskSearchTree 是 XAgent 内部用于组织复杂任务求解过程的核心树状数据结构它以ToolNode为节点记录 Agent 在解决每个子任务时逐步调用工具、产生思考与输出结果的完整链路。本文以 XAgent/data_structure/tree.py 与配套文档 Markdown_Docs/XAgent/data_structure/tree.md 为骨架结合节点实现与 ReACT 搜索算法源码深入讲解树的构造、深度/子树统计、父子关系建立以及它在真实任务执行中的调用方式帮助你理解 XAgent 是如何把一次次的 LLM 推理与工具调用沉淀成一棵可回溯、可统计、可提交的任务树。TaskSearchTree 类概览一棵承载任务搜索行为的树在 XAgent 的代码库中TaskSearchTree被定义在 XAgent/data_structure/tree.py其类注释明确说明TaskSearchTree 表示一棵具有特定任务搜索行为specific task searching behavior的树数据结构。它的职责不是通用意义上的多叉树工具类而是为内层循环搜索算法inner loop search提供一棵记录当前子任务从开始到结束每一步操作的链式树。类定义如下class TaskSearchTree: TaskSearchTree represents a tree data structure with specific task searching behavior. Attributes: root (ToolNode): Root node of the tree. now_expand_num (int): Maintains current expanding number for nodes during traversal. def __init__(self): self.root: ToolNode ToolNode() self.root.expand_num 0 self.now_expand_num 1从属性设计上可以看到它的两个核心成员属性类型语义rootToolNode树的根节点默认是一个新建的空ToolNode其expand_num被固定为 0now_expand_numint遍历过程中维护的当前扩展编号用于给新加入的节点按扩展顺序编号与 Plan 树的区别两种树各司其职XAgent 中还存在另一棵树——由 XAgent/data_structure/plan.py 中的Plan类构成的计划树Plan Tree它管理任务计划含子任务 ID、状态、父子关系。而TaskSearchTree管理的是每个子任务内部的执行过程当某个子任务被真正执行时Agent 每推理并调用一次工具就会在树上新增一个节点。因此两者是计划级与执行级两个不同粒度的树结构读者不应混淆。构造方法__init__初始化根节点与扩展编号__init__方法不接收任何参数内部只做三件事def __init__(self): self.root: ToolNode ToolNode() self.root.expand_num 0 self.now_expand_num 1创建根节点直接实例化一个ToolNode并赋给self.root。根节点代表任务尚未开始的初始状态它不携带任何真实的 Agent 行为数据。根节点不参与扩展self.root.expand_num 0将根节点的扩展编号固定为 0表示根节点本身不会被当作一次扩展。从 1 开始计数self.now_expand_num 1表示当前下一个将要被扩展的节点编号为 1即树中第一个真实操作节点将从编号 1 开始。注意点沿用文档说明并结合源码该函数无参数创建TaskSearchTree()即可完成初始化初始化的根节点默认不会被扩展如果业务上需要根节点也参与扩展可以通过修改其expand_num属性实现不过在当前 ReACT 实现中根节点始终只作为起始锚点now_expand_num表示下一个可分配的扩展编号它随每次建立父子关系自增实际反映树上真实节点不含根的数量。查询方法get_depth与get_subtree_sizeTaskSearchTree的深度与子树大小查询都采用委托给根节点的实现方式def get_depth(self): return self.root.get_depth() def get_subtree_size(self): return self.root.get_subtree_size()这里的关键在于树本身不维护任何统计信息所有统计逻辑都定义在ToolNode上见 XAgent/data_structure/node.py。ToolNode 上的深度计算ToolNode.get_depth通过递归回溯父节点计算深度def get_depth(self): if self.father None: return 0 return self.father.get_depth() 1根节点的father为None因此深度为0每个子节点深度 父节点深度 1由于TaskSearchTree.get_depth()委托给self.root.get_depth()返回的正是整棵树的最大深度。使用注意get_depth依赖父子关系被正确建立即father指针正确否则计算结果会出现偏差同时因为它采用递归实现极端情况下过深的链可能引起递归开销需要配合配置中的max_subtask_chain_length限制链长详见后文。ToolNode 上的子树大小计算ToolNode.get_subtree_size采用递归累加的方式统计以当前节点为根的子树节点总数def get_subtree_size(self): if self.children []: return 1 now_size 1 for child in self.children: now_size child.get_subtree_size() return now_size叶子节点children为空子树大小为1非叶子节点的大小 自身 1 所有子节点子树大小的累加对TaskSearchTree而言调用get_subtree_size()即得到整棵任务树的总节点数。值得注意的语义细节在ToolNode层面子树大小包含当前节点自身叶子返回 1而关联文档对TaskSearchTree.get_subtree_size的说明中提到子树的节点数不包括根节点本身这一说法与node.py的实现存在表述差异。以源码为准TaskSearchTree.get_subtree_size()返回的是root.get_subtree_size()其中根节点计入统计根没有子节点时返回 1。读者在实际阅读旧文档或调试时应以 XAgent/data_structure/node.py 的实际行为为准避免被注释误导。建边方法make_father_relation建立父子关系并编号make_father_relation(father, child)是树从单节点生长为链/树的唯一入口源码如下def make_father_relation(self, father, child): if not (isinstance(father, ToolNode) and isinstance(child, ToolNode)): raise TypeError(Father and child both need to be instances of ToolNode.) child.expand_num self.now_expand_num self.now_expand_num 1 child.father father father.children.append(child)其执行流程分为三步类型校验father与child必须同时是ToolNode实例否则抛出TypeError提示信息为Father and child both need to be instances of ToolNode.分配扩展编号把当前的now_expand_num写入child.expand_num然后now_expand_num 1从而保证树中每个真实节点都拿到唯一的、按加入顺序递增的扩展编号双向建边将child.father指向father并把child追加到father.children列表中完成父认子、子认父的双向关联。注意使用前必须确保father与child节点均已创建并存在于树中传入非ToolNode类型会直接抛异常因此调用方如 ReACT 算法总是用agent.message_to_tool_node(...)生成的ToolNode来调用expand_num不仅用于标识顺序还能配合now_expand_num推导当前树上真实扩展节点的数量。节点基石ToolNode 的完整结构要真正用好TaskSearchTree必须理解其节点类型ToolNode。它继承自抽象基类Node见 XAgent/data_structure/node.py初始化时定义了如下字段self.father: ToolNode None self.children: list[ToolNode] [] self.expand_num 0 self.data { content: , thoughts: { properties: { thought: , reasoning: , plan: , criticism: , }, }, command: { properties: { name: , args: , }, }, tool_output: , tool_status_code: ToolCallStatusCode.TOOL_CALL_SUCCESS, } self.history: MessageHistory MessageHistory() self.workspace_hash_id 各字段含义字段类型说明fatherToolNode父节点指针childrenlist[ToolNode]子节点列表expand_numint扩展顺序编号由make_father_relation分配datadict节点核心数据内容、thoughts思考/推理/计划/批评、command命令名与参数、工具输出、工具调用状态码historyMessageHistory该节点对应的消息历史见 XAgent/message_history.pyworkspace_hash_idstr工作区哈希 ID用于关联文件系统快照此外ToolNode还提供两个对树的运行至关重要的方法process属性从当前节点一路回溯到根节点把沿途每个节点的data按根→当前顺序拼成一个列表供 ReACT 算法构造你已经完成的步骤提示词使用to_json对data做深拷贝并把tool_status_code枚举值转换成其名称字符串如TOOL_CALL_SUCCESS得到 JSON 兼容格式便于持久化或回放展示。ToolNode的详细字段说明与示例可见配套文档 Markdown_Docs/XAgent/data_structure/node.md。实战TaskSearchTree 在 ReACT 内层搜索中的调用链TaskSearchTree并非孤立存在它被内层循环搜索算法ReACTChainSearch直接使用实现在 XAgent/inner_loop_search_algorithms/ReACT.py 中。该算法继承自 XAgent/inner_loop_search_algorithms/base_search.py 的BaseSearchMethod在初始化时维护了一个树列表class ReACTChainSearch(BaseSearchMethod): def __init__(self, xagent_core_components: XAgentCoreComponents): super().__init__() self.tree_list [] self.finish_node None self.xagent_core_components xagent_core_components每轮尝试生成一棵新树在generate_chain方法中每次尝试attempt都会追加一棵全新的TaskSearchTreeself.tree_list.append(TaskSearchTree()) now_attempt_tree self.tree_list[-1] now_node now_attempt_tree.root也就是说tree_list中每棵树对应一次完整的链式搜索尝试多次尝试max_try失败或成功后由run方法统一判定搜索状态SearchMethodStatusCode.HAVE_AT_LEAST_ONE_ANSWER/FAIL见 XAgent/utils.py 中的枚举定义。循环生长深度受限的链式扩展树的生长发生在while循环中其终止条件直接使用树的深度while now_node.get_depth() config.max_subtask_chain_length: ... new_tree_node agent.message_to_tool_node(new_message) ... tool_output, tool_output_status_code, need_for_plan_refine, using_tools \ self.xagent_core_components.function_handler.handle_tool_call(new_tree_node) ... now_attempt_tree.make_father_relation(now_node, new_tree_node) ... now_node new_tree_node关键点深度即进度now_node.get_depth()表示当前链已走了多少步当它达到配置的max_subtask_chain_length时循环停止防止无限生长节点来源new_tree_node由agent.message_to_tool_node(new_message)生成——该方法见 XAgent/agent/tool_agent/agent.py把 LLM 返回的 message含content、arguments、function_call转换为一个携带思考与命令的ToolNode其中data[command][properties][name]就是 Agent 决定调用的工具名边即操作记录make_father_relation(now_node, new_tree_node)把上一步节点与新节点连成链expand_num按 1、2、3……依次分配状态即结束信号当tool_output_status_code为SUBMIT_AS_SUCCESS或SUBMIT_AS_FAILED时中断循环self.finish_node now_node记录终点节点供上层如 XAgent/workflow/working_memory.py 中注册子任务并记录finish_node.get_depth()作为处理长度使用。节点数据如何回放给 LLM树的链式结构还被用于构造下一轮推理的上下文make_message(now_node, ...)读取now_node.process即从根到当前节点的所有data序列并在config.enable_summary开启时用summarize_action压缩后作为你已经完成的步骤注入用户消息。这样 LLM 每走一步都能看到整条历史链而历史链正是由TaskSearchTree一步步累积起来的。配置联动用max_subtask_chain_length约束树高树的高度上限来自全局配置项max_subtask_chain_length默认配置见 assets/gpt-3.5-turbo_config.ymlmax_subtask_chain_length: 15配套的常用配置还包括max_plan_refine_chain_length: 3 # 计划精炼链长度 max_plan_tree_depth: 3 # 计划树最大深度 max_plan_tree_width: 5 # 计划树最大宽度 enable_ask_human_for_help: False # 是否允许向人类求助同一套配置也出现在 assets/xagentllama.yml。从源码看max_subtask_chain_length在 ReACT.py 中被三处使用作为while循环终止条件、判断是否强制调用subtask_submit当now_node.get_depth() config.max_subtask_chain_length - 1时最后一步必须提交子任务、以及作为提示词中的max_length占位符。由此可见调大该值可让 Agent 在单个子任务内执行更多步骤链更深但也意味着更长的上下文与更多工具调用调小则会更快进入subtask_submit收尾。最小可运行示例手动搭建一棵任务树综合 Markdown_Docs/XAgent/data_structure/tree.md 的示例输出与源码实现可以手动构造一棵任务树并验证各方法行为from XAgent.data_structure.node import ToolNode from XAgent.data_structure.tree import TaskSearchTree # 1. 初始化一棵任务树 tree TaskSearchTree() print(tree.get_depth()) # 0初始只有根节点 print(tree.get_subtree_size()) # 1根节点自身计为 1 # 2. 构造两个真实操作节点并建立父子关系 father ToolNode() child ToolNode() tree.make_father_relation(tree.root, father) # father 的 expand_num 1 tree.make_father_relation(father, child) # child 的 expand_num 2 print(tree.get_depth()) # 2root - father - child print(tree.get_subtree_size()) # 3三个节点 print(father.expand_num, child.expand_num) # 1 2 print(child.father is father, father.children) # True [child] # 3. 类型校验非 ToolNode 会抛 TypeError try: tree.make_father_relation(father, not a node) except TypeError as e: print(e) # Father and child both need to be instances of ToolNode.小结TaskSearchTree 的设计要点组合而非继承TaskSearchTree内部持有ToolNode根节点统计逻辑全部下沉到节点层树类只做转发职责清晰编号机制now_expand_num与expand_num配合为每个操作节点提供全局唯一的扩展顺序号深度受限树的生长深度由配置max_subtask_chain_length控制从源头规避了递归统计与上下文无限膨胀的风险贯穿执行主链路从 ReACT 搜索到工作记忆注册TaskSearchTree提供的深度、终点节点与process链式数据是 XAgent 实现复杂任务多步求解、可回溯、可总结、可提交的底层支撑。如果需要进一步了解节点细节与搜索算法整体流程可继续阅读仓库内的 Markdown_Docs/XAgent/data_structure/node.md 与 Markdown_Docs/XAgent/inner_loop_search_algorithms/ReACT.md。赞分享AI Agent大模型后端任务调度【免费下载链接】XAgentAn Autonomous LLM Agent for Complex Task Solving项目地址https://gitcode.com/gh_mirrors/xa/XAgent点击查看免费下载相关推荐快速完整的微信聊天记录导出备份、统计一次搞定快速完整的微信聊天记录导出备份、统计一次搞定 WeChatMsg 是一款本地运行的微信聊天记录导出工具能把记录导出为 HTML、Word、CSV 三种格式Swift Algorithm Club 之 Trie 字典树Swift 前缀树数据结构的原理与实现详解Swift Algorithm Club 之 Trie 字典树Swift 前缀树数据结构的原理与实现详解 导读 Trie又称前缀树 prefix tree、示例工程教程高级树结构解析B树、三元搜索树在C-Sharp-Algorithms中的实现原理高级树结构解析B树、三元搜索树在C Sharp Algorithms中的实现原理 C Sharp Algorithms是一个功能强大的C 算法库提供了标准数后端上一篇flow-to-typescript-codemod与React从React.Node到React.ReactNode的转换技巧下一篇【亲测免费】 使用node-neo4j连接Neo4j数据库教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网