普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-字节跳动技术团队

AI 与星辰大海:2025,从新手到开挂勇士的奇幻旅程

作者:Data-TnS-Engineering-FE 团队

前言

曾几何时,代码敲击声回荡在深夜的办公室,你是否也曾幻想过有一个全能助手替你分担工作?如今,这个美好的愿景不再是空中楼阁。

想象一下,当你正为产品设计苦思冥想时,突然耳边传来 AI 的灵感火花;

开发过程中,AI 像是个比你还了解自己的最佳拍档,为你提供独到的建议;

当繁琐的测试工作如排山倒海而来,它早已帮你先行解决那些隐秘的 Bug;

交付环节则如同一个老练的质检专家,在每一个细节上都帮你擦亮眼睛;

而在运维阶段,它更是你的“夜间守卫者”,早早预警潜在问题;

有人说,AI 的到来让开发者的身份发生了质的飞跃,从“头发稀疏的代码独行侠”变成了“开挂勇士”。在 AI 的协助下,谁不想在产品设计上满怀创意、在代码编写上行云流水、在测试中无懈可击,在运维中高枕无忧呢?

在这个充满奇思妙想的科技时代,AI 既是你的忠实伙伴,又是你的全能助手,更是一位风趣的导师。它不知疲倦地助你不断攀登职业高峰,让每一步开发都像是一场精彩绝伦的探险。是时候跳出你的舒适区,体验 AI 如何点亮开发者们的星辰大海。

准备好了吗?接下来,我们将揭开这段 AI 助力开发的奇妙旅程,从产品设计到运维管理,为你重新定义“效率”、“质量”和“体验”。 每一刻都充满了独特的惊喜和乐趣,相信这将是一段你不想错过的神奇之旅。让我们一起踏上这段充满无限可能的旅程,重新发现开发的独特魅力与无穷乐趣。

在 AI 的魔法世界里,开发者不再是孤军奋战的英雄”

助力业务发展

随着 LLM 能力的迭代和更新,越来越多过去 LLM 无法很好解决的问题重新进入了大家的视野。在人审领域下人们一般都会围绕着 LLM 是否可以完全替代审核员对内容进行审核进行讨论与探索。在进一步进行详细的 LLM 赋能人工审核流程相关的例子前我们需要先了解一下“人类审核员”面对的一些挑战和要求:

  • 能处理复杂的内容(e.g.可能既可以是 A,也可以是 B,还可以是 C)并给出最合适的答案
  • 质量 & 稳定性:对于相同类型的内容要给出相同的结果
  • 效率:在不损伤质量的前提下需要达成一些数量上的要求

先说结论目前 LLM 的能力还无法完全替代审核员,更多的是在各方面提供辅助从而提升审核员的审核质量和效率。所以在产品结合的思路上,我们主要关注在如何利用 LLM 简化或加速审核员对于单一任务的操作并完成了一些功能的落地。

前置思考

说到 LLM 最出名的当属于 ChatGPT 了,如 GPT-4o

当我们思考 LLM 能为审核员带来什么样的辅助时我们首先想到的就是如何利用这些现有的天花板模型。这些模型一般都有良好的指令执行能力,并且在处理一些通用文字相关的问题时一般具有非常好的表现。

例如我们可以提供一段文字内容给模型,并告诉模型我们希望它帮助我们从中提取出不符合某些规则的内容如:潜在的语言攻击,歧视内容,潜在色情内容等等。

当有了模型识别出来的内容后可以通过一些特殊的形式展示这些信息如高亮等。 这可以帮助审核员在审核过程中快速捕捉到内容中存在的潜在风险并加速其对当前内容的审核。听起来是不是很简单?实际上将一个如此简单的辅助能力从离线验证到最终上线需要考虑的远远不止这些:

  • 成本:模型是以 token 来进行计算的。一段文字会先被转换成 token 然后再传给模型,同时模型输出的也是一堆 token 然后会再被转换成我们看得懂的语言。所以如何无损高效的压缩传给模型的内容是一个非常重要的课题。(如删除重复的内容等)
  • 合规:因为模型在迭代过程中需要大量的训练数据,厂商都会收集模型在实际使用过程中的数据以补充其训练数据集。如果将一些敏感或隐私数据不经过处理的直接传给模型可能会带来合规风险。(如公司的保密数据,或者用户的不公开信息等)我们可不希望当其他人问模型你的银行卡密码时模型能准确无误的回答上来hhh。
  • 时延:模型能力强大是有取舍的。可以姑且先理解为(在计算资源不变的前提下)模型的规模越大->模型的能力越强 -> 每次回答你问题的速度就越慢。同时给模型输入越多时返回的时长也会相应的延长。
  • 其他 n+ 问题:服务可用性,模型拒绝回答,模型选型等等...

但是由于模型能力的局限性,我们只能对标准文字内容审核提供符合标准的辅助能力。那对于像视频或者音频中出现的文字,或者话语或者一些复杂的问题怎么办?

例如我们想对于一个歌曲中的歌词进行风险识别。

这个时候我们就要引入分步的解决思路,歌曲中歌词的风险识别可以被拆分成如下工作流

暂时无法在飞书文档外展示此内容

举例来说,在预处理环节我们可能会需要对 ASR 转换的文字内容进行整理如加入标点符号,分句断句等。模型对于长文字内容的处理能力会随着内容变长而下降。我们需要根据业务的诉求和实际情况进行灵活调整。抛去 ASR 环节不说,后边三步可以有两种实现思路:

  1. 在一个 Prompt 中通过分步的方式指导模型进行处理

    1. 会更好的保留整体的上下文,但是可能会碰到如内容太长超出 context window size 的情况并且随着内容的变长模型的完成时间也会变慢。
  1. 将每一步拆分开然后通过串行调用的方式完成多次模型调用。

    1. 可能会丢失一些上下文内容,但是因为进行拆分后每一部分的长度都是相近的所以在模型响应时长和 context window 上限的问题上则有比较好的表现。

如果我们想让模型帮我们翻译火星文呢(没办法,业务上就是有这个诉求 hhh)

对于一些有高定制化诉求的场景普遍厂商也会开放对现有模型就行二次训练的能力。比如对于将内容从 A 语言翻译到B 语言并且对语言风格/用词有明确需求时,可以考虑对基本模型做一次 SFT(Supervised Fine Tuning) 。也就是对自身特殊诉求收集数据集并使用这个数据集对模型进行二次训练以达到更好的表现。

如果你说识别什么的还是太复杂了,有没有更简单的应用场景?

在实际产品开发过程中,我们经常性的需要对圈定/给定的一组数据进行频繁的离线验证以确保目前的能力表现是符合我们预期的。又或者在产品迭代的过程中我们时常需要一个指标/分数来量化当前的能力/体验。举例来说,在进行多目标语言混合翻译的能力开发过程中,我们在 prompt enginnering 过程中需要时刻关注模型的表现并确保每一次修改都不会对能力造成较大的退步。Multidimensional Quality Metrics (MQM) 是一个多维度指标翻译质量分析框架。我们可以通过自然语言的方式向模型输入我们的对于(译文与原文对比)不同维度上的要求如:

  • 翻译准确性

    • 是否丢失一些信息
    • 是否凭空捏造了一些信息
    • 翻译错误/未准确翻译
    • 未翻译
    • ...
  • 流畅度

    • 语法是否正确
    • 标点是否正确
    • 拼写
    • ...
  • 风格

    • 是否用了一些抽象的词

    • 是否是正式文风

    • ...

得益于 LLM 优秀的自然语言处理能力和跨语言能力,LLM 可以基于我们输入的多个维度来对译文进行评估并最终给出评估结果。需要注意的是为了保证模型输出结果的准确性我们一般不会直接要求模型输出如:0-10 分的打分。我们会尽量让模型在一个给定的状态下进行枚举。如:严重程度(严重, 普通, 可忽略不计)并通过代码对枚举进行映射后计算出最终的得分。这样可以最大程度上避免 LLM 输出不稳定和幻觉等问题。

实际上 MQM 这种多维度评估体系也可以应用在翻译之外的领域,如润色、故事生成等。甚至可以被用在图片打分。他们背后的原理都是类似的,都是通过发挥 LLM 出色的自然语言理解能力 + 通过自然语言描述框架来实现一些复杂的打分工作。

功能落地

上边我们只是利用了 LLM 的自然语言处理能力。如果我们想让模型帮我们回答一些不是通识性知识点的问题时该怎么办呢?

暂时无法在飞书文档外展示此内容

模型也可以像人一样,碰到不会的东西时可以先去查询然后再基于查询结果进行判断和回答

比如说我们想为审核员提供一个问答机器人回答一些审核领域相关的问题,或者基于以往的审核结果进行回答。

又或者我们想基于某一个数据库中的结果对审核员的问题进行回答

但是对于不同类型的内容,知识库在生产过程中采用的分割策略,结构等会大大影响最终问答质量的表现。如:

  • Chunking: 如当对于大段内容进行拆分时要拆分的多细,拆分后的信息是否应该保留整体上下文方便后续参考时使用等。
  • Embedding Model: 需要针对需要支持的语言来选择向量化模型,不同的模型在召回准确度上也有不小的区别。

当我们检索到的相关知识后可以通过将这些信息连同原始问题一并作为输入给模型并让模型基于相关知识点尝试回答。

当我们积累了一系列能力后,如果快速的向其他方向上推广和扩展?

随着越来越多基于 LLM 能力的需求落地,我们发现其实所有的 LLM 能力都可以被总结为:一个带顺序的分步流程。其中模型调用只是工作流中的一个步骤/节点。如果我们能快速的复用这个分步流程就可以快速的对取得成功的能力进行推广。

(分步流程可视化示意图)

随着我们对现有的一些优秀 LLM 编排能力库/平台的深入了解,我们发现现有的方案都无法很好的对我们的业务场景提供100%的支持。甚至大多数都无法通过合规这一环节。更不用说可能业务有自己的模型、数据库、基础能力等等。我们需要在业务下实现一套定制的流程引擎

流程引擎本质上可以被拆解为一下两种图的类型:

  • DAG 有向无环图。一般用作承载经典 Workflow 场景。需要注意的是这种图不能包含循环所以一般被用作实现单一 Agent 能力。
  • FSM 有限状态机。用作实现如 Multi-Agent 场景或有环的场景。是 DAG 的一个补充。但是需要注意的是因为状态机同时之后有一个激活状态所以并发分支等能力无法通过 FSM 实现。

当我们实现了上述两种流程引擎后可以进行组合实现更复杂的能力如:FSM 中嵌套 DAG,DAG 中嵌套 FSM 等等。

当我们有了上述流程引擎和对应的 DSL 之后。我们就可以在业务间快速复用能力(只要复制一下 DSL 或基于现有的 DSL 做二次开发就好了)。

可能你会问,为什么不根据需求直接把这些逻辑写在代码里呢?实际上在日常开发过程中我们发现大部分功能都是通过对有限能力的组合来实现的。如果不做流程引擎的建设会带来很大效率上的降低以及多余的开发量。

其他实践

Hornbill 是内部用于多个平台的 Oncall 工单管理工具,拥有三种升级策略。我们处理工单的团队包括用户运营团队和研发工程师,并在创建工单时自动拉入相关人员进行协作。由于审核员需要快速处理大量审核任务,Hornbill 通过 24 小时的用户运营团队快速解决问题,技术问题会被升级至研发解决。

Hornbill SDK 可整合到平台中,帮助用户在提交工单前通过 FAQ 寻找解决方案,从而减少不必要的工单。每周约有 几百个由用户运营团队处理的工单,因此减少工单数量可以让团队将时间用于更高价值的任务。

为了提高工单解决的效率并减少工单数量,我们引入了 AI 功能。

首先,相似工单检测能够有效识别并链接具有相同问题的工单,通过创建问题描述的嵌入向量,并使用向量相似性计算如余弦相似度,系统在用户提交新工单时可提示已有相似工单,推荐用户加入现有工单而非新建,或在工单提交后提醒用户操作团队进行链接,从而减少重复工单处理的工作量。

其次,AI 摘要生成功能则通过自动生成问题的总结,在群聊中梳理出问题描述、原因和结论,提供给不同班次的用户操作团队以保持问题处理的一致性。这一功能通过将所有的群聊内容传递给大型语言模型生成总结,从而避免因班次交接导致的上下文丢失,提高工单解决的连续性和效率。这些先进的 AI 功能减少了用户操作团队在票务处理上的重复性工作,让他们能够将更多时间投入到更具价值的任务中,不仅提升了团队的工作效率,也提高了用户问题解决的速度和质量,为用户提供了更优质的体验。

另外,Hornbill SDK FAQ 搜索功能旨在解决用户自行反馈且可通过非技术知识解决问题的工单。 通过将常见问题生成 FAQ,用户可以搜索相关知识库,并根据输入查询定制 FAQ 内容,使其更易理解。方法是为常见的可自解决问题创建 FAQ,并对 FAQ 问题进行向量嵌入,将其存储在向量数据库中。当用户输入问题时,我们利用余弦相似度比对向量嵌入,以找到匹配的 FAQ,并通过大型语言模型总结 FAQ 内容,展示给用户。这一功能减少了不必要的工单,提升了用户的自助解决效率。

总结

当我们完成了对流程引擎的落地,同时在流程中有技巧性的使用 LLM。基本上领域下的产品/业务诉求都可以基于这一套框架来实现。LLM 除了可以为我们做分类,识别等功能以外也可以帮助我们做离线验证/数据评估等工作。 与可视化编排界面配合可以大幅降低使用门槛。对于一个想要使用 LLM 能力来实现业务诉求的业务方,不论是 PM 同学还是运营同学都可以进行尝试可以大大解决研发的人力缺口。

提升开发体验

AI 编程在今年有了比较大的发展,因为出现了 Cursor、Windsurf、v0、bolt.new 这些,在不同场景下,成为了能指数级提升生产力的工具。这不仅仅得益于越来越强的模型能力,也得益于许多在应用/交互上的创新与探索。

工具形态

传统工具的替代品

搜索、文档工具、设计稿代码生成工具等

多智能体自然语言编程

代表工具:gpt-pilot、gpt-engineer、MetaGPT

这类工具直接通过自然语言与一个多智能体系统进行交互,多智能体系统会在内部划分多个角色/任务,如:程序员/开发/调试、架构师/设计、产品经理/拆解、项目经理/任务管理等。

但这类工具要将整个工程从新建到功能推进完全交给智能体维护,用户只能通过指令和自然语言对话对工程进行控制,并且受限于上下文窗口,也无法构建复杂的平台系统。使得这些工具都没有办法用于程序员日常的生产和实际的项目里。只能作为非专业人士的玩具,或者 AI 研究的尝试。

blog.pythagora.ai/2023/09/04/…

github.com/geekan/Meta…

代码辅助工具

代表工具:Github Copilot、Continue.dev、MarsCode

以 continue.dev 为例,这类工具的核心功能大概就是上述几类:以代码块/文件为上下文 Chat、Tab 自动补全、选中代码块进行自然语言编辑、在聊天框中对代码块进行应用。

这些功能已经在开发中被普遍使用了,开源的模型、插件对这些功能的支持也非常成熟了,不同公司内部也有类似的解决方案。

好用,但还不够强大,伴随着这些功能的组合和深度优化,期待更具生产力的工具逐步被开发出来并具备商业价值。

🆕 复合型编程IDE/插件

代表工具:Cursor、Windsurf、Cline

暂时无法在飞书文档外展示此内容

Windsurf 的 Cascade 工具,与 Cursor 的 Composer 类似

暂时无法在飞书文档外展示此内容

在代码辅助工具的基础上进一步增强代码整合能力,包括:

  • 能够有效汇总上下文
  • 能够将代码块转化为文件变更,以支持同时编辑多个文件
  • 能够管理AI批量编辑后文件的暂存状态,并灵活跳转到不同历史编辑版本

这些能力大大提高了使用体验和生产效率。

Cursor 在刚出来的时候也收获了大量非程序员以及博主们的力捧,但我们在日常使用中依然不多。主要还是因为无法直接用于我们日常工作的工程,但随着一些使用方法的探索,以及模型能力/上下文检索能力的进一步提升,越来越多的编程人员开始在日常工作中使用,以提升生产效率。

而下面的最佳实践也提供一种日常使用的工作流,该工作流基于文档,构建长期 AI 可维护的大型项目。

🆕 快速原型构建机

代表工具:v0、Bolt.New

其实和上面的 IDE 差不多,但更多的结合了 WebIDE 和 WebContainer 的优势。

常规的使用场景是:对于完全没有技术背景的角色来说,他们不知道如何为自己的需求创建一个工程并完成前期的原型验证工作,而这类 WebIDE 工具则提供了一个完美的平台让他们来得到一个可供开箱即用的工程原型。

在此之后,则可以将这个工程下载到本地,使用 Cursor 继续进行工程的后续维护和开发。

最佳实践

虽然新的工具将功能可用性提升了,但存在短板,需要结合一些使用的条件和方法:

  • 需要应用一整套闭环的工作流。其重点在各种文档的记录上(项目记忆力),让人和文档交互,完善设计,才能最终让 LLM 能更好的结合上下文生产出可靠的代码。
  • 不是所有场景都可以。逻辑性代码完全没问题,但无法还原视觉设计的细节。因此,在工作流中,通过 AI 完成逻辑代码的部分后,样式工作还得需要前端工程师来编写。
  • 技术栈和工程结构有限制。需要选择用比较老,且主流的技术栈和版本,社区训练物料比较多。
  • 合规和数据安全问题

工作流

暂时无法在飞书文档外展示此内容

这个工作流以文档为核心,适时的对项目和变更进行总结,以便在新增需求的时候,工具可以获得足够且精简的上下文,来精确生成新需求所需要的设计和代码。这些文档包括:

  1. 整个项目的描述,包括:项目简介、技术栈、文档结构、项目架构等全局信息
  2. Feature 文档,以及每个 feature 的技术实现细节
  3. 模块文档,在每个模块下,对于该模块代码的总结和索引

本质上讲,现阶段,如果我们将 AI Coding 工具拟人化,它有很多缺点:记忆力差,没有从代码仓库中持续积累特定于此仓库的经验,相当于每次找了一个新人来开发

所以,一个好的工具、开发者,应该是能够合理组织和提供一个指令足够的上下文的;一个完全适配的项目应该是,简单化模块化的原子能力 + 清晰的模块声明。类似于微前端、微服务这种组织形式可能更有利于文档的组织

未来展望

工作模式

这有一篇来自于红杉资本的文章中文版)描述了他们对生成式AI发展的展望:

虽然我对这个发展路径以及时间节点存疑,但也同样让我在想,结合AI,未来的编程是怎么样的呢?什么样的目的、什么样的实现方式、什么样的产品形态呢?往大了想,这些都太难回答了

但在一些小点上,站在程序员的角度,还是有一些想象的:

  • 近几年,就可以看见程序员的能力模型要求会有一些变化,更有 AI 辅助经验的,在生产力上会比纯手写更有优势

    • 就像在应用层,高级语言编程替代汇编语言(wiki: 编程语言世代
  • 语言本身的学习变得简单,当 JS 开发工程师想使用其他语言时,变得没什么门槛

  • 没有合规模型工具的公司,在生产力上会落后,人也一样

  • 会出现便宜的 AI 辅助编程解决方案,例如:

    • 针对特定技术栈的小参数 LLM ,本地 32G 内存的笔记本,就可以足够提升生产力了

更远的未来,如果产品形态和生产方式发生质的改变:

  • 那人可能可以专注于新的领域的扩展,而不用纠结于现有生产工具的熟练度
  • 模型越来越强大,实现这件事,真的可能会变成**「念咒语」**

工具链

现在的技术栈、工程化等方式还是基于人来建设的,但当 AI 编程占领主导之后,会有什么样的工具链来驱动呢?什么样的工具链对于 AI 编程更友好。

例如上面提到的最佳实践的工作流来说,整体围绕文档驱动,在生成文档的时候,我们也需要将项目代码转换成上下文提供给大语言模型,而 github.com/yamadashy/r… 就是一种可以将仓库代码打包成 LLM 上下文的工具。

这块还处于特别早期,因为具体的工作流还没有固化下来,但可以预见,会有越来越多的工具链产生。

推动测试进程

为什么做单元测试

LLM 生成单元测试代码,在 22 年底就已经取得了非常惊艳的效果:用例工整,分支覆盖详尽,mock 数据齐全。生成的用例如果可用,提交到仓库后会成为代码资产的一部分。如果用例有问题,这部分代码也不会直接影响到生产环境。所以,AI 单元测试是大语言模型第一次尝试落地工业生产环境的完美试验场景。

另外,单元测试本身就是研发环节中非常重要的一部分。全面的单元测试可以辅助发现很多变更引起的风险和问题。业界知名的开源软件必定包含大量自动化运行的单元测试,这在多人协作开发过程中至关重要。

我们在 23 年也尝试过使用 AI 生成单测。但是当时代码报错多、人工修复成本大,初步尝试的结果不尽如人意,于是暂且搁置了。24 年中,在业务痛点的驱使下,我们重启了 AI 单测的调研。这一次我们找到了新的角度,解决了报错多和人工修复成本大的问题,让大家看到了 AI 单测落地的可能性。

AI 单元测试效果如何

衡量标准

在讨论效果如何之前,首先要讨论如何衡量效果。怎样算效果好,怎样算不好?

在 23 年的调研中,我们没有具体的评判标准,只通过看到的结果得出一些主观判断。在 24 年的实践中,我们尝试以客观的评判标准为主,主观的感受为辅。

由于评判标准是为了服务于我们的目标,所以我们先花了点时间思考我们到底希望 AI 在单测这件事上做什么?我们希望通过引入 LLM,对编写单测提效,同时通过大量单测代码的引入,提高 bug 召回率,提升代码质量,减少线上bug。

基于这个目标,最终我们梳理出这样的指标体系:

  • 核心指标:bug 召回率

  • 准入指标:单测可执行率,单测覆盖率

  • 过程指标

    • 千行代码用例数、测试用例独立性
    • 单测执行时长
    • 项目渗透率、单测可维护性、研发接受度
    • 单测生成速度

实际效果

所以实际效果如何呢?

横向观测下,我们对比生成 case 数、case 可执行率以及单测覆盖率。大部分模型都基本满足了准入要求,小部分模型可能由于发布时间较早或提示词适配度低等工程原因,没有取得可用的表现。

在解决了一些难点问题后,我们在试点仓库做了推进。对于千行以下源码的小批量生成,单测可用率保持在 100%,覆盖率保持在 80% 左右。随着源码复杂度增加,可用率和覆盖率均略有下降,但整体表现已经进入了值得期待的状态。

除了客观数据,研发接受度(主观感受)也很重要。通过阅读我们看到,纯逻辑类函数 AI 可以做到考虑各种情形、针对特定情形 mock 数据并给出断言;React 组件 AI 可以做到考虑多种情况,并且试图在渲染的 dom 结构中寻找关键元素做断言,配合人工矫正生成部分快照可以低成本、高覆盖率完成单测编写;同时AI在边界条件测试上比人类更加严谨,也会通过函数名、注释等信息发现人类考虑不周之处。

从效率和 bug 召回的角度,我们都看到了希望,因此,我们开始在部门内推进 AI 单元测试。

为何会取得不错的效果

首先,我们标准化了基础设施,解决了一些基建问题。如果一类 case 有 3 种写法,那么我们选择其中一种我们最希望的写法让 AI 固定下来,同时帮 AI 打通任何调用 API 上的难题。

其次,人类程序员要懂单元测试。比如 Arrange-Act-Assert 单测组织方式,如何 mock 数据,如何模拟交互,如何合理断言等等。如果人本身不擅长做这件事,也就没办法更好地评判 AI 做的好还是不好。这和使用 LLM 学习其他领域、进行创意启发等场景是不同的,人类必须是相比 AI 更专业的角色。

最后,靠业界优秀论文和公司内团队支撑。从 23 年到 24 年,在 AI 单元测试领域出现了不少靠谱的论文,比如 Meta 的 arxiv.org/pdf/2402.09… 团队也是在调研之后,和公司内 Codeverse 团队一起,在我们的业务上落地了 AI 单元测试能力。

AIGC 落地的最后一公里

AI 单测真正的落地,还需要业务仓库研发同学的协助,不只是基础配置和存量单测代码的合入,还有对后续日常研发流程的改变。

接入AI单测能力,我们提供了3个模块:基础依赖包、流水线配置、ut-helper 本地工具。由于模型在纯函数上表现更好,在组件上略有欠缺,所以我们的推进策略是优先覆盖 P0 仓库的所有工具函数和通用组件,对于业务属性较强的代码暂不推进。 通过收集流水线上报的单测执行数据,我们建立了数据看板,展示仓库接入率、全量覆盖率、增量覆盖率和单测执行失败明细。

对于研发日常的影响,大家问的最多的问题是:生成了 case 之后还需要人类 review 吗?AI 有帮助人类纠错的能力吗?我们希望是不需要人工介入且能帮助发现潜在风险,而且也看到了一些希望。随着存量代码的覆盖完成,增量代码的覆盖更是对人类和 AI 的协作的考验。 AI 是否真的能在研发需求迭代过程中帮助研发规避潜在风险,部门的 bug 估分比是否能切实下降,这都是我们拭目以待的事情。

总结

在今年的实践中,我们对于 AI 落地这件事又有了更多的认识。它不是人类驱使一个远远强大于自己的怪物,落地工业生产场景仍然是靠人类往前迈一步,指挥 AI 在指定范围内做事。另外,AI 在真实落地中的挑战远不止模型训练,AI 和 AI、AI 和基建之间,有很多工程化的工作,它们可以在很大程度上改变最终的效果,有时会比实验室的大模型 pk 榜有趣得多。

保障交付质量

交付质量(Delivery Quality)是指在软件开发过程中,最终交付给客户或用户的软件产品所具备的质量水平。它涵盖了多个方面,包括功能性、可靠性、性能、安全性、易用性、可维护性等。交付质量的高低直接影响到用户满意度、产品市场竞争力以及企业的声誉。

交付质量对于软件开发非常关键,交付质量的劣化会带来用户满意度下降、维护成本增加、品牌声誉下降等问题,甚至会缩短产品的生存周期。交付质量的保障覆盖软件开发过程的所有环节,包括需求设计、开发、测试、发布上线、运维阶段,本文将从各个软件开发环节展开聊一下质量保障的手段和能力。

质量保障传统手段

完整的质量保障策略,需要各个阶段的努力,常见的质量保障框架包括基础的规范、工具、质量防控手段和质量度量:

暂时无法在飞书文档外展示此内容

保障交付质量是一个比较大的话题,交付质量问题可能出现在软件的开发生命周期任意一个环节,需求设计环节中的需求逻辑问题、开发阶段的代码质量问题和测试阶段的测试漏放问题最终都会导致交付质量的下降,常见的质量保障手段和理论基础:

  • 持续集成/持续交付(CI/CD) :持续集成(Continuous Integration, CI)和持续交付(Continuous Delivery, CD)是一种自动化软件交付流程的方法,通过频繁地集成代码、自动化测试和部署,确保软件始终处于可发布状态。

  • 测试驱动开发(TDD) :测试驱动开发(Test-Driven Development, TDD)是一种开发方法,要求开发者在编写功能代码之前先编写测试用例。通过这种方式,确保代码在开发过程中始终符合预期,提高代码质量和可维护性。

  • 行为驱动开发(BDD) :行为驱动开发(Behavior-Driven Development, BDD)是一种协作开发方法,通过自然语言描述系统行为,确保开发团队、测试团队和业务团队对需求有共同的理解。BDD 强调从用户的角度出发,编写可执行的测试用例。

  • DevOps:DevOps 是一种文化和实践,旨在通过开发(Development)和运维(Operations)团队之间的紧密协作,实现快速、可靠的软件交付。DevOps 强调自动化、监控和反馈,确保软件在整个生命周期中保持高质量。

  • 质量保证(QA)自动化:质量保证自动化工具和框架可以帮助开发团队自动化测试流程,确保软件在不同环境和配置下的稳定性和可靠性。

大模型如何赋能

LLM 的越加成熟为交付质量的提升带来了更多的可能,在整个软件开发生命周期过程中,当前阶段下 LLM 想要替代某一个角色的所有工作还不太可能,但是 LLM 已经可以在各个阶段为各个不同角色带来正向的作用,以下是一些相关的实践参考:

代码生成与重构

LLM 可以根据上下文生成高质量的代码片段,或者重构现有代码以提高其可读性和性能。代码生成能力在 LLM 上的应用和探索已经有较久的时间,随着模型能力的增强和各种工具的诞生和强化,代码生成和重构能力已经达到一个基本可用的状态,同时也有更多的专门为代码而生的代码模型逐步问世,例如 Claude 3.5 Sonnet、CodeGemma、Code Llama、Codex。

相关实践:

  1. MarsCode:智能编程助手,提供以智能代码补全为代表的核心能力,支持主流编程语言及 IDE,能在编码过程中提供单行或整个函数的建议;同时提供代码解释、单测生成、问题修复、AI Chat等辅助功能,提升编码效率与质量;

代码审查自动化

LLM 可以用于自动化代码审查,帮助开发者在代码提交前发现潜在的问题。例如,LLM 可以检测代码中的潜在错误、不规范的编码风格、安全漏洞等。

测试用例生成

LLM 可以根据代码逻辑生成测试用例,帮助开发者覆盖更多的代码路径,提高测试的全面性和有效性。例如,LLM 可以生成边界条件测试、异常处理测试等。

自动化部署与监控

LLM 可以辅助自动化部署流程,确保代码在不同环境中的正确性和一致性。此外,LLM 还可以帮助监控系统状态,及时发现和处理潜在问题。

相关实践:

  • 基于日志系统的自动化归因和排障能力,能实现在大范围故障中实现智能归因,找到根因,在实践中已经取得较好的效果;

团队协作与沟通

LLM 可以促进团队成员之间的协作与沟通,例如通过自动生成会议纪要、任务分配建议等,帮助团队更高效地协同工作。

相关实践:

  1. 飞书智能伙伴:飞书智能伙伴在群聊、会议、邮件等多个办公场景提供智能化能力,极大的提高了工作效率和沟通协作的质量;

展望大模型可探索方向

LLM 能力在交付质量保障中展现了较为强大的能力,在代码生成、代码审查、测试用例等方向已经存在一定的成熟度,但仍存在较大的潜力:

  1. 准确性提升:通过不断优化模型、训练策略、评估指标完善等策略,不断提升模型能力的准确性,降低心智负担
  2. 业务定制化:与现有的完善保障体系进行工具和流程的集合,融入业务特性,建设个性化 Agent,降低 LLM 使用成本,提升 LLM 能力覆盖度
  3. 全流程自动化:探索 LLM 自动化保障体系,管控整个需求开发周期,能够在各环节产物进行自动化质量保障,例如需求文档质量保障、代码质量问题回捞、用例质量保障、发布过程保障、线上运维监控等功能串联,形成全套的自动化方案

当前的发展现状来看,LLM 的能力还是在融入当前成熟的质量保障框架能力中,提升软件研发生命周期的的效率和质量,长期展望来看,LLM 会逐步接管质量保障的各个环节,实现高度自动化

运维管理优化

为什么需要更智能的运维

研发同学的一天,可能大部分时间不是用于开发,而是在处理各种各样的信息,比如告警、oncall、指标异常等等。

从这个角度讲,日常的运维管理比单纯的开发占据了研发的更多时间。从时间分配的角度上来说,对运维提效,可能比对开发提效相比带来的体感提升更大。

传统运维从触达渠道上可以分为两种方式:

  1. 系统日志上报,总结出各种各样的指标,当指标出现异常时,发送告警给研发
  2. 某系统 fatal 故障,发送卡片让研发检查自己负责的服务是否有问题

在这种模式下,主要的痛点在于:

  1. 收到这些运维管理的信息后,研发需要查询大量的上下文,然后定位问题
  2. 对于没有达到告警阈值的一些 case,缺乏触达能力

在这个基础上,我们需要更智能的定位分析能力和数据处理总结能力

AI 落地的思路

  • 如何处理海量的数据

用 AI 来分析海量数据,一个痛点就是受限于现在AI的语境,无法直接把所有数据都扔进去分析。

但是如果用知识/向量库的方法,用 RAG 的方法去分析,又无法让 AI 站在所有数据的角度去分析。

所以其中的一个思路是把分析的步骤拆解,一次给出局部的数据,然后通过对局部数据的分析,一步步缩小数据范围,在这个范围内给出更大体量的数据,然后让其做出更具体的分析。

  • 更多的上下文

在AI对于数据分析的基础上,查找出对应数据的上下文,然后让其总结分析。

比起单纯的数据类告警,这种分析帮助研发节省下在不同平台里查询上下文的时间。举个例子,当我们收到监控平台报警,可能会需要去埋点上报、流量监控等多个平台再去找一些次级数据,验证一些初步的判断。

在这一步,如果可以自动化的取到数据,然后给出初步的分析,就可以提高处理的效率。

  • 趋势性的数据分析

把尽可能多的数据提供给 AI,相当于 AI 帮我们观察了各种 dashboard,比如很多需要人去分析得出的尖刺,就可以让 AI 去查找。相比写死代码去分析,更加的智能弹性和节省开发人效。

数据分析的实践

目前 PAI 的数据分析,即遵循了这样的步骤:

  1. 观察整体PAI数据,分析出有尖刺的时间点
  2. 在有尖刺的时间点内,给出PAI的全部次级数据,深入分析
  3. 基于次级数据查找监控平台的对应错误,提供上下文以供分析
  4. 总结所有分析,并结构化数据发出推送卡片

暂时无法在飞书文档外展示此内容

出现演练事故,PAI的报警给出了准确的时间段,并在这个基础上给出了具体的usecase和场景,甚至具体的API。LLM能较大提升分析的效率,仅目前的分析看时间抓取的准确率达到100%,后续增加多数据源的输入(事故通报,上线记录,更多slardar错误抓取等)能增强分析的深度。

总结

一个很深入的感受是,随着 AI 能力的进化,对 AI 的使用反而应该更加的精细。

使用他要像对待一位新加入的同事,如果要让他负责你日常中的运维管理工作,你需要注意:

  1. 整理好并清晰的告诉他做这件事的步骤
  2. 准确的提供数据和保持语义化
  3. 尽量充分的上下文

结语

回顾过去的一年,AI 技术的飞速进步已深刻改变了团队的工作方式,也让我们逐渐认识到,AI 早已不再是遥不可及的梦想,而是我们日常工作中的得力助手。

团队在过去一年中对 AI 进行了大量的探索和研究。AI 单测的引入如及时雨,不仅提升了测试覆盖率和代码质量,还显著减少了人工修复的成本。AI 在内容审核领域极大地提升了审核员的工作效率和质量。在开发效率方面,AI 提供了全方位的智能代码补全、自动化代码审查、代码生成与重构、代码审查自动化等能力,涵盖了开发的方方面面,这极大的提升了我们开发效率。尽管目前 AI 相关工具还有很多不足之处,但 AI 发展的速度已让我们不得不正视起来。未来,当模型的准确性进一步提高,AI 有望在开发和生产的各个环节中提供更加全面和高效的解决方案。

技术的进步和应用场景的拓展,预示着 AI 将在我们日常开发中扮演愈发重要的角色。通过与 AI 的协作,我们相信技术生产力将达到新高度,为用户带来更好的体验。在这场技术革命中,我们迎风破浪,勇敢前行。以更开放的心态拥抱变革,用创新推动技术进步。每次新场景的落地和应用,都是团队智慧与汗水的结晶。

未来已来,我们早已做好准备,你呢?

仅需3步,稳定快速!火山引擎边缘大模型网关全面支持DeepSeek系列模型

DeepSeek 作为大模型新锐,凭借其在算法、架构及系统等核心领域的创新突破,迅速获得业界瞩目。在巨大的热度下,面对海量请求,越来越多用户遇到了请求失败、调用超时、结果无法返回等稳定性问题。

火山引擎边缘大模型网关通过一个 API 接入多家模型服务,利用全球边缘节点就近调用,提升响应速度;支持故障自动切换、重试和超时控制,确保服务可靠性;兼容 OpenAI 接口标准,可快速集成 DeepSeek 等模型,降低接入成本。

目前,火山引擎边缘大模型网关已全面支持 DeepSeek 系列模型,可通过两种方式进行模型使用:

  • 一是通过平台预置模型, 边缘大模型网关新增由火山方舟提供的 DeepSeek R1、DeepSeek V3、DeepSeek-R1-Distill-Qwen-7B/32B,您可直接使用并对其创建网关访问密钥,无需与三方模型提供商交互;

  • 二是通过自有三方模型, 边缘大模型网关新增由 DeepSeek 开放平台提供的 DeepSeek R1、DeepSeek V3 以及火山方舟提供的 DeepSeek R1、DeepSeek V3、DeepSeek-R1-Distill-Qwen-7B/32B,您可以将您在第三方模型平台的密钥纳管至边缘大模型网关,以实现通过边缘大模型网关签发的网关访问密钥进行对应模型的访问与调用。

01 3 步快速调用 DeepSeek

火山引擎边缘大模型网关支持通过一个 API 接口访问多家大模型提供商的模型与智能体,在端侧基于遍布全球的边缘计算节点就近调用。利用边缘云基础架构优势,提高模型访问速度,为终端用户提供更快速、可靠的 AI 服务体验。

在接入大模型的同时,通过配置调用顺序、自动重试、请求超时等能力,能够实现模型调用失败自动请求备用模型、单次请求失败自动重试、单次调用响应时间配置。通过产品化的配置,您可以迅速创建出与 OpenAI 的 API 和 SDK 完全兼容的网关访问密钥(API),并通过选配 DeepSeek 模型进行调用,节省大量适配成本,快速完成业务接入。

Step1 选择 DeepSeek 调用方式

调用平台预置 DeepSeek

边缘大模型网关的平台预置模型中上新了由火山方舟提供的 DeepSeek 模型,您可通过登录产品控制台查看支持模型,并通过点击创建网关访问密钥进行勾选。使用平台预置的模型 DeepSeek,您无需与模型提供商进行交互,可以直接通过边缘大模型网关进行模型配置与调用。

调用自有三方 DeepSeek

如果希望使用在火山方舟平台或 DeepSeek 开放平台购买的 DeepSeek 模型调用额度,您需要通过在边缘大模型网关平台创建对应模型提供商的调用渠道,在创建调用渠道时,需要提供您在第三方模型平台的密钥,同时勾选大模型以明确当前调用渠道可进行调用的模型配置。

完成调用渠道配置后,您可通过创建网关访问密钥勾选对应的 DeepSeek 模型,实现大模型的快速调用。

Step2 创建网关访问密钥

完成前序的 DeepSeek 模型选择后,您可在网关访问密钥创建的第二步进行模型调用配置,以更好地保障在终端业务调用时的稳定性。

  • 通过设置调用顺序,您可以手动调整上一步选择的模型调用顺序,可以根据不同厂商的容灾策略以及不同尺寸模型的降级进行设置,在前一个模型调用失败后,大模型网关将依次调用后续模型,直到成功调用一个模型。如果所有模型都调用失败,则返回错误响应。

  • 通过重试次数,您可以设置对一个模型进行调用的最大重试次数。当一个模型调用失败后,大模型网关将重新尝试调用此模型,直到重试次数耗尽。

  • 通过启用缓存,大模型网关会就近调用结果返回在边缘节点,从而加快重复查询、缩短响应时间并降低成本。

  • 通过设置**缓存的保留时长,**一旦超过指定时长,缓存将被清除。

  • 通过请求超时定义,您可以设置单次模型调用的超时时长,模型请求发出后,若在超时时长内未收到响应,则判定该请求失败。

Step3 服务调用与观测

当您根据上述流程完成网关访问密钥创建,您可以在网关访问密钥列表中查看已完成创建的信息。在详情页面,可以看到基本信息、用量统计、请求方式等详细信息。

通过详情页调用示例,您可以获得由边缘大模型网关提供的请求示例代码,包含 Curl 和 Python。当您从网关访问密钥绑定的模型中选择一个模型后,代码中的model参数值将自动替换成模型对应的值。如果网关访问密钥绑定了多个同一类型的模型,那么当选择一个模型后,可以通过单击右侧的图标查看模型故障转移的预览效果。当前模型调用失败时,大模型网关将依次调用后续的模型。在调用时,您需要将详情页 - 请求方式中的密钥替换示例代码中的$VEI_API_KEY

边缘大模型网关可根据您通过网关向模型发出的请求以及模型的响应来统计您的用量。不同模型提供商对模型用量的计量方式有所不同,根据模型调用计量方式,您的调用详情可以在用量统计中进行查看。

同时,通过云监控 - 大模型网关模块,您可以查询以网关访问密钥为维度的总用量(已消耗的 tokens 总量)与用量速率(每秒消耗的 tokens 额度)。

02 200 万 tokens 免费额度,体验边缘大模型网关

当前,火山引擎边缘大模型网关已适配 15+ 种主流大模型厂商及多个智能体提供商,点击www.volcengine.com/docs/6893/1… 了解并咨询 DeepSeek 模型~ 了解更多边缘大模型网关产品详情。

文档详情:

www.volcengine.com/docs/6893/1…

Jeddak星火计划-开启申报

摘要:抖音集团安全研究团队发起Jeddak星火计划,致力于支持安全计算领域科学研究与技术攻坚,解决机密计算、密态计算、隐私数据保护等领域的关键问题。即日起开启课题征集申报,诚邀高校及研究机构的学者报名参与。

1. Jeddak星火计划介绍

当下,数字化、智能化已然成为推动经济发展、科技创新的核心驱动力。尤其是近年来大模型应用的爆发式增长,对于垂直领域的数据需求呈指数级攀升。但同时,对于数据与计算的安全也提出了更高的要求。随着各行业数字化智能化水平的不断加深,隐私计算与安全计算领域正在发挥愈发重要的作用,确保数据在被使用、共享和分析过程中,其机密性、完整性和可用性得以有效保障。

抖音集团重视安全领域的研究与落地,其安全研究团队研发的Jeddak可信隐私计算平台具备多方安全计算、联邦学习、可信执行环境、差分隐私和同态加密等多项能力。主要面向端到端数据安全和计算过程中的数据隐私保护,助力实现安全合规的数据流通交易与融合共享。

抖音集团安全研究团队发起Jeddak星火计划,致力于支持高校及科研机构学者深入开展安全计算领域技术攻坚,解决该领域关键问题。

即日起,将开启Jeddak星火计划课题征集申报,希望助力机密计算、密态计算、隐私数据保护等方面的研究,结合大模型给安全计算领域带来的新要求,通过软硬件结合等多种路线与方式,推动相关技术向前发展。

现诚邀高校及研究机构的学者报名参与,和我们共同攻克安全计算关键问题,在这一前沿领域开拓创新,为构建安全可靠的软硬件生态贡献智慧与力量。我们将为每个课题提供40~70万元经费支持。

2. 征集研究方向:

机密计算基础设施

在大模型应用爆发式增长的时代,机密计算基于其底层可信硬件的安全性,为数据隐私和模型资产安全提供了一种灵活、高效、实用的解决方案。在大模型实际生产应用场景中,课题计划进一步完善机密计算基础设施建设,降低可信服务的部署使用门槛、提升执行效率。

建议的研究方向(可任选其中一个或多个结合):

  1. 大规模机密容器服务基础设施

  2. 高性能的TEE I/O(网络、存储)

  3. 跨TEE节点(CPU+GPU)的高性能计算

  4. 大模型推理计算加速(例如,prefill / decode分离等优化技术)

机密计算信任体系:

机密计算可有效保护敏感数据与模型,使其免受泄露、窃取与滥用风险。除了可信硬件体系提供的机密性与完整性保障以外,课题计划进一步对其中运行的基础软件栈与应用负载提供可信构建、度量、分析证明能力,以实现更加完整的信任体系,提升用户对企业服务的信任度。

  建议的研究方向(可任选其中一个或多个结合):

  1. 可信编译系统构建方法:探索可信编译系统构建方法,确保软件供应链发布的代码和待运行负载具有对应性,并进一步建设面向机密计算服务的CICD流程;

  2. 可度量体系构建方法:针对目前CVM远程证明缺乏应用层度量的问题,以及无法全面开源的问题,建立一套安全评估体系,用来确定更加合理的度量域,并且完善相应的度量方案。

  3. 可信程序分析方法:探索高效可用的程序分析框架,来判断应用本身的安全性,规避运行代码中隐私泄露、安全合规等风险,兼容多种代码语言和应用场景。

高性能安全多方计算

安全多方计算由于具有可证明的安全性,被广泛应用于数据分析、机器学习等场景,为敏感数据提供安全保护能力。在实际生产环境中,面对十亿甚至百亿规模的海量数据以及大模型动辄数十B的参数,课题计划设计更为高效的安全多方计算协议,使其在海量数据分析和大模型训练推理场景中更加实用。

  建议的研究方向(可任选其中一个或多个结合):

  1. 在百亿至千亿量级的数据查询分析场景下,时间、空间、通信最优的安全多方计算数据分析算法、范式以及系统框架(如存储、通信、调度等)

  2. 面向十亿以上参数规模大模型的高性能安全多方计算训练算法与协议,包括基础算子与迭代算法实现、跨层优化设计,以及推理平台实现等;

  3. 基于安全多方计算、同态加密等技术的隐私保护向量数据库的索引结构、检索算法与查询优化。

软硬件融合密态计算:

结合硬件设备与密码学实现高性能密态计算,是安全领域一直以来的核心探索方向。高速计算硬件可以有效加速密码学运算,可信硬件设备可以减少密码学算法中的复杂交互步骤,从而达到安全与效率兼具的密态计算目标,又弥补了单纯依靠硬件或者密码学实现密态计算的局限。课题计划针对大数据计算以及大模型训练推理场景,探索可实用的软硬件融合密态计算技术。

建议的研究方向(可任选其中一个或多个结合):

  1. 结合可信硬件设备(例如,TEE、TPM等)的密码学信任模型构建,以及安全多方计算或同态加密等算法与协议的高性能实现;

  2. 基于专用或通用硬件设备的高性能密码算法与协议实现,包括但不限于同态加密、安全多方计算等,以及低成本、低功耗、高可靠、易维护的配套加速硬件方案。

隐私保护数据合成

在大模型技术不断发展的今天,数据成为最重要的资源。如何尽可能广泛地采集数据并将其应用于大模型训练以获得性能提升,但是同时又要严格保护用户隐私、避免敏感数据违规使用,是企业面临的主要安全难题之一。课题计划探索面向大模型的隐私保护数据合成技术,既能保持原有数据的分布特性,同时能够消除隐私与敏感信息,为数据在模型训练中的合规使用提供新路径。

  建议的研究方向(可任选其中一个或多个结合):

  1. 基于生成对抗网络、扩散模型、概率合成等方法的隐私保护多模态数据(文本、图像或声音)合成技术

  2. 面向多模态数据的隐私信息度量方法与评估指标,为合成数据的隐私保护程度建立科学合理、可操作的评估手段

3. 申请条件:

面向高校/科研院所的学者开放申请,申请者须符合以下条件:

  1. 工作于具有独立法人资格的高校/研究机构,从事可信计算、安全计算、隐私计算、密码学等相关领域的科学研究。

  2. 在上述领域具备优秀的研究基础、创新能力,以及高度投入的科研热情,有能力带领团队参与并完成课题研究与实践。

4. 申报方式:

  • 点击“阅读原文”,在飞书文档中获取《Jeddak星火计划申请表》
  • 填写《申请表》后,发送至邮箱Jeddak-education@bytedance.com;

注:报名截止时间为北京时间2025年2月28日24点。

5. 计划里程碑

时间 事项
2024年1月24日 开启申报
2025年2月28日 截止申报
2025年3月31日 结果公布&启动签约
2025年10月31日 中期评审
2026年4月30日 结题评估

向AI未知之境出发,字节跳动启动 Seed Edge 研究计划!

为支持以更长周期攻坚 AI 课题,豆包大模型团队正式启动 Seed Edge 研究计划!

Seed Edge 以寻找通用智能的新方法为目标,专注于对智能边界的探索和长期研究挑战。

Seed Edge 鼓励探索更长周期、具有不确定性和大胆的 AI 研究课题,也鼓励跨模态、跨方向的交叉合作,为项目成员提供宽松的研究环境,并实行更长周期的考核方式,让大家可以放手去挑战真正颠覆性的 AI 课题!

目前,Seed Edge 初步确定了五大研究方向,将为所有入选课题提供单独的算力资源保障。 Seed Edge 初步研究方向包括:

探索推理能力的边界

以 o1 为代表的技术路线证明了推理能力可以推动智能边界提升,我们也在实践中发现对推理能力的研究才刚刚开始,有大量未探索的问题值得去深耕。无论是预训练算力级别的大规模强化学习,还是预训练和强化学习的迭代,或是可泛化的 Test-Time Scaling ,每一项技术突破都会推动智能边界前进一步。

探索感知能力的边界

我们相信智能和交互是相辅相成的,通用模型除了有极高的智能水平,还需要具备极强的和人类沟通交流的能力。我们会统一生成和理解表示,探索世界模型建模;从真实世界原始数据直接进行压缩,探索比语言更好的对世界进行表示的建模,突破智能的边界受限于语言的约束。

探索软硬一体的下一代模型设计

Transformer 的高效很大程度上取决于它与 GPU 的适配性,能充分发挥 GPU 的性能。我们希望面向下一代训练和推理硬件的结构特点设计下一代模型,从软硬件一体的角度思考未来的模型结构特性,达到训练效率、推理效率、模型性能的多目标同时优化,并进一步压榨下一代硬件的能力,突破模型能力的边界。

探索下一代学习范式

探索对未来的学习范式有变化的方向,挑战现有范式的“共识”,给更高效地实现通用智能提供基础和可能性。通过可解释性研究理解模型学习能力的来源,并进一步提出新的学习视角,探索模型在学习过程中的表现,和生物启发的学习过程对比,发现现有模型学习范式可以改进的空间:探索比 Next-Token Prediction 更高效的学习目标;探索比 Backpropagation 更高效的学习方法;探索比大数据 Pretraining + Alignment 更高效的学习范式。

探索下一个 Scaling 方向

继 Pretraining Scaling 和 Test-Time Scaling 之后,下一个 Scaling 方向对未来智能边界的演化会起到重要的作用。探索下一个 Scaling 方向,并推动智能边界的进步。

点击“链接”,加入豆包大模型团队

大语言模型应用开发框架 —— Eino 正式开源!

图片

作者|Eino 项目组

今天,经过字节跳动内部半年多的使用和迭代,基于 Golang 的大模型应用综合开发框架 —— Eino,已在 CloudWeGo 正式开源啦!

Eino 基于明确的“组件”定义,提供强大的流程“编排”,覆盖开发全流程,旨在帮助开发者以最快的速度实现最有深度的大模型应用。

你是否曾有这种感受:想要为自己的应用添加大模型的能力,但面对这个较新的领域,不知如何入手;想持续的站在研究的最前沿,应用最新的业界成果,但使用的应用开发框架却已经数月没有更新;想看懂项目里的用 Python 写的代码,想确定一个变量或者参数的类型,需要反复查看上下文确认;不确定模型生成的效果是否足够好,想用又不太敢用;在调试、追踪、评测等开发之外的必要环节,还需要额外探索学习其他配套的工具。如果是,欢迎了解和尝试 Eino,因为 Eino 作为旨在覆盖 devops 全流程的大模型应用开发框架,具有如下特点:

  • 内核稳定,API 简单易懂,有明确的上手路径,平滑的学习曲线。
  • 极致的扩展性,研发工作高度活跃,长期可持续。
  • 基于强类型语言 Golang,代码能看懂,易维护,高可靠。
  • 背靠字节跳动核心业务线的充分实践经验。
  • 提供开箱即用的配套工具。

Eino 已成为字节跳动内部大模型应用的首选全代码开发框架,已有包括豆包、抖音、扣子等多条业务线、数百个服务接入使用。

项目地址:github.com/cloudwego/e…

未来,我们将以 Eino 开源库为核心代码仓库,坚持内外用一套代码,与社区共建最优秀的大模型应用开发框架。

0 1
快速认识 Eino

Eino 是覆盖 devops 全流程的大模型应用开发框架,从最佳实践样例的 Eino Examples,到各环节的工具链,都是 Eino 的领域:

图片

Composition of Eino Framework from a devops perspective

那么 Eino 具体能做什么?首先,Eino 由一个个大模型领域的“组件”组成,比如最核心的是与大模型交互的 Chat Model:

model, _ := ark.NewChatModel(ctx, config) // 创建一个豆包大模型message, _ := model.Generate(ctx, []*Message{    SystemMessage("you are a helpful assistant."),    UserMessage("what does the future AI App look like?")}

像上面这样一个个的直接使用组件,当然没问题,Eino 提供了大量有用的组件实现供选择。但是,大模型应用有它们自身的特点和规律,比如:

  • 核心是大模型,业务逻辑围绕“如何给大模型充分、有效的上下文”以及“如何让大模型的输出可靠的影响环境”,核心的组件类型、数据类型和交互模式是可以枚举的,整体可以由有向图来描述。
  • 大模型输出的特点是流式输出,意味着模型的下游都需要有效的处理流式数据,包括流的实时处理、流的复制、多个流的合并、单个流的拼接等。
  • 以有向图为基础,衍生出并发处理、扇入扇出、通用横切面、option 分配等一系列子问题。

Eino 的编排能力,是上述通用问题的充分解决方案。

以 ReAct Agent 为例:一个 ChatModel(大模型),“绑定”了 Tool(工具),接收输入的 Message,由 ChatModel 自主判断是否调用 Tool 或输出最终结果。Tool 执行结果会再次成为给到 ChatModel 的 Message,并作为下一轮自主判断的上下文。

图片

上述基于 ChatModel 进行自主决策和选路的 ReAct Agent,便是基于 Eino 的 组件 和 Graph 编排 来实现, 代码清晰简洁,可与流程图清晰对应。

  • 代码实现详见:「flow/agent/react 」的实现

  • ReAct Agent 用户手册详见:「react_agent_manual」

「flow/agent/react」github.com/cloudwego/e…

「react_agent_manual」www.cloudwego.io/zh/docs/ein…

在 Eino 中,这是几十行代码的图编排:

// 构建一个 ReAct Agent,编译为一个输入为 []*Message,输出为 *Message 的 Runnable
// 创建包含 state 的 Graph,用户存储请求维度的 Message 上下文graph = NewGraph[[]*Message, *Message](   WithGenLocalState(func(ctx context.Context) *state {      return &state{Messages: make([]*Message, 0, config.MaxStep+1)}   }))
// 将一个轮次中的上下文和响应,存储到 Graph 的临时状态中modelPreHandle = func(ctx context.Context, input []*Message, state *state) ([]*Message, error) {    state.Messages = append(state.Messages, input...)    return state.Messages, nil}
_ = graph.AddChatModelNode(nodeKeyModel, chatModel, WithStatePreHandler(modelPreHandle))
_ = graph.AddEdge(START, nodeKeyModel)
_ = graph.AddToolsNode(nodeKeyTools, toolsNode)
// chatModel 的输出可能是多个 Message 的流// 这个 StreamGraphBranch 根据流的首个包即可完成判断,降低延迟modelPostBranch = NewStreamGraphBranch(   func(_ context.Context, sr *schema.StreamReader[*Message]) (endNode string, err error) {      defer sr.Close()
      if msg, err := sr.Recv(); err != nil {         return "", err      } else if len(msg.ToolCalls) == 0 {         return END, nil      }
      return nodeKeyTools, nil   }, map[string]bool{nodeKeyTools: true, END: true})
_ =  graph.AddBranch(nodeKeyModel, modelPostBranch)
// toolsNode 执行结果反馈给 chatModel_ = graph.AddEdge(nodeKeyTools, nodeKeyModel)
// 编译 Graph:类型检查、callback 注入、自动流式转换、生成执行器agent, _ := graph.Compile(ctx, WithMaxRunSteps(config.MaxStep))

在上面这几十行代码的背后,Eino 自动做了一些事情:

  • 类型检查,在 compile 时确保相邻的节点的类型对齐。

  • 流式封装,编译出的 Runnable 既可以 Invoke 调用,也可以 Stream 调用,无论内部的 Tool 是否支持流。

  • 并发管理,对 state 这个公共状态的读写是并发安全的。

  • 横切面注入,如果某个组件(比如一个 tool)没有实现 callbacks 注入,则 Eino 自动注入。

  • Option 分配,编译出的 Runnable 可以灵活接收并把 option 分配给指定的节点。

02
Eino 的独特优势

基于大语言模型的软件应用正处于快速发展阶段,新技术、新思路、新实践不断涌现,我们作为应用开发者,一方面需要高效、可靠的把业界共识的最佳实践应用起来,另一方面需要不断学习和提升认知,从而能够整体理解这个新领域的可能性。因此,一个优秀的大模型应用开发框架,既需要封装领域内“不变”的通用核心要素,又需要基于最新进展敏捷的横向和纵向扩展

另一方面,目前较为主流的框架如 LangChain,LlamaIndex 等,都基于 Python,虽然能借助 Python 较为丰富的生态快速实现多样的功能,但是同时也继承了 Python 作为动态语言所带来的“弱类型检验”和“长期维护成本高”等问题。在大模型应用快速进入大规模线上运行阶段的当下,基于 Golang 这一强类型语言而实现的高可靠性高可维护性,逐渐具有更大的价值。

基于大模型的应用开发是相对较新的领域,有时需要摸着石头过河,靠实践来检验认知。依托字节跳动高频应用豆包、抖音等的多样场景、快速迭代和海量反馈,Eino 在实践驱动设计方面有独特的优势。

最后,生产级的框架需要面对真实、复杂的业务场景,因此,除了直观易用的 API 设计之外,提供有针对性设计的开发工具可以有效的帮助开发者理解和应对复杂性、加速开发过程。

内核稳定

我们认为,存在一个常见的组件列表,共同构成了大模型应用的常见组成部分。每类组件作为一个 interface,有完善、稳定的定义:具体的输入输出类型,明确的运行时 option,以及明确的流处理范式。

在明确的组件定义基础之上,我们认为,大模型应用开发存在通用的基座性质的能力,包括但不限于:处理模型输出的流式编程能力;支持横切面功能以及透出组件内部状态的 Callback 能力;组件具体实现超出组件 interface 定义范围的 option 扩展能力。

在组件定义和通用基座能力的基础上,我们认为,大模型应用开发存在相对固定的数据流转和流程编排范式:以 ChatModel(大模型)为核心,通过 ChatTemplate 注入用户输入和系统 prompt,通过 Retriever、Document Loader & Transformer 等注入上下文,经过 ChatModel 生成,输出 Tool Call 并执行,或输出最终结果。基于此,Eino 提供了上述组件的不同编排范式:Chain,链式有向无环图;Graph,有向图或有向无环图;Workflow,有字段映射能力的有向无环图。

上述设计和功能共同构成了 Eino 的稳定内核:

图片

敏捷扩展

每类组件都可以横向扩展出不同的实现,比如 ChatModel 组件可以有 OpenAI、Gemini、Claude 等不同的实现等。这些具体的实现,在实现组件 interface 从而可作为组件参与编排的基础上,可以实现和持续扩展自身的特殊功能。

当实际业务场景中,出现需要进入编排但是不对应任何组件定义的功能时,Eino 支持将自定义 function 声明为 Lambda 类型。Lambda 有用户声明的输入输出以及 option 类型,可支持全部的流处理范式,具备完整的 Callback 能力,在编排视角等价于官方组件。

在大模型应用开发领域,存在并且持续会涌现多个组件的特定编排范式,这些范式封装了验证有效的研究成果或实践经验,比如 ReAct Agent,Host Multi-Agent 等。这些开箱即用的封装,浓缩了大模型应用开发领域的最佳实践,会随着我们认知的提升持续纵向扩展。

在组件和图执行过程中,开发者可以在固定的时机嵌入自定义的回调逻辑,用于注入横切面功能。

综上所述,Eino 框架具备充分的可扩展性:

图片

高可靠易维护

基于 Golang 写 Eino 代码时,开发者可以充分利用 Golang 的强类型特性,为所有的组件、Lambda、编排产物等声明具体类型。这像是为代码绘制了一幅精确的地图,开发者可以沿着清晰的路径进行维护和扩展,即使在项目规模不断扩大、功能持续迭代的情况下,依然能够保有较高的可维护性。

同时,Eino 编排能力也充分利用了强类型系统的编译时校验能力,尽可能将类型匹配问题暴露的时机提前到 graph 的编译时,而不是 graph 的运行时。尽早并明确的暴露类型匹配问题,有助于开发者迅速定位和修复,减少因类型错误在运行时引发的难以排查的故障和性能问题。

另一方面,Eino 遵循模块化设计,核心库以及各组件实现是单独的 go module,每个 go module 做到依赖最小化。同时,API 设计以“精简”、"直观"和“同构性”为原则,辅以由浅入深的全面文档,尽可能让学习曲线更平滑。最重要的是,Eino 采用清晰的分层设计,每层职责明确、功能内聚,在提升维护性的同时能更好的保证稳定性。

Eino 框架结构图:

图片

实践驱动

Eino 框架的设计开发过程,扎根于 “满足真实需求” 与 “实践驱动设计” 这两大基石之上。功能的演进过程与字节跳动各业务线的接入过程紧密结合,始终倾听开发者的声音,并通过实际使用效果来检验设计的合理性。比如我们收到来自抖音的“希望能够以字段为粒度在图中映射和传递数据”的需求,以此为基础设计了 Workflow;倾听来自豆包的使用痛点,增强作为模型输入输出类型的 Message 结构体。在未来的开源生态共建过程中,我们会继续坚持上述原则,满足更广大的用户和开发者的真实需求,并在更大的范围内认真实践和精进。

图片

工具生态

链路追踪、调试、可视化,是编排引擎的三个重要辅助工具。Eino 内置了 tracing callback,并与 Langfuse 平台做了集成。同时提供了 IDE 插件,可以在写代码的过程中随时可视化查看编排出的 graph,并进行调试运行,甚至可以通过 UI 拖拽的方式快速构建 graph 并导出为 Eino 代码。

图片

03
快速上手

针对 Eino 的学习和使用,我们提供了完善的 Eino 用户手册,帮助大家快速理解 Eino 中的概念,掌握基于 Eino 开发设计 AI 应用的技能,赶快通过「Eino: 快速开始」尝试使用吧~

如有任何问题,可通过下方的飞书群或者「Eino Issues」和我们沟通、反馈~

「Eino: 快速开始」www.cloudwego.io/zh/docs/ein… Issues」github.com/cloudwego/e…

相关链接

项目地址:

github.com/cloudwego/e…

项目官网:www.cloudwego.io

图片

扫描二维码加入飞书社群

字节跳动观测数据埋点标准化实践

来源|字节跳动基础架构-可观测团队

背景

随着字节跳动业务规模不断扩大,对存量和新增业务的服务质量承诺变得越发关键。稳定性治理方面:怎样支持保障服务线上的高可用性,或者在出现故障/事故时,如何高效且迅速地止损、定位分析影响面已成为一个重要议题。

稳定性建设所涉及的话题十分广泛,涵盖流程梳理与标准化、数据标准化、SLO 定义、故障自愈、事故复盘和演练等方面,字节跳动基础架构可观测团队提供的稳定性平台建设思路是“事前预防、事中处理、事后复盘、事后补救/反哺事前”这四个阶段。

其中, 观测数据标准化以及一系列配套的数据链路,如:数据埋点、数据消费、数据存储、数据查询、离线数仓等,都是后续稳定性建设的重要数据基石。

并且,由此引申出排障/止损效率的问题,由于字节的服务/基础设施是分层建设的,包括端侧客户体验层、网络接入层、应用服务层、基础设施层、IDC\资源层等,不同层面的统计/描述口径是否一致、能否对应,以达到在跨层间能上卷下钻和平层内过滤聚合的“车同轨书同文”效果,这对于大幅提升整体排查效率, 让 SRE/GOC 同学能够自助完成端到端的问题排查就显得尤为重要。

img_v3_02ik_a98c1a06-a522-493e-a443-78169a1b9f3g.png

拥有统一的观测数据标准, 能够在很大程度上提升团队间的排障效率,从人工分析的方式提升至更大程度的自助/自动化排障的阶段。

埋点标准化的重要性

提高研发效率 & 降低研发协同成本

  • 面向排障方面:跨层间的上下文过滤便捷,术语统一。
  • 进行历史数仓分析(容量优化)时,整体数据处理逻辑的适配成本会大幅降低。
  • 用户的学习曲线陡峭,理解心智负担沉重。

为 AIOps 提供强有力的数据支撑

观测数据属于 AIOps 的五大基石(数据、知识、算法、代码联动、人机协同)之一。在清华裴丹老师的《AIOps 落地的 15 条原则》里,也都提及了数据的重要性。

拥有数据标准化和统一的访问体验,为后续稳定性的终极目标 MTTR 1-5-10(1 分钟发现,5 分钟响应以及 10 分钟快恢复)提供了数据层面的保障。包括同层数据的聚合 / 过滤,以及跨层数据的下钻和上卷,都会有统一的使用方式。

名词解释

名词 解释
Metrics 2.0 字节跳动内部使用广泛的时序数据处理引擎,提供了时序数据收集、存储和聚合查询的功能。2.0 版本提供引入多值概念,打平prometheus 4类指标类型语义、支持秒级打点& 存储周期定制化等多租户特性、 端到端高性能优化的分布式时序集群版本。
BytedTrace BytedTrace是字节跳动使用的一套集成了 Tracing/Logging/Metrics 三种能力的可观测性解决方案,提供了从采集、传输、存储、检索到前端产品化交互的整套能力。它定义了统一的数据模型(Trace 、Span 、Event、Metrics 等),提供了各语言配套 SDK,并与公司各主流框架组件实现默认集成。
观测埋点 TagKV Metrics TagKV 是一种用于标记和管理度量数据的键值对(Key-Value Pair)格式。通常用于监控系统、分布式追踪系统和日志管理系统等领域,TagKV 提供了一种灵活且高效的方法来分类和筛选数据。
Measurement 可观测对象的某个指标,如服务的上游调用延时,物理机的 CPU 使用率。Measurement 是带有可观测对象的 context的,是语义化的,同时能识别在不同条件下应该使用哪个版本的指标以及对应的 TagKV。而且可以根据观测对象的元数据条件,同时关联多个时序数据源,便于按需时序数据源切换。
SLO Service Level Objectives,服务级目标是指服务提供方对所提供服务的某些性能或质量指标所设定的目标值。这些指标通常用于衡量服务的可用性、性能和其他关键属性,旨在确保服务达到预期的质量水平。
TCE Toutiao Cloud Engine,为字节跳动内部提供的高度可用、弹性扩展的容器服务。
PSM Product Subsys Module,是字节跳动内部服务的唯一标识。
GOC Global Operations Center,基于字节跳动各类研发,运维体系下的高可用产品能力,结合稳定性保障策略及运营机制,提供字节跳动全线基础产品的可靠性服务与设施稳定性保障,达成字节跳动全线业务各类场景下的端到端高可用性。

字节埋点标准化挑战与拆解思路

挑战: 历史上可观测性埋点质量偏低

首先,我们对埋点标准化进行定义,包括但不仅限于如下的标准定义,包括覆盖完整、定义统一、计量准确、面向引擎友好等四大方面。

img_v3_02ik_56f19e79-13a2-4bdf-aa70-2d6d49e02b8g.png

简而言之,在 2020 年以前,从覆盖完整定义统一计量准确面向引擎友好等维度来看,字节整体的观测数据埋点存在一定的差距。

具体如下:

  • 负载均衡 埋点

    • 计量准确:中等水平

      • 存在较严重的打点丢失问题
    • 面向引擎友好:较低水平

      • 指标打点对于配置预计算不友好
      • 指标名膨胀也比较严重
  • 微服务 埋点

    • 覆盖完整:中等水平

      • 20 年前 Tracing 方案还在 V1 版本
    • 计量准确:中等水平

      • 遇到高基数的指标会被封禁
    • 面向引擎友好:较低水平

      • 指标打点对于配置预计算不友好
      • 指标名膨胀也比较严重
      • 加权计算也不好实现
  • 语言 运行时 埋点

    • 定义统一:较低水平

      • Golang & C++ 框架 不同的版本定义的指标格式都不太一样
    • 面向引擎友好:较低水平

      • 指标打点对于配置预计算不友好
  • 容器指标 埋点

    • 覆盖完整:较低水平

      • 没有日志采集覆盖
    • 计量准确:中等水平

      • 遇到高基数的指标会被封禁
    • 面向引擎友好:较低水平

      • 指标打点对于配置预计算不友好
  • 基础架构 存储 & 数据库 埋点

    • 覆盖完整:较低水平

      • 存储、数据库、MQ 客户端没有黄金指标打点
      • 没有日志采集覆盖
    • 计量准确:较低水平

      • 不同存储、数据库、MQ 产品打点格式 都不一
    • 面向引擎友好:较低水平

      • 指标打点对于配置预计算不友好

思路: 分层&向后兼容推进埋点标准化

总结来说,之前的字节服务端观测数据质量大致存在三类问题。

  • 同层数据/跨层数据不一致。
  • 观测的多模态数据类型(指标、日志、链路)的数据定义不统一。
  • 观测数据格式对引擎不够友好,例如所有数据都在 default 租户的一个大仓里,再比如很多观测指标的定义对于预计算不友好。

针对上述问题,我们采取了如下的多个思路逐一进行解决。

实施思路

一方面,在埋点侧就尽可能统一埋点 TagKV 定义,而且平台级 TagKV 都通过环境变量或者请求上下文自动注入对应的 Tag Value, 以防止由业务手工注入带来的人工错误。

另一方面,对于指标、链路和日志侵入式 SDK,我们通过字节内部的远程过程调用框架以及存储、数据库、消息中间件的客户端 SDK 搭载嵌入中间件,对于业务来说,能相对透明地升级到最新特性的版本。另外, 对于远远低于 SDK 基线版本的服务, 我们也通过字节软件供应链安全治理平台通过编译卡点的不同程度[warning 提示/发布卡点]推动业务升级。

在 负载均衡、应用、中间件、存储计算组件等各个纵向方面, 我们也主动与对应的平台对接,推动指标、日志、链路的埋点注入。

最后,在指标埋点上也额外关注对于多租户的声明,以达到一定的分库分表功能,以及多值声明,以最大程度减少数据消费和存储成本。如下所示, 就是团队在各个不同观测对象的埋点方面所做的业务推进情况。

img_v3_02ik_82b1a459-030a-43f1-8db0-808d5cc209eg.jpg

难点: 识别和解决

类似观测数据标准化的工作历经多年,牵涉的团队众多,整个过程并非毫无波折。遇到问题时要解决问题并思考能否将其标准化或者平台化,同时也要考虑能否尽可能地复用其他团队的能力和工具来助力我们进一步推广。当时如何高效地推动业务升级是我们的主要目标。

[业务推进] 高效推动业务升级观测SDK

在 Metrics SDK 需要升级到基线版本的情况下,以前的做法是在字节软件供应链安全治理平台上配置版本拦截,提醒用户升级,但是整体升级效率比较低,我们也无法跟踪用户的升级进展。因此我们联合字节软件供应链安全治理平台团队实现 SDK 自动升级功能。

Metrics ****SDK 自动升级

Metrics ****SDK 自动升级功能可以自动实现在当前业务代码库的代码提交期间,如果检测到对应集成的metrics SDK 低于基线版本,则会向用户推送代码提交失败的通知,提醒用户需要主动升级到metrics SDK基线版本或以上的操作。

远程过程调用 框架 & 基础组件客户端 集成 ****BytedTrace ****SDK 集成

观测团队多年来持续推动公司的远程过程调用 框架以及基础组件客户端 集成 BytedTrace SDK **** ****借助字节软件供应链安全治理平台进行递进式卡点推广,依靠代码血缘平台来推动框架、组件的基础库版本实现升级。在存有流量的微服务上,BytedTrace SDK的覆盖比例按照 TCE pod 接入情况来计算,当前已达到 95%。

从服务的优先级角度而言,公司当前96% 的 P0 服务中已接入 Bytedtrace SDK 。

[业务推进] 提升基础组件观测埋点质量

TCE 调度 / 运行时 打点格式设计思路

前文提到,提升业务层、应用层、容器层等多层间指标的跨层关联和下钻能力是指标标准化的一个重要目标,而实现跨层关联的关键动作在于保证同一含义的指标 TagKV 在各层上的定义保持统一,为实现这一点,我们对各个层次上的核心组件进行了统一的设计,具体如下:

层次 核心组件/着手点 埋点标准化设计思路
业务层 Metrics 2.0 SDK - 内置统一的平台级TagKV,提供横向跨语言、跨服务的TagKV统一
应用层 运行时 指标、远程过程调用 指标 - 横向上,提供统一的、跨语言的指标名定义
  • 纵向上,对齐Metrics 2.0 SDK 平台级TagKV规范 | | 容器层 | 与调度合作,对容器指标采集agent(TCE调度)进行标准化改造 | - 对齐Metrics 2.0 SDK 平台级TagKV规范 |
  1. 首先,我们在 Metrics 2.0 SDK 内置定义了一套平台级 TagKV,这样所有使用 Metrics 2.0 SDK 的业务打点都会携带标准的预定义的 TagKV。这些共同TagKV包括: _cluster、_psm、_pod_name、_ipv4 等。

  2. 在应用层,挑选了对业务排障、应用观测常用且通用的两类指标(运行时、远程过程调用)进行标准化,目标是在横向上,提供跨语言、统一的指标名、TagKV语义定义等;在纵向上,对齐 Metrics 2.0 SDK 平台级 TagKV 规范,以便于跨层关联。以 运行时 指标为例,其定义规范如下:

    1. 不同语言的指标采用统一命名约定:runtime. {runtime} . {metric}[ . {field}]
    2. 不同语言类似含义指标采用统一命名风格:如 go、java 中统计堆对象申请量的指标都命名为memory.allocated_bytes
    3. 必须包含 Metrics 2.0 SDK TagKV 规范的平台级 TagKV,如 _psm、_pod_name 等
  3. 在容器层,与调度团队共同推动其 TCE 容器指标采集 agent(TCE调度) 的指标标准化改造,指标 TagKV 对齐Metrics 2.0 SDK TagKV 规范。

通过将这些核心组件进行标准化改造,为跨层的指标关联和下钻提供了能力基础。同时,在每个核心组件的指标定义上,我们还通过以下两个方式进一步提升埋点的性能和成本收益,第一点是对各个组件使用独立租户,实现资源的隔离,保障写入稳定性和查询性能;

指标 租户名 集群类型
运行时 apm.runtime 独立集群
远程过程调用 框架 apm.rpc 独立集群
TCE 容器指标 computation.tce 独立集群

第二点是在语义明确的前提下,尽量使用多值格式定义指标,降低存储成本。以 TCE调度 指标为例,将原来 mem 相关的四个指标合并为一个多值指标的四个字段,存储成本大致可以被认为降低至四分之一。

原指标 改造后多值指标名 改造后多值字段
tce.host.mem_total inf.tce.host.mem total
tce.host.mem_free free
tce.host.mem_available available
tce.host.mem_used used

[配套工具] 帮助平滑迁移观测数据

[工具1] 语义 化指标替换

我们提供语义化指标替换,称为Measurement,其能力就是对原始 Metrics 打点的语义化封装;同时能识别在不同条件下应该使用哪个版本的指标以及对应的 TagKV。这两个关键能力能够促使在做数据迁移时,观测大盘和报警基本达到比较平滑的状态。

原始 Metrics 打点:直接写入时序数据库(可以是 metrics \ influxdb \ prometheus)的数据。

语义 封装:用标准的语义化来包装原始的 metrics 打点数据。 比如 go 服务的 gc 数量的 metrics 打点是 go.{{.psm}}.numGcs,其中{{.psm}}为具体的 psm, 我们会定制一个语义化指标名叫 "runtime.go.gc_num"来表达 go 服务的 gc 数量,包括用统一的 TagKV 来封装对应的原始 TagKV。 不管是 open api 还是前端调用, 都用指标 "runtime.go.gc_num" 对measurement 服务进行调用。

不同条件下的查询 路由:需要这个能力是因为在字节内部原始 Metrics 的打点会不断的升级, 比如 golang 运行时 历史上会有 v1 、v2 、v3 多个版本,我们需要能够在给定的输入信息条件下去查询到对应的指标版本。这个判断条件实现的逻辑一般为可用输入的 psm 名字构成 Metrics go v1 的指标名,再根据指标名的数据是否存在来判断是 runtime v1、runtime v2 或者 runtime v3 的版本,指标判断也以此类推。或者可以通过 psm 的 scm 编译信息确定该 psm 编译的 golang 运行时 版本是 v1、v2 或者 v3。 通过对应条件的判断来做到对应数据的查询路由。

img_v3_02ik_5c2f9415-8b2f-4dd5-a031-dff8ce63af6g.png

在有了 Measurement 能力后,我们抽象出了 Measurement 服务,该服务作为观测大盘和报警的一个数据源。在尽量不需要用户介入的情况下完成数据打点的迁移和替换。

当前借助 Measurement 能力,针对公司的 远程过程调用、HTTP 等框架,容器引擎、FaaS、机器学习推理等平台,还有负载均衡、缓存、数据库、消息队列等基础组件,以及golang 运行时 等,均进行了统一的标准化语义封装,这些语义化封装在观测平台上均有所展现。

[工具2] Metrics 前缀分流

怎样帮助业务顺利地迁移到新租户,同时确保新老指标的查询方式均可使用,是我们在推动业务租户迁移时所面临的较大挑战。

针对上述问题,观测团队起初推进引导用户主动迁移至新租户,旨在实现租户隔离,提供更优的稳定性保障,进行精细化容量治理以降低成本。然而,后来发现主动迁移的速度太慢,赶不上打点量的自然增长。于是,推出了让用户无感知的被动租户迁移方案。大致思路是依据某些特定的指标前缀,主要涵盖一级 / 二级前缀,通过特定配置把这些指标分别路由到不同的新租户,并且在新租户上支持查询翻译,即便用户不修改查询租户,继续用 Default 租户查询仍能正常获取数据。该方案具有以下优势:

  1. 业务在读写两侧无需进行代码变更,就能将流量迁移到新租户集群。
  2. 最大程度减少不同租户间因集群变更和读写流量变化对线上稳定性产生的相互影响,提供更出色的稳定性保障。
  3. 精准对接业务线租户,便于后续进行打点流量治理、容量规划以及资源充值等操作。

具体的实现由 Metrics 组件中各模块的相互配合完成,包括写入、控制面、查询、数仓等方面,大致的实现流程如下:

前缀分流租户的整个过程存在众多细节,为减少过程中的过多人为操作,防止出现某些环节被遗忘的情况,观测团队设计了分流流程工单以及白屏化运维平台,尽可能让整个操作流程实现自动化,提高分流租户的效率。此外,前缀分流迁移新租户的整个过程对于业务来说成本为零,同时对于 观测团队而言不像依赖业务方主动迁移那样周期漫长,其周期短、生效时间快,能够收敛团队人力的持续投入。

总的来说,观测团队提供了一种让用户无感知、实现无缝迁移新租户的方案,用户的核心观测大盘和报警也无需修改,最大程度降低了埋点标准化对用户的打扰。

埋点标准化字节的实践与效果

观测数据质量前后对比

经过 2020-2022 年推进 BytedTrace SDK 覆盖率、2023 年推动云基础组件和应用层指标租户迁移之后, 从埋点标准化的 4 个维度看,都有不同程度的质量提升。

  • 负载均衡

    • 计量准确:较高水平 [2020年为中等水平]

      • 通过 2.0 SDK 三个特性, 基本消除丢点的问题:

        • 打点本地聚合
        • 面向字节流的 codec 编码
        • Agentless 投递
    • 面向引擎友好:较高水平 [2020年为较低水平]

      • 实现面向预计算友好的效果
    • 成本收益:

      • Metrics 2. 0 打点商品成本相对 1.0 下降 94%
      • Metrics 2. 0 很好地解决了打点封禁问题,特别是在一些配置量巨大的核心集群,解决了其超过 90%打点无法查询的情况
      • Metrics2. 0 TLB 机器成本初步统计主容器和 adaptor 打平,同时相对 1.0 节约了 ms2 的 15000 核资源
  • 微服务

    • 覆盖完整:较高水平 [2020年为中等水平]

      • 80%以上 PSM 覆盖到 BytedTrace SDK 集成
    • 计量准确:中等偏上水平 [2020年为中等水平]

      • 高基数的指标封禁问题 由于迁移到了新租户 可以做封禁阈值定制化
      • [计划中] 升级 bytedTrace 内的 metrics 2.0 SDK 降低丢点的风险
    • 面向引擎友好:较高水平 [2020年为较低水平]

      • 实现面向预计算友好的效果
    • 成本收益:

      • 以计算关键组件 Consumer 为例,新租户只需要老租户 20%的资源,就可以完成相同数据的写入计算;其他写入计算类组件也类似
      • 以存储关键组件 tsdc 为例,新租户只需要老租户 55%的资源,就可以完成数据的写入、存储
  • 语言 运行时

    • 定义统一:较高水平 [2020年为较低水平]

      • 统一了不同语言和框架的 运行时 打点格式
  • 容器指标

    • 覆盖完整:中等水平 [2020年为较低水平]

      • TCE调度 接入日志租户
    • 计量准确:较高水平 [2020年为中等水平]

      • 引入多值 降低指标名数量

      • 高基数的指标封禁问题 由于迁移到了新租户 可以做封禁阈值定制化

      • 通过 2.0 SDK 三个特性, 基本消除丢点的问题

        • 打点本地聚合
        • 面向字节流的 codec 编码
        • Agentless 投递
    • 面向引擎友好:较高水平 [2020年为较低水平]

      • 实现面向预计算友好的效果
  • 基础架构 存储 & 数据库

    • 计量准确:较高水平 [2020年为中等水平]

      • 引入多值 降低指标名数量

      • 高基数的指标封禁问题 由于迁移到了新租户 可以做封禁阈值定制化

      • 通过 2.0 SDK 三个特性, 基本消除丢点的问题

        • 打点本地聚合
        • 面向字节流的 codec 编码
    • 面向引擎友好:中等水平 [2020年为较低水平]

      • 打点格式调整的 支持预计算配置
    • 成本收益:

      • 以 mysql 迁移为例

        • Mysql 租户 成本节省 45.7%
        • Mysql 租户 带宽节省了 80%

截止到今年年初, Metrics 在中国国内区域已经接入 60+ 租户,占总流量的 70% 左右。

赋能效果总结

加速微服务端到端根因定位

通过指标标准化 & 多模观测数据 [指标, 日志,链路]标签术语的标准化, 我们实现面向微服务的上卷 & 下钻关联分析。

也使得使得跨层问题根因分析有了可能性:

目前端到端根因定位覆盖了60%以上的报警场景,日均触发根因定位 50余万 次,用户对定位结果的正反馈率超过80%。

简化服务性能离线数仓构建

在实现了在线观测数据的标准化,并将其导入统一的存储介质之后,构建字节整体关于服务性能、容量、吞吐量的数仓大盘就更加便捷。比如 展现某服务的单核 QPS 分时热力图 如下:

目前基于微服务应用性能数仓已覆盖公司超97%的微服务量化,有效支持字节跳动各业务线服务性能、服务应用健康度度量,由此带动一系列精准的成本优化。

观测底座自身收益

  • 从稳定性角度看,由于引入metrics多租户概念,所以我们能够通过逻辑租户映射到物理资源,从而降低故障半径,减少不同租户间流量的相互干扰。
  • 从成本角度看,我们能够依据每个租户的副本数、存储时长 TTL、打点的最小精度以及多值定义,最大程度地降低写入流量和存储容量的成本。metrics 多租户迁移前后对比,成本节省幅度在 20% ~ 80% 不等。

总结

历经上述观测埋点套件 BytedTrace SDK推广、Metrics 指标标准化迁移和推广、部分业务接入日志多租户,字节后端观测数据的质量在覆盖完整度定义统一计量准确面向引擎友好四个方面上取得了显著的质量提升。这也为后续的全景全栈高效排障奠定了坚实的基础,帮助更多业务团队在业务稳定性方向持续建设。


依托字节跳动内部可观测团队大规模技术实践,通过内外合力,在火山引擎上推出了应用性能监控全链路版(APMPlus)、托管 Prometheus(VMP)、云监控等可观测产品,致力于为用户提供全面、智能、高效、易用且安全的全栈可观测解决方案。

目前 APMPlus Server 端监控已正式 GA 并支持最新的大模型链路追踪相关能力,欢迎咨询了解。

🔗 相关链接

APMPlus www.volcengine.com/product/apm…

VMP www.volcengine.com/product/pro…

云监控 www.volcengine.com/product/clo…

详解veImageX助力卓特视觉智能、高效生成设计素材

前言

设计素材行业为设计师和创意工作者提供丰富的视觉和创意资源。数字媒体和互联网的迅猛发展,促使这一行业市场规模不断扩大,用户对设计素材的个性化和定制化需求与日俱增。卓特视觉,作为Adobe Stock中国区官方合作伙伴,自2014年成立以来,始终致力于推动中国创意产业的繁荣发展。在AI的技术浪潮中,卓特视觉选择与火山引擎veImageX(一站式图片解决方案)携手合作,旨在通过AIGC加成,更加智能和高效的生成设计素材,进一步拓宽创意表达的边界。

卓特视觉(Droit Vision),Adobe Stock中国区官方合作伙伴,全面整合全球范围内的高质量图片、矢量插画、高清视频及音效音乐等素材资源,专注于为新媒体、设计、广告、各类垂直行业及个人用户,提供一站式的视觉素材和解决方案,助力创意人士和企业提升其视觉作品的品质和影响力。

至今,卓特视觉在线销售高清正版图片总数超5.6亿和超3,600万条高清视频。自2014年成立以来,卓特视觉成功为众多知名企业提供了安全、高效、优质的视觉创意解决方案,赢得了广泛的企业级客户信任。

场景概述

在设计素材行业,传统的商业模式通常由创作者提供内容并上传至平台,平台负责销售和分发,同时负责版权等问题,用户通过付费获取平台的高质量素材资源,平台则根据销售情况与创作者分成。而在AI的技术推动下,平台会提供一系列的AIGC工具,帮助用户实现图片生成、放大、扩展、风格转换等效果,同时收取使用这些功能的费用。

图片来自卓特视觉官网

方案介绍

火山引擎veImageX基于字节跳动的图像领域最佳应用实践,提供端到端的一站式图片解决方案。

整体架构

一套方案解决上传、存储、图像处理、分发、解码、QoS&QoE监控的全链路方案,覆盖从内容生产端到图像消费端。

veImageX的服务端具备强大的实时处理能力,不仅包含了裁剪、缩放、格式转换等基础图像处理功能,还提供了画质评估、画质增强、智能裁剪、超分、盲水印等丰富的AI组件能力。

卓特视觉接入了veImageX的哪些能力

一、画质评估

画质评估组件支持模仿人类对于图像的视觉感受,从而对图像的各方面进行评分。评分指标有大众美学评分、噪声强度评分、纹理丰富度评分和色调均衡程度评分等。veImageX通过抖音集团内部的大量线上业务实验发现,图片画质优劣对点击率、停留时长等消费类指标有正相关影响,间接影响用户收益指标。卓特视觉通过画质评估组件,对线上的海量素材文件进行了广泛的评估,在网站尽量展示评分较高的图片,并在用户查询图片时,优先推荐同类型中评分高的图片。这一系列举措不仅提升了网站整体的图片质量及用户的满意度,还促进了业务增长,并获得了良好的用户口碑。

二、智能裁剪

智能裁剪是 veImageX 提供的全新图片裁剪附加能力,支持对输入图片进行指定尺寸变换,能够自动判断主体区域的位置,并支持自动化适配不同尺寸图片内容的裁剪。卓特视觉的用户分布在各行各业,用途包含宣传页、海报、杂志、电商平台、户外广告等,对图片的尺寸和表现侧重点都有个性化的要求,卓特视觉通过智能裁剪能力批量对原图进行裁剪,自动化适配用户对于不同尺寸的要求,同时确保在任何尺寸下,图片主体都能处于最佳位置。快速高效满足客户需求的同时,也拓宽了产品的适用边界。

三、存储

卓特视觉目前拥有超过5.6亿的正版素材,并且数量仍在持续高速增长,占用的存储空间日益庞大,成本也与日俱增,veImageX提供存储服务,同时支持根据上传时间变更存储类型的智能降冷策略,有效节省存储的成本。此外, 为了进一步帮助企业降低存储成本,veImageX通过自研BVC算法,提供全球领先的极限图片压缩比,对比JPEG压缩率提升8-10倍,在不降低图片质量的前提下,在保持图片清晰度基本不变的情况下,单张图片体积节约超过70%,可以实现显著的成本节约。

四、分发

veImageX作为端到端的图片解决方案,除了强大的AI图像处理能力,还提供存储和分发能力,在分发阶段,veImageX利用自建 CDN 节点进行灵活的智能调度,为国内外用户提供极致的观看体验。卓特视觉通过使用veImageX的高效分发方案,确保了全球用户访问的快速和稳定。

设计素材行业其他需求的能力

一、智能生图能力

用户在平台可能会遇到不符合设计标准的素材,不仅影响了创作效率,同时也会影响平台的口碑,因此,引入AIGC智能生图能力显得尤为重要,当现有素材无法满足需求时,可以通过AIGC快速生成。veImageX结合豆包的AI生图方案,最新上线了智能生图能力,封装了文生图、图生图一站式解决方案。支持将豆包生成的图片进行后处理,包含存储、压缩、二次处理、超分辨率、盲水印、裁剪、适配、分发等。典型功能如下图展示:

  • 文生图场景

  • 图生图场景

此外,veImageX智能生图能力还支持桥接第三方模型文生图、图生图服务,直接对接veImageX进行上传、编码、存储与管理,并支持完善的后处理服务。大大扩展了方案的灵活性。

二、智能审核

设计素材平台如果遇到涉黄、涉暴的素材上传,不仅涉嫌法律风险,而且对平台的品牌可信度将会是极大的折损,而面对每天数以十万计的素材,人工审核显然无法满足。veImageX 提供了图片智能审核功能,支持分类型智能检测图片中涉黄、涉暴恐、违法违规等十几种禁用行为,并返回最终识别结果。识别并预警用户上传的不合规图片,协助平台快速定位处理。

三、盲水印

在设计素材行业,素材的版权归属一贯容易产生争议。在版权意识和版权法逐渐完善的今天,稍有不慎可能就会产生法律纠纷。veImageX兼顾版权追踪和图片美观,支持对图片添加盲水印,同时支持对图像提取盲水印信息,方便追踪溯源。盲水印是一种肉眼不可见的水印方式,可以在保持原图图片美观的同时,又可以保护资源版权。对原图进行解码后,可以得到盲水印信息证明图像的版权归属,避免未经授权的复制和拷贝而造成的版权问题。

四、超分辨率

设计素材平台的用户在制作海报、广告牌等场景时,往往需要对原始素材进行放大,同时需要保持放大后图像的清晰度,即所谓的“无损放大”。veImageX支持将图像做2-8倍智能放大,并保持处理后图像的清晰度,使图像更加清晰、锐利、干净,给用户带来良好的视觉体验。

五、智能背景移除

用户在使用平台提供的设计素材时,如果发现图片中的主体部分符合需求,但是为了配合使用场景、符合品牌调性等原因,需要对原始图片中的背景进行移除。veImageX的智能背景组件,支持保留图像的主体并抠除其复杂的背景,从而生成保留主体的透明底图片。veImageX提供了多种图像处理模型,支持精细化图像主体轮廓处理,可大幅度提升图像处理效率,降低人工成本。

结语

在AI的技术浪潮中,传统的设计素材行业正在向AI时代迈进,以满足客户日益个性化、精细化、创意化的诉求。火山引擎veImageX凭借夯实的技术底座和强大的AI能力,与卓特视觉携手合作,共同迈入设计素材行业AI新纪元,助力我国视觉版权服务市场的蓬勃发展。

了解更多:www.volcengine.com/product/ima…

半空:LLM 辅助的 Go2Rust 项目迁移

试想一下:将一个 Golang 项目(大象)改写为(装进) Rust(冰箱) 总共需要几步?

“Gopher in 冰箱” by DALLE3

背景

当 Rust 语言为我们展示出在「性能」、「安全」、「协作」等方面诱人的特性之后,却因为其陡峭的学习/上手曲线拒人千里之外。是否存在一种科技,能够帮助我们的同学在语言学习项目迁移上完美并行,最终真正将 Rust 项目迁移这个看似美好的荆棘之果转变为触手可得的「低垂果实」呢?

为了将美好的愿望转变为实际,我们结合 LLMs 做了一些尝试,利用 LLMs 在编程语言上体现出的「涌现」能力,设计了一套基于 LLMs 的应用开发基座(ABCoder),在这个基座之上进一步演进出了我们本篇的主角:「半空」。

ABCoder 是字节内部一个编程向 LLMs 应用开发基座,包含自研的 LLMs 原生解析器、工具(Tools)以及工作流(Workflows),对编程项目本身进行深度解析、理解和压缩,并将其制作为源码知识库(Source code as Knowledge),之后利用这类知识库实现对 LLMs 推理过程中所需上下文进行补齐,从而构建出高质量、低幻觉、稳定的编程类 LLMs 应用。有关 ABCoder 更多的介绍可以参考这里

半空

TL;DR 传送门

按照 ABCoder 的设想,让 LLMs 理解编程项目的入口就是结合对项目的解析、理解、压缩后的知识关联和构建,这对于一个轻量化的应用来说可能足够(ABCoder 当前已经能够实现将一个标准 Hertz 项目“转述”为一个 Volo-HTTP 项目),但对应到实际场景中的业务项目来说(增加大量业务属性且复杂度更高),要想真正让 LLMs 完整理解整个项目,并且在有需要的时候让 LLMs 完整的将整个项目“转述”为另外一个语言的项目时我们就需要对我们的解析、理解、压缩、应用流程进行更加细粒度的设计和优化了。

「半空」主要讨论的就是对于复杂项目的理解提升辅助 LLMs 渐进式多轮迭代构建出一个复杂项目的可行性。核心需要解决的是因为项目规模提升所带来的复杂度以及上下文规模提升和 ABCoder 所制作的对应知识库知识密度跟不上的矛盾。

内核简述

罗马不是一日建成的,参考软件工程标准的项目迭代方式,迭代一个越庞大的项目,引入的标准作业流程和所花费的迭代周期和人力就越多。ABCoder 要想深刻的解析并理解一个大型项目,一口永远吃不成一个胖子。

好消息是构建一个复杂项目的过程是有迹可循的的,ABCoder 需要做的其实就是逆着项目构建的路径,反向解析出项目构建过程中涉及到的不同粒度的知识库。

之后将这些知识库输入 LLMs 驱动的 Workflows,通过构建渐进式的多轮迭代流,将原来的项目以任意编程语言又输出出来,基于对知识库的持续构建,甚至实现为其他语言的项目:语言翻译

意译 or 直译?

相较于给 LLMs 一段代码,让他直接翻译为另外一个语言(直译),「半空」所做的类比下来更像是:帮助 LLMs 理解代码,之后经过抽象和设计结合我们希望它采纳的知识,重写出另外一个语言实现的版本(意译)。

理解和设计

按照 ABCoder 的通用处理流,一个任意庞大的项目我们几乎都可以通过解析、级联压缩的方式构建函数、方法、结构体、变量的语义化映射。但仅仅通过这些散落的信息 LLMs 是没有办法高效的建立一个对这个项目系统深刻的理解。因此我们在做 LLMs 辅助的项目文档梳理应用的时候,就已经开始下意识的做一些单元聚合工作了:通过将某个包(文件/模块)中的函数、方法、结构体、变量语义化含义进一步抽象,得到关于这个包(文件/模块)的语言和框架无关的高层次语义化抽象,按照这个思路,我们可以自底向上抽象,到最终项目维度。

举个直观的例子,对于 Hertz 的项目,任意一个 Hertz 项目在项目维度都能够抽象为形如:这个项目是一个基于 HTTP 应用框架的应用,它或许注册了/a/b/c 路由 (Route)的 GET 方法(Method),关联了某个对应的逻辑(Handler)

仔细分析这个抽象,尝试对其中蕴含的细节进行总结:

  1. 一个基于 Hertz 的 Golang 项目,在经过某个维度的抽象之后,丢掉了大量细节,留下了一些在当前维度的关键信息。在上述例子中,我们得到的抽象已经不关心这个项目具体采用的语言实现和具体涉及到的应用框架了,仅仅需要关注的是 HTTP 框架应用以及 HTTP 应用必备的信息:注册了某个路由,处理了某个业务逻辑。

  2. 通过这层抽象,我们可以将任意一个复杂项目映射出了一个最简单的迭代入口:启动一个 HTTP 应用框架,并注册处理某个 URL 的某个逻辑函数。

  3. 对整个复杂项目的理解过程被我们巧妙的转换为对一个项目自底向上的逐层抽象的过程,如果我们能将这个抽象过程做的足够清晰和准确,对于一个完成抽象的项目来说,我们反过来也得到了一个支持我们至顶向下层层细化的项目构建流。

  4. 理论上通过增加、减少、优化各层级抽象,我们就能不断提升对这个项目深度理解的效果。

多轮的抽象和迭代的本质是项目在不同维度上多语言实现和 ABCoder 抽象语义的不断对齐:

配合语言对应的知识库建设,按照标准抽象块(已归一化逻辑)进行知识检索,分层分模块持续迭代,填充核心逻辑,辅助业务完成项目构建。

实施和测试

当我们通过上述解析和抽象,得到了关于一个项目完整的理解知识,之后就可以至顶向下辅助 LLMs 逐层实现项目的渐进式迭代了。同样,接着上一小结里提到例子来说,我们在这层抽象上做的事情就是:

  1. 根据「HTTP 应用框架」匹配目标语言对应的知识,比如检索出 Volo-HTTP 库的知识(如果我们的目标是将这个应用实现为一个 Rust 项目),之后结合 Volo-HTTP 提供的框架初始化逻辑,拉起一个 Volo-HTTP 的项目
  2. 之后按照本层抽象剩下的描述信息,完成**「/a/b/c** 路由 **和对应处理函数」**的注册
  3. 由于本层抽象并不具备这个处理函数的详细描述信息,因此仅仅需要生成一个空实现的桩函数即可
  4. 之后我们所做的所有变成,二次确认完成了具体实现和对应语义化抽象的对齐

以上即是对一轮迭代核心流程的描述,完成本轮迭代之后即可开启下一层抽象的对齐。之后按照这个流程持续的迭代这个项目。

因为抽象本身会丢掉本层部分细节,而丢掉的这部分细节其实还是保留在抽象前的层级中的,对应迭代路径来说,上一层丢掉的细节一定会在下一层迭代中被补充回来。因此,通过多轮的迭代构建出来的项目,理论上也并不会丢失具体的实现细节。

每一层迭代后都会有一次人工介入时机 —— 即可以及时人工介入修改代码并反馈到后续的翻译轮次中,这也是「半空」的核心能力之一 —— 在这个切面上能够按需的扩展任意的软件测试解决方案,包括时下流行的:LLMs 辅助 UT 生成等技术。等到所有的修改和测试通过之后,即可开启下一层的迭代或者选择直接退出手动接管剩余的翻译工作。

交付内容

作为用户最为关心的部分,「半空」究竟在项目 Go2Rust 转换(存量 Golang 项目改写为 Rust)上帮助我们做到哪些事情呢?其实非常简单,好比将大象装进冰箱,「半空」辅助下的 Go2Rust 自动化迁移也是三个核心步骤:

  1. 打开冰箱门:基于 ABCoder 对存量 Go 项目完成系统解析,产出函数粒度的项目理解原料

  2. 把大象放进去:基于项目理解原料产出将该项目改写为 ****Rust 对应的项目设计文档

  3. 关上冰箱门:基于设计文档中指引的迭代顺序,全自动可控地,产出各层迭代代码

实际上,结合简介中的描述,聪明的小伙伴也许已经发现:「半空」作为一套通用框架,应用面其实并不仅仅局限在 Go2Rust 上,对于任意语言之间的相互转换逻辑上都是完全一致的,区别在于对语言特异性处理和特定语言的知识库构建。「半空」一期重点针对 Go2Rust 场景完成内场的适配和持续打磨,后续如果有对更多语言栈(Python2Go/Java2Go/...)的切换诉求也非常欢迎勾搭~

项目实战举例

一个使用「半空」做 Go2Rust 项目转换的示例

项目介绍

Easy_note 是 CloudWeGo 社区对外提供的一个基于 Hertz 和 KiteX 的实现复杂、功能覆盖度高的业务实战示例项目;其使用 Hertz 提供了若干 API 接口,并在接口实现中通过 KiteX client 发起对下游 KiteX Server RPC 接口的调用。

本次使用「半空」翻译的是其 API 模块,其主要功能列表如下:

  • 用户管理

    • 用户注册 (HTTP 接口 -> RPC 调用)
    • 用户登录 (HTTP 接口 -> RPC 调用)
  • 笔记管理

    • 创建笔记 (HTTP 接口 -> RPC 调用)

    • 查询笔记 (HTTP 接口 -> RPC 调用)

    • 更新笔记 (HTTP 接口 -> RPC 调用)

    • 删除笔记 (HTTP 接口 -> RPC 调用)

涉及到的 Hertz/KiteX 框架相关的核心能力如下:

  • 初始化 Hertz Server
  • 注册 Hertz 路由和 handler
  • 实现 Hertz 自定义中间件(JWT、服务发现)
  • 实现 Hertz 的 handler 逻辑
  • 使用 KiteX Client 调用下游接口

流程说明

从输入原始项目产出 ABCoder 理解知识原料开始,「半空」会结合函数粒度知识原料,自底向上完成整个项目的逐层抽象和理解,之后至顶向下完成重构设计的制定,同时确定项目渐进式构建顺序:从粗粒度 知识映射细粒度 知识映射到最后逐个 Package 的实现,最终完成 Golang 项目到 Rust 项目的渐进式构建(意译)。这个过程中项目构建进度完全由用户掌控,结合人工修改反馈辅助协同,推动项目完成 Go2Rust 迁移落地。

上图提到的 Golang AST / Rust AST 是 ABCoder 在分析仓库代码,将函数、方法、结构体、变量等定义以树形关联出来的数据结构体集合,是一个能够与项目一比一映射的 LLMs 原生 抽象语法树

设计阶段:Package 翻译顺序

根据 ABCoder 解析后的项目原料,「半空」自动化根据 Package 的依赖关系完成了使用 Rust 重构这个项目所需的设计文档的编写,自顶向下得到如下迭代顺序:

  1. "github.com/cloudwego/b…":项目的二进制入口和基础框架搭建
  2. "github.com/cloudwego/b…":HTTP 通用 handler 的实现
  3. "github.com/cloudwego/b…":HTTP 通用 router 的注册
  4. "github.com/cloudwego/b…":HTTP 业务 router 的注册
  5. "github.com/cloudwego/b…":HTTP 业务 handler 的实现
  6. "github.com/cloudwego/b…":请求下游的 RPC 封装
  7. "github.com/cloudwego/b…":通用/业务中间件具体实现

实施阶段:根据设计文档顺序逐步展开

  1. " easy_note/cmd/api "

对应 MR: github.com/cloudwego/b…

main package,主要实现了 HTTP server 的初始化、路由注册调用等能力

Golang 原始实现 「半空」意译效果
main() main()
customizedRegister() customized_register()
常量定义[本轮不实现,只mock] 常量定义[mock实现]
  • 结果评估

    • 目录:

      • 所有 main package 的内容,都生成到 Rust 项目的 /src/bin/main.rs下;后续支持细粒度的文件模块映射
    • 内容:

      • 翻译的函数内容逻辑,基本正确;会将函数的具体过程用顺序表示出来,便于进行修改
    • 错误:

      • Opentelemetry 相关的使用报错;原因:目前还没有注入相关知识;不影响正常逻辑,先注释掉
    • Mock:

      • Main package 会依赖其他包的内容,因此会将其他 package 下的内容进行 mock,确保可以正确编译,但是 mock 的内容不一定完全准确,会在后续迭代完成最终实现;具体 mock 内容可参考上面的示例
  • 修改记录

    • 对 main/init 中涉及 Opentelemetry 的代码注释掉
  • 优化方式

    • 通过补充内场 Opentelemetry 相关缺失知识可以进一步提升完备率和可编译度
  • 数据统计

    • 生成节点完备率=无需改造的节点/生成节点总数

      可编译度=1-修改的代码行数/生成的代码总行数

    • 生成节点完备率: 50%

    • 生成代码可编译度:73%

  1. " easy_note/cmd/api/hertz_handler "

对应 MR: github.com/cloudwego/b…

hertz_handler package 主要实现了一个 ping handler,用于处理 ping-pong 请求

Golang 原始实现 Rust 意译效果
Ping() ping()
  • 结果评估

    • 目录:

      • 所有 Golang cmd/api/hertz_handler包的内容,都生成到 Rust 项目的 /src/cmd/api/hertz_handler/mod.rs
    • 内容:

      • 翻译的函数内容逻辑完全正确
    • 错误:

      • Cargo.toml 里没有加入 "serde_json" 依赖,导致报错
    • Mock:

      • 没有尝试参考 hertz 去 mock 状态码和 utils.H,而是自行利用 volo-http 框架的能力完成响应返回
  • 修改记录

    • 增加 "serde_json"
  • 优化方式

    • 在 cargo.toml 知识里增加通用、常用的依赖
  • 数据统计

    • 生成节点完备率=无需改造的节点/生成节点总数

      可编译度=1-修改的代码行数/生成的代码总行数

    • 生成节点完备率:100%

    • 生成代码可编译度:95%

  1. " easy_note/cmd/api/hertz_router "

对应 MR: github.com/cloudwego/b…

hertz_router 包主要实现 Hertz 路由的总体注册逻辑,调用 idl 生成的路由

Golang 原始实现 「半空」意译效果
GeneratedRegister() generated_register()
Register()[本轮不实现,只mock] register()[mock]
  • 结果评估

    • 目录:

      • 所有 Golang cmd/api/hertz_``router包的内容,都生成到 Rust 项目的 /src/cmd/api/hertz_router/mod.rs
    • 内容:

      • 翻译的函数内容逻辑完全正确
    • 错误:

      • 没有正确地将下层依赖 pub 出来,而是直接使用了依赖路径
    • Mock:

      • IDL 生成的路由注册部分,将其 mock 出来
  • 修改记录

    • 将 "hertz_router/demoapi" mod pub 出来
  • 优化方式

    • 在生成代码后对新增内容做一次解析和关联
  • 数据统计

    • 生成节点完备率=无需改造的节点/生成节点总数

      可编译度=1-修改的代码行数/生成的代码总行数

    • 生成节点完备率: 100%

    • 生成代码可编译度:88%

  1. " easy_note/cmd/api/hertz_router/demoapi "

对应 MR: github.com/cloudwego/b…

hertz_router/demoapi package 主要实现了具体了路由注册(idl 映射)以及 Hertz 中间件的定义

Golang 原始实现 「半空」意译效果
Register() register()[路由注册有问题,需要 check & 修改]
rootMw() root_mw()[包含了中间件里的 mock 实现]
mw 定义 mw 定义
CreateUser[本轮不实现,只mock] create_user[mock]
  • 结果评估

    • 目录:

      • 所有 Golang cmd/api/hertz_``router/demoapi包的内容,都生成到 Rust 项目的 /src/cmd/api/hertz_router/demoapi/mod.rs
    • 内容:

      • register(): 路由注册的逻辑对应上了,但是实现不对;生成的路由没有和原始的路由一比一映射成功,是根据函数的描述自行生成的路由:需要用户手动将路由修改正确,参照生成的用法很快就可以实现

      • root_mw():

        • 能够以注释的形式描述出来 root_mw 里所需要做的内容,但是没有正确实现。因为 volo 里没有这样把多个中间件组成一个切片的操作:需要用户自行补充实现
        • 没能实现 recovery、RequestId、Gzip 的中间件逻辑;主要原因是无法推测出这些功能在 rust 里的实现方式:需要用户自行补充实现
      • 其余的中间件均正常

    • 错误:

      • register() 的路由注册逻辑不对,优化思路如下:

        • 这部分是 IDL 映射的内容,本身就被拆的比较细;后续会做框架之间的 IDL 映射
        • 增强函数的细节描述
    • Mock:

      • Mock 实现了所有的handler内容,这部分没什么问题
  • 修改记录

    • 对路由逻辑进行重新梳理和注册
    • 对 recovery/request_id/jwt 中间件的逻辑进行实现(ps. 示例还未实现,暂时注释掉)
    • 删除/添加一些依赖信息
  • 优化方式

    • 增强细节逻辑的总结&实现能力
  • 数据统计

    • 生成节点完备率=无需改造的节点/生成节点总数

      可编译度=1-修改的代码行数/生成的代码总行数

    • 生成节点完备率:62%

    • 生成代码可编译度:76%

  1. " easy_note/cmd/api/hertz_handler/demoapi "

对应 MR: github.com/cloudwego/b…

  • Hertz_handler/demoapi package 主要实现了具体的 HTTP 接口实现,下面使用 "create_note" 作为展示
Golang 原始实现 「半空」意译效果
CreateNote() create_note()
SendResponse() send_response()
rpc CreateNote[本轮不实现,只mock] rpc create_note[mock]
ErrNo[本轮不实现,只 mock] ErrNo[mock]

handler 这轮翻译完,出现的代码报错较多,主要原因如下:

  1. 是代码量本身比较大,同样错误报错多次

  2. handler 里涉及了一些业务逻辑以及业务在 golang 里的特定的用法,LLMs 不能很好转换

以下都以"create_note" 接口为例,进行结果评估

  • 结果评估

    • 目录:

      • 所有 Golang cmd/api/hertz_``handler/demoapi包的内容,都生成到 Rust 项目的 /src/cmd/api/hertz_handlers/demoapi/mod.rs
    • 内容:

      • create_note(): 能把原 create_note 的逻辑按顺序进行实现,包括 获取参数、发起调用、返回响应等
      • send_response(): 基本能实现出原接口的含义,但是错误较多,图里展示的是手动修改过的
    • 错误:

      • create_note(): 逻辑是正确的,主要有以下错误

        • mock 的结构体,没有带 #[derive(Debug, Deserialize)]需要用户补
        • send_response() 的调用无法对齐,一直报错
        • 获取请求上下文的时候,可能会有误传参
      • send_response(): 整体逻辑是对的,但是不会用 volo-http 的写响应方式

    • Mock:

      • 直接 mock 的内容基本都正确不需要修改
      • 没有去对二级依赖进行mock,导致会有些编译错误;例如,当前接口依赖了 "rpc/create_note",其又依赖了 "NoteDate" 类型,这个没有进行实现
  • 修改记录

    • send_response 的逻辑重新实现
    • 修改 handler 的调用逻辑,以及一些 ctx 上下文传参的问题
    • 增加/删除一些依赖信息
  • 优化方式

    • 补充 volo-http 的请求/响应相关操作示例,指导 LLMs 生成更准确的 SDK 使用姿势
  • 数据统计

    • 生成节点完备率=无需改造的节点/生成节点总数

      可编译度=1-修改的代码行数/生成的代码总行数

    • 生成节点完备率:14%

    • 生成代码可编译度:88%

至此,我们就完成了 "github.com/cloudwego/biz-demo/easy_note/cmd/api" 这个 moudle 的全部翻译,用户在 check 完整个项目后,即可以编译 & 运行项目。

总结

整体意译效果说明

  • 函数翻译完备性

完备性说明:完全无需人工介入的函数统计为完备函数

package 生成函数的个数 完备函数的个数 完备率
easy_note/cmd/api 4 2 50%
easy_note/cmd/api/hertz_handler 1 1 100%
easy_note/cmd/api/hertz_router 1 1 100%
easy_note/cmd/api/hertz_router/demoapi 13 8 62%
easy_note/cmd/api/hertz_handler/demoapi 7 1 14%
  • 代码可编译度

可编译说明:相对于整体生成代码行数,人工介入修改的代码行数占比,需要修改的代码越少,可编译度越高

package 生成函数的行数 人工修改的代码行数 可编译度
easy_note/cmd/api 106 28 73%
easy_note/cmd/api/hertz_handler 19 1 95%
easy_note/cmd/api/hertz_router 9 1 88%
easy_note/cmd/api/hertz_router/demoapi 173 38 76%
easy_note/cmd/api/hertz_handler/demoapi 254 30 88%

整体上,通过知识库的持续建设和关键知识的补齐,「半空」在完备性和可编译度上也会随之持续提升。

语言学习和项目迁移

在这个过程中,结合「半空」为我们生成的 Rust 项目设计文档,从整体项目的角度出发,逐步对每个包进行深入理解、翻译与确认。这一过程条理清晰、循序渐进地将一个 Golang 项目从零构建为一个 Rust 项目。同时,我们一同参与项目构建的每一个迭代,「半空」每一个迭代生成的代码完全遵循内场和业内 Rust 项目编写的最佳实践,这不仅帮助我们深刻理解整个项目,同时也为学习一门新语言提供了极大的支持。通过这种逐步渐进迁移的方式,我们能够不断深入学习并掌握 Rust 语言及项目本身,最终成功完成项目的转型。

ICLR 2025 Workshop 征稿:推动基础模型的开源、开放、可复现

国际学习表征会议 ICLR 作为深度学习领域的顶级学术活动,将于 2025 年 4 月 24-28 日在新加坡举办。期间,首届 Open Science for Foundation Models (SCI-FM) Workshop 将在会场同步开启,现面向业界进行征稿。

时下,基础模型(Foundation Models)在自然语言处理、计算机视觉、语音识别和多模态理解等领域展现出显著价值。然而,高性能基础模型通常隐藏了训练数据、架构设计等关键技术细节,限制了基础模型的进一步发展。 

为此,2025 年 ICLR 期间,第一届 Open Science for Foundation Models (SCI-FM) Workshop 将一同举办。该 Workshop 致力于推动基础模型的开源、开放、可复现。我们邀请了国内外基础模型相关学者参与本次 Workshop ,赋能基础模型的发展和繁荣。 即日起,Open Science for Foundation Models (SCI-FM) Workshop 开启论文征稿,欢迎相关研究者投递。 所有被接受论文都将以海报形式呈现,同时,我们将选择约 3 篇论文进行简短的口头报告,另授予 2 篇投稿优秀论文奖。

1. 论文征集要求

本次 Workshop 鼓励投稿论文尽可能提供开源材料,包括但不限于数据集、模型、训练过程等等。投稿类型分为长文与短文,其中,短文正文不超过 6 页,长文正文不超过 9 页,投稿文章末尾须包含参考文献和附录。主题包括(但不限于)以下内容:

  • Open Datasets:通过手动或算法方法获取、管理和合成预训练、指令和偏好数据集。

  • Open Foundation Models:预训练策略,包括数据扩展、模型架构、多模态和多任务预训练。学习算法,如元学习、模型融合、模型合并和为开放、可扩展模型设计的持续学习。

  • Open Training Protocols:对缩放规律、可解释性、复杂性分析、涌现能力等现象进行训练动力学研究。对齐技术,包括提示调优、前缀调优、指令调优和具有人类 AI 反馈的强化学习。

  • Open Evaluation:制定开放的评测基准,评估协议和指标。

  • Open Compute Efficiency Techniques:专注于模型蒸馏、压缩、量化和优化注意力或内存机制,以提高基础模型的计算效率。

  • Open Multi-Modal Foundation Models:扩展到视觉、音频等多模态场景。

  • Open Interactive and Agent Systems:对话式人工智能、交互式学习模型、多代理系统的开发。

  • Open Replication of Proprietary Systems:复现专有的基础模型和系统,确保更广泛的研究和开发的透明度和可重复性。

2. 投稿入口与时间

投稿入口: openreview.net/group?id=IC…** 提交开放日期: 2025 年 1 月 6 日提交截止日期: 2025 年 2 月 10 日接收/拒绝通知: 2025 年 3 月 5 日同时,本次 Workshop 诚邀相关领域学者参与审稿,填写链接中的表单即可报名:forms.office.com/e/SdYw5U75U…

3. 现场演讲嘉宾(部分)

图片

** **

4. SCI-FM 组织委员会成员

图片

本次活动赞助方:字节跳动。

ByteHouse技术详解:基于OLAP构建高性能GIS地理空间能力

在数字化时代,地理空间分析(Geospatial Analytics)成为辅助企业市场策略洞察的重要手段。无论是精准广告投放,还是电商物流的效率优化,都离不开对地理空间数据的查询、分析和可视化处理,以便助力企业更好决策。

以一家连锁咖啡店为例:

该店想要在新城市开设分店,并希望确保新店铺的位置能够最大化利润。

首先,商家通过收集新城市的地理数据,包括人口分布、交通流量等,建立了一个详细的地理信息数据库。然后,商家利用空间数据分析工具,对这些数据进行了深入分析。

通过人口分布数据,商家发现新城市的一些区域人口密集,潜在顾客群体较大。同时,交通流量数据显示,某些区域的交通流量较大,意味着这些区域的顾客流动性较高,有利于店铺的曝光和吸引顾客。

此外,商家还分析了同行情况竞争对手的位置,以避免在已有众多同类型店铺的区域开设分店。空间数据分析帮助商家识别了那些既有足够潜在顾客,又相对较少竞争者的区域。

基于这些分析结果,商家最终确定了新店铺的位置。开设分店后,由于选址精准,店铺迅速吸引了大量顾客,销售额和利润均实现了预期目标。

以上案例离不开对地理空间数据库的支持。一些传统的地理信息系统数据库具备丰富的地理空间对象结构、成熟的空间索引能力,在导航、旅游、智能城市等典型应用场景中被广泛使用。

但随着实时分析报表等OLAP市场的扩大,地理空间分析也作为新的增值特性被业界几大OLAP主流产品所推广。OLAP+GIS能力在满足用户地理空间数据分析的基础上,还能在数据体量大、实效性要求高的情况下,满足业务高性能查询的需求。

作为火山引擎推出的一款OLAP引擎,ByteHouse近期发布了高性能地理空间分析GIS能力,为位置洞察、人群圈选等场景提供高性能地理数据分析服务。本篇内容将从技术实现角度,详细介绍ByteHouse如何集成GIS能力,并通过benchmark测试,展示ByteHouse与市场同类产品相比(ClickHouse、StarRocks、PostGIS、DuckDB)的性能情况。

应用场景和价值

位置洞察: 例如,在给定中心点的情况下,展示半径X公里内的圆内其他商家的同一商品的客流分布、经营情况等,有助于帮助商家客户洞察竞争对手情况,为定价策略和市场定位提供数据支持。

作战地图: 给定特定多边形,观察多边形内部商家的供给和客流量,为即时零售业务的配送优化提供决策依据。例如:生活服务的即时零售业务需要观察实时的配给。

经过我们对行业上相关业务场景的需求分析,商家或者销售代理等客户需要的是一种“对某个地理空间(多边形/圆)内的对象进行多种业务维度的分析和决策能力”。从整个执行链路来看,链路不仅含GIS的二维空间数据筛选,还有经典OLAP的聚合和关联分析等逻辑,因此可以总结出一层GIS+OLAP链路的抽象。从性能优化角度来看,OLAP优化器有必要去结合GIS的特性来进行适配,提升端到端的总体性能。

详细介绍

在关键性能层面,ByteHouse GIS在列式小批组织的数据结构上引入RTree等二维空间索引能力,并在CPU硬件层面实现了二维空间函数的性能优化,整体提升了端到端性能。在功能层面,兼容OGC标准,支持导入标准GIS文件格式,目前已支持超过50个主流的空间函数。更值得一提的是,我们还在探索在我们自研的优化器上结合GIS特性适配,如在高效的多表关联上适配GIS等,以及GPU硬件层面优化二维空间函数。

二维空间索引

回顾业务场景:给定一个查询窗口(通常是一个多边形或圆),返回包含在该查询窗口中的物体。

如果要提升查询性能,读取的数据量通常是比较关键的,那取决于:

1)数据的排序方式 2)数据读取的粒度 3)索引

社区ClickHouse数据组织

ByteHouse 是火山引擎基于开源 ClickHouse 进行了深度优化和改造的版本。 ClickHouse 社区版直接按照Order By latitude, longtitude里面的纬度进行排序,再按照经度排序。

因为经度上相距很远的数据可能被放到一个mark,而查询是一个多边形和圆,查询的模式和数据的组织不匹配从而造成严重的读放大问题,导致数据局部性较弱。

微信截图_20241226100633.png

ByteHouse空间索引:Google S2 + R tree

ByteHouse GIS 通过使用Google S2 [3]库将所有的经纬度点从二维转换转换成一维,并排序。排序后的经纬度点效果如下图:

图片来源:[3]

由于ByteHouse的数据是按照列式存储,相比于传统的行级别索引,我们会对S2排序后的经纬度数据,先按照小块粒度切分,再利用RTree来索引每个小块数据。这样,基于小块粒度的RTree索引的存储开销更小,加载和查询效率更高。给定一个查询的多边形或圆,RTree能快速索引到匹配的数据块。由于每个数据块内的经纬度数据是按照二维层面聚集,这样使得相邻的点在二维空间上更加紧密,数据局部性更好。

ByteHouse GIS索引结构

针对某个具体场景中给出的一个圈选范围,需要返回范围内的所有POI (Point of Interest)点。下面两幅图分别展示了传统经纬度排序方式(Order By latitude, longitude)和ByteHouse GIS索引排序方式(Order By point)的圈选效果。其中,图中黑色的框代表了所有数据块,红色部分代表了圈选命中的数据块。

从结果中看出,传统经纬度排序命中的范围会横跨很广的纬度,造成读取许多无用的数据。而按照ByteHouse GIS索引搜索出的数据块只集中在北京地域,正好满足圈选所需的最小数据块集合。

传统经纬度排序方式的搜索效果

ByteHouse GIS排序方式的搜索效果

兼容OGC标准

数据类型

按照OGC标准,新增7种几何类型,包括Point、LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection。

存储层面上,传统GIS数据库(例如,PostGIS)将几何数据序列化为Blob类型,读取时需要额外花费反序列化的开销。而ByteHouse GIS则按照数值数组和列式方式存储,减少存储量、序列化和反序列化开销。

空间函数

功能上,ByteHouse GIS目前已支持超过50个通用的空间函数,下面表格列举了几大函数分类。另外,我们针对个别高频使用的空间函数进行了基于列式数据存储格式的性能优化。

微信截图_20241226100839.png

存量数据迁移

同时,ByteHouse GIS也支持常见数据格式的导入与导出,包括WKT、WKB、GeoJson、ShapeFile、Parquet、CSV和Arrow等文件格式。

Benchmark 测试

标准NYC Taxi数据集

为了说明性能效果,我们基于两个关键的 GIS 函数,使用 NYC Taxi 数据集,选取纽约的 3 个地理区域,将ByteHouse、ClickHouse、StarRocks、PostGIS、DuckDB进行了性能对比(以上对比的版本参照发文日期的最新版本)。

在本次测试中,我们选取了两个关键的 GIS 函数:ST_DistanceSphereST_Within;并使用 NYC Taxi 数据集(Size:21GB;条数:169,001,162),数据集将纽约拆分成多个地理区域(比如 Brooklyn,Manhattan),本实验选取其中 3 个不同大小的地理区域(按照过滤度区分:zone 1、zone 2、zone 3)进行了性能对比。

  1. ST_Within 函数性能对比:在 ST_Within 函数的测试中,从查询延迟来看,OLAP引擎的整体查询延时低于1s,由于二维空间索引和向量化的数据处理方式,ByteHouse查询延时最低;当前版本的DuckDB由于没有空间索引,同时采用了BLOB的存储方式,数据扫描和反序列化开销比较大,查询性能不好;采用行存的PostGIS在大范围搜索的情况下(zone3),虽然有索引加持,依然会有较重的读放大,查询延时超过6s。从每秒吞吐量来看,ByteHouse通过索引降低了数据读取和反序列化开销,展现出明显优势,其次为PostGIS,在小范围搜索(zone1和zone2)情况下表现优秀。

ST_Within函数性能对比

ST_Within每秒处理空间查询数

  1. ST_DistanceSphere 函数性能对比:在 ST_DistanceSphere 函数的测试中,在处理相同数据集和查询时,ByteHouse具备二维空间索引过滤和向量化计算的优势,性能控制在0.1s以内。ClickHouse和StarRocks同样具备较好的0.1s-1s内的较好性能表现。

ST_DistanceSphere 函数性能对比

基于标准数据集的测试结果来看,对比传统的PostGIS:

  • ByteHouse GIS将OLAP和GIS结合了起来。在OLAP层面,ByteHouse对比PostGIS已经有计算优势。
  • 在GIS层面,空间数据对象按照列的方式存储,而非序列化成字节数组,在存储上能够做到更加紧凑并节省空间,在计算上能够充分发挥向量化的优势。
  • 特别是在空间函数层面,可以利用硬件的并行化能力提速。

对比社区ClickHouse:

  • ByteHouse GIS兼容OGC标准,场景上能够水平替换之前PostGIS的场景。
  • 另外,空间索引能力可以大大减少ClickHouse的读放大的现象。
  • 还有,ByteHouse自研的优化器同样具备适配GIS特性的能力。

业务数据集

在电商场景中,ByteHouse GIS能力不仅满足平台商家运营快速分析商家经营状态、管理商家的需求,还将数据读取量减少超过50%,进一步降低了磁盘IO以及计算带来的CPU开销。

总结

本文具体拆解了ByteHouse GIS能力的技术实现方案,并将ByteHouse、ClickHouse、StarRocks、PostGIS、DuckDB五款数据库产品的性能进行分析和比较。

结论总结如下:ByteHouse在ST_DistanceSphere 函数及ST_Within 函数的查询延迟低于其他产品,查询吞吐量更高,具备比较明显的性能优势。

需要注意的是,性能测试结果取决于多个因素,在实际应用中,需要综合考虑各种因素,如数据规模、可扩展性、易用性、稳定性、安全性以及是否需要与其他系统集成等其他因素进行综合选择,并对数据库进行合理的配置和优化,以获得最佳的性能表现。

对于专注于地理空间数据分析的项目,PostGIS能提供了全面的地理空间功能支持,是一个比较好的选择。然而,如果地理空间数据只是大数据分析的一部分,且如果性能是首要考虑因素,那么ByteHouse、ClickHouse、StarRocks、DuckDB是合适的选择,其中ByteHouse GIS 功能不仅提供了高性能的地理空间分析能力,还具有易于使用、实时分析和云原生等特点,这使得企业可以更灵活、更高效地利用地理空间数据。

参考

  1. PostGIS: postgis.net/
  2. OGC: www.ogc.org/standard/sf…
  3. Google S2: s2geometry.io/
  4. Geos: libgeos.org/
  5. clickhouse.com/docs/en/sql…
  6. Cuda: developer.nvidia.com/cuda-toolki…
  7. github.com/rapidsai/cu…
  8. github.com/arctern-io/…
  9. halfrost.com/go_spatial_…

ROG:高性能 Go 实现

本文根据字节跳动服务框架团队研发工程师在 CloudWeGo 技术沙龙暨三周年庆典中演讲内容《ROG——高性能 Go 实现》整理。

作者|不愿意透露姓名的小刘市民

ROG 之缘起

ROG 的诞生是因为我们一部分业务使用 Rust 重写之后,获得了非常好的收益,比如 AVG、CPU、MEM、P99,这些数据表现非常好,大约节省了接近 50%的 CPU,内存大大降低。

这个性能数据让人眼红,因此团队考虑既然 Rust 有这么好的性能,我们有没有办法提升一下用户在 Go 上的性能?

图片

在和一些用户的对接中我们发现,让用户把 Go 业务通过 Rust 重写,难度其实非常大。很多用户会抱怨 Rust 的一些问题让他们很痛苦,比如,Rust 生命周期太复杂,泛型系统太复杂,报错看不懂,编程速度慢等等。因为这一系列问题,所以让用户把原来的 Go 项目通过 Rust 重写,对于用户来说是很难推动的事情。

于是,我们就有了一个大胆的想法,如果我们可以像使用 Rust 那样的编译技术去生成性能更好的可执行文件,同时使用 Rust 重写 Go 的 Runtime 和 GC 这两个核心组件,再通过几乎零开销的 FFI(Foreign Function Interface) 方式来支持 Rust 和 Go 之间的互调,是不是可以让用户 Go 的源码也能达到接近 Rust 的性能。这就是我们的初衷,因此有了 ROG 这个项目。

ROG 进展

我们目前测试了一些简单的场景,比如快排和二分、Simple Lisp。这些都是通过 time 命令来计算两个二进制文件执行所需要的时间。目前在快排、二分上,Go 的执行需要 5.97s,ROG 的执行需要 4.12s,在 Simple Lisp 这个项目上,Go 需要 8.17s,ROG 执行只需要 7.09s。

图片

从以上几个基本数字来看,在一些简单的场景下,ROG 会比 Go 性能好很多。但这只是一些非常简单的 case。如果面对一些非常复杂的 case 呢?比如在复杂的微服务场景下,ROG 会有怎样的性能领先?ROG 在上个季度刚好能够支撑 Kitex Benchmark 跑起来,目前我们完成了一次压测。

图片

我们使用 Kitex 官方的 Benchmark 工具完成了简单的 RPC 调用测试(github.com/cloudwego/k… 100,测试包大小 1024kb 的体积。在这个测试中,Go 的 QPS 可以达到 27W,ROG 28W。虽然 ROG 的 QPS 只比 Go 领先了一点,但是 P99 上有很大的提升。我们在测试过程中发现了 ROG 还有很多可以挖掘能力,只是还需要进一步优化。

架构设计

通过刚才几个性能场景测试,我们发现 ROG 相比 Go 在不同的场景下,多多少少有一些领先。但是为什么 ROG 相比于 Go 会有这样的领先呢?早期我们其实也经历过 ROG 测试结果比 Go 还差 50%的状态。所以想先给大家介绍一下我们的设计架构。

图片

从图中可以看出,首先会有一个 ROG 的前端来处理用户的 Go 源码,在前端经历 Parser 解析后生成 AST(Abstract Syntax Tree),做符号解析,每个函数,每个类型的符号。然后进行类型检查,分析出函数的签名以及每个变量的类型。这是一套非常常见的前端处理流程。

在经历这个过程之后,会产生一个中间语言叫做 MIR(Rust's Mid-level Intermediate Representation),之后会基于 MIR 去做一些前端时的优化,比如编译时计算、常量传播计算、逃逸分析(能够分析出哪些变量应该被逃逸到堆上去)、Inliner、SROA,以及对于特定 Go 函数的优化。

在这些优化算法处理之后,会生成一份 LLVM IR(Intermediate Representation),之后把它交给 ROG 后端。ROG 后端是我们自己魔改的一个 LLVM 版本。在 LLVM codegen 阶段我们给每个函数插入了一些对应的 Stack Check 以及对应的 STW(Stop The World) Checkpoint 指令,同时生成相应的 GC Barrier。

优化好之后就生成一份比较高质量的二进制代码了。这是对于 Go 语言的处理,而对于 Go 的 Runtime & GC 这部分,我们基本上完全是重写的。通过 Rust 重写之后,我们把这些代码通过自己维护的一个 Rust 版本去构建、打包好,调成对应的 LLVM 文件,最后和用户的 Go 代码连接起来,形成一个最终的二进制文件。这就是我们的编译流程。

收益来源

这个编译架构为什么相比 Go 或多或少有些性能优化呢?有哪些领先点?其实领先点主要来源于三个部分。

图片

第一部分,编译优化。因为 ROG 利用了 LLVM 积累多年的编译优化算法,能够生成一些性能更好的代码,而 Go 的编译优化会为编译速度做出一定牺牲。

第二部分,ROG 提供了跨语言 LTO(Link Time Optimization) 以及 FFI,通过几乎零开销的方式调用 Rust 提供的方法,因此在一些需要更高性能的场景,用户可以使用 Rust 开发,由 ROG 进行编译并进行调用。而 Go 对于 FFI 会使用 CGO,并且 CGO 会存在一些 overhead。

第三部分,Runtime&GC。ROG 完全使用 Rust 重写,再通过上面提供的 FFI 来保证调用的性能,而 Go 的 Runtime & GC 则是完全使用 Go 原生实现的。单纯从语言的表达能力上限来说,Go 远不如 Rust,所以如果我们通过 Rust 来重写 Runtime & GC 这两部分组件,理论上会比 Go 拥有更好的性能。

面临的挑战

介绍完性能来源之后,可能很多人会有疑问,貌似我们的主要性能受益都是来自于 LLVM。LLVM 本身优化已经做得很好了,我们做的是不是就是非常简单地把一个 Go 源码翻译到 LLVM 就行了呢?

其实整个事情并没有那么简单,在这一年里,我们踩过非常多的坑。以下举几个简单的例子。

Go Runtime

如果大家之前了解过 TinyGo,就会发现 TinyGo 的思路和 ROG 非常接近——TinyGo 也是把 Go 的源码给翻译到 LLVM。我们可以回想下在使用 TinyGo 的时候遇到过什么问题。

首先,TinyGo 需要用户手动通过 runtime.Gosched 这个函数来进行协作调度,所以它对用户代码是有影响的。如果用户没有在关键的地方去插入这个函数调度,会对它的调度产生影响。另外,TinyGo 本身也不支持多线程,并且缺少相应的 channel timer reflect 等 lib 的支持。

而 ROG 把这些问题都解决了,ROG 会在编译阶段插入代码,完成协作式调度,并且 ROG 设计的本身也是为了高性能,所以自然会对多线程进行支持,并且 ROG 对于 channel timer reflect 全部都重写。对于我们来说,解决 TinyGo 的不足的过程也相当艰难,毕竟重写整个 Runtime & GC 等是一个非常大的工作量。

图片

Safety FFI

假设如果我们要在 Go 提供 FFI,当用户写出这样的代码会发生什么事情?

图片

左边这张图是用户写的一份 Go 代码,里面有函数。rog_test(a *int32) 这个函数可能就是 FFI 提供的一个外部函数。如果用户直接去调用这个外部函数,而 rog test 本身是由 Rust 实现的,如右图,当我们写出这样的代码的时候,会发生什么事情?

因为 rust_tup 被 Rust Allocator 管理,Go GC 无法扫描到这个变量,所以这个变量 a 也无法被 GC 扫描到,而 “a” 这个变量是被 Go 的 Allocator 管理的,所以如果  a 无法被 GC 扫描到,那么 a 就会被 free 掉。但是这个时候,`` rust_tup 仍然会持有变量 a 的指针,在 Go 那边相当于是一个对外内存引用了 Go 的一个对象,但是因为 Go 扫不到这个对象,所以这个对象就被 free 掉了,但是对外内存仍然引用这个指针。

当我们提供 FFI 的时候,很有可能会面临这样的情况。这种情况该怎么处理?在 ROG 这边,我们就会通过一个模改的 Rust 编译器,提供一个 Managed Chekcer 去限制用户写出这样的代码,在编译器阶段保证用户不会写出这样的代码,保证 FFI 的安全性。这是 ROG 解决这个问题的思路。

Roadmap & 未来规划

CGO

目前 ROG 虽然能跑过 Kitex Benchmark,并在内部一些服务上做了测试,但它仍有很多功能需要改进,比如 CGO。CGO 是 Go 语言用来提供 FFI 的一种方案,但 ROG 的 FFI 是通过一种非常简单粗暴的方式提供的。目前 ROG 的 FFI 需要用户手工去标记 ROG,写上 rog:linkname 标记。这样我们在链接时才能链接上对应的符号。而 CGO 可以让用户简单的直接在 Go 文件的一个注释里写上 C 代码 import C,通过 import c 这个 package 来进行调用。

从 FFI 来说,CGO 会比现在的 ROG 方便很多,而且已有很多的开源库,以及字节内部一些服务,他们也在使用 CGO。我们在未来会支持 CGO,兼容 CGO 的表达方式,提供 ROG 需要的 FFI,生成 ROG 需要的 FFI 代码进行调用。

宏/编译器生成代码

Rust 宏在我看来是一个非常强的功能,因为 Rust 宏可以简单地在每个 Rust 进行标记,申明这个结构可以提供 Serialize(序列化)和 Deserialize(反序列化)这两种方法。这样就可以在编译时为它生成序列化和反序列化的代码,直接进行调用,而不需要像 Go 原始的 JSON,它有反射开销。而这种反射开销在需要高性能的序列化场景会有很大的性能开销。为了解决 Go 的反射开销,sonic 做出了 JIT 方案,而 JIT 对开发 sonic 的开发者来说,负担是非常大的。

图片

那么如果我们可以把 Rust 宏的理念引入到 ROG 中,会有什么样的体验?

首先,更好的开发体验。以 Kitex 举例,我们可以直接在编译时,通过宏为每个 IDL 生成 clint 的代码,这样就不需要用户去手动调用一些 main 去生成。

其次,更高效的序列化。像 JSON 这种序列化,我们可以通过类似 Rust 宏的方式在编译时生成好序列化和反序列化所需要的代码,直接调用,这样就可以省掉反射的开销。

图片

关于宏带来的案例,我们还在继续探索中,之后我们会基于宏做一些更好更方便的尝试。这也是我们对于宏的规划。但是不得不提到的是,宏的出现会对 Go 本身有一定的影响,因此可能只会通过注释的方式去提供,保证对 Go 语法的兼容性;并且只会在 JSON 等序列化这些地方进行一些替换,保证用户的开发体验不会受到影响。

开源

ROG 未来肯定会进行开源,并且贡献到社区。目前我们的想法是 2024 年先在公司内部完成一些业务的试用,能够稳定地上生态环境,并且能够取得一定的收益。在这些都稳定并且处理好 Go 本身大部分特性问题之后,才会将其开源。因此如果顺利,最早可能会需要等到 2025 年的第二季度才会去准备开源工作。欢迎大家保持关注~

-END-

项目地址

GitHub:github.com/cloudwego

官网:www.cloudwego.io

三周年演讲 PPT 下载链接:github.com/cloudwego/c…

NDSS 2025|Prompt泄露风险:抖音集团安全研究团队揭露多租户KV缓存共享漏洞

抖音集团安全研究团队和南方科技大学可信系统安全实验室合作的研究论文揭示了大语言模型安全领域服务框架的侧信道漏洞,利用多租户场景下的KV缓存共享机制精确恢复了用户提示词。本工作成果《I Know Wha

豆包视觉理解模型正式发布,一元钱可处理近300张高清图片

今天,在火山引擎 Force 大会上, 字节跳动正式发布豆包视觉理解模型 ,为企业提供极具性价比的多模态大模型能力。豆包视觉理解模型千tokens输入价格仅为3厘,一元钱就可处理284张720P的图片

今晚7点开播!豆包大模型团队 NeurIPS 2024 中选论文精讲

NeurIPS 2024 刚刚在温哥华落幕,大会汇聚了全球顶尖 AI 研究成果,字节跳动豆包大模型团队超 20 篇论文中选,在多个前沿领域成果亮眼。 今晚 7 点,豆包大模型视觉基础研究负责人冯佳时、
❌
❌