尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Cutter 开发者入门与代码贡献指南:从环境搭建到编码规范的完整实战

Cutter 开发者入门与代码贡献指南:从环境搭建到编码规范的完整实战 应用安全桌面应用开发工具【免费下载链接】cutterFree and Open Source Reverse Engineering Platform powered by rizin项目地址https://gitcode.com/gh_mirrors/cu/cutter点击查看免费下载本文基于 Cutter 官方开发者文档整理而成。Cutter 是一款由 rizin 驱动的免费开源逆向工程平台本文面向希望为其贡献代码的开发者从编译源码、挑选 Issue到遵循 CutterCore 开发模式与编码规范、提交 Pull Request再到在 Qt Creator、VS Code、CLion、Visual Studio 等主流 IDE 中搭建开发环境逐步给出可落地的操作路径。读完本文你将掌握一套完整的 Cutter 代码贡献工作流。开发者文档导航三份核心文档Cutter 的开发者文档入口位于 docs/source/contributing/code.rst它本身是一个 toctree 索引页将开发者内容组织为三个子文档作为新贡献者应首先通读新开发者教程code/getting-started.rst —— 完整的贡献流程克隆编译、挑选 Issue、定位源码、编写代码、提交 PR开发指南code/development-guidelines.rst —— 编码规范与代码约定CutterCore 使用方式、命名规则、Qt 信号连接、clang-format/clang-tidy 工具链等开发环境配置code/ide-setup.rst —— 在 Qt Creator、VS Code、CLion、Visual Studio 中搭建开发环境的具体建议。此外docs/source/building.rst 详细描述了各平台的命令行编译步骤含 Troubleshootingdocs/source/contributing.rst 则汇总了代码、文档、插件、翻译四条贡献路线。若你遇到构建问题优先查阅 building 文档中的Building:Troubleshooting一节若需要 IDE 配置建议则阅读 ide-setup 文档。第一步克隆并编译 Cutter在开始写代码之前必须先在自己的环境Linux、Windows 或 macOS上成功构建 Cutter并验证它能正常运行。克隆源码时需携带子模块Rizin 是 Cutter 的核心依赖以 submodule 形式存在于rizin/目录git clone --recurse-submodules https://github.com/rizinorg/cutter克隆完成后会得到如下目录结构cutter/- |-docs/ # Cutter 文档 |-rizin/ # Rizin 子模块 |-src/ # Cutter 源码在 Linux 上推荐使用 CMake 构建默认会自动编译 bundled Rizinmkdir build cd build cmake .. cmake --build .CMake 3.12 可简化为cmake -B build cmake --build build构建完成后可执行文件Cutter位于build/目录下运行./build/cutter常用的 Cutter CMake 构建选项仓库根目录的 CMakeLists.txt 中定义了完整的构建选项以下为开发中最常涉及的几个选项默认值说明CUTTER_USE_BUNDLED_RIZINON从./rizin子模块自动编译 Rizin而非搜索系统安装的 Rizin定义于 CMakeLists.txtCUTTER_ENABLE_PYTHONOFF启用 Python 集成Python 插件的前置条件CUTTER_ENABLE_PYTHON_BINDINGSOFF使用 Shiboken 自动生成 Python 绑定Python 插件的必要条件CUTTER_ENABLE_KSYNTAXHIGHLIGHTING默认开启使用 KSyntaxHighlighting 进行代码高亮CUTTER_ENABLE_GRAPHVIZ默认开启启用 Graphviz 图布局CUTTER_ENABLE_TRANSLATIONSON构建并安装翻译文件见 CMakeLists.txtCUTTER_ENABLE_SIGDBOFF下载并安装 sigdb仅在CUTTER_USE_BUNDLED_RIZINON时可用见 CMakeLists.txtCUTTER_QT6Qt 主版本号允许值为5、6见 CMakeLists.txtCUTTER_INCLUDE_GIT_HASHON在完整版本字符串中包含 git 提交哈希CUTTER_VERSION_SUFFIX空打包方用于区分基于同一上游版本的多个构建CUTTER_EXTRA_PLUGIN_DIRS空额外插件搜索路径列表适用于 Linux 发行版打包CUTTER_ENABLE_DEPENDENCY_DOWNLOADSOFF启用依赖下载主要用于 CI 环境准备二进制发布包CUTTER_PACKAGE_DEPENDENCIESOFF安装步骤中包含第三方依赖用于发布包配置示例构建带 Python 插件支持的版本cmake -B build -DCUTTER_ENABLE_PYTHONON -DCUTTER_ENABLE_PYTHON_BINDINGSON显式关闭某个选项cmake -B build -DCUTTER_ENABLE_PYTHONOFF开发建议除非你正在处理打包相关问题否则推荐使用默认的 bundled Rizin 配置即保持CUTTER_USE_BUNDLED_RIZINON。它是确保 Rizin 版本兼容的最简单方式也便于在多个 Cutter 分支之间切换。若需使用系统安装的其他版本 Rizin可设置CUTTER_USE_BUNDLED_RIZINOFF在 Linux 上若存在多个 Rizin 版本可通过PKG_CONFIG_PATH环境变量指定所需安装。注意使用与 Cutter 不匹配的 Rizin 版本可能导致编译失败。常见故障排查详见 docs/source/building.rstCMake 找不到 Qt确认 Qt 版本正确找到其 prefix包含 bin/、lib/、include/ 的目录通过CMAKE_PREFIX_PATH指定例如cmake -DCMAKE_PREFIX_PATH/opt/Qt/5.9.1/gcc_64 ..运行 Cutter 时提示找不到librz_*.so部分系统 linker 默认使用 RUNPATH 而非 RPATH可添加-DCMAKE_EXE_LINKER_FLAGS-Wl,--disable-new-dtags重新编译或将 Rizin 安装路径加入LD_LIBRARY_PATHrz_*.h: No such file or directory检查rizin/子模块是否与上游同步运行git submodule update --init --recursiverz_core development package not foundPATH环境变量可能不包含/usr/local/bin将其加入PATHmacOS 上可在 Qt Creator 的Build Environment中追加。第二步找到适合自己的任务如果暂时没有明确想做的功能可以从 Cutter 的 Issue 与 feature-request 列表出发。Cutter 使用 GitHub 的多个项目管理和追踪功能来组织成百上千个 Issue熟悉它们有助于快速筛选LabelsIssue 与 PR 的标签用于描述类型、所属功能、难度和优先级。建议从good-first-issue标签开始这些 Issue 已被维护者标记为适合新贡献者Projects项目看板按特定功能或问题聚合任务。Cutter 为调试器、Hexdump 组件、反编译组件、High DPI 支持等设有专属看板适合想参与大型功能或了解某模块进展的贡献者Milestones每个新版本发布前维护者会把高优先级的 bug 和 feature 请求汇聚到里程碑中。若想参与下一版本最需要的工作可以关注开放的 MilestoneIssue 认领挑选 Issue 前先确认其未被他人认领若已被认领可以在评论区询问认领者是否有意继续。部分已认领的 Issue 实际已被弃置可被其他贡献者接手。提示修复自己最困扰的 pet peeve 是一个绝佳的起点——任何让你对 Cutter 感到不便的具体问题都可能是一个合适的切入点。第三步熟悉现状并定位源码动手前先运行 Cutter 熟悉你要修改的功能当前的行为。例如若是对话框理解其角色与用户使用方式若是 bug按照 bug 报告中的步骤尝试复现。定位实现该功能的源文件时推荐两种方法按功能名搜索文件例如想改进 Sections 组件可在源码中寻找SectionsWidget.cpp与SectionsWidget.h实际位于 src/widgets/SectionsWidget.cpp 与 src/widgets/SectionsWidget.h搜索界面中出现的独特字符串在整个代码库中搜索对话框、组件或功能中出现的字符串通常能定位到相关的.cpp或.ui文件再由此导航到正确位置。Cutter 的源码集中在src/目录下组件类位于 src/widgets对话框位于 src/dialogs核心逻辑位于 src/core 与 src/common。若对二进制分析任务不熟悉可以随时在社区向其他开发者请教该功能的设计意图与使用方法。第四步理解 CutterCore——与 Rizin 交互的唯一入口开发指南code/development-guidelines.rst强调CutterCore 是整个进程中与 Rizin 建立联系的唯一主类全进程唯一通过Core()即可访问Core()-getOffset();使用seek修改当前寻址位置修改 Rizin 的 seek 必须使用CutterCore::seek(const RVA offset)因为该方法会发出CutterCore::seekChanged(RVA offset)信号驱动各组件刷新。永远不要调用cmd(s offset)直接操作 Rizin。Core()-seek(0x00C0FFEE);从 src/core/Cutter.cpp 的源码实现可以看到seek(ut64 offset)内部通过rz_core_seek_and_save执行寻址并在地址变化时调用updateSeek()而seek(const QString thing)src/core/Cutter.cpp则先用rz_num_math解析表达式再寻址。Cutter 还支持静默寻址seekSilent它不会触发seekChanged事件也不会向 seek 历史中添加新条目——适用于不希望界面随之刷新的场景其实现见 src/core/Cutter.cppvoid CutterCore::seekSilent(ut64 offset) { CORE_LOCK(); if (offset RVA_INVALID) { return; } rz_core_seek(core, offset, true); }创建 Widget 时连接 seekChanged 信号新增 Widget 时务必连接CutterCore::seekChanged(RVA offset)信号使其在 Rizin seek 改变如切换到另一个函数时刷新输出。这是 Cutter 组件间同步的核心机制。第五步编码规范与工具链开发指南规定Cutter 大体遵循 Qt 官方编码风格 的定制版本并强制使用clang-format与clang-tidy检查。clang-format提交 Pull Request 前需要用 clang-format8.0 或更新版本格式化代码。仓库根目录的 _clang-format 定义了项目专用规则由 scripts/update_clang_format.sh 生成基于 clang-format 15.0.4其中关键的定制项包括列宽上限 100、缩进 4 空格、强制单行条件语句加花括号InsertBraces: true、include 分组重排IncludeBlocks: Regroup、Qt 头文件归入第二优先级等。格式化单个文件clang-format -stylefile -i src/filename.cpp若改动涉及整个src目录的多个文件可对全部源码执行find ./src -regex .*\.\(cpp\|h\) -exec clang-format -stylefile -i {} \;clang-tidy在格式化之外使用 clang-tidy13.0 或更新版本捕捉潜在风格违规。首先配置构建以生成compile_commands.json并运行 autogen 工具cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPERelease -DCUTTER_QT6 -DCUTTER_USE_BUNDLED_RIZINON -DCMAKE_EXPORT_COMPILE_COMMANDSON cmake --build build --target Cutter_autogen对自最近一次提交以来修改过的文件运行 clang-tidygit diff -U0 --no-color HEAD~1 | clang-tidy-diff.py -p1 -path build/对整个src目录运行排除第三方与自动生成文件正则来自 scripts/clang-tidy.py 中的DEFAULT_REGEXrun-clang-tidy -p build .*src/(?!(themes|bindings|fonts|img|translations|Cutter_autogen)).*\.(cpp|h)$clang-tidy 还支持-fix自动修复风格违规但修复不一定完美提交 PR 前务必人工核对git diff -U0 --no-color HEAD~1 | clang-tidy-diff.py -fix -p1 -path build/仓库自带的 Python 封装脚本Cutter 在 scripts 目录下提供了封装脚本避免手敲上述命令。python scripts/clang-format.py -h输出的参数如下usage: clang-format.py [-h] [-C CLANG_FORMAT] [-c] [-v] [-f FILE] [-d DIFF] Clang format the cutter project options: -h, --help show this help message and exit -C, --clang-format CLANG_FORMAT path of clang-format -c, --check enable the check mode -v, --verbose use verbose output -f, --file FILE formats (or checks) only the given file -d, --diff DIFF format all modified file related to branch从源码scripts/clang-format.py看该脚本扫描src目录下的*.cpp、*.h、*.hpp跳过translations支持单文件、diff 分支改动或全目录三种模式-c/--check以--Werror --dry-run方式仅检查而不改写适合 CI。clang-tidy.py的用法为usage: clang-tidy.py [-h] [-T RUN_CLANG_TIDY] [-p BUILD_PATH] [-j JOBS] [-r REGEX] [-i] [-q] clang-tidy regex wrapper options: -h, --help show this help message and exit -T, --run-clang-tidy RUN_CLANG_TIDY Path of run-clang-tidy binary -p, --build-path BUILD_PATH Path to the build directory -j, --jobs JOBS Number of parallel execution jobs -r, --regex REGEX Regex pattern for filtering files -i, --fix Apply fixes automatically -q, --quiet Suppress configuration logs低层编码约定开发指南还列出了如下命名与风格约定变量使用 camelBack 命名首字母小写后续单词首字母大写避免无意义命名缩写仅首字母大写不使用前导/尾随下划线包括m_前缀int Height // 错误 string name_of_widget // 错误 int a, b; // 错误 int height; // 正确 string nameOfWidget; // 正确 int height, width; // 正确 CutterJsonWigdet cutterJSONWidget; // 错误 CutterJsonWigdet cutterJsonWigdet; // 正确class Example { private: int index_; // 错误 int m_index; // 错误 int index; // 正确 };匿名命名空间源文件私有的全局实体变量、函数、结构体等即内部链接应定义在匿名命名空间中namespace { int globalCounter 0; bool doSomething() { ... return true; } };函数与变量一致使用 camelBack 命名未使用的参数通过注释掉参数名或直接省略名称的方式处理避免使用Q_UNUSEDvoid do_something() {} // 错误 void DoSomething() {} // 错误 void doSomething() {} // 正确 // 坏使用 Q_UNUSED void doSomething(int one, int two) { Q_UNUSED(one) ... } // 好注释掉参数名 void doSomething(int /*one*/, int two) { ... } // 好省略参数名 void doSomething(int, int two) { ... }类使用 CamelCase每个单词首字母大写缩写仅首字母大写成员变量遵循变量命名约定无前导/尾随下划线或m_前缀class memory_dock_widget; // 错误 class memoryDockWidget; // 错误 class MemoryDockWidget; // 正确 class CutterJSONWidget // 错误 class CutterJsonWidget // 正确类型转换避免 C 风格强转优先使用 C 转换static_cast、const_cast等对QObject使用qobject_cast。智能指针优先使用 C 智能指针unique_ptr、shared_ptrRegisterProfileDialog *ui; // 坏 std::unique_ptrUi::RegisterProfileDialog ui; // 好花括号与 Qt 官方指南不同Cutter 规定条件语句即使只有一行也必须使用花括号// 错误 if (address.isEmpty()) return false; // 正确 if (address.isEmpty()) { return false; } // 错误 for (int i 0; i 10; i) qDebug(%i, i); // 正确 for (int i 0; i 10; i) { qDebug(%i, i); }循环使用 C11 range-based for// 好 - 需要元素副本 for (auto import : importsArray) { doSomething(import); } // 好 - 不需要副本 for (auto import : importsArray) { doSomething(import); } // 好 - 无需修改 for (const auto import : importsArray) { doSomething(import); }auto在迭代器、lambda、类型转换、new初始化等场景优先使用autoauto it myMap.find(key); auto multiply [](int a, int b) - int { return a * b; }; auto myFloat static_castfloat(myInt); auto *item new QListWidgetItem(text);nullptr只使用nullptr不要使用0或Q_NULLPTRstd::unique_ptrQObject obj nullptr;连接 Qt 信号的方式必须使用现代成员函数指针语法连接信号与槽支持编译期类型检查与 lambda// 通常在构造函数中连接当前类的成员 connect(this-ui-button1, QPushButton::clicked, this, MyObject::buttonClicked); // 好 // 直接连接其他对象的槽 connect(checkbox, QCheckBox::toggled, widget, QWidget::setEnabled); // 好 // 使用 lambda 传递额外参数 connect(button1, QPushButton::clicked, this, [this](){ foo(getBar()); }); // 好禁止使用旧式宏语法、自动名称连接如on_actionNew_triggered()槽命名以及缺少接收对象的 3 参 connect——这些方式可能在运行时静默失效// SIGNAL 与 SLOT 宏 connect(sender, SIGNAL(clicked), this, SLOT(buttonClicked)); // 坏 // 自动名称连接 slot: void on_actionNew_triggered(); // 坏 // 缺少接收对象的 3 参数 connect connect(sender, SomeObject::signal, [this](){ this-foo(getBar()); }); // 坏头文件 include 组织尽量只在头文件中包含必要的定义避免引发额外编译若仅需知道类存在而无需完整原型可前置声明class MyClassThatExists; private: std::unique_ptrMyClassThatExists classInstance;然后在.cpp中包含该类头文件。include 顺序为从本地到全局先引号形式的本地头文件如#include common/Helpers.h空行后是 Qt 头文件如#include QShortcut最后是标准 C 头文件且须按字母序排列。这一规则由 clang-format 自动处理对应 _clang-format 中的IncludeBlocks: Regroup与IncludeCategories配置。Doxygen 注释API 参考文档由 Doxygen 生成函数文档使用如下格式仅当函数用途无法从名称直接看出时才需要文档如getCount()无需注释syncRemote()应注释/** * brief Add new parameters to the accumulator * param params The parameters to add * return True if the parameters were added, false otherwise */ bool accumulate(RefreshDeferrerParams params) { ... };类建议在定义上方加文档说明其用途/** * brief A short description about SomeDialog */ class SomeDialog { ... };第六步编写代码、提交 Pull Request制定实现计划并在对应 Issue 中讨论动手编码前务必通读本文第五节的编码规范。若不确定如何实现可参考现有代码中同一组件或类似组件是否实现了相似行为拷贝现有代码时思考其为何如此实现——同样的因素未必适用于你的场景旧代码本身也可能是次优的。完成代码后提交变更并遵循 Pull Request 模板打开 PR。模板要求填写Test plan若进行了 GUI 修改用截图展示效果列出评审者用于验证变更符合预期的操作步骤借此机会考虑潜在的边界情况与不同类型的输入即使步骤看似琐碎、开发时已执行过也应完整执行一遍——这有助于发现最终清理阶段引入的错误并确保没有遗漏。PR 合并后还可以继续回到第一步挑选新任务或者请导师/其他开发者推荐新的 Issue。作为新贡献者也可以随时向维护者建议改进本文档缺失的信息。第七步IDE 开发环境搭建Cutter 对 IDE 没有硬性要求任何 CMake 集成良好的 IDE 均可胜任。开发构建默认使用 bundled Rizin见前文选项表唯一的例外是打包类工作。src/中大量组件使用.ui文件XML 格式Qt Designer 可独立用于可视化编辑许多编辑器也支持 XML 高亮与代码折叠。Qt Creator优点内置 Qt API 帮助、.ui可视化编辑器Qt Designer、调试器中 Qt 类型的显示辅助不足查看非项目文件如 Rizin 源码不便非 Linux 系统上最简单的安装方式需要 Qt 账号登录。项目配置File → Open File or Project...选择cutter/CMakeLists.txt选择 kit 后点击Configure Project。clang-format启用内置 Beautifier 插件在 clang-format 选项页选择 Use predefined style: File即可在保存文件时自动格式化。修改 CMake 配置点击左侧 Projects 按钮选择 Build所有 CMake 选项可在图形化编辑器中修改。编辑.ui文件双击在可视化编辑器中打开需要按文本编辑时右键选择Open With → Plain Text Editor。注意从可视化模式切回代码模式会以只读方式打开并提示 This file can only be edited in Design mode。VS Code优点插件数量庞大对非项目文件有良好的回退机制推荐插件C/C微软官方 C 支持、CMake ToolsCMake 项目集成、CMake编辑 CMake 文件时的语言支持与上一插件功能互补需同时安装。项目配置File → Open Folder...选择克隆目录安装推荐插件后CMake Tools 会弹出配置提示选择[Unspecified]kit若错过弹窗可用Ctrl-Shift-P执行CMake: Configure。修改 CMake 配置首次配置后Ctrl-Shift-P执行CMake: Edit CMake CacheCutter 相关选项大多以 CUTTER 开头。格式化C/C 扩展内置 clang-formatCtrl-Shift-I格式化文档Ctrl-K Ctrl-F仅格式化选区建议在设置中配置自动格式化。构建、运行、调试状态栏与命令面板提供CMake: BuildF7、CMake: Run Without DebuggingShiftF5、CMake DebugCtrlF5。默认无参启动需要传参时创建.vscode/launch.json可从模板Debug: Select and Start Debugging → Add configuration.. → C,C: (gdb) Launch生成。gdb 的 Qt 类型漂亮打印从 KDevelop 的 gdb printers 脚本下载脚本后在~/.gdbinit中添加python import sys sys.path.insert(0, /path/to/folder/with/pretty_printer_scripts) from qt import register_qt_printers register_qt_printers (None) end set print pretty 1CLion特点插件数量中等许多为 JetBrains 第一方插件无免费版本首次打开项目分析文件较慢.cpp/.h 切换首次可能需要数秒。项目配置File → Open选择克隆目录在左侧文件列表中打开cutter/CMakeLists.txt点击黄色提示栏中的Load CMake project。修改 CMake 配置File → Settings → Build,Execution,Deployment → CMake与命令行一致使用-DOPTION_NAMEVALUE格式。编辑.ui文件默认双击无反应可拖拽到文本编辑器打开也可在File → Settings → Editor → File Types中将*.ui关联从Qt UI Designer Form改为 XML 或 Files Opened in Associated Applications。Visual Studio推荐 Visual Studio 2019CMake 集成最佳更老版本建议通过命令行或cmake-gui生成 VS 工程再打开。安装时选择 Desktop development with C 组件即可后续可通过 VS Installer 增删组件。项目配置用 VS 打开克隆目录通过Project → CMake Settings打开 CMake 设置编辑器勾选所需选项或保持默认使用 vcpkg 时 VS 会自动检测也可在CMake toolchain file字段指定 toolchain 路径不使用 vcpkg 时在CMake command arguments:字段配置 Qt 路径。为让调试器可读地显示 Qt 类型可安装 Qt Visual Studio Tools 插件会生成Documents/Visual Studio 2019/Visualizers/qt6.natvis文件生成后可卸载插件。格式化VS 默认支持 clang-format直接使用 Cutter 根目录的_clang-format文件可通过Tools → Options → Text Editor → C/C → Formatting调整触发方式。编辑.ui文件默认以 XML 文本打开可通过右键Open With...配置为 Qt Designer。进阶话题Git 子模块与 Rizin 的同步Rizin 以 git submodule 形式内嵌于 Cutterrizin/目录Cutter 需要紧跟 Rizin 的最新版本以获取新特性、bug 修复与性能改进因此更新子模块是 Cutter 开发中的常见工作。在 Cutter 根目录查看所有子模块git config --file .gitmodules --get-regexp path | awk { print $2 }一次性更新所有子模块git submodule foreach git pull origin master git add submodule_name_1 submodule_name_2 git commit -m Update submodules多数情况下只需更新 rizin 子模块cd rizin git checkout dev git pull cd .. git add rizin git commit -m Update rizin submoduleQt 开发相关资源若将开发涉及 Qt 的列表/表格类组件建议阅读 Qt 官方文档中的 Signals Slots、Model/View Programming、QAction 等专题可在 Qt 帮助系统中直接查阅。结语Cutter 的贡献流程可以概括为构建 → 选 Issue → 熟悉现状 → 定位源码 → 按规范编码 → 提交含 Test plan 的 PR → 迭代。其中通过Core()访问唯一的 CutterCore 实例、用seek()而非cmd(s ...)修改寻址、遵循 camelBack/CamelCase 命名与 clang-format/clang-tidy 工具链约束是保证代码库一致性的关键。希望这篇指南能帮助你迈出向 Cutter 贡献代码的第一步。赞分享应用安全桌面应用开发工具【免费下载链接】cutterFree and Open Source Reverse Engineering Platform powered by rizin项目地址https://gitcode.com/gh_mirrors/cu/cutter点击查看免费下载相关推荐note-gen低配笔记实测流畅吗note gen低配笔记实测流畅吗 一句话总评 note gen本地优先的 AI 笔记应用Rust WebView 混合架构在 4GB 内存的老笔AI 应用桌面应用移动开发知识管理ChatGPT-Tool-Hub高级玩法自定义工具开发指南与最佳实践ChatGPT Tool Hub高级玩法自定义工具开发指南与最佳实践 ChatGPT Tool Hub是一个强大的开源ChatGPT工具生态系统让你能够将工LMCache 贡献指南实战从环境搭建、测试到代码规范的完整开发流程LMCache 贡献指南实战从环境搭建、测试到代码规范的完整开发流程 LMCache 是一个面向大语言模型推理场景的 KV Cache 加速层通过缓存与管理人工智能大模型模型推理服务缓存上一篇Firebase-queue完全指南构建容错、多工作进程的实时作业队列系统下一篇如何按 provider 分组定制 Dify API 构建只打包指定向量数据库与追踪 provider创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表