據(jù)結(jié)構(gòu)詳解:TaskSearchTree 任務(wù)搜索樹的實現(xiàn)原理與實戰(zhàn))
AI Agent大模型后端任務(wù)調(diào)度【免費(fèi)下載鏈接】XAgentAn Autonomous LLM Agent for Complex Task Solving項目地址https://gitcode.com/gh_mirrors/xa/XAgent點擊查看免費(fèi)下載TaskSearchTree 是 XAgent 內(nèi)部用于組織復(fù)雜任務(wù)求解過程的核心樹狀數(shù)據(jù)結(jié)構(gòu)它以ToolNode為節(jié)點記錄 Agent 在解決每個子任務(wù)時逐步調(diào)用工具、產(chǎn)生思考與輸出結(jié)果的完整鏈路。本文以 XAgent/data_structure/tree.py 與配套文檔 Markdown_Docs/XAgent/data_structure/tree.md 為骨架結(jié)合節(jié)點實現(xiàn)與 ReACT 搜索算法源碼深入講解樹的構(gòu)造、深度/子樹統(tǒng)計、父子關(guān)系建立以及它在真實任務(wù)執(zhí)行中的調(diào)用方式幫助你理解 XAgent 是如何把一次次的 LLM 推理與工具調(diào)用沉淀成一棵可回溯、可統(tǒng)計、可提交的任務(wù)樹。TaskSearchTree 類概覽一棵承載任務(wù)搜索行為的樹在 XAgent 的代碼庫中TaskSearchTree被定義在 XAgent/data_structure/tree.py其類注釋明確說明TaskSearchTree 表示一棵具有特定任務(wù)搜索行為specific task searching behavior的樹數(shù)據(jù)結(jié)構(gòu)。它的職責(zé)不是通用意義上的多叉樹工具類而是為內(nèi)層循環(huán)搜索算法inner loop search提供一棵記錄當(dāng)前子任務(wù)從開始到結(jié)束每一步操作的鏈?zhǔn)綐?。類定義如下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從屬性設(shè)計上可以看到它的兩個核心成員屬性類型語義rootToolNode樹的根節(jié)點默認(rèn)是一個新建的空ToolNode其expand_num被固定為 0now_expand_numint遍歷過程中維護(hù)的當(dāng)前擴(kuò)展編號用于給新加入的節(jié)點按擴(kuò)展順序編號與 Plan 樹的區(qū)別兩種樹各司其職XAgent 中還存在另一棵樹——由 XAgent/data_structure/plan.py 中的Plan類構(gòu)成的計劃樹Plan Tree它管理任務(wù)計劃含子任務(wù) ID、狀態(tài)、父子關(guān)系。而TaskSearchTree管理的是每個子任務(wù)內(nèi)部的執(zhí)行過程當(dāng)某個子任務(wù)被真正執(zhí)行時Agent 每推理并調(diào)用一次工具就會在樹上新增一個節(jié)點。因此兩者是計劃級與執(zhí)行級兩個不同粒度的樹結(jié)構(gòu)讀者不應(yīng)混淆。構(gòu)造方法__init__初始化根節(jié)點與擴(kuò)展編號__init__方法不接收任何參數(shù)內(nèi)部只做三件事def __init__(self): self.root: ToolNode ToolNode() self.root.expand_num 0 self.now_expand_num 1創(chuàng)建根節(jié)點直接實例化一個ToolNode并賦給self.root。根節(jié)點代表任務(wù)尚未開始的初始狀態(tài)它不攜帶任何真實的 Agent 行為數(shù)據(jù)。根節(jié)點不參與擴(kuò)展self.root.expand_num 0將根節(jié)點的擴(kuò)展編號固定為 0表示根節(jié)點本身不會被當(dāng)作一次擴(kuò)展。從 1 開始計數(shù)self.now_expand_num 1表示當(dāng)前下一個將要被擴(kuò)展的節(jié)點編號為 1即樹中第一個真實操作節(jié)點將從編號 1 開始。注意點沿用文檔說明并結(jié)合源碼該函數(shù)無參數(shù)創(chuàng)建TaskSearchTree()即可完成初始化初始化的根節(jié)點默認(rèn)不會被擴(kuò)展如果業(yè)務(wù)上需要根節(jié)點也參與擴(kuò)展可以通過修改其expand_num屬性實現(xiàn)不過在當(dāng)前 ReACT 實現(xiàn)中根節(jié)點始終只作為起始錨點now_expand_num表示下一個可分配的擴(kuò)展編號它隨每次建立父子關(guān)系自增實際反映樹上真實節(jié)點不含根的數(shù)量。查詢方法get_depth與get_subtree_sizeTaskSearchTree的深度與子樹大小查詢都采用委托給根節(jié)點的實現(xiàn)方式def get_depth(self): return self.root.get_depth() def get_subtree_size(self): return self.root.get_subtree_size()這里的關(guān)鍵在于樹本身不維護(hù)任何統(tǒng)計信息所有統(tǒng)計邏輯都定義在ToolNode上見 XAgent/data_structure/node.py。ToolNode 上的深度計算ToolNode.get_depth通過遞歸回溯父節(jié)點計算深度def get_depth(self): if self.father None: return 0 return self.father.get_depth() 1根節(jié)點的father為None因此深度為0每個子節(jié)點深度 父節(jié)點深度 1由于TaskSearchTree.get_depth()委托給self.root.get_depth()返回的正是整棵樹的最大深度。使用注意get_depth依賴父子關(guān)系被正確建立即father指針正確否則計算結(jié)果會出現(xiàn)偏差同時因為它采用遞歸實現(xiàn)極端情況下過深的鏈可能引起遞歸開銷需要配合配置中的max_subtask_chain_length限制鏈長詳見后文。ToolNode 上的子樹大小計算ToolNode.get_subtree_size采用遞歸累加的方式統(tǒng)計以當(dāng)前節(jié)點為根的子樹節(jié)點總數(shù)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葉子節(jié)點children為空子樹大小為1非葉子節(jié)點的大小 自身 1 所有子節(jié)點子樹大小的累加對TaskSearchTree而言調(diào)用get_subtree_size()即得到整棵任務(wù)樹的總節(jié)點數(shù)。值得注意的語義細(xì)節(jié)在ToolNode層面子樹大小包含當(dāng)前節(jié)點自身葉子返回 1而關(guān)聯(lián)文檔對TaskSearchTree.get_subtree_size的說明中提到子樹的節(jié)點數(shù)不包括根節(jié)點本身這一說法與node.py的實現(xiàn)存在表述差異。以源碼為準(zhǔn)TaskSearchTree.get_subtree_size()返回的是root.get_subtree_size()其中根節(jié)點計入統(tǒng)計根沒有子節(jié)點時返回 1。讀者在實際閱讀舊文檔或調(diào)試時應(yīng)以 XAgent/data_structure/node.py 的實際行為為準(zhǔn)避免被注釋誤導(dǎo)。建邊方法make_father_relation建立父子關(guān)系并編號make_father_relation(father, child)是樹從單節(jié)點生長為鏈/樹的唯一入口源碼如下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)其執(zhí)行流程分為三步類型校驗father與child必須同時是ToolNode實例否則拋出TypeError提示信息為Father and child both need to be instances of ToolNode.分配擴(kuò)展編號把當(dāng)前的now_expand_num寫入child.expand_num然后now_expand_num 1從而保證樹中每個真實節(jié)點都拿到唯一的、按加入順序遞增的擴(kuò)展編號雙向建邊將child.father指向father并把child追加到father.children列表中完成父認(rèn)子、子認(rèn)父的雙向關(guān)聯(lián)。注意使用前必須確保father與child節(jié)點均已創(chuàng)建并存在于樹中傳入非ToolNode類型會直接拋異常因此調(diào)用方如 ReACT 算法總是用agent.message_to_tool_node(...)生成的ToolNode來調(diào)用expand_num不僅用于標(biāo)識順序還能配合now_expand_num推導(dǎo)當(dāng)前樹上真實擴(kuò)展節(jié)點的數(shù)量。節(jié)點基石ToolNode 的完整結(jié)構(gòu)要真正用好TaskSearchTree必須理解其節(jié)點類型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父節(jié)點指針childrenlist[ToolNode]子節(jié)點列表expand_numint擴(kuò)展順序編號由make_father_relation分配datadict節(jié)點核心數(shù)據(jù)內(nèi)容、thoughts思考/推理/計劃/批評、command命令名與參數(shù)、工具輸出、工具調(diào)用狀態(tài)碼historyMessageHistory該節(jié)點對應(yīng)的消息歷史見 XAgent/message_history.pyworkspace_hash_idstr工作區(qū)哈希 ID用于關(guān)聯(lián)文件系統(tǒng)快照此外ToolNode還提供兩個對樹的運(yùn)行至關(guān)重要的方法process屬性從當(dāng)前節(jié)點一路回溯到根節(jié)點把沿途每個節(jié)點的data按根→當(dāng)前順序拼成一個列表供 ReACT 算法構(gòu)造你已經(jīng)完成的步驟提示詞使用to_json對data做深拷貝并把tool_status_code枚舉值轉(zhuǎn)換成其名稱字符串如TOOL_CALL_SUCCESS得到 JSON 兼容格式便于持久化或回放展示。ToolNode的詳細(xì)字段說明與示例可見配套文檔 Markdown_Docs/XAgent/data_structure/node.md。實戰(zhàn)TaskSearchTree 在 ReACT 內(nèi)層搜索中的調(diào)用鏈TaskSearchTree并非孤立存在它被內(nèi)層循環(huán)搜索算法ReACTChainSearch直接使用實現(xiàn)在 XAgent/inner_loop_search_algorithms/ReACT.py 中。該算法繼承自 XAgent/inner_loop_search_algorithms/base_search.py 的BaseSearchMethod在初始化時維護(hù)了一個樹列表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中每棵樹對應(yīng)一次完整的鏈?zhǔn)剿阉鲊L試多次嘗試max_try失敗或成功后由run方法統(tǒng)一判定搜索狀態(tài)SearchMethodStatusCode.HAVE_AT_LEAST_ONE_ANSWER/FAIL見 XAgent/utils.py 中的枚舉定義。循環(huán)生長深度受限的鏈?zhǔn)綌U(kuò)展樹的生長發(fā)生在while循環(huán)中其終止條件直接使用樹的深度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關(guān)鍵點深度即進(jìn)度now_node.get_depth()表示當(dāng)前鏈已走了多少步當(dāng)它達(dá)到配置的max_subtask_chain_length時循環(huán)停止防止無限生長節(jié)點來源new_tree_node由agent.message_to_tool_node(new_message)生成——該方法見 XAgent/agent/tool_agent/agent.py把 LLM 返回的 message含content、arguments、function_call轉(zhuǎn)換為一個攜帶思考與命令的ToolNode其中data[command][properties][name]就是 Agent 決定調(diào)用的工具名邊即操作記錄make_father_relation(now_node, new_tree_node)把上一步節(jié)點與新節(jié)點連成鏈expand_num按 1、2、3……依次分配狀態(tài)即結(jié)束信號當(dāng)tool_output_status_code為SUBMIT_AS_SUCCESS或SUBMIT_AS_FAILED時中斷循環(huán)self.finish_node now_node記錄終點節(jié)點供上層如 XAgent/workflow/working_memory.py 中注冊子任務(wù)并記錄finish_node.get_depth()作為處理長度使用。節(jié)點數(shù)據(jù)如何回放給 LLM樹的鏈?zhǔn)浇Y(jié)構(gòu)還被用于構(gòu)造下一輪推理的上下文make_message(now_node, ...)讀取now_node.process即從根到當(dāng)前節(jié)點的所有data序列并在config.enable_summary開啟時用summarize_action壓縮后作為你已經(jīng)完成的步驟注入用戶消息。這樣 LLM 每走一步都能看到整條歷史鏈而歷史鏈正是由TaskSearchTree一步步累積起來的。配置聯(lián)動用max_subtask_chain_length約束樹高樹的高度上限來自全局配置項max_subtask_chain_length默認(rèn)配置見 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 # 是否允許向人類求助同一套配置也出現(xiàn)在 assets/xagentllama.yml。從源碼看max_subtask_chain_length在 ReACT.py 中被三處使用作為while循環(huán)終止條件、判斷是否強(qiáng)制調(diào)用subtask_submit當(dāng)now_node.get_depth() config.max_subtask_chain_length - 1時最后一步必須提交子任務(wù)、以及作為提示詞中的max_length占位符。由此可見調(diào)大該值可讓 Agent 在單個子任務(wù)內(nèi)執(zhí)行更多步驟鏈更深但也意味著更長的上下文與更多工具調(diào)用調(diào)小則會更快進(jìn)入subtask_submit收尾。最小可運(yùn)行示例手動搭建一棵任務(wù)樹綜合 Markdown_Docs/XAgent/data_structure/tree.md 的示例輸出與源碼實現(xiàn)可以手動構(gòu)造一棵任務(wù)樹并驗證各方法行為from XAgent.data_structure.node import ToolNode from XAgent.data_structure.tree import TaskSearchTree # 1. 初始化一棵任務(wù)樹 tree TaskSearchTree() print(tree.get_depth()) # 0初始只有根節(jié)點 print(tree.get_subtree_size()) # 1根節(jié)點自身計為 1 # 2. 構(gòu)造兩個真實操作節(jié)點并建立父子關(guān)系 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三個節(jié)點 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.小結(jié)TaskSearchTree 的設(shè)計要點組合而非繼承TaskSearchTree內(nèi)部持有ToolNode根節(jié)點統(tǒng)計邏輯全部下沉到節(jié)點層樹類只做轉(zhuǎn)發(fā)職責(zé)清晰編號機(jī)制now_expand_num與expand_num配合為每個操作節(jié)點提供全局唯一的擴(kuò)展順序號深度受限樹的生長深度由配置max_subtask_chain_length控制從源頭規(guī)避了遞歸統(tǒng)計與上下文無限膨脹的風(fēng)險貫穿執(zhí)行主鏈路從 ReACT 搜索到工作記憶注冊TaskSearchTree提供的深度、終點節(jié)點與process鏈?zhǔn)綌?shù)據(jù)是 XAgent 實現(xiàn)復(fù)雜任務(wù)多步求解、可回溯、可總結(jié)、可提交的底層支撐。如果需要進(jìn)一步了解節(jié)點細(xì)節(jié)與搜索算法整體流程可繼續(xù)閱讀倉庫內(nèi)的 Markdown_Docs/XAgent/data_structure/node.md 與 Markdown_Docs/XAgent/inner_loop_search_algorithms/ReACT.md。贊分享AI Agent大模型后端任務(wù)調(diào)度【免費(fèi)下載鏈接】XAgentAn Autonomous LLM Agent for Complex Task Solving項目地址https://gitcode.com/gh_mirrors/xa/XAgent點擊查看免費(fèi)下載相關(guān)推薦快速完整的微信聊天記錄導(dǎo)出備份、統(tǒng)計一次搞定快速完整的微信聊天記錄導(dǎo)出備份、統(tǒng)計一次搞定 WeChatMsg 是一款本地運(yùn)行的微信聊天記錄導(dǎo)出工具能把記錄導(dǎo)出為 HTML、Word、CSV 三種格式Swift Algorithm Club 之 Trie 字典樹Swift 前綴樹數(shù)據(jù)結(jié)構(gòu)的原理與實現(xiàn)詳解Swift Algorithm Club 之 Trie 字典樹Swift 前綴樹數(shù)據(jù)結(jié)構(gòu)的原理與實現(xiàn)詳解 導(dǎo)讀 Trie又稱前綴樹 prefix tree、示例工程教程高級樹結(jié)構(gòu)解析B樹、三元搜索樹在C-Sharp-Algorithms中的實現(xiàn)原理高級樹結(jié)構(gòu)解析B樹、三元搜索樹在C Sharp Algorithms中的實現(xiàn)原理 C Sharp Algorithms是一個功能強(qiáng)大的C 算法庫提供了標(biāo)準(zhǔn)數(shù)后端上一篇flow-to-typescript-codemod與React從React.Node到React.ReactNode的轉(zhuǎn)換技巧下一篇【親測免費(fèi)】 使用node-neo4j連接Neo4j數(shù)據(jù)庫教程創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考