跳到主要内容

数据截至 (上游 commit 482e72519a4e)

让平台成为 Agent 的 LLM 节点:对话、工具调用循环、规划 Agent

30 秒导读: 前几章讲的是"一张图怎么被跑起来"(见 03-workflow-engine.md)。这一章讲图里那几个真正接大模型的节点:从"调一次模型就返回"的对话节点,到"模型自己决定调工具、看结果、再决定"的工具循环节点,再到"会先列计划、卡住了会反问用户"的规划 Agent。正是这几个节点,把 FastGPT 从"可视化工作流"升级成"Agent 平台"。


1. 这是什么(零基础也能懂)

一句话定义: 本章讲的是工作流里把大语言模型接进来的那几种节点——它们的区别不在"调哪个模型",而在"给模型多大的自主权"。

先建立最重要的一个直觉:自主权是逐级放开的

节点模型的自主权一句话
基础对话节点无。调一次,出答案你问,它答
工具调用节点中。可反复自己挑工具你给它工具,它自己边想边用
规划 Agent 节点高。会先列计划、卡住会反问你给它目标,它自己拆步骤、追问、执行到完成
辅助 LLM 节点极小。做一件固定小事分类 / 抽字段 / 扩写问题

给谁用: 搭工作流的人。想做个"知识库问答机器人",用对话节点就够;想做个"能查资料、能算数、能调外部 API 的助手",用工具调用节点;想做个"给个复杂任务能自己规划着干完"的 Agent,用规划 Agent 节点

一句话直觉/类比:

基础对话 = 一次性问答:模型是"应答机"
工具循环 = 模型手里多了一串遥控器(工具),它按需一个个按,看反馈再按下一个
规划 Agent = 工具循环 + 一块随身白板(plan):先在白板上写下步骤,
干一步勾一步,缺料就举手问你(ask),全部勾完才收工

本章不重复的部分: 节点怎么被调度器选中并喂进参数,已在 03-workflow-engine.md 讲过;知识库检索节点内部(向量化、混合检索)归 05-knowledge-base.md。本章只钻这几个节点自己的算法与多轮控制


2. 顶层全景(它大概怎么转)

2.1 三层节点,一个共用内核

最关键的架构事实:工具循环节点和规划 Agent 节点,底下是同一个 while 循环 runAgentLoop。对话节点则不进这个循环,只调一次。

一次对话请求

┌─────────────┼──────────────────────┐
▼ ▼ ▼
① 基础对话节点 ② 工具调用节点 ③ 规划 Agent 节点
dispatchChat dispatchRunTools dispatchRunAgent
Completion │ │
│ ▼ ▼
│ runToolCall runFastAgentMainLoop
│ (toolCall.ts) (loop/index.ts:叠加 plan/ask)
│ │ │
│ └───────────┬───────────┘
▼ ▼
createLLMResponse runAgentLoop ← 共用的多轮工具循环内核
(调一次模型) (loop/base.ts:while 里反复调模型 + 执行工具)

怎么读这张图:从左到右自主权递增。①走一条直线;②③都汇入 runAgentLoop 这个 while 循环,区别只在③在循环外面多包了一层 plan/ask 的编排(runFastAgentMainLoop;上游已移除早期的 stop-gate 机制)。

2.2 各部件一句话职责

部件干什么在哪个文件
dispatchChatCompletion基础对话:组 prompt、拼知识库引用、流式返回dispatch/ai/chat/dispatchChatCompletion.ts:22
dispatchRunTools工具节点入口:备料、算钱、拼 assistantResponsesdispatch/ai/toolcall/index.ts:36
runToolCall工具节点主编排:装配 provider/runtime,调 runAgentLoopdispatch/ai/toolcall/toolCall.ts:36
runAgentLoop多轮工具循环内核:压缩→调模型→跑工具→判停ai/llm/agentLoop/provider/fastAgent/loop/base.ts:190
dispatchRunAgent规划 Agent 入口:备工具/文件/沙箱/memorydispatch/ai/agent/index.ts:93
runFastAgentMainLoop单主 Agent 循环:在内核外叠 plan/ask(原 runUnifiedAgentLoopai/llm/agentLoop/provider/fastAgent/loop/index.ts:155
辅助节点分类 / 抽字段 / 问题扩写,各调一次模型classifyQuestion.tsextract.tsfunctions/queryExtension.ts

3. 核心原理(逐个机制,由浅入深)

3.1 基础对话节点 dispatchChatCompletion

它要解决的小问题: 把"用户这句话 + 历史 + 知识库引用 + 系统提示"拼成一份合法的 messages,调一次模型,把答案流式吐回前端。

思路: 全程只有一次模型调用,没有循环。难点全在调用之前的 prompt 组装调用期间的流式转发

① 知识库引用怎么塞进去(user 还是 system?)。 getDatasetCiteData 决定引用文本放进 user 消息还是 system 消息:

  • aiChatQuoteRole === 'user',或引用模板里含 {{question}} 占位符 → 引用拼进 user 输入
  • 否则 → 引用拼进 system 提示
// 示意,非源码:引用角色的判定
const quoteRole =
aiChatQuoteRole === 'user' || datasetQuotePrompt.includes('{{question}}')
? 'user'
: 'system';

真实实现见 chat/datasetCite.ts:13 getDatasetCiteDataquoteRole:54)的计算;引用文本由 replaceVariable{{quote}}/{{question}} 填进模板。

② 系统提示怎么拼。 getChatMessages 把三段用固定分隔符串起来——模型自带的默认系统提示、用户填的系统提示、知识库引用系统提示:

// 示意,非源码:三段系统提示拼接
const concatenateSystemPrompt = [
model.defaultSystemChatPrompt,
systemPrompt,
datasetCiteSystemPrompt
].filter(Boolean).join('\n\n===---===---===\n\n');

真实实现 chat/chatMessages.ts:50 concatenateSystemPrompt。随后 filterGPTMessageByMaxContextchat/chatMessages.ts:99)按 maxContext - maxTokens旧到新裁历史,保证不超窗。

③ 流式转发。 调用 createLLMResponse 时挂了两个回调,把模型吐出的 reasoning / answer 增量实时推给前端 SSE:

  • onReasoningdispatchChatCompletion.ts:171)→ 推 reasoning_content
  • onStreamingdispatchChatCompletion.ts:175)→ 推 text

两者都受开关控制(aiChatReasoningisResponseAnswerText)。

④ 收尾。 算钱(formatModelChars2Points dispatchChatCompletion.ts:185,用户自带 key 则计 0 分),把 OpenAI 格式的 completeMessages 转回 FastGPT 的对话结构(GPTMessages2Chats dispatchChatCompletion.ts:201),作为 history 输出。

关键点: 对话节点没有"再问一次模型"的能力。它把 answerText 同时挂到 toolResponsedispatchChatCompletion.ts:244),所以它也能被当作工具节点的一个子工具来调——但它自己内部不会循环。


3.2 工具调用 Agent 循环:多轮 while 是怎么转的

这是本章的核心。工具节点让模型自己决定调哪个工具,执行完把结果回填,再问模型,直到模型不再要工具为止。

3.2.1 入口备料:dispatchRunTools

dispatchRunToolstoolcall/index.ts:36)负责循环开始前的准备:

  1. 按模型能力位与用户开关,收敛 vision 等能力(index.ts:79);
  2. useToolNodeListindex.ts:95)从图里收集"连在本节点下面的工具节点";
  3. 把自己的入口标记清掉props.node.isEntry = falseindex.ts:102)——本轮之后由子工具接管交互恢复入口;
  4. useToolMessagesindex.ts:104)拼 messages,并把用户上传文件的 URL 换成模型可读取的文件 id;
  5. runToolCall,消息先经 buildAgentLoopCoreRequestMessages 适配(index.ts:182),保留历史里的工具调用结构。

3.2.2 主编排:runToolCall 把职责拆成 hook

runToolCalltoolCall.ts:36)本身只做编排,把重活分给两个装配器(上游重构:原来的 useToolRunner/useToolStreamResponse 等 hook 已合并进统一 agentLoopCore 装配层):

装配器职责
createAgentLoopCoreRuntimeEnvironment收集 nodeResponse / SSE 推流 / 工具响应裁剪
createToolCallToolProvider把工具节点整理成工具目录 + 真正执行一个工具(跑子工作流 / sandbox / 读文件)

然后它调统一入口 runAgentLoopCoreWithSummary(内部走 runAgentLoop 内核),关键参数:

  • maxRunAgentTimes: 50toolCall.ts:155)——最多 50 轮;
  • batchToolSize: 1toolCall.ts:163)——工具串行执行(注释点明:ToolCall 节点内部工具依赖流式输出、交互状态和 nodeResponse 顺序;普通 Agent 入口再按并发放开);
  • toolRuntime: { toolProvider }toolCall.ts:157)——把"执行工具"的实现注入内核。

3.2.3 内核 while 循环:runAgentLoop

runAgentLoopbase.ts:190)是②③共用的心脏。一轮 while(base.ts:374)做四件事:

while (runTimes < maxRunAgentTimes) {
1. 压缩上下文 onCompressContext:超长才压,压完记 checkpoint
2. 调模型 createLLMResponse:拿 answer / reasoning / toolCalls
3. 若有工具调用 逐个/批量 runTool → 结果作为 tool 消息 append 回 messages
4. 判停 无工具调用?→ break
}

第 2 步的防死循环细节: 若模型连续多轮只调工具不给答案,consecutiveRequestToolTimes 累加(base.ts:479);一旦超过 5,下一轮强制 tool_choice: 'none'base.ts:434)逼它给最终答案。有答案时该计数清零(base.ts:482)。

第 3 步的并行控制: 内核支持工具并行(batchRunbase.ts:668),并发度由 batchToolSize 统一控制(base.ts:635)。工具节点传 batchToolSize: 1 全串行;Agent 节点则按入口配置放开并行。

第 3 步的结果回填: 工具结果统一经 appendToolRunResults 按批次写回成 tool 消息(base.ts:610),保证后续模型看到的上下文稳定;工具响应默认还会过一遍 compressToolResponse 压缩(base.ts:556)。

真实实现引用:

// base.ts:685 循环结束条件(四选一即停)
if (toolCalls.length === 0 || !!toolChildPause || stopAgentLoop || isAborted?.()) {
break;
}

即:模型不再要工具、命中交互工具、被特殊停止工具叫停、或用户主动中止。

3.2.4 stopTool:一个"只发信号"的工具

工作流里可以放一个"停止工具调用"节点。它的 dispatch 什么都不干,只在 nodeResponse 里插一个标记:

// toolcall/stopTool.ts:10 dispatchStopToolCall
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
toolStop: true // 只发信号,不算执行错误
}
};

信号怎么被内核听见?链路是:toolStop → 子流程摘要的 hasToolStopworkflowToolRunner 把它转成工具结果的 stop: trueagentLoopCore/application/runtime/workflowToolRunner.ts:174)→ 内核里 stopLoopstopAgentLoop = truebase.ts:624)→ 下一次判停 break。它让"模型说完这句就收工"变成工作流可编排的一步。

3.2.5 计费累积与 assistantResponses 组装

计费——为什么要"每次调用单独计价再累加"? 因为模型可能是梯度计费(用量越大单价不同)。若把多轮 token 求和后一次性算钱会算错。所以内核每轮单独 formatModelChars2Points 后累加 llmTotalPointsbase.ts:488)。这个值一路传成 toolCallTotalPointstoolCall.ts:180),最后 dispatchRunTools 里再加上工具本身的花费:

// toolcall/index.ts:206
const totalPointsUsage = modelTotalPoints + toolTotalPoints;

assistantResponses——落库给谁看? 循环里模型产生的所有 assistant 消息(含工具调用轮)由内核统一维护成 FastGPT 的结构化对话值 AIChatItemValueItemType[]runToolCallassistantResponses 输出),这就是持久化进 assistant.value、前端渲染成"思考+工具卡+回答"的东西。出库前再过 filterAgentLoopCoreToolResponseToPreviewindex.ts:207)把工具长响应裁成预览。


3.3 规划 Agent:dispatchRunAgent + runUnifiedAgentLoop

规划 Agent 在工具循环之上,多了两样东西:plan(随身白板)ask(举手提问)

3.3.1 入口 dispatchRunAgent 备了什么料

dispatchRunAgentagent/index.ts:93)比工具节点多备三样:

  1. 引擎分支AGENT_ENGINE === 'piAgent' 时选 piAgent provider,默认走 fastAgent loop(agent/utils.ts:12),由 getWorkflowAgentLoopProvider() 接进统一 runAgentLoopCoreWithSummary 入口(index.ts:285);
  2. 子应用/工具汇总getSubappsindex.ts:235)把用户选的工具、系统工具、知识库/文件工具、sandbox 工具合成两份——completionTools(给模型看的描述)和反查真实实现的映射;
  3. memory 恢复readAgentLoopCoreProviderStateMemoryindex.ts:277)读回上一轮 ask 暂停时存下的上下文。

然后创建一个"工作流适配器" runtime(createWorkflowAgentLoopRuntimeindex.ts:253),把工具执行、SSE、计费、nodeResponse 全做成回调传进去——通用 agent loop 不感知 workflow,全靠这些回调把两边接起来。

3.3.2 单主循环 runFastAgentMainLoop

runFastAgentMainLooploop/index.ts:155,即原来的 runUnifiedAgentLoop不自己写 while,而是复用 3.2.3 的 runAgentLoop,只是往里注入了几类"内部工具"的处理。它给模型的工具集是(tools/index.ts:46 getToolsForFastAgentLoop):

runtimeTools(业务工具) + ask_user(追问) + set_plan / update_plan(维护计划)

normalizeToolCatalogtools/index.ts:22)会剔除跟内部工具重名的业务工具,防止模型把控制工具当业务工具用。

onRunToolloop/index.ts:363)里按工具名分路:

模型调的工具处理
ask_user解析追问载荷,存进 pendingAsk(含消息快照),返回 stop: true 暂停循环(loop/index.ts:365-393
set_plan / update_planapplySetPlan / applyPlanUpdate 改白板状态(loop/index.ts:395-410
其它(业务工具)落到 runtime.executeTool 真跑(loop/index.ts:502

3.3.3 交互式规划:plan 的维护(stop gate 已移除)

先说明一处上游变化: 早期版本里有一道 stop gate(停止门)——模型准备收工时由 runStopGate 检查白板、不满足就造 <stop_gate_feedback> 消息打回继续干,并配 shouldRequirePlanFromMessages 正则识别"用户明确要计划"。这套机制(stop gate、requirePlan、forceFinalAnswerOnly)在当前版本已整体移除,循环只保留 3.2.3 的四条自然判停条件;是否建 plan 完全交给模型按系统提示自行决定。

现在 plan 的维护就是两个工具背后的状态机(domain/systemTool/plan/state.ts):

  • set_planapplySetPlanstate.ts:144)校验参数后建整份计划,非法参数返回兜底 plan + 失败消息(state.ts:151);
  • update_planapplyPlanUpdatestate.ts:159)接受 updates(批量改步状态)与 add_steps(追加新步)。状态更新保持原子性——注释点明"校验失败时不追加新步骤"(state.ts:158):任一步 patch 找不到目标 step 就整批退回原计划、不落任何改动(state.ts:195)。没有活跃计划时直接提示先 set_planstate.ts:181)。

3.3.4 ask:暂停、planId、恢复同一份 messages

这是规划 Agent 最巧的一环——追问用户后,怎么在用户回答后接着原来的思路跑,而不是重开一份上下文。

追问工具 ask_userdomain/systemTool/ask/tool.ts:45 createAskAgentTool)要求模型给出 question + 2~5 个可直接点选的 optionstool.ts:17AgentAskPayloadSchema),且只在硬阻塞时才准用:缺必需输入、工具不可用、目标不明(约束写在工具描述里,tool.ts:50)。

暂停与恢复的时序:

第 1 轮(用户提问)
模型调 ask_user
→ loop/index.ts:378 存 pendingAsk(含当时完整 messages 快照)
→ 循环 stop
→ dispatch 层转成 interactive 卡片给前端(agent/index.ts:324 agentPlanAskQuery)
→ 把 pendingMainContext 写进节点 memory(agent/index.ts:326 起)
→ 给这次追问打上 askId(agent/index.ts:329)

〔前端展示选项,等用户点〕

第 2 轮(用户回答)
→ readAgentLoopCoreProviderStateMemory 读回 pendingMainContext(agent/index.ts:277)
→ userAnswer = 用户这次的输入(agent/index.ts:298,buildAgentLoopCoreInput)
→ runFastAgentMainLoop 把用户答案作为"那个 ask 工具的 Tool 响应"
append 回上次的 messages 快照,延续同一条链(loop/index.ts:180)

关键代码(loop/index.ts:180):

// 示意,非源码:恢复时不是重开,而是把用户答案补成 ask 工具的响应
const messages =
input.pendingMainContext && input.userAnswer !== undefined
? [
...input.pendingMainContext.messages, // 上次中断时的完整消息链
{ role: 'tool',
tool_call_id: input.pendingMainContext.askToolCallId, // 对准那次 ask
content: normalizeToolResponseContent(input.userAnswer) }
]
: buildInitialMessages({ input, hasRuntimeTools });

askId 的作用(agent/index.ts:329 起注释点破):保存时把它回写到用户答案上,下一轮 chats2GPTMessages 据此跳过那条 UI-only 的追问气泡,既保证上下文连续,又保证缓存命中。

3.3.5 sub agent:Agent 手里的六类"手脚"

规划 Agent 能调的工具,落地成 dispatch/ai/agent/sub/ 下几类 sub agent(上游精简:原 file / model 两类 sub agent 已移除——文件读取改由 agentLoop 的 read_files 系统工具承担,domain/systemTool/readFile/index.ts:4;"叫个小模型干杂事"的 model 子代理不再单独存在)。它们各自是一段独立的执行逻辑,被 runtime 的 executeTool 按工具名分发:

sub agent干什么入口
app把另一个 App / 插件当工具跑(子工作流)sub/app/index.ts:69 dispatchApp / :202 dispatchPlugin
dataset知识库检索工具,检索后可再用 LLM 自动筛相关分块sub/dataset/index.ts:167 dispatchAgentDatasetSearch
sandbox代码沙箱运行时准备与技能装配(真执行在 sandbox 接口层)sub/sandbox/index.ts:1(re-export ensureAgentSandboxRuntime 等)
tool系统/用户配置的普通工具sub/tool/index.ts:59 dispatchTool

dataset sub agent 检索完还会让模型再挑一遍最相关分块(检索本身归 05-knowledge-base.md)。


4. 辅助 LLM 节点(各做一件固定小事)

这三类节点也调模型,但没有循环、没有工具,只把模型当成一个"结构化函数"。

① classifyQuestion(问题分类 / 路由)。 dispatchClassifyQuestionclassifyQuestion.ts:38)给模型一份类目列表和用户输入,让它吐出命中的类目 key,命中不了就落到最后一个兜底类目(classifyQuestion.ts:66)。它的输出是跳过哪些分支句柄skipHandleIdclassifyQuestion.ts:87)——即用分类结果控制图往哪条边走。它还把上次分类结果写进 memory(classifyQuestion.ts:90)供多轮参考。

② contextExtract(字段抽取)。 dispatchContentExtractextract.ts:43)给模型一份 JSON schema,让它从文本里抽字段。拿到回答后 sliceJsonStr 切出 JSON 段(extract.ts:214)、json5.parse 容错解析(extract.ts:246),再校验必填字段是否齐全给出 success。解析失败不报错,返回空对象兜底(extract.ts:236)。

③ queryExtension(问题扩写)。 queryExtensionfunctions/queryExtension.ts:108)把一句原始问题扩写成多条检索问题(默认 10 条,queryExtension.ts:116),喂给知识库检索以提召回率。它把历史组成 few-shot 拼进 prompt(queryExtension.ts:147),是典型的"一次调用、结构化输出"。

共同模式: 三者都直接调 createLLMResponse(不走 runAgentLoop),都用 getWorkflowSourceNodeKey 存/取 memory,都在末尾 usagePush 计费。它们是"把 LLM 当纯函数",与前面"把 LLM 当自主 Agent"形成对照。


5. 巧妙之处(可借鉴的技术)

  1. 一个内核,两种自主权。 工具节点和规划 Agent 共用 runAgentLoopbase.ts:190),差异全靠"往内核注入不同回调/参数"表达——onRunToolbatchToolSize。加一层能力不用改内核,只加编排。

  2. ask 恢复是"补一条工具响应",不是重建上下文。 用户答案被当作那次 ask_user 的 Tool 响应 append 回消息快照(loop/index.ts:180),配合 askId 跳过 UI 气泡(agent/index.ts:329),把"人机多轮追问"无缝缝进"模型工具循环"里。(早期的 stop gate "feedback 消息打回"机制已上游移除,见 3.3.3。)

  3. 梯度计费的正确姿势:每轮单独计价再累加。 llmTotalPoints += totalPointsbase.ts:488)而非"总 token 一次算",避免阶梯定价下的系统性算错。

  4. 连续工具调用的防死循环。 超过 5 轮只调工具不出答案就强制 tool_choice: 'none'base.ts:434),给自嗨的模型一个硬刹车。


6. 边界与局限

  • 对话节点不会循环。 dispatchChatCompletion 只调一次模型;要多轮工具就得用工具节点。它虽把答案挂进 toolResponsedispatchChatCompletion.ts:244)可被当子工具调,但自身无自主权。
  • 工具节点内工具串行。 runToolCallbatchToolSize: 1toolCall.ts:163),因为子工具依赖流式顺序与交互状态;需要并行只有规划 Agent 的普通工具路径才放开。
  • ask 只允许硬阻塞。 工具描述(domain/systemTool/ask/tool.ts:50)和 schema(tool.ts:14)都限定:只有缺必需输入 / 工具不可用 / 目标完全不明才准追问,偏好和可假设的信息不许问。
  • fastAgent loop 暂不支持交互工具。 onRunInteractiveTool 无执行器时直接返回 "Interactive tool is not supported in fastAgent loop yet"(loop/index.ts:509-513)——规划 Agent 里的工具不能再触发子级交互式中断。
  • 没有本地 stop gate 兜底。 计划没干完就收工的场景现在只靠系统提示约束(stop gate 已移除,见 3.3.3);update_plan 状态机本身有原子性校验,但不阻止模型提前停。
  • askId 依赖 memory 落库。 ask 恢复靠节点 memory(agent/index.ts:277 起读写 providerState);memory 丢失则无法接回原上下文。

7. 横向对比(与本组其它章)

想了解去哪章
节点/边/引用/变量的数据模型01-workflow-data-model.md
一次对话从 API 到 SSE 返回的端到端路径02-chat-pipeline.md
调度器怎么把整张图跑起来、怎么调用本章节点03-workflow-engine.md
本章(LLM 节点内部的算法与多轮控制)你在这里
知识库检索节点内部(向量化、混合检索、重排)05-knowledge-base.md

8. 代码地图(导航索引)

主题文件路径符号名
基础对话节点packages/service/core/workflow/dispatch/ai/chat/dispatchChatCompletion.tsdispatchChatCompletion
知识库引用角色判定packages/service/core/workflow/dispatch/ai/chat/datasetCite.tsgetDatasetCiteData
系统提示拼接 + 裁窗packages/service/core/workflow/dispatch/ai/chat/chatMessages.tsgetChatMessages
工具节点入口packages/service/core/workflow/dispatch/ai/toolcall/index.tsdispatchRunTools
工具节点主编排packages/service/core/workflow/dispatch/ai/toolcall/toolCall.tsrunToolCall
工具执行装配packages/service/core/workflow/dispatch/ai/toolcall/toolCall.tscreateToolCallToolProvider(原 useToolRunner 等 hook 已并入)
停止信号工具packages/service/core/workflow/dispatch/ai/toolcall/stopTool.tsdispatchStopToolCall
多轮工具循环内核packages/service/core/ai/llm/agentLoop/provider/fastAgent/loop/base.tsrunAgentLoop
规划 Agent 入口packages/service/core/workflow/dispatch/ai/agent/index.tsdispatchRunAgent
单主 Agent 循环packages/service/core/ai/llm/agentLoop/provider/fastAgent/loop/index.tsrunFastAgentMainLoop
plan 状态机packages/service/core/ai/llm/agentLoop/domain/systemTool/plan/state.tsapplySetPlan / applyPlanUpdate
追问工具packages/service/core/ai/llm/agentLoop/domain/systemTool/ask/tool.tscreateAskAgentTool
Main Agent 系统提示packages/service/core/ai/llm/agentLoop/domain/mainPrompt.tsbuildDefaultAgentSystemPrompt
工具可见性packages/service/core/ai/llm/agentLoop/provider/fastAgent/tools/index.tsgetToolsForFastAgentLoop
sub agent · app/插件packages/service/core/workflow/dispatch/ai/agent/sub/app/index.tsdispatchApp / dispatchPlugin
sub agent · 知识库packages/service/core/workflow/dispatch/ai/agent/sub/dataset/index.tsdispatchAgentDatasetSearch
读文件系统工具packages/service/core/ai/llm/agentLoop/domain/systemTool/readFile/index.tsREAD_FILES_TOOL_NAME / createReadFilesTool
辅助节点 · 分类路由packages/service/core/workflow/dispatch/ai/classifyQuestion.tsdispatchClassifyQuestion
辅助节点 · 字段抽取packages/service/core/workflow/dispatch/ai/extract.tsdispatchContentExtract
辅助节点 · 问题扩写packages/service/core/ai/functions/queryExtension.tsqueryExtension