一句话定义:DAP(Debug Adapter Protocol,调试适配器协议)是编辑器和各种语言的调试器之间的一套通用对话格式,它把“下断点、单步执行、查看变量”变成标准化的请求与事件。
它解决的是乘法问题
没有它的时候,一个编辑器要支持 Python、Java、Go、C++ 的调试,就得为每个调试器写一套对接代码;反过来,每个调试器也要为每个编辑器适配一遍。N 个编辑器配 M 个调试器,就是 N×M 份工作。
DAP 插在中间:调试器一侧写一个 adapter(适配器),编辑器一侧实现一个客户端,双方只认这一套 JSON 消息,工作量从 N×M 降到 N+M。可以把它想成一副同声传译耳机:你(编辑器)只会说一种语言,对面(GDB、JDWP、debugpy、Node 的 inspector 等)各说各的方言,都由耳机翻译。
协议的消息只有三类:请求(request)、响应(response)、事件(event)。客户端发出 setBreakpoints 设置断点,程序跑到那里时,适配器主动推一个 stopped 事件;客户端再发 stackTrace 拿调用栈、发 variables 展开某个栈帧里的变量。传输层通常就是标准输入输出上的 JSON 消息,也可以走 socket。
和 LSP 的区别
两者常被一起提,因为思路同源,但负责的事情不同:
| LSP(语言服务器协议) | DAP(调试适配器协议) | |
|---|---|---|
| 负责 | 静态地理解代码 | 运行时观察程序 |
| 典型能力 | 补全、跳转定义、报错诊断 | 断点、单步、栈帧、变量值 |
| 使用时机 | 写代码时 | 程序跑起来之后 |
| 典型消息 | 请求补全 | setBreakpoints、stopped |
它也要和 GDB/MI、JDWP、Chrome DevTools Protocol 区分开:那些是某个具体调试器的原生接口,DAP 往往在它们外面包一层,做成统一接口。所以能力上限取决于底层调试器:连接建立时会通过 capabilities 声明支持哪些功能,客户端不能假设人人齐全。
对从业者的意义
编辑器插件作者受益最直接:实现一次调试界面,就能对接所有提供适配器的语言。语言工具作者也一样:写一个 adapter,就能被所有支持 DAP 的编辑器调用。
对做编码智能体的团队,这层协议的价值这几年才真正显出来。以往智能体调试基本靠“读日志、加打印、改代码、再跑一遍”,一轮轮试。接上 DAP 之后,它可以在可疑的行上下断点,让程序停住,把 variables 返回的结构读出来,看到底哪个字段是空的;再单步走进函数,用 evaluate 在当前进程上下文里求一个表达式。这比盲改代码省下很多轮往返。要注意的坑是:管好会话生命周期(启动、附加、终止),按 capabilities 做降级处理,别把调试端口随随便便暴露出去。
普通职场人不必记住这个缩写,只需要知道:换个编辑器、换门语言,调试体验还能保持一致,靠的就是这样一层看不见的翻译。
