作为移动开发你不能不了解的编译流程

作者:京东零售 李臣臣

阅读本文,或许能够了解关于以下的几个问题: 1、编译器是什么?为什么会有编译器这样一个东西? 2、编译器做了哪些工作?整个编译过程又是什么? 3、Apple的编译器发展历程以及为什么会抛弃GCC换成自研的LLVM? 4、从编译器角度看Swift与OC能够实现混编的底层逻辑

一、找个翻译官,说点计算机能懂的语言

说点常识,众所周知,作为开发者我们能看懂这样的代码:

int a = 10;
int b = 20;
int c = a + b;

而对于计算机貌似只能明白这样的内容:

注:使用 od -tx1 /tmp/binary.bin 可以根据需要输出二进制、八进制或者十六进制的内容

这样看的话,计算机除了知道1与0的含义,其他的字符内容完全不知道。为了去给计算机下达我们需要的指令,我们又不得不得按照计算机能够懂得语言与其进行通信交流,怎么办呢?我们貌似需要找一个翻译,将我们的想要下达的指令内容交给翻译让其成计算机能够识别的指令进行内容传达,这样计算机就能通过翻译来一步步执行我们的指令动作了,那这个翻译其实就是我们经常说到的编译器。

说到编译器呢?它的历史还是很悠久的,早期的计算机软件都是用汇编语言直接编写的,这种状况持续了数年。当人们发现为不同类型的中央处理器CPU编写可重用软件的开销要明显高于编写编译器时,人们发明了高级编程语言。简单说就是由于中央处理器CPU的差异,使得软件的开发成本很高,我们要针对不同的CPU编写不同的汇编代码,而且不同的CPU架构呢相对应的汇编的指令集也有差异。如果在汇编体系之上定义一套与汇编无关的编码语言,通过对通用的这样语言进行转换,将其转换成不同类型的CPU的汇编指令,是不是就能解决不同CPU架构适配的问题呢?那其中的定义的通用编码语言就是我们所说的高级语言,比如C/C++、Object-C、Swift、Java等等,而其中的汇编翻译转换工作呢则交由具体的编译器进行实现。

二、说到编译器当然少不了Apple

对于Apple的编译器,就不得不说一下GCC与LLVM的相爱相杀了。由于编译器涉及到从高级开发语言到低级语言的转换处理,复杂度自然不必多说。我们都知道Apple产品软件的开发语言是Objective-C,可以认为是对C语言的扩展。而C语言所使用的编译器则是大名鼎鼎的GCC,此时的GCC肯定是妥妥的大哥了,所以早些年为了不必要的资源投入,对于自家OC(Objective-C简称OC)编译器的开发索性直接拿大哥的代码GCC进行二次开发了,没错,从主干版本中拉个独立分支搞起。这么看的话,Apple早期就已经开始了降本增效了?

随着OC语言的不断迭代发展,语言特性也就愈来愈多,那编译器的新特性能力支持当然也得跟得上啊?但是C也在不断的迭代发展,GCC编译器的主干功能当然也越来越多,OMG!单独维护的OC编译器版本对GCC主干的新功能并没有很好的同步,关键在合并功能的时候不可避免的出现种种冲突。为此,Apple曾多次申请与GCC主干功能合并同步,GCC乍一看都是OC 特性feature,跟C有毛线关系?所以关于合并的优先级总是排到最低,Apple也是没有办法,结果只能是差异化的东西越来越多,编译器的维护成本也变得异常之高。

除了以上的问题之外,GCC整体的架构设计也是非模块化的,那什么是模块化呢?比如我们通常在系统设计的时候,会将各个系统的功能进行模块化分割设计,不同的模块能够单独为系统内部提供不同的功能。同时呢,我们还能把这些模块单独抽离出来提供给外部使用,这就增大了系统的底层的灵活度,简单说就是能够直接使用模块化的接口能力。

所以Apple深知定制化的GCC编译器将是后续语言迭代升级的绊脚石,内部也在不断的探索能够替代GCC的替代品。在编译器的探索路上,这里不得不说一下Apple的一位神级工程师 Chris Lattner(克里斯·拉特纳),可能光说名字的话可能没有太多人知道他,那如果要说Swift语言的创始人是不是就有所耳闻了?由于克里斯在大学期间对编译器的细致的研究,发起了LLVM(Low Level Virtual Machine)项目对编译的源代码进行了整体的优化。Apple将目光放在了克里斯团队身上,同时直接顾用了他们团队,当然克里斯也没有辜负众望,在 Xcode从 3.1实现了llvm-gcc compiler,到 3.2实现了Clang 1.0, 再到4.0实现了Clang 2.0 ,后来在Mac OS X 10.6 开始使用LLVM的编译技术,到现在已经将LLVM发展成为了Apple的核心编译器。

三、LLVM编译器的编译过程与特点

对于传统的编译器,主要分为前端、优化器和后端,引用一张通用的简洁的编译过程图,如下:

简单来说,针对于源代码翻译成计算机底层代码的过程中呢要经历三个阶段:前端编译、优化器优化、后端编译。通过前端编译之后,针对编译的产物进行优化处理,最后通过后端完成机器码的生成。而对于LLVM编译器来说,这里我们以OC的前端编译器Clang为例,它负责LLVM的前端的整体编译流程(预处理、词法分析、语法分析和语义分析),生成中间产物LLVMIR,最后由后端进行架构处理生成目标代码,如下图:

可以看出LLVM将编译的前后端独立分开了,前端负责不同语言的编译操作,如果增加一个语言的编译支持,只需要扩展支持当前语言的前端编译支持(Clang负责OC前端编译、SwiftC负责Swift前端编译)即可,优化器与后端编译器整体均不用修改即可完成新增语言的支持。同理,对于后端,如果需要新增新的架构设备的支持,只需要扩展后端架构对应编译器的支持即可完成新架构设备的支持,这也是LLVM编译器的优点之一。

3.1、编译器前端

在XCode中针对于OC与Swift的编译有着不同的前端编译器,OC采用Clang进行编译,而Swift则采用SwiftC编译器,两种不同的编译器前端在编译之后,生成的中间产物都是LLVMIR。这也就解释了对于高级语言Swift或者OC开发,哪怕是混编,在经过各自的编译器前端编译之后,最终的编译产物都是一样的,所以选用哪种开发语言对于最终生成的中间代码IR都是通用的。对于Clang的整体编译过程,如下图所示:

预处理

通过对源代码中以“#”号开头如包含#include,宏定义制定#define等扫描。然后进行源代码定义替换,进行头文件内容的展开。通过预处理器把源文件处理成.i文件。

词法分析

在词法分析完成之后会生成 token 产物,它是做什么的?这里不贴官方的解释了,简单点说就是对源代码的原子切分,切分成能够底层描述的单个原子,就是所谓的token,至于token长什么样子?可以通过 clang 的命令执行编译查看生成的原子内容:

clang -fmodules -E -Xclang -dump-tokens xxx.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        int a = 0;
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}


我们拿工程的main.m 做个测试,编译生成的内容如下:

注:如果遇到 main.m:8:9: fatal error: ‘UIKit/UIKit.h’ file not found 错误,可以加上系统基础库路径如下:

clang \
-fmodules \
-E \
-Xclang \
-dump-tokens  \
-isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk  \
main.m 

可以发现,计算机在进行源码处理的时候,并不能像人一样能够理解整个源码内容的含义。所以为了进行转换,在进行源码分析的时候,将整体的内容进行单词切分,形成原子为后续的语义分析做准备,整体的切分过程大致采用的是状态机原理。

语法分析

在完成词法分析之后,编译器大致理解了每个源码中的单词的意思,但是对于单词组合起来的语句内容并不能理解。所以接下来需要对单词组合起来的内容进行识别,也就是我们所说的**语法分析**。 语法分析的原理有点模板匹配的意思,怎么理解呢?就是我们常说的语法规则,在编译器中预置了相关语言的语法规则模板,如果匹配了相关的规则,则按照相关语法规则进行解析。举个例子,比如我们在OC中写一个这样的语句:

int a = 100;

这是一种通用的赋值语法格式,所以在编译器进行语法分析的时候,将其按照赋值语法的规则进行解析,如下:

通过对原子token的组合解析,最终会生成了一个抽象语法树(AST),AST抽象语法树将源代码转换成树状的数据结构,它描述了源代码的内容含义以及内容结构,它的生成能够让计算机更好的理解和处理中间产物。以XCode生成的默认项目的main.m内容为例,在 clang 中我们依旧可以查看具体的抽象生成树(AST)的样子,可以对源码进行如下的编译:

clang \
-isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-fmodules \
-fsyntax-only \
-Xclang \
-ast-dump \
main.m

编译后的结果如下:

简单转换一下树形视图,大致长这样:

可以发现,经历过语法分析之后,源代码转换成了具体的数据结构,而数据结构的整体生成是后续进行语义分析生成中间代码的基础前提。

语义分析

在经历过语法分析之后,编译器会对语法分析之后生成的抽象语法树(AST)再次进行处理,需要注意的是编译器并不会直接通过AST编译成目标代码,主要原因是因为编译器将编译过程拆分了前后端,而前后端的通信的媒介就是IR,没错就是之前提到过的LLVMIR这样一个中间产物。该中间产物与语言无关,同时与cpu的架构也无关,那么为什么要加上中间产物这个环节,直接生成目标代码难道不是更好吗?我们都知道cpu的不同架构直接影响cpu的指令集,不同的指令集对应不同的汇编指令,所以针对于不同的cpu架构要对应生成不同适配的汇编指令才能正常的运行到不同的cpu架构的机器上。如果将前后端的编译过程绑定死,那么就会导致每增加一个新的编译前端,同时增加对所有cpu架构的后端的支持(1对n的关系),同理,如果增加新的一个cpu架构支持,编译前端也需要通通再实现一遍,这个工作量是很重复以及繁琐的。所以为了避免这样的问题,Apple对编译器的前后端进行了拆分,用中间产物来进行前后端的逻辑适配。

对于语义分析生成中间产物的过程,也可以通过 Clang 的编译命令查看,具体如下:

# 生成扩展为.ll的便于阅读的文本格式
clang \
-isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-S \
-emit-llvm \
main.m \
-o \
main.ll

# 生成二进制格式,扩展为.bc
clang \
-isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-emit-llvm \
-c \
main.m \
-o \
main.bc

编译后生成的内容如下:

; ModuleID = 'main.m'
source_filename = "main.m"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-ios16.2.0-simulator"

%0 = type opaque
%struct._class_t = type { %struct._class_t*, %struct._class_t*, %struct._objc_cache*, i8* (i8*, i8*)**, %struct._class_ro_t* }
%struct._objc_cache = type opaque
%struct._class_ro_t = type { i32, i32, i32, i8*, i8*, %struct.__method_list_t*, %struct._objc_protocol_list*, %struct._ivar_list_t*, i8*, %struct._prop_list_t* }
%struct.__method_list_t = type { i32, i32, [0 x %struct._objc_method] }
%struct._objc_method = type { i8*, i8*, i8* }
%struct._objc_protocol_list = type { i64, [0 x %struct._protocol_t*] }
%struct._protocol_t = type { i8*, i8*, %struct._objc_protocol_list*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct._prop_list_t*, i32, i32, i8**, i8*, %struct._prop_list_t* }
%struct._ivar_list_t = type { i32, i32, [0 x %struct._ivar_t] }
%struct._ivar_t = type { i64*, i8*, i8*, i32, i32 }
%struct._prop_list_t = type { i32, i32, [0 x %struct._prop_t] }
%struct._prop_t = type { i8*, i8* }

@"OBJC_CLASS_$_AppDelegate" = external global %struct._class_t
@"OBJC_CLASSLIST_REFERENCES_$_" = internal global %struct._class_t* @"OBJC_CLASS_$_AppDelegate", section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8
@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (%struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_" to i8*)], section "llvm.metadata"

; Function Attrs: noinline optnone ssp uwtable
define i32 @main(i32 %0, i8** %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca %0*, align 8
  %7 = alloca i32, align 4
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %8 = call i8* @llvm.objc.autoreleasePoolPush() #1
  %9 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
  %10 = bitcast %struct._class_t* %9 to i8*
  %11 = call i8* @objc_opt_class(i8* %10)
  %12 = call %0* @NSStringFromClass(i8* %11)
  store %0* %12, %0** %6, align 8
  store i32 0, i32* %7, align 4
  call void @llvm.objc.autoreleasePoolPop(i8* %8)
  %13 = load i32, i32* %4, align 4
  %14 = load i8**, i8*** %5, align 8
  %15 = load %0*, %0** %6, align 8
  %16 = call i32 @UIApplicationMain(i32 %13, i8** %14, %0* null, %0* %15)
  ret i32 %16
}

; Function Attrs: nounwind
declare i8* @llvm.objc.autoreleasePoolPush() #1

declare %0* @NSStringFromClass(i8*) #2

declare i8* @objc_opt_class(i8*)

; Function Attrs: nounwind
declare void @llvm.objc.autoreleasePoolPop(i8*) #1

declare i32 @UIApplicationMain(i32, i8**, %0*, %0*) #2

attributes #0 = { noinline optnone ssp uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+ssse3,+x87" "tune-cpu"="generic" }
attributes #1 = { nounwind }
attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+ssse3,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11}
!llvm.ident = !{!12}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 16, i32 2]}
!1 = !{i32 1, !"Objective-C Version", i32 2}
!2 = !{i32 1, !"Objective-C Image Info Version", i32 0}
!3 = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"}
!4 = !{i32 1, !"Objective-C Garbage Collection", i8 0}
!5 = !{i32 1, !"Objective-C Is Simulated", i32 32}
!6 = !{i32 1, !"Objective-C Class Properties", i32 64}
!7 = !{i32 1, !"Objective-C Enforce ClassRO Pointer Signing", i8 0}
!8 = !{i32 1, !"wchar_size", i32 4}
!9 = !{i32 7, !"PIC Level", i32 2}
!10 = !{i32 7, !"uwtable", i32 1}
!11 = !{i32 7, !"frame-pointer", i32 2}
!12 = !{!"Apple clang version 13.1.6 (clang-1316.0.21.2.5)"}

从编译的产物来看,其中也包含了常见的内存分配、所用到的标识定义等内容,可以明显的发现生成的中间产物已经没有任何源代码语言的影子了。同时我们会发现针对于中间代码,寄存器(%+数字)的使用好像没有个数限制,为什么呢?因为中间代码只是将源代码进行了中间代码的描述转义,此时并没有相关的目标架构信息可供参考使用,所以针对于变量的引用也仅仅是中间层的标识。在后端编译的过程中会将中间的这些寄存器的引用再次进行指令的转换,最终会生成对应CPU架构指令集的汇编代码。

还记得XCode中的BitCode开关选项吗?它决定了编译生成的中间产物IR是否需要保存,如果保存的话,会把当前的中间产物插入到可执行文件的数据段中,保留这些中间产物内容又有什么作用呢?我们知道在没有保留中间产物之前,为了确保所有cpu架构的机型能够正常安装打出的安装包,在打包的时候会把能够支持的所有cpu架构的集合进行合并打包,生成一个Fat Binary,确保安装包能够适配所有的机型,这样会有一个问题,比如ARM64架构的机器在安装的时候只需要ARM64的架构二进制文件即可,但是由于安装包里兼容了所有的cpu架构,其他的架构代码实际上根本没有用到,这也就间接的导致了安装包的体积变大。而苹果在应用分发的时候,是知道目标机器的cpu架构的,所以如果能够将中间的编译产物交给AppStore后台,由Appstore后台通过编译后端优化生成目标机器的二进制可执行文件,去除无用的兼容架构代码,进而缩减安装包的体积大小。这也即是BitCode的出现目的,为了解决编译架构冗余的问题,同时也为APP的瘦身提供参考。

编译器在进行语义分析期间还有一个重要的过程叫做静态分析(Static Analysis),llvm官方文档是这样介绍静态分析的:

The term “static analysis” is conflated, but here we use it to mean a collection of algorithms and techniques used to analyze source code in order to automatically find bugs. The idea is similar in spirit to compiler warnings (which can be useful for finding coding errors) but to take that idea a step further and find bugs that are traditionally found using run-time debugging techniques such as testing.↳

Static analysis bug-finding tools have evolved over the last several decades from basic syntactic checkers to those that find deep bugs by reasoning about the semantics of code. The goal of the Clang Static Analyzer is to provide a industrial-quality static analysis framework for analyzing C, C++, and Objective-C programs that is freely available, extensible, and has a high quality of implementation.

静态分析它能够帮助我们在编译期间自动查找错误,比起运行时的时候去找出错误要更早一步,可以用于分析 C、C++ 和 Objective-C 程序。编译器通过静态分析依据AST中节点与节点之间的关系,找出有问题的节点并抛出警告错误,达到修改提醒的目的。比如官方文档中介绍的内存泄露的静态分析的案例:

除了官方的静态分析,我们常用的OCLint也是在编译器生成AST抽象语法树之后,对抽象语法树进行遍历分析,达到校验规范的目的,总结一下编译前端的所经历的流程:通过源码输入,对源码进行词法分析将源码进行内容切割生成原子token。通过语法分析对原子token的组合进行语法模板匹配,生成抽象语法树(AST)。通过语义分析,对抽象语法树进行遍历生成中间代码IR与符号表信息内容。

3.2、编译器后端

编译器后端主要做了两件重要的事情: 1、优化中间层代码LLVMIR(经历多次的Pass操作) 2、生成汇编代码,最终链接生成机器码

编译器前端完成编译后,生成了相关的编译产物LLVMIR,LLVMIR会经过优化器进行优化,优化的过程会经历一个又一个的Pass操作,什么是Pass呢?引用官方的解释:

The LLVM Pass Framework is an important part of the LLVM system, because LLVM passes are where most of the interesting parts of the compiler exist. Passes perform the transformations and optimizations that make up the compiler, they build the analysis results that are used by these transformations, and they are, above all, a structuring technique for compiler code.

我们可以理解为一个个的中间过程的优化,比如指令选择、指令调度、寄存器的分配等,输入输出也都是IR,如下图:

在最终优化完成之后,会生成一张DAG图给到后端。我们知道DAG是一张有向的非环图,这个特性可以用来标识硬件的特定顺序,方便后端的内容处理。我们也可以根据自己的需要通过继承Pass来写一些自定义的Pass用于自定义的优化,官方对于自定义的Pass也有相关的说明,感兴趣的同学可以去看看(链接放在本文最后了)。在经过优化之后,后端依据不同架构的编译器生成对应的汇编代码,最终通过链接完成机器码的整体生成。

四、编译器让计算机更懂人类

可以发现编译器是计算机高级语言的中梁砥柱,现在随着高级语言的发展越来越迅速,向着简单高效灵活的方向不断前进,这里面与编译器的发展有着密切的联系。同时随着编译器的发展升级,让高级语言到低级语言的转换变得更高效,同时也为诸多的跨平台语言实现提供了诸多可能。通过对计算机底层语言的层层抽象,诞生了我们所熟知的计算机高级语言,让我们能够用人类的思维逻辑进行指令输入,而抽象的层层翻译处理则交给了编译器,它的存在建立了人类与计算机沟通的重要桥梁。

参考:

The Architecture of Open Source Applications: LLVM (aosabook.org)

LLVM Language Reference Manual — LLVM 17.0.0git documentation

相关推荐

GPT-6凌晨上线智能UI:智能UI时代的技术架构重构与应对之道

从命令行到图形界面,计算机行业用了三十年证明了同一件事:人类需要视觉的、空间的、可重复的交互表面,纯文字从来只是过渡形态。智能界面的出现,意味着AI行业第一次大规模承认了这个规律——对话是创造的接口,界面是使用的归宿。生成的界面如何稳定下来?个人的数据与身份如何安全地沉淀?Agent的行动能力如何可信地放大?谁能把这三个问题答好,谁就站在了Personal Agent图形界面时刻的浪潮之巅。

京东科技开发者 199

AI 会写代码之后,交付为什么还没变快?

AI 写代码,只是这场变革的起点,而不是终点。真正决定一个组织能否吃到 AI 红利的,不是"能不能用 AI 写出代码",而是敢不敢用 AI 重新设计交付流程、重新划分角色分工、重新组织团队结构。研发视角能把工具和个人推到极致,却会在组织的边界前止步;只有站上组织视角,把AI × 工具一路推进到AI × 组织,那些被 AI 照出来的摩擦,才会真正被抹平。国际技术部 Multi AI Native 想做的,正是这件事——让 Agent 成为你可靠的数字战友,让每一次迭代交付都快一个量级;

京东科技开发者 296

OxygenData-Vibe搭建:用 AI Coding重新定义数据看板搭建

长期以来,数据看板开发主要有两种方式:一种是使用标准化 BI 工具,交付快、成本低,但图表样式、交互体验和功能扩展都受限于 BI 本身的能力;另一种是自研定制开发,能够充分满足复杂和个性化需求,但需要投入大量研发资源,交付周期也更长。比如一个通过 BI 只需要 1 天就能搭建出来的看板,如果采用自研方式,可能需要投入 30 PD 的研发人力。既然 BI 交付这么快,为什么不把所有看板需求都交给 BI?原因在于,业务某些场景需要的不只是把数据展示出来。

京东科技开发者 354

当我谈数据库高可用时我谈些什么

大家都经常听到说数据库的双1,那双1指的是什么呢?参数=1(最安全)≠1(更快但可能丢)每次提交都把 redo 刷盘0/2:攒着刷,崩溃丢最近 1 秒每次提交都把 binlog 刷盘0/N:攒着刷,崩溃丢最近若干条"双 1"(都设为 1)= 单机不丢已提交事务的前提。生产核心库必须双 1;代价是每次提交两次 fsync。小结:单机层用 WAL 提速、用 2PC 保证 redo/binlog 一致、用崩溃恢复裁决半成品事务、用双 1 兜底持久性。

京东科技开发者 306

ScoAI:面向供应链的运筹原生决策智能

ScoAI 关注的并非 AI 能否进入供应链,而是 AI 参与供应链决策之后,哪些内容适合通过数据学习,哪些关系必须显式计算,以及哪些结果需要依据执行记录进行验证。其基本分工是:供应链业务定义问题、资源与行动后果,运筹学表达目标、约束和协同关系,AI 提供可复用的估计与判断。真正用于改进后续决策的,不仅是模型生成的初始结果,还包括实际采取的行动及其产生的业务反馈。

京东科技开发者 395

拆解海博 AI-Native 落地保障:海博团队 AI 知识库能力建设

绕了一大圈,回头看,我们其实一直在解开头那三个结:AI 读不懂系统、影响面靠人脑记、测试预期没处查。做到这里,本质上是把"AI 用不好"这个问题,从"是不是该换个更强的模型"重新定义成了"怎么把上下文经营好"。顺着这个思路,我们攒下了这么几样东西:•。

京东科技开发者 545

从零构建一个生产级记忆型 AI Agent —— AgentScope 项目全景技术与学习指南

domain 层端口接口(domain)职责适配器实现(infra)对话流水账读写经验记忆检索/落库请求级幂等四态会话元数据 CRUD会话级 Skill 选择态Skill 元数据HITL 权限网关(domain 内含逻辑,见第四篇)StreamSink技术中立的流式下沉(client 层桥接)以幂等端口为例,头部注释明确写道:实现 domain 端口,持有实体不出 infra 边界,对外只暴露嵌套视图GateResult。

京东科技开发者 602

数据涅槃:Agentic 重塑搜推数据生产体系

当前大模型已经能够理解需求、生成代码和解释错误,但数据生产的难点并不只是“写出一段 SQL”。一个需求还要经过数据源确认、口径澄清、加工编排、资源规划、任务提交、调度运行、结果交付和故障处置。任何一步缺少都可能让“看起来正确”的任务变成生产事故。

京东科技开发者 364

81.8 秒的视频,我们改了 15 个版本:一次纯 Codex 驱动的 AI-native 视频实践

81.8 秒、15 个版本,这些数字真正想说明什么?视频制作正在从一组专业软件里的复杂操作,变成一场围绕目标、参考和反馈展开的持续对话。AI 已经可以接住从脚本到交付的大量工作。你可以把精力留给更重要的部分:理解观众、选择故事、判断好坏。过去停在 PPT 和脑海里的想法,现在有机会被快速做成一支能看、能改、也愿意转发的视频。如果明天就要介绍你手头的项目,你最想先把哪一步交给 AI?

京东科技开发者 458

Codex 驱动的移动端AI全栈开发:从 Relay 原型图到可交付链路

AI 编程工具降低了跨端开发门槛,但“让 AI 写页面”并不等于“完成移动端交付”。PC 运营端页面通常以功能闭环为中心,设计约束较弱,研发可以基于组件库快速搭建;移动端 C 端页面直接面对用户,Relay 原型图和 UI 设计稿不仅是视觉参考,更是交互、信息层级、状态反馈和业务信任感的综合契约。

京东科技开发者 584

Claude Code vs Codex 记忆体系深度对比

Claude Code —— 分层自动记忆系统四层分层架构(CLAUDE.md / Auto Memory / Session Memory / AutoDream)构成自优化闭环,系统主动捕获、分类、去冗并跨会话接力经验。走的是自动化优先路线——让机器替人记忆、替人整理,以少量可控性的不确定换取强大的经验积累能力。Codex —— 声明式契约系统两层扁平架构(AGENTS.md 静态声明层 + 会话上下文层),记忆完全由人显式订立、显式修改,系统只读契约、绝不擅自变更。走的是可控性优先。

京东科技开发者 617

上游给空、下游拿到 -1,我把故障一直追到了 commons-beanutils 的构造函数

这篇手记是一份事后复盘,写在我们已经把修复推上线、把测试用例跑绿、把约束文档沉淀进团队库之后。非研发同事,可以直接从第八章开始看,前七章都是讲排障和代码分析。故障本身不复杂,修复也不复杂,但这次排查真正让我想写下来的,不是技术本身——而是从问题提报到解决全程AI的作用:问题定位:业务方反馈了一个异常单号说「下发失败」,一线运维同事直接在日志系统捞到相关的系统日志,用IDE打开下游代码库,把异常信息给到JoyCode——AI 直接指向了问题是serialType值非法,系统只接受0和1,而当前上游给的是-1。

京东科技开发者 714

SKill测评:谁才是真正的Skill之王?

巧合的是,阿里近期开源了一款名为 Skill-Up 的测评工具——它专门面向 Agent Skill 开发者,支持在 Claude Code、Codex、Qoder CLI 等真实 Agent Engine 环境中验证 Skill 的功能正确性,并可将失败用例转化为精准的修复指引,同时支持本地与 CI 环境的持续回归。2026年,Skill 的热度不减,已成为 AI 辅助编程领域的标配。在日常研发中,无论是沉淀个人经验,还是将团队的优秀范本标准化,将知识“蒸馏”为 Skill 已是大家的常规操作。

京东科技开发者 633

让AI真正参与工作!JoyDesk已上线京东云

JoyDesk提供覆盖AI资产管理、权限控制、安全策略、审批审计、运营监控等企业级治理能力,支持按组织、角色和项目进行精细化权限管理,并支持私有化部署,确保企业数据边界清晰、AI行为全程可追溯、安全策略灵活可配置,满足金融、政企等行业对数据安全和合规治理的要求,让AI真正成为企业可信赖的生产力。JoyDesk支持MCP协议、标准API、技能等多种开放接入方式,可快速连接企业现有业务系统,将业务能力封装为AI可调用的技能和专家,让AI能够理解业务流程、调用业务系统、完成业务任务,而无需改变企业原有IT架构。

京东科技开发者 668

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手【最终版】

这个项目展示了AI Agent 在垂直内容创作领域端到端自动化:从内容理解到播客输出,全流程 AI 驱动多模态融合:文字、图片、音频、视频统一处理专业级音频质量:音色设计 + 智能混音,输出广播级品质低门槛使用:可视化配置 + 自然语言交互,无需技术背景。

京东科技开发者 559

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(高级篇)

这是一个 AI 驱动的播客创作助手,专注于从文字/视频/音频内容识别到播客制作的全流程。你可以用自然语言与它对话,也可以上传视频和音频,它会理解意图、调用相应的语音合成工具,把他们变成真实的播客音频。

京东科技开发者 726

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(进阶篇)

这是一个AI 驱动的音频内容创作助手。你可以用自然语言与它对话,它会理解意图、调用相应的语音合成工具,把文字变成真实的播客音频,并保存在本地。关于项目的详细介绍见第一篇文章:第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)这篇文章更多介绍进阶篇添加的功能。

京东科技开发者 549

【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)

这个项目让我第一次把前端、后端、AI Agent 三块内容独立串联起来。技术选型上每一块都是当下主流的方案,实际跑通整条链路之后对"全栈"有了更具体的感受。框架版本要锁定:LangChain 这类快速迭代的框架,版本差异带来的问题远比你想象的多先跑通链路,再追求完美:新手阶段能把用户输入 → AI 处理 → 结果展示这条链跑通,比深入某个技术点更有价值多看官方文档:相比博客和教程,官方文档更新及时,特别是 LangGraph 这种新框架后续计划继续迭代,欢迎交流!

京东科技开发者 607

ICLR 2026 | 面向序列决策的贝叶斯集成方法

在这篇工作中,我们关注的是。

京东科技开发者 295

ICML 2026|NaviAgent:面向 Oxygen 智能体的可扩展工具编排

大型语言模型(LLM)越来越多地作为函数调用代理,调用外部工具来处理超出其静态知识范围的任务。然而,它们通常一次只调用一个工具,缺乏对任务结构的全局视图。由于工具之间往往相互依赖,这会导致错误累积和可扩展性差,尤其是在扩展到数百或数千个工具时。为了解决这些局限性,我们提出了NaviAgent,一种显式的双层架构,它通过基于图的工具关系建模将任务规划与工具执行解耦。在规划层,基于 LLM 的代理决定是直接响应、澄清意图,还是检索并执行一个独立于工具间复杂性的工具链。

京东科技开发者 388

实测 9 款 AI 架构图工具:从 Mermaid 美化到 GPT-Image2,一份选型清单

支持 5 类图(架构图、工作流图、时序图、数据流图、生命周期/状态机图),主题 4 种(Classic、Signal Flow、Blueprint、Editorial)+ 3 种色彩模式(Dark / Light / System Auto),输出格式含 HTML、PNG、SVG、WebM、Share Card PNG、JSON IR。它把提示词图库、图像生成案例、参考图编辑流程和命令行工具整合在一起,让 AI Agent 能直接调用 GPT Image 2 模型完成文生图、图生图、参考图编辑等任务。

京东科技开发者 903

别再守着 Claude Code 了——学会指挥它自主干活

回到开头那句:别再一句一句地喂它、然后守着屏幕。真正的用法是——把一件事想清楚、划好边界、给它一个能自我验证的目标,然后交出去。你会发现,省下来的时间不是一点半点。把重复的判断和执行还给机器,把真正需要你的判断留给自己。

京东科技开发者 585

CVPR 2026|Oxygen XR 团队在原生鱼眼高斯泼溅技术上带来新突破

京东零售技术团队联合清华大学提出DirectFisheye-GS框架,创新性地将鱼眼相机模型原生嵌入3D高斯泼溅管线,通过跨视角联合优化策略直接处理鱼眼图像,避免了传统去畸变预处理导致的信息损失。该方案在多个公开数据集上达到SOTA性能,有效解决了单视角优化引起的几何畸变和视角不一致问题,同时保持与现有3DGS工具链的兼容性。技术已应用于电商3D重建和4D体积视频等场景,显著提升了沉浸式体验。相关论文被CVPR 2026接收,为宽视场场景重建提供了高效解决方案。

京东科技开发者 354

拆解海博 AI-Native 落地保障:Harness、双 Loop、知识库与技能自主迭代实践

海博研发RoadMap 的落地,彻底终结了 AI 编程“对话式玄学”的旧模式,真正落地了AI Native 研发工程化。它证明 AI 规模化赋能研发的核心,不在于堆砌大模型能力,而在于用工程思维重构 AI 的工作范式:用结构化流程替代随机对话,用固化规则约束模型行为,用持续沉淀实现能力复利,用分层架构支撑规模化落地。

京东科技开发者 638

让 AI 快速「读懂」你的代码仓:Joy-Code-Graph 云端图谱服务的三次进化

代码知识图谱不是要取代 AI 的智能,而是要补齐它对代码全局关系的认知盲区。当 AI 能一眼看清「谁调用了谁、改动会波及哪里」,它写出的代码才真正靠谱;当这份认知安全地留在自己手里、随时被 Joygen 调用、并且永久沉淀在代码仓里——AI 编程的体验,才算真正上了一个台阶。这,就是我们做 Joy-Code-Graph 云端服务的初衷。能力共享,数据自主;一次建图,长期受用。

京东科技开发者 751

【Coding生态】从代码托管到 AI 能力底座:与Coding一起共建 AI 研发生态

前两种框架能够很好地支撑对话式能力和预设流程式能力。它们适合基于提示词或预设工作流完成研发问答、知识查询、流程编排等任务。但在更复杂的研发场景中,仅仅“回答问题”还不够。当用户业务逻辑复杂时,一旦场景进入真实落地阶段,例如下载代码、修改文件、执行命令、安装依赖、运行测试、读取完整代码文件,或者与自有系统联动、将结果渲染到 Coding 页面、推送到京 ME,AI 应用需要的不只是知识能力,还需要真实的代码库操作能力和独立可执行环境。

京东科技开发者 768

GPT-5.6、ChatGPT Work 与 Codex 更新食用指南

三档模型当前都提供 1,050,000 tokens 上下文、922,000 tokens 最大输入和 128,000 tokens 最大输出,接受文本与图像输入、输出文本,并支持 web search、file search、Computer Use、MCP、Tool Search、Skills、Apply Patch 等工具。Work 可以读取你提供的文件,通过插件连接 Slack、Google Drive、SharePoint、邮箱、日历、CRM 或项目系统,调用获批工具,并在执行中展示进度。

京东科技开发者 865

把业务流程沉淀成高质量 Skill 的实践路径

使用Record & Replay创建的Skill,尽管创建流程简单,但它的本质实际是让 AI 去操作页面,这不但有可能影响我们对电脑的操作,还有可能无法稳定复现。创建Skill时,可以使用 Codex 的 Record & Replay 插件,把一次手工操作录下来,再让 AI 生成可复用流程。刚开始切换时,很容易遇到接口报错,可以让Codex排查原因进行优化,直到可以稳定执行;补充相应业务背景,说明这件事为什么要做,谁会用,输入从哪来,输出给谁看。电脑休眠、关机、软件没开时,本地任务可能无法执行。

京东科技开发者 835

政企前台AI-Agent的架构实践:打造可复用的 AI 服务平台-【灯塔计划】

不止是爆火的龙虾,还是各种Skills,MCP、Agent,脱离的具体的场景谈方案,最后只会是一场空。这一套可复用的 AI 服务平台,截止目前,通过前后端对接规范文档,多项能力已快速接入到各个场域的不同端平台,大幅降低研发成本, 并且各业务线新场景的接入从原来的 “开发驱动” 转为 “配置驱动”,接入周期从天维度缩短至小时级。

京东科技开发者 863

京东 Oxygen xLLM 大模型推理引擎正式捐赠开放原子开源基金会,共建国产 AI Infra 生态

下一阶段 AI 基础设施的核心命题,不是简单的算力堆叠,而是让工程本身变得智能——让调度系统能自主感知负载特征并动态优化,让推理引擎能根据模型结构和硬件特性自动生成最优执行方案,让整个 AI 工程链路具备自感知、自决策、自优化的能力。未来,Oxygen xLLM 的项目官网、社区沟通渠道及微信社区将陆续向开发者开放,诚邀芯片厂商、模型厂商、云厂商、企业用户及个人开发者共同参与,让国产大模型推理引擎在开放生态中加速成长。硬件层,原生支持 GPU / NPU / MLU 多类 AI 芯片,深度适配国产芯片。

京东科技开发者 1004
上一篇: 在京东如何做好前端系统的可观测性
下一篇: 混合多云第一课——多云多活为何被称为“技术皇冠上的明珠”
京东云开发者
京东云开发者 企业官方账号 企业官方账号
博客等级 码龄8年 1万+粉丝 2140原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值