主要技术:Next.js、FastGPT、MongoDB
项目目录
项目简介
FastGPT 是开源 AI Agent 构建平台,把知识库、模型调用、工具和用户交互组织成可视化工作流。用户可以导入资料搭建问答应用,也可以把分析任务封装为子应用,让主流程分别调用并汇总结果;发布后可通过分享页面、嵌入窗口或接口使用。
仓库中的知识库助手示例会根据资料回答“FastGPT 是什么”,同时展示引用和调试信息。项目以 TypeScript、Next.js 实现应用界面与接口,使用 MongoDB 保存应用和版本数据;服务端工作流层负责节点调度、文件上下文与检索,向量检索后端按部署配置选择。

项目亮点
- 子应用有明确的数据交接。 调用节点读取指定版本的图,显式传入问题、所选历史、附件与变量,以独立的
WorkflowVariableState执行,再回传回答、历史、用量和交互状态,便于主流程组合已有能力。 - 附件按本次任务重新筛选。 对象
key保留稳定身份,临时链接用于访问;在已建立根文件上下文的调用中,当前问题和显式附件优先于历史文件,再按身份去重、限量并派生子上下文,避免旧附件占满当前任务输入。 - 融合、重排和引用预算分开控制。 默认文本检索用加权
RRF合并向量与全文候选,再按配置进行模型重排;重排失败退回召回结果。候选规模、分数过滤和最终引用的Token软预算分别处理。 - 子流程交互可以回到父流程。 子应用停在交互节点时,以
childrenInteractive回传待补充信息;下一次调用恢复相应节点与边,继续生成后续结果。
技术架构
projects/app/ 承载应用界面和聊天接口,MongoDB 中的应用、版本及图配置交给服务端工作流运行器。节点沿连线调度,通过变量引用取得输入;知识库节点调用默认检索链,子应用节点则创建自己的运行图和变量状态,完成后将约定输出交回父流程。
资源上下文约束应用版本可访问的资源,文件上下文管理本次运行登记的文件引用。调用子应用时,分别构建它的资源和文件上下文。
| 代码位置 | 职责 |
|---|---|
projects/app/src/pages/api/v1/chat/completions.ts |
接收聊天请求并进入应用执行 |
packages/service/core/workflow/dispatch/ |
节点调度与子应用执行、结果和交互回传 |
packages/service/core/workflow/utils/context.ts、packages/service/core/workflow/utils/fileContext.ts |
运行上下文、附件选择与文件引用登记 |
packages/service/core/workflow/resource/ |
读取应用资源、版本及子应用资源上下文 |
packages/service/core/dataset/search/defaultRecall/ |
多路召回、融合、重排和引用过滤 |
如何写进简历
资料预审台 · 供应商资料预审助手 | 零售业场景示例
项目描述:面向采购人员的供应商资料预审系统,核对营业执照、资质证书和产品授权材料,结合准入条款汇总缺件、主体差异及待确认项,供采购复核。
技术栈:TypeScript、Next.js、FastGPT、MongoDB
主要工作:
- 子应用隔离与预审交接:基于
FastGPT将执照主体、经营资质和产品授权拆为子应用,显式传入检查目标、附件与历史;为子应用建立独立WorkflowVariableState,回收结构化意见和交互状态,再由主流程汇总供应商预审结果。 - 附件身份与子任务上下文:以对象
key和文件引用区分稳定附件身份与临时下载地址,按当前输入派生子任务文件上下文;对历史与新增文件去重、限量并优先保留当前材料,减少旧供应商附件挤占本次预审输入。 - RRF 召回融合与引用控制:对准入条款融合向量与全文候选,采用加权
RRF平衡语义问法和资质名称,再衔接可选重排;按内容去重、有效重排状态过滤与引用软预算组织证据,为跨附件核对提供相关条款。 - 跨证照字段与有效期核对:将子应用返回的主体代码、授权范围和有效期转为统一字段,在业务代码中比对跨附件的一致性;区分缺件、过期、主体差异与识别失败,将判断结果关联到原材料和准入条款,形成可复核意见。
教案搭档 · 课程教案协作助手 | 教育业场景示例
项目描述:面向教师的课程教案协作系统,结合教学大纲与课程资料生成讲解、案例和练习,支持补充教学要求后继续编排并汇总教案。
技术栈:TypeScript、Next.js、FastGPT、MongoDB
主要工作:
- 教案模块化与子应用状态:将讲解、案例和练习封装为子应用,显式传递教学目标、课程资料和所选历史;以独立
WorkflowVariableState执行各模块,汇总回答与过程结果,处理同一教学目标下的多步骤内容生成。 - 课程资料的混合检索与精排:在指定课程资料范围内以加权
RRF融合关键词和向量召回,兼顾教材术语与口语化要求;结合可选重排、同内容去重及引用预算筛选定义、案例和练习依据,控制重复证据占用上下文。 - 嵌套交互恢复与结果回收:通过
childrenInteractive将子应用的补充问题回传主流程,教师提交后恢复对应节点与边,并沿历史重建必要输出;统一回收模块回答、历史与用量,使待补充输入与后续教案生成衔接。
面试题与回答
3 题如何说明 Agent 工作流各节点的输入、输出和数据流?
展开收起
核心回答
我会以“父工作流调用一个子应用”为例,讲清父节点选取输入、子应用创建运行状态、内部节点沿边执行、结果再回到父流程这条链。
FastGPT的应用调用节点把子应用当作一个可组合的执行单元,传递的不只是问题字符串,还包括所选历史、文件、变量和交互恢复信息;回传的也不只是回答文本,还有历史、用量和交互状态。例如父流程已取得用户问题和附件,需要调用一个已有分析子应用:父节点把问题接到
userChatInput,把附件接到fileUrlList,再映射子应用需要的变量。子应用内部完成解析与分析后,父流程继续引用它的回答或历史输出。具体流程应以各节点的字段交接说明,不能只列“解析、分析、回答”三个名称。1. 父节点选哪些数据传进去?
dispatchRunAppNode()将节点参数分成问题、历史配置、文件列表、流式开关以及其余子应用变量。- 问题:普通调用根据
userChatInput和选定文件构造子查询;交互恢复则沿用当前查询,并传入子流程恢复信息。 - 历史:
getHistories()根据history选取上下文。传数组表示明确引用这些历史,数字表示按窗口选择,压缩检查点另有保留规则;没有配置时不会直接把父流程全部历史传入。 - 文件:显式
fileUrlList先转换为运行时文件;未指定时兼容使用当前查询里的文件。随后派生子文件上下文,对当前输入、文件变量和历史文件做去重与数量筛选。子应用拿到的是筛选后的查询和历史,不是父流程所有附件的无条件集合。
因此,文件 URL、持久化文件元数据和文件正文要分开:传入 URL 代表子流程可以继续引用该文件,真正解析正文仍由相应节点完成。
2. 子应用怎样变成可执行的图?
节点先加载目标应用、所选版本及其资源上下文,再读取该版本的
nodes、edges、chatConfig。存储节点转换成运行时节点,入口根据普通启动或交互恢复选择;普通边初始化为waiting,恢复时可使用保存的边状态和节点输出。边决定调度关系,节点输入中的变量引用决定实际读取哪些值。子流程随后创建独立的
WorkflowVariableState,按自己的变量配置初始化输入,并加入当前用户、会话、历史和时间等运行时信息。传入父variableState的一个用途,是让子流程通过运行时文件 URL 找回原始文件元数据;它并不表示父子所有变量共用一个可任意覆盖的字典。准备完成后,runWorkflow()接收运行时图、筛选后的查询与历史,以及子变量状态执行。父子调用的实际数据映射。3. 子流程结束后,哪些结果回到父流程?
包装节点从
assistantResponses提取文本,放入data.answerText;data.history则由筛选后的历史、本次用户输入和子流程回答组成。交互恢复时不重复添加同一条用户输入。后续节点引用这些输出,不需要读取子流程所有内部节点状态。运行统计走另一条交接:汇总
flowUsages的点数,通过usagePush记入父节点,并从子流程运行摘要合并输入/输出 Token、引用集合和子响应数量。runTimes、反馈与记忆更新也分别返回,不能把界面显示的回答文字当成完整运行结果。如果子流程停在交互节点,返回的
workflowInteractiveResponse会包装成childrenInteractive,下一次调用再取出childrenResponse恢复执行。关闭子应用流式输出只影响对外流式内容,子工作流仍会运行,回答文本仍可作为节点数据返回。输出、用量与交互包装。4. 版本、资源和异常怎样影响这条数据流?
loadChildWorkflowWithResource()先取得目标应用,再取得工作流版本,并为子版本创建资源上下文。命中资源快照也会实时读取应用实体;未命中时按运行人成员身份校验读取权限。指定的有效版本 ID 若能匹配目标应用,就读取该版本;无有效匹配时回到正式版本读取逻辑,优先正式版本指针,再取最新发布版本,历史应用另有兼容。因此不能把“配置过版本 ID”直接说成“任何情况下都严格锁定这一版本”。子应用资源与版本加载、版本选择。空输入或缺少目标应用 ID 会直接返回节点错误;应用加载、变量初始化和子流程执行等异常由该节点的异常分支转换成错误结果,而不是正常回答。排查时沿四个位置检查就能缩小范围:父节点实际映射的字段、筛选后交给子流程的输入、实际读取的版本与资源,以及回传的是普通结果还是交互/错误状态。
这种封装让同一子应用可以被多个流程复用,但也增加了一层输入映射和结果包装。接口应明确哪些值传入、哪些值回传;需要父流程继续使用的数据,应通过显式输出交接,不能依赖子应用内部状态自动回写。
参考资料
- 国内:FastGPT:工作流与插件:依据节点输入可引用全局变量或前序输出、连线状态控制执行的说明,组织父节点输入与子图调度的解释;子应用具体映射按固定源码展开。
- 国内:JavaGuide:Workflow、Graph 和 Loop:采用节点读取和更新数据、边决定执行路径的划分,解释数据交接与控制流的区别。
- 国内:zero2Agent:并行执行 Fan-out / Fan-in:采用分支结果需要明确汇总规则的解释,支撑原通用答案中并行更新不能默认安全覆盖的说明。
- 国内:WeThinkIn:Agent 设计模式与工作流编排:采用状态契约、执行节点和运行依赖分别管理的思路,解释子变量状态与真实工具执行的职责。
- 国外:LangChain:Graph API overview:依据节点局部更新、状态 Schema 与 reducer 的约定,保留通用答案中的状态合并解释;FastGPT 的运行时实现按自身源码说明。
- 国外:LangChain:Streaming:依据完整状态、局部更新和自定义事件的区别,说明通用答案中流式内容为何不等于完整状态或最终结果。
- 问题:普通调用根据
AI 工作流有哪些与节点、状态和恢复机制相关的安全风险?
展开收起
核心回答
FastGPT的子应用调用需要同时控制子应用能使用哪些资源和这次能看到哪些文件。父流程能运行,不表示子应用可以读取父流程所有附件;一个 URL 能被解析,也不表示它就是已经授权的内部文件。该版本通过版本资源上下文和派生文件上下文分别处理这两件事。子应用会继承父应用的全部权限吗?
loadChildWorkflowWithResource()先加载子应用及实际取得的版本,再根据该版本的资源清单创建独立resourceContext。资源加载也有两条路径:命中版本快照清单的应用,按数据库实时存在、未删除及团队条件读取;未命中清单的动态资源,则按运行人的读权限检查,并限制团队范围。应用资源加载、子应用资源上下文。这是一种版本资源授权与动态访问分开的设计,不能描述成“所有资源每次都按最终聊天用户的 ACL 重新验证”。如果业务要求权限撤销立刻影响已发布流程,应进一步核对发布授权、资源快照和当前执行身份之间的产品规则。
怎样防止父流程附件全部进入子流程?
在根文件上下文已经准备好的执行路径中,
runWithDerivedWorkflowFileContext()从本次问题、显式文件变量和所选历史挑选文件,按稳定身份去重并限制数量;当前输入优先于历史附件,再由父上下文derive()创建子文件上下文。从子文件上下文取得未被选入的文件引用会被拒绝;另行输入的 URL 仍可能走外链下载器,不代表所有网络读取都受这份名单控制。文件选择与隔离、未登记 URL 的外链路径。内部文件的可信身份来自对象 key。根入口先校验 key 所属的来源、用户和会话范围,再生成模型可用链接;子流程只能继承父上下文已登记的私有文件引用,不能通过陌生 key 或伪造 URL 获得对象访问能力。签名 URL 是访问地址,文件身份和权限则保留在服务端引用中。根入口登记、子文件准入。
交互恢复和外链读取还能怎样追问?
子应用交互恢复会根据保存的交互信息重建运行节点、连线,并再次建立子变量及文件上下文。这不自动保证业务写操作只执行一次;如果子流程连接创建工单等工具,仍应由工具服务维护动作幂等和资源状态检查。恢复与上下文重建。
外链下载走 HTTP(S) 检查、超时和流式大小限制,并使用统一安全请求器:逐跳检查重定向,直连时复核 DNS 解析地址并将连接固定到已验证 IP。域名白名单是另一层可配置策略,留空会放行该层检查,不能据此忽略下游已有防护。外链下载、重定向处理、直连地址固定。
这些能力仍有部署边界:生产环境对回环、未指定地址和云元数据目标有基础阻断,其他私网限制受
CHECK_INTERNAL_IP控制;开发和代理路径还需分别核验,代理最终解析不能由本机的地址固定保证。因此应按实际环境测试外链和恢复场景,不能声称所有部署都已安全验收。网络策略、代理分支。混合检索权重如何配置?
展开收起
核心回答
FastGPT的默认文本检索流程在选择mixedRecall后,同时召回向量结果与全文结果,通过embeddingWeight控制加权RRF:向量一路用 $w$,全文一路用 $1-w$。它融合的是名次贡献,不是把原始向量分数与全文分数按比例相加。调参时需要把两种权重分开:
embeddingWeight控制向量与全文的融合;rerankWeight控制原召回排名与模型重排排名的融合。最低相关度similarity是否生效,还取决于搜索模式和重排是否成功;最终引用量由limit的Token预算控制。双路召回与融合
countRecallLimit()在混合模式中将向量和全文候选上限分别设为80、60;多查询结果先在各自通道内合并,再分别截到该上限。这些是当前实现的候选配置,不是最终引用条数。候选数量、通道内合并。服务函数中
embeddingWeight缺省为0.5。两路都有结果且参与融合时,datasetSearchResultConcat()按数据块标识合并,累加:$$ S(d)=\frac{w}{60+r_{\text{vector}}(d)}+\frac{1-w}{60+r_{\text{fulltext}}(d)} $$
其中,$r$ 表示该通道中从 1 开始的名次,只计入该数据块实际出现的通道。这里的
60是源码中的平滑常数;若仅剩一路有效列表,函数直接返回该列表。权重传入、RRF 实现。重排、阈值和引用上限
重排需要开启
usingReRank,并有有效查询和重排模型。文本候选去重后交给模型;rerankWeight缺省为0.5,模型重排结果与原融合结果再做一次加权RRF。设为1时采用模型重排列表;重排失败则回退到原召回列表,并返回usingReRank=false。重排启用条件、重排融合与失败回退。最终先做内容去重,再处理阈值和引用预算。有效重排时,带
reRank分数的结果按该分数与similarity比较,缺少这类分数的结果保留;未有效重排时,只有纯向量模式按向量分数过滤,混合模式不启用这项最低相关度过滤。因此,混合搜索里只提高similarity,未必会减少结果,需要同时检查实际返回的usingReRank和usingSimilarityFilter。过滤条件。随后按
limit控制引用的Token预算。该实现按块累计并保留跨过预算的当前块,因此不是严格逐Token截断,也不能用增大limit代替扩大召回候选池。最终处理顺序、引用预算处理。用什么问题验证
例如在供应商准入资料库中,分别准备“
ISO 9001证书需要提交哪些材料”和“质量管理资质怎么准备”这类问题。先固定重排、问题扩展与引用预算,只调整embeddingWeight,检查对应资料条款能否进入前列;再独立调整rerankWeight,观察精确标准名称有没有被相近资质说明挤掉。如果文档已在候选池中但融合后靠后,才针对权重比较;若候选池没有对应文档,则回查索引、全文分词、查询与集合过滤。上述答法针对文本混合检索,图片描述和图片向量在该版本中还有独立融合分支。