docs(agent-hooks-guide): add comprehensive guide for agent hooks usage and implementation
18 KiB
18 KiB
Agent Hooks GUI 学习与复现指南
本指南逐一梳理 src/pages/agent/hooks 目录下的所有 Agent 相关 Hooks,帮助你理解画布、抽屉、运行与日志的全链路逻辑,并可据此复现功能。文档涵盖职责、导出、参数与返回、关键逻辑、典型用法及注意事项。
约定:
useGraphStore为画布状态中心(维护nodes/edges及更新方法);@xyflow/react用于节点/边渲染与交互;Operator、NodeHandleId、NodeMap定义节点类型、句柄及渲染映射。
图数据获取与构建
use-fetch-data.ts
- 作用:页面挂载时获取 Agent 详情并注入画布。
- 导出:
useFetchDataOnMount()→{ loading, flowDetail } - 关键逻辑:
- 读取
useFetchAgent()的data.dsl.graph,通过useSetGraphInfo()写入nodes/edges。 - 首次挂载
refetch()刷新详情。
- 读取
- 用法:在页面
useEffect中调用以初始化画布。 - 注意:
data.dsl.graph可能为空,已做空处理。
use-set-graph.ts
- 作用:将后端返回的
IGraph写入画布。 - 导出:
useSetGraphInfo()→setGraphInfo({ nodes = [], edges = [] }: IGraph) - 关键逻辑:有数据才更新,避免清空现有状态。
- 关联:与
useFetchDataOnMount搭配使用。
use-build-dsl.ts
- 作用:根据当前
nodes/edges构建可保存的 DSL 数据对象,并过滤占位符节点(Operator.Placeholder)。 - 导出:
useBuildDslData()→{ buildDslData(currentNodes?) } - 关键逻辑:
- 过滤占位符节点与相关边。
buildDslComponentsByGraph(filteredNodes, filteredEdges, data.dsl.components)生成组件列表。- 返回
{ ...data.dsl, graph, components }。
- 用法:保存前构建 DSL;导出 JSON 时使用。
use-export-json.ts
- 作用:导出当前画布图为 JSON 文件。
- 导出:
useHandleExportJsonFile()→{ handleExportJson } - 关键逻辑:
downloadJsonFile(buildDslData().graph, \ \${data.title}.json`)`
use-save-graph.ts
- 作用:保存画布到后端;在打开调试时确保最新 DSL。
- 导出:
useSaveGraph(showMessage?: boolean)→{ saveGraph(currentNodes?), loading }useSaveGraphBeforeOpeningDebugDrawer(show)→{ handleRun(nextNodes?), loading }useWatchAgentChange(chatDrawerVisible)→ 自动 debounce 保存,并回显更新时间字符串。
- 关键逻辑:
- 基于路由
id+data.title+buildDslData组装dsl调用useSetAgent。 - 打开调试前先
saveGraph(),再resetAgent()清空旧消息。 useDebounceEffect:每 20 秒在节点/边变化时自动保存(聊天抽屉打开时不保存)。
- 基于路由
节点创建与拖拽
use-add-node.ts
- 作用:核心节点添加逻辑,含初始参数、坐标计算、特殊类型处理(Agent/Tool/Iteration/Note)。
- 导出:
useInitializeOperatorParams()→{ initializeOperatorParams, initialFormValuesMap }useGetNodeName()→ 根据Operator返回国际化默认名称。useCalculateNewlyChildPosition()→ 计算新增子节点位置避免覆盖。useAddNode(reactFlowInstance?)→{ addCanvasNode, addNoteNode }
- 内部逻辑:
useAddChildEdge()、useAddToolNode()、useResizeIterationNode()。 - 关键逻辑:
- 初始化各
Operator的form默认值;Agent/Extractor 等会注入llm_id、默认 prompts。 - 迭代节点
Operator.Iteration自动创建Operator.IterationStart子节点并设为extent='parent'。 - Agent 底部子 Agent 的水平分布通过已有子节点
x轴最大值计算,避免重叠;Tool 节点仅允许一个(检查是否已有NodeHandleId.Tool连接)。 - 通过
reactFlowInstance.screenToFlowPosition将点击位置转换为画布坐标;右侧新增子节点时自动用NodeHandleId连线。 - 容器内新增子节点时可能触发父容器宽度调整以容纳子节点。
- 初始化各
- 典型用法:
const { addCanvasNode } = useAddNode(reactFlowInstance); // 在菜单点击或连接拖拽结束时: addCanvasNode(Operator.Agent, { nodeId, position: Position.Right, id: NodeHandleId.Start })(event); - 注意:
Operator.Placeholder节点draggable=false。- Tool 节点唯一性;容器内节点可能触发 resize。
use-connection-drag.ts
- 作用:连接拖拽起止处理;在拖拽终止位置弹出节点选择下拉,创建占位符节点并连线。
- 导出:
useConnectionDrag(reactFlowInstance, onConnect, showModal, hideModal, setDropdownPosition, setCreatedPlaceholderRef, calculateDropdownPosition, removePlaceholderNode, clearActiveDropdown, checkAndRemoveExistingPlaceholder)→{ nodeId, onConnectStart, onConnectEnd, handleConnect, getConnectionStartContext, shouldPreventClose, onMove } - 关键逻辑:
onConnectStart记录起点节点/句柄及鼠标起始位置,区分点击/拖拽(<5px 移动视为点击)。onConnectEnd计算下拉面板位置;点击则清理状态并关闭下拉;拖拽时先移除旧占位符,再创建新占位符并连线。onMove在画布滚动/缩放时隐藏下拉并清理占位符。
use-dropdown-position.ts
- 作用:屏幕与 Flow 坐标互转,计算下拉菜单位置使其对齐占位节点。
- 导出:
useDropdownPosition(reactFlowInstance)→{ calculateDropdownPosition, getPlaceholderNodePosition, flowToScreenPosition, screenToFlowPosition } - 关键逻辑:按常量
HALF_PLACEHOLDER_NODE_WIDTH、DROPDOWN_HORIZONTAL_OFFSET、DROPDOWN_VERTICAL_OFFSET计算偏移。
use-placeholder-manager.ts
- 作用:占位符节点的创建、删除与状态追踪;替换为真实节点后自动建立连接并同步位置。
- 导出:
usePlaceholderManager(reactFlowInstance)→{ removePlaceholderNode, onNodeCreated, setCreatedPlaceholderRef, resetUserSelectedFlag, checkAndRemoveExistingPlaceholder, createdPlaceholderRef, userSelectedNodeRef } - 关键逻辑:
- 保证面板上仅有一个占位符。
onNodeCreated(newNodeId):真实节点位置与占位符对齐;按占位符的起点连线;删除占位符及相关边。- 使用
reactFlowInstance.deleteElements执行批量删除。
use-before-delete.tsx
- 作用:拦截删除操作,保护
Operator.Begin、容器首节点(Operator.IterationStart非成对删除时阻止)及下游 Agent/Tool 的联动删除。 - 导出:
useBeforeDelete()→{ handleBeforeDelete } - 关键逻辑:
UndeletableNodes:Begin 与 IterationStart 的特殊保护。- 当包含 Agent 节点时,额外删除其下游 Agent/Tool(节点与边)。
- 用法:作为 React Flow 的
onBeforeDelete回调。
use-change-node-name.ts
- 作用:处理节点名称与 Tool 名称变更,避免重复命名并同步到画布。
- 导出:
useHandleNodeNameChange({ id, data })→{ name, handleNameBlur, handleNameChange } - 关键逻辑:
- Tool 名称变更写入父 Agent 的
tools字段,通过updateNodeForm(agentId, nextTools, ['tools'])。 - 普通节点名通过
updateNodeName(id, name)更新;同一画布内不可重名,重复则提示message.error('The name cannot be repeated')。
- Tool 名称变更写入父 Agent 的
use-agent-tool-initial-values.ts
- 作用:为 Agent Tool(非画布节点)生成初始参数(裁剪/重组)以适配对话调用。
- 导出:
useAgentToolInitialValues()→{ initializeAgentToolValues(operatorName) } - 关键逻辑:对不同
Operator精简参数,如去除query/sql/stock_code,或仅保留smtp_*邮件字段等。
use-form-values.ts
- 作用:表单初始值合并,优先使用节点已有
data.form,否则回退为defaultValues。 - 导出:
useFormValues(defaultValues, node?)
use-watch-form-change.ts
- 作用:监听
react-hook-form表单变化并同步到画布节点form。 - 导出:
useWatchFormChange(id?, form?) - 关键逻辑:手动获取
form.getValues(),再updateNodeForm(id, values)。
use-move-note.ts
- 作用:便签(Note)悬浮预览位置跟随鼠标。
- 导出:
useMoveNote()→{ ref, showImage, hideImage, mouse, imgVisible } - 关键逻辑:使用
useMouse()监听坐标,更新ref.current.style.top/left。
运行与日志
use-run-dataflow.ts
- 作用:保存画布后触发数据流运行(SSE),打开日志抽屉并设置当前
messageId。 - 导出:
useRunDataflow({ showLogSheet, setMessageId })→{ run(fileResponseData), loading, uploadedFileData } - 关键逻辑:
- 先
saveGraph(),再send({ id, query: '', session_id: null, files: [file] })到api.runCanvas。 - 校验响应成功后设置
uploadedFileData/file与messageId。
- 先
use-cancel-dataflow.ts
- 作用:取消当前数据流,并停止日志拉取。
- 导出:
useCancelCurrentDataflow({ messageId, stopFetchTrace })→{ handleCancel } - 关键逻辑:
cancelDataflow(messageId)返回code===0时调用stopFetchTrace()。
use-fetch-pipeline-log.ts
- 作用:拉取运行日志(trace),判断是否完成与是否为空,控制轮询。
- 导出:
useFetchPipelineLog(logSheetVisible)→{ logs, isLogEmpty, isCompleted, loading, isParsing, messageId, setMessageId, stopFetchTrace } - 关键逻辑:
END且trace[0].message非空视为完成,随后stopFetchTrace();抽屉打开时重置isStopFetchTrace=false继续拉取。
use-download-output.ts
- 作用:从日志中提取
END节点输出并下载 JSON。 - 导出:
findEndOutput(list),isEndOutputEmpty(list),useDownloadOutput(data?)→{ handleDownloadJson }
抽屉与面板显示
use-show-drawer.tsx
- 作用:统一管理“运行/聊天抽屉”、“单节点调试抽屉”、“表单抽屉”、“日志抽屉”的显示逻辑。
- 导出:
useShowFormDrawer()→{ formDrawerVisible, hideFormDrawer, showFormDrawer(e, nodeId), clickedNode }useShowSingleDebugDrawer()→{ singleDebugDrawerVisible, hideSingleDebugDrawer, showSingleDebugDrawer }useShowDrawer({ drawerVisible, hideDrawer })→{ chatVisible, runVisible, onPaneClick, singleDebugDrawerVisible, showSingleDebugDrawer, hideSingleDebugDrawer, formDrawerVisible, showFormDrawer, clickedNode, onNodeClick, hideFormDrawer, hideRunOrChatDrawer, showChatModal }useShowLogSheet({ setCurrentMessageId })→{ logSheetVisible, hideLogSheet, showLogSheet(messageId) }useHideFormSheetOnNodeDeletion({ hideFormDrawer })→ 在节点被删除时自动关闭表单抽屉。
- 关键逻辑:
- 当
drawerVisible=true:若 Begin 有输入则显示“运行”抽屉;否则显示“聊天”抽屉。 - 点击节点打开表单抽屉(排除 Note/Placeholder/File);点击节点上的“播放”图标打开单节点调试抽屉。
hideRunOrChatDrawer同时关闭运行/聊天抽屉与外层抽屉。
- 当
Begin 相关选项与变量
use-get-begin-query.tsx
- 作用:围绕 Begin 节点数据构建输入、输出、变量与组件引用选项。
- 导出:
useSelectBeginNodeDataInputs()→ Begin 的输入BeginQuery[]useIsTaskMode(isTask?)→ 返回是否任务模式(从 Begin 的form.mode或传入)。useGetBeginNodeDataQuery()→ 读取 Begin 的inputs并转换为列表。useBuildNodeOutputOptions(nodeId?)→ 输出引用选项(带OperatorIcon)。useBuildVariableOptions(nodeId?, parentId?)→ Begin 变量与节点输出选项合并。useBuildComponentIdOptions(nodeId?, parentId?)→ 组件引用(排除某些节点,容器内仅引用同层或外部节点)。useBuildComponentIdAndBeginOptions(nodeId?, parentId?)→ Begin + 组件引用。useGetComponentLabelByValue(nodeId)/useGetVariableLabelByValue(nodeId)→ 根据值反查标签。
- 注意:容器内节点引用受限:子节点只能引用同容器同层节点或外部节点,不能引用父节点。
use-build-options.tsx
- 作用:轻量版,仅构建节点输出选项。
- 导出:
useBuildNodeOutputOptions(nodeId?)
use-is-pipeline.ts
- 作用:读取路由查询判断是否显示数据流画布(
AgentQuery.Category === DataflowCanvas)。 - 导出:
useIsPipeline()
聊天与共享
use-chat-logic.ts
- 作用:对话中处理“等待用户填写”的表单组件逻辑,组装提交时的
beginInputs。 - 导出:
useAwaitCompentData({ derivedMessages, sendFormMessage, canvasId })→{ getInputs, buildInputList, handleOk, isWaitting } - 关键逻辑:将消息中的
data.inputs与用户填写值合并后发送。
use-cache-chat-log.ts
- 作用:聊天事件按
message_id分片缓存,支持过滤与清空。 - 导出:
useCacheChatLog()→{ eventList, currentEventListWithoutMessage, currentEventListWithoutMessageById, setEventList, clearEventList, addEventList, filterEventListByEventType, filterEventListByMessageId, setCurrentMessageId, currentMessageId } - 关键逻辑:排除
Message/MessageEnd事件,保留节点事件日志用于“运行日志”展示。
use-send-shared-message.ts
- 作用:分享页的消息发送逻辑(可任务模式),包含参数弹窗与自动触发任务。
- 导出:
useSendButtonDisabled(value)→ 空字符串时禁用。useGetSharedChatSearchParams()→ 从 URL 解析from/shared_id/locale等。useSendNextSharedMessage(addEventList)→{ sendMessage, hasError, parameterDialogVisible, inputsData, isTaskMode, hideParameterDialog, showParameterDialog, ok }
- 关键逻辑:
url动态拼接:agentbots或chatbots。- 任务模式且
inputs为空时自动触发一次空参数运行。
外部资源与工具
use-find-mcp-by-id.ts
- 作用:在 MCP 服务列表中按 ID 查找服务器对象。
- 导出:
useFindMcpById()→{ findMcpById(id) }
use-open-document.ts
- 作用:打开在线文档(Agent 组件说明)。
- 导出:
useOpenDocument()→openDocument()(window.open到https://ragflow.io/docs/dev/category/agent-components)。
use-show-dialog.ts
- 作用:系统 API Key 管理与分享预览。
- 导出:
useOperateApiKey(idKey, dialogId?)→{ removeToken, createToken, tokenList, creatingLoading, listLoading }usePreviewChat(idKey)→{ handlePreview }useSelectChartStatsList()→ 将统计数据转换为图表用的{ xAxis, yAxis }[]。
- 关键逻辑:创建/删除系统 token;打开分享页预览 URL(根据
idKey判断 Agent 或 Chat)。
布局与其他
use-calculate-sheet-right.ts
- 作用:根据
body宽度返回抽屉的右侧定位类名。 - 导出:
useCalculateSheetRight()→'right-[620px]' | 'right-1/3'
use-iteration.ts
- 作用:当前文件为空,预留迭代相关的后续逻辑。
- 导出:暂无(空文件)。
复现建议与流程
-
画布初始化:
- 页面挂载时调用
useFetchDataOnMount(),保证nodes/edges与后端一致。 - 如需区分是否数据流画布,调用
useIsPipeline()控制界面模式。
- 页面挂载时调用
-
节点与连接交互:
- 连接拖拽中,使用
useConnectionDrag的onConnectStart/onConnectEnd/onMove管理占位符与下拉。 - 选择节点类型后,配合
usePlaceholderManager.onNodeCreated(newNodeId)落地真实节点并连线。 - 添加节点通过
useAddNode.addCanvasNode(type,{ nodeId, position, id })(event),自动计算位置与连线。
- 连接拖拽中,使用
-
表单与抽屉:
- 点击节点打开
useShowFormDrawer.showFormDrawer(e, nodeId);点击“播放”打开useShowSingleDebugDrawer。 - 外层抽屉控制
useShowDrawer;运行/聊天抽屉根据 Begin 输入状态切换。 - 表单变更用
useWatchFormChange(id, form)将react-hook-form值同步到data.form。
- 点击节点打开
-
运行与日志:
- 上传文件后,调用
useRunDataflow.run(fileResponseData);在日志抽屉中用useFetchPipelineLog(logSheetVisible)拉取状态。 - 取消运行用
useCancelCurrentDataflow.handleCancel();下载输出用useDownloadOutput.handleDownloadJson()。
- 上传文件后,调用
-
保存与导出:
- 使用
useSaveGraphBeforeOpeningDebugDrawer在每次调试前保存并重置状态。 - 自动保存:
useWatchAgentChange(chatDrawerVisible)在无聊天抽屉时 debounce 落库。 - 导出 JSON:
useHandleExportJsonFile.handleExportJson()。
- 使用
易踩坑与注意事项
- 占位符节点必须在保存 DSL 时过滤,否则会污染组件图(
useBuildDslData已处理)。 - Tool 节点只允许有一个;
useAddNode中对 Tool 的添加有唯一性校验。 - 容器类(Iteration)中的引用受限:子节点只能引用同容器同层节点或外部节点,不能引用父节点(
useBuildComponentIdOptions)。 - 名称唯一性:画布内节点与 Agent 下的 Tool name 均需唯一(
useChangeNodeName)。 - 聊天日志拉取需在 END 且 trace 有 message 时停止,否则会持续轮询(
useFetchPipelineLog)。 - 运行/聊天抽屉切换逻辑依赖 Begin 输入是否存在(
useShowDrawer)。
关联常量与工具(理解 hooks 需要)
Operator、NodeHandleId、NodeMap:定义节点类型和画布渲染类型。generateNodeNamesWithIncreasingIndex:为新增节点生成不重复的默认名称。getNodeDragHandle(type):为不同节点类型定义可拖拽句柄范围。buildNodeOutputOptions、buildBeginInputListFromObject、buildBeginQueryWithObject:构建下拉选项与输入/变量结构。
如需按模块拆分为多页或增加“最小复现代码片段”,可进一步细化。也可以优先从“连接拖拽 + 占位符 + 节点添加”链路开始,逐步验证 UI 与数据正确性。