next-mdx-remote

next-mdx-remote

优化Next.js应用中MDX内容的加载与渲染

next-mdx-remote是一个轻量级工具集,用于在Next.js应用中高效处理MDX内容。它支持在getStaticProps或getServerSideProps中加载MDX,并在客户端进行水合渲染。通过提供serialize函数和MDXRemote组件,开发者可以灵活处理各种来源的MDX文本,支持自定义组件、作用域和前置元数据解析。该库优化了性能,简化了MDX的使用流程,适合构建灵活的内容驱动型Next.js网站。

Next.jsMDXReact组件渲染前端开发Github开源项目

next-mdx-remote

一组轻量级实用工具,允许在 getStaticPropsgetServerSideProps 中加载 MDX,并在客户端正确地进行水合。

next-mdx-remote


安装

npm install next-mdx-remote

如果与 Turbopack 一起使用,在解决此问题之前,你需要在 next.config.js 中添加以下内容:

const nextConfig = { + transpilePackages: ['next-mdx-remote'], }

示例

import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' import Test from '../components/test' const components = { Test } export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} components={components} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = 'Some **mdx** text, with a component <Test />' const mdxSource = await serialize(source) return { props: { source: mdxSource } } }

虽然在同一个文件中看到这两个部分可能有些奇怪,但这正是 Next.js 的一个特点 —— getStaticPropsTestPage 虽然出现在同一个文件中,但它们在两个不同的地方运行。最终,你的浏览器包不会包含 getStaticProps,也不会包含它仅在服务器上使用的任何函数,因此 serialize 将完全从浏览器包中移除。

重要提示:请非常谨慎地将任何 next-mdx-remote 代码放入单独的"实用工具"文件中。这样做可能会导致 Next.js 的代码分割功能出现问题 - 它必须能够清晰地确定哪些内容仅在服务器端使用,哪些应该保留在客户端包中。如果你将 next-mdx-remote 代码放入外部实用工具文件中并出现问题,请先移除它并从上面的简单示例开始,然后再提交问题。

其他示例

<details> <summary>解析前置元数据</summary>

Markdown 通常与前置元数据配对使用,这通常意味着需要对 markdown 的处理方式进行一些额外的自定义处理。为了解决这个问题,next-mdx-remote 提供了可选的前置元数据解析功能,可以通过向 serialize 传递 parseFrontmatter: true 来启用。

以下是使用示例:

import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' import Test from '../components/test' const components = { Test } export default function TestPage({ mdxSource }) { return ( <div className="wrapper"> <h1>{mdxSource.frontmatter.title}</h1> <MDXRemote {...mdxSource} components={components} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = `--- title: Test --- Some **mdx** text, with a component <Test name={frontmatter.title}/> ` const mdxSource = await serialize(source, { parseFrontmatter: true }) return { props: { mdxSource } } }

vfile-matter 用于解析前置元数据。

</details> <details> <summary>使用 `scope` 向组件传递自定义数据</summary>

<MDXRemote /> 接受一个 scope 属性,使所有值都可以在你的 MDX 中使用。

scope 参数中的每个键值对都将作为 JavaScript 变量公开。例如,如果你有一个 scope{ foo: 'bar' },它将被解释为 const foo = 'bar'

这特别意味着你需要确保 scope 参数中的键名是有效的 JavaScript 变量名。例如,传入 { 'my-variable-name': 'bar' } 会产生_错误_,因为键名不是有效的 JavaScript 变量名。

同样重要的是要注意,scope 变量必须作为_组件的参数_使用,不能在文本中间渲染。这在下面的示例中有所展示。

import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' import Test from '../components/test' const components = { Test } const data = { product: 'next' } export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} components={components} scope={data} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = 'Some **mdx** text, with a component using a scope variable <Test product={product} />' const mdxSource = await serialize(source) return { props: { source: mdxSource } } }
</details> <details> <summary>将 `scope` 传入 `serialize` 函数</summary>

你也可以将自定义数据传入 serialize,它会将值传递并使其在结果中可用。通过将 source 的结果展开到 <MDXRemote /> 中,数据将可用。

请注意,传入 serialize 的任何 scope 值都需要是可序列化的,这意味着无法传递函数或组件。此外,scope 参数中的任何键名都必须是有效的 JavaScript 变量名。如果你需要传递不可序列化的自定义 scope,可以直接将 scope 传递给渲染时的 <MDXRemote />。上一节有一个如何做到这一点的示例。

import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' import Test from '../components/test' const components = { Test } const data = { product: 'next' } export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} components={components} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = 'Some **mdx** text, with a component <Test product={product} />' const mdxSource = await serialize(source, { scope: data }) return { props: { source: mdxSource } } }
</details> <details> <summary> 来自 <code>MDXProvider</code> 的自定义组件<a id="mdx-provider"></a> </summary>

如果你想让组件在应用程序中的任何 <MDXRemote /> 中可用,可以使用 @mdx-js/react 中的 <MDXProvider />

// pages/_app.jsx import { MDXProvider } from '@mdx-js/react' import Test from '../components/test' const components = { Test } export default function MyApp({ Component, pageProps }) { return ( <MDXProvider components={components}> <Component {...pageProps} /> </MDXProvider> ) }
// pages/test.jsx import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = 'Some **mdx** text, with a component <Test />' const mdxSource = await serialize(source) return { props: { source: mdxSource } } }
</details> <details> <summary> 带点的组件名(例如 <code>motion.div</code>) </summary>

包含点(.)的组件名,如 framer-motion 中的组件,可以像其他自定义组件一样渲染,只需在组件对象中传递 motion

import { motion } from 'framer-motion' import { MDXProvider } from '@mdx-js/react' import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} components={{ motion }} /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = `Some **mdx** text, with a component: <motion.div animate={{ x: 100 }} />` const mdxSource = await serialize(source) return { props: { source: mdxSource } } }
</details> <details> <summary>延迟水合</summary>

延迟水合会推迟客户端上组件的水合。这是一种优化技术,可以改善应用程序的初始加载,但可能会导致 MDX 内容中任何动态内容的交互性出现意外延迟。

注意:这将在你渲染的 MDX 周围添加一个额外的包装 div,这是为了避免渲染过程中的水合不匹配所必需的。

import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote } from 'next-mdx-remote' import Test from '../components/test' const components = { Test } export default function TestPage({ source }) { return ( <div className="wrapper"> <MDXRemote {...source} components={components} lazy /> </div> ) } export async function getStaticProps() { // MDX 文本 - 可以来自本地文件、数据库或任何地方 const source = 'Some **mdx** text, with a component <Test />' const mdxSource = await serialize(source) return { props: { source: mdxSource } } }
</details>

API

该库暴露了一个函数和一个组件,分别是 serialize<MDXRemote />。这两者被有意隔离在各自的文件中 —— serialize 旨在在服务器端运行,因此在 getStaticProps 中运行,它在服务器/构建时运行。而 <MDXRemote /> 则旨在在客户端,即浏览器中运行。

  • serialize(source: string, { mdxOptions?: object, scope?: object, parseFrontmatter?: boolean })

    serialize 接收一个 MDX 字符串。它还可以选择传入选项,这些选项直接传递给 MDX,以及一个可以包含在 MDX 作用域中的 scope 对象。该函数返回一个对象,旨在直接传递给 <MDXRemote />

serialize( // 原始MDX内容字符串 '# hello, world', // 可选参数 { // 可用于任何自定义MDX组件参数 scope: {}, // MDX的可用选项,更多信息请参见MDX文档 // https://mdxjs.com/packages/mdx/#compilefile-options mdxOptions: { remarkPlugins: [], rehypePlugins: [], format: 'mdx', }, // 指示是否从MDX源解析frontmatter parseFrontmatter: false, } )

访问 https://mdxjs.com/packages/mdx/#compilefile-options 获取可用的 mdxOptions

  • <MDXRemote compiledSource={string} components?={object} scope?={object} lazy?={boolean} />

    <MDXRemote /> 使用 serialize 的输出以及可选的组件参数。其结果可以直接渲染到您的组件中。要延迟内容的水合并立即提供静态标记,请传递 lazy 属性。

    <MDXRemote {...source} components={components} />

替换默认组件

渲染将在底层使用 MDXProvider。这意味着您可以用自定义组件替换HTML标签。这些组件列在MDXJS的组件表中。

一个使用案例是使用您偏好的样式库渲染内容。

import { Typography } from "@material-ui/core"; const components = { Test, h2: (props) => <Typography variant="h2" {...props} /> } ...

如果您愿意,也可以用 <MDXProvider /> 包裹整个应用,而不是直接将组件传递给 <MDXRemote />。参见上面的示例

注意:由于组件名称中的 "/",th/td 将无法工作。

背景与理论

在Next.js应用中加载MDX文件没有一个很好的默认方式。之前,我们编写了 next-mdx-enhanced 以便能够将MDX文件渲染到布局中并导入其frontmatter来创建索引页。

next-mdx-enhanced 的这个工作流程还可以,但引入了一些限制,我们已在 next-mdx-remote 中移除了这些限制:

  • 文件内容必须是本地的。 您不能将MDX文件存储在另一个仓库、数据库等中。对于足够大的操作,最终会在创作内容的人和处理内容呈现的人之间产生分歧。在同一个仓库中重叠这两个关注点会使每个人的工作流程更加困难。
  • 您受限于基于文件系统的路由。 您的页面是根据其位置生成URL的。或者您可能使用 exportPathMap 重新映射它们,这会给作者带来困惑。无论如何,以任何方式移动页面都会破坏东西 -- 要么是页面的URL,要么是您的 exportPathMap 配置。
  • 您最终会遇到性能问题。 Webpack是一个JavaScript打包工具,强制它加载数百/数千页的文本内容会导致内存需求激增。Webpack将每个页面存储为具有大量元数据的独特对象。我们的一个实现中,仅有几百个页面就需要超过8GB的内存来编译网站。构建时间超过25分钟。
  • 您在构建关系数据的方式上会受到限制。 当您的整个数据结构是解析成JavaScript对象并保存在内存中的frontmatter时,将内容组织成动态的、相关的类别是困难的。

因此,next-mdx-remote 改变了整个模式,使您不是通过导入来加载MDX内容,而是通过 getStaticPropsgetServerProps 来加载 -- 您知道的,就像加载任何其他数据一样。该库提供了以性能良好的方式序列化和水合MDX内容的工具。这消除了上面列出的所有限制,而且成本显著降低 -- next-mdx-enhanced 是一个非常重的库,有大量自定义逻辑和一些令人烦恼的限制。我们的非正式测试显示构建时间减少了50%或更多。

自本项目最初创建以来,Kent C. Dodds制作了一个类似的项目,mdx-bundler。该库支持MDX文件中的导入和导出(只要您手动读取每个导入的文件并传递其内容),并自动处理frontmatter。如果您有很多文件都导入和使用不同的组件,您可能会受益于使用 mdx-bundler,因为 next-mdx-remote 目前只允许在所有页面中导入和使用组件。重要的是要注意,这个功能是有代价的 -- 对于基本的markdown内容,mdx-bundler 的输出至少比 next-mdx-remote 的输出大400%。

我如何用这个构建博客?

数据显示,所有开发者工具用例的99%都是构建不必要的复杂个人博客。开玩笑的。但说真的,如果您试图为个人或小型企业使用构建博客,请考虑只使用普通的HTML和CSS。您绝对不需要使用重量级的全栈JavaScript框架来制作一个简单的博客。当您几年后回来进行更新时,您会感谢自己的,因为届时您所有的依赖项不会有10个破坏性的发布。

如果您真的坚持要这样做,请查看我们官方的Next.js示例实现。💖

注意事项

环境目标

next-mdx-remote 生成的用于实际渲染MDX的代码针对支持模块的浏览器。如果您需要支持较旧的浏览器,请考虑转译 serializecompiledSource 输出。

import / export

importexport 语句不能在MDX文件内部使用。如果您需要在MDX文件中使用组件,应该将它们作为prop提供给 <MDXRemote />

希望这是有意义的,因为为了工作,导入必须相对于文件路径,而这个库允许从任何地方加载内容,而不是只从设定的文件路径加载本地内容。至于导出,MDX内容被视为数据,而不是模块,所以我们无法访问可能从传递给 next-mdx-remote 的MDX中导出的任何值。

安全性

这个库在客户端评估JavaScript字符串,这是它MDXRemotes MDX内容的方式。如果不小心处理,将字符串评估为javascript可能是一种危险的做法,因为它可能启用XSS攻击。重要的是要确保您只将 serialize 函数生成的 mdxSource 输入传递给 <MDXRemote />,如文档中所指示。不要将用户输入传递给 <MDXRemote />

如果您的网站有禁止通过 evalnew Function() 进行代码评估的CSP,您需要放宽该限制以使用 next-mdx-remote,这可以通过使用 unsafe-eval 来完成。

TypeScript

这个项目确实包含TypeScript使用的原生类型。serialize<MDXRemote /> 都有正常预期的类型,该库还导出了一个可用于为 getStaticProps 的结果添加类型的类型。

  • MDXRemoteSerializeResult<TScope = Record<string, unknown>>:表示 serialize 的返回值。可以传递 TScope 泛型类型来表示您传入的作用域数据的类型。

以下是TypeScript中简单实现的示例。对于TypeScript的每种配置,您可能不需要完全按照这种方式实现类型 - 这个示例只是演示在需要时可以应用类型的位置。

import type { GetStaticProps } from 'next' import { serialize } from 'next-mdx-remote/serialize' import { MDXRemote, type MDXRemoteSerializeResult } from 'next-mdx-remote' import ExampleComponent from './example' const components = { ExampleComponent } interface Props { mdxSource: MDXRemoteSerializeResult } export default function ExamplePage({ mdxSource }: Props) { return ( <div> <MDXRemote {...mdxSource} components={components} /> </div> ) } export const getStaticProps: GetStaticProps<{ mdxSource: MDXRemoteSerializeResult }> = async () => { const mdxSource = await serialize('some *mdx* content: <ExampleComponent />') return { props: { mdxSource } } }

React Server Components (RSC) 和 Next.js app 目录支持

在服务器组件中使用 next-mdx-remote,特别是在Next.js的 app 目录中,可以通过从 next-mdx-remote/rsc 导入来支持。之前,序列化和渲染步骤是分开的,但是从现在开始,RSC使这种分离变得不必要。

一些值得注意的区别:

  • <MDXRemote /> 现在接受一个 source 属性,而不是接受来自 next-mdx-remote/serialize 的序列化输出
  • 不能再通过使用 @mdx-js/reactMDXProvider 上下文来提供自定义组件,因为RSC不支持React上下文
  • 要在传递 parseFrontmatter: true 时在MDX外部访问frontmatter,请使用 next-mdx-remote/rsc 导出的 compileMdx 方法
  • 不再支持 lazy 属性,因为渲染发生在服务器上
  • <MDXRemote /> 必须在服务器上渲染,因为它现在是一个异步组件。客户端组件可以作为MDX标记的一部分渲染

有关RSC的更多信息,请查看 Next.js文档

示例

假设在使用 app 目录的Next.js 13+应用程序中使用。

基本

import { MDXRemote } from 'next-mdx-remote/rsc' // app/page.js export default function Home() { return ( <MDXRemote source={`# Hello World This is from Server Components! `} /> ) }

加载状态

import { MDXRemote } from 'next-mdx-remote/rsc' // app/page.js export default function Home() { return ( // 理想情况下,这个加载旋转器应确保没有布局偏移, // 这是如何提供这样一个加载旋转器的示例。 // 在Next.js中,您也可以使用 `loading.js` 来实现这一点。 <Suspense fallback={<>Loading...</>}> <MDXRemote source={`# Hello World This is from Server Components! `} /> </Suspense> ) }

自定义组件

// components/mdx-remote.js import { MDXRemote } from 'next-mdx-remote/rsc' const components = { h1: (props) => ( <h1 {...props} className="large-text"> {props.children} </h1> ), } export function CustomMDX(props) { return ( <MDXRemote {...props} components={{ ...components, ...(props.components || {}) }} /> ) }
// app/page.js import { CustomMDX } from '../components/mdx-remote' export default function Home() { return ( <CustomMDX // h1 现在使用 `large-text` 类名渲染 source={`# 你好世界 这是来自服务器组件的内容! `} /> ) }

在 MDX 外部访问前置元数据

// app/page.js import { compileMDX } from 'next-mdx-remote/rsc' export default async function Home() { // 可选择为前置元数据对象提供类型 const { content, frontmatter } = await compileMDX<{ title: string }>({ source: `--- title: RSC 前置元数据示例 --- # 你好世界 这是来自服务器组件的内容! `, options: { parseFrontmatter: true }, }) return ( <> <h1>{frontmatter.title}</h1> {content} </> ) }

替代方案

next-mdx-remote 对其支持的功能有明确的观点。如果你需要 next-mdx-remote 未提供的额外功能,以下是一些可以考虑的替代方案:

你可能不需要 next-mdx-remote

如果你正在使用 React 服务器组件,并且只是想使用带有自定义组件的基本 MDX,你只需要核心的 MDX 库,不需要其他东西。

import { compile, run } from '@mdx-js/mdx' import * as runtime from 'react/jsx-runtime' import ClientComponent from './components/client' // MDX 可以从任何地方获取,比如文件或数据库。 const mdxSource = `# 你好,世界! <ClientComponent /> ` export default async function Page() { // 将 MDX 源代码编译为函数体 const code = String( await compile(mdxSource, { outputFormat: 'function-body' }) ) // 然后你可以在服务器上运行代码,生成一个服务器 // 组件,或者你可以将字符串传递给客户端组件进行 // 最终渲染。 // 使用运行时运行编译后的代码并获取默认导出 const { default: MDXContent } = await run(code, { ...runtime, baseUrl: import.meta.url, }) // 渲染 MDX 内容,提供 ClientComponent 作为组件 return <MDXContent components={{ ClientComponent }} /> }

如果你不打算将编译后的字符串传递给数据库或客户端组件,你也可以使用 evaluate 来简化这个过程,它可以在一次调用中编译和运行代码。

import { evaluate } from '@mdx-js/mdx' import * as runtime from 'react/jsx-runtime' import ClientComponent from './components/client' // MDX 可以从任何地方获取,比如文件或数据库。 const mdxSource = ` export const title = "MDX 导出演示"; # 你好,世界! <ClientComponent /> export function MDXDefinedComponent() { return <p>MDX 定义的组件</p>; } ` export default async function Page() { // 运行编译后的代码 const { default: MDXContent, MDXDefinedComponent, ...rest } = await evaluate(mdxSource, runtime) console.log(rest) // 输出 { title: 'MDX 导出演示' } // 渲染 MDX 内容,提供 ClientComponent 作为组件,以及 // 导出的 MDXDefinedComponent。 return ( <> <MDXContent components={{ ClientComponent }} /> <MDXDefinedComponent /> </> ) }

许可证

Mozilla 公共许可证 2.0 版

编辑推荐精选

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

热门AI工具生产力协作转型TraeAI IDE
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

聊天机器人AI助手热门AI工具AI对话
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

热门AI工具AI办公办公工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

模型训练热门AI工具内容创作智能问答AI开发讯飞星火大模型多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

AI助手热门AI工具AI创作AI辅助写作讯飞绘文内容运营个性化文章多平台分发
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

下拉加载更多