Ghidra 调试器 Agent 开发实战:以 drgn 为例完整走通添加流程
本文基于 Ghidra 官方开发者课程文档《Adding a debugger》,系统讲解如何为 Ghidra 调试框架添加一个全新的调试器 Agent(代理)。文章以 Ghidra 仓库中真实存在并持续维护的 drgn Agent 为案例,从 launcher 脚本、对象模型 schema、构建逻辑到 Python 命令/方法/事件回调实现与集成测试,逐层拆解一个 Agent 的四大组成部分、关键调用链与常见陷阱,帮助读者掌握独立开发 Ghidra 调试器 Agent 的完整技术路径。
1. Ghidra 调试器 Agent 架构总览
Ghidra 调试子系统的设计目标是支持跨平台调试:它在 GUI(Java 侧)与各类本地原生调试器之间引入了 Agent 层——即“能够接收原生调试器信息并转交给 Ghidra GUI 的客户端”。当前仓库中内置的 Agent 包括:
- dbgeng:支持 Windows 调试器(kd/dbgeng 接口);
- gdb:支持各平台上的 gdb;
- lldb:面向 macOS 与 Linux;
- jpda:面向 Java 进程;
- drgn:本仓库中的“教学级”实例,对接 Meta 开源的 drgn 调试器——一个可对运行中的 Linux 内核、用户态进程以及 core dump 提供(只读)脚本化接口的调试工具。
除 jpda 外,这些 Agent 均用 Python 3 编写,并统一通过一套基于 protobuf 的协议与 GUI 通信,协议定义见 trace-rmi.proto。每个 Agent 在结构上都可以拆成四个部分(文档指出这是“有点武断的划分”,但足够作为心智模型):
| 组成部分 | 路径(以 drgn Agent 为例) | 作用 |
|---|---|---|
debugger-launchers |
debugger-launchers | 一组 .bat/.sh/.py 启动脚本,负责设置环境变量并拉起目标 |
schema.xml |
schema.xml | 对象模型 schema(注意:虽为 XML 文件,但不是 XML Schema) |
src/ghidradrgn |
src/ghidradrgn | Python 包:架构映射、命令、钩子、方法、工具函数 |
build.gradle |
build.gradle | Gradle 构建逻辑 |
文档给出了一条重要的工程策略:由于各 Agent 的大量代码相互相同或相似,复制一个现有 Agent 并重命名所有 agent 专属的变量、方法,是成本最低的起点。以 dbgeng 为模板起步通常意味着开发后期会留下大段需要清理的“残骸代码”。仓库佐证了这一点:drgn Agent 的 pyproject.toml 等文件均沿用标准 Agent 骨架,且 commands.py 中大量模式常量与 hooks 结构与 dbgeng 一脉相承。
2. 第一个 launcher:local-drgn.sh
2.1 为什么选 dbgeng 的启动模式而非 gdb/lldb 模式
编写 Agent 最难的一环是确定正确的初始启动模式(initial launch pattern)。gdb 与 lldb 虽然支持 Python 作为脚本语言,但其核心并非 Python:它们的 launcher 先启动原生调试器,再让调试器加载作为插件的 Agent。dbgeng Agent 则反转了这一模式——Agent 本身就是一个 Python 应用,通过 Pybag 包以 COM 方式访问原生 kd 接口。drgn 本身就是纯 Python 编写的,因此天然适合“Agent 主动驱动调试器”这一模式,这也是文档作者从最初复制 lldb Agent 改为基于 dbgeng 开发的原因。
dbgeng 的 debugger-launchers 目录里是 .bat 文件,每个 .bat 再去调用 data/support 下的 .py 文件。由于 drgn 是 Linux 专属调试器,需要把 .bat 转成 .sh,转换相当直接:行注释从 :: 改为 #,环境变量引用从 %VAR% 改为 $VAR。
drgn Agent 最终实现了三个 launcher,分别对应用户态、内核态与 core dump 场景:local-drgn.sh、kernel-drgn.sh 与 core-drgn.sh。其中 local-drgn.sh 是最典型的样例,其关键部分如下:
#@title drgn
#@desc <html><body width="300px">
#@desc <h3>Launch with <tt>drgn</tt></h3>
...
#@menu-group drgn
#@icon icon.debugger
#@help drgn#attach
#@depends Debugger-rmi-trace
#@env OPT_TARGET_PID:int=-1 "PID" "The target's process id"
export OPT_TARGET_KIND="user"
# sudo -E drgn -p "$OPT_TARGET_PID" ../support/local-drgn.py
# or 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'
drgn -p "$OPT_TARGET_PID" ../support/local-drgn.py
注意最后一条命令:它直接调用 drgn 命令行并显式传入 Python 入口脚本 local-drgn.py,而不是让 drgn 走默认的 run_interactive(该调用不会返回)。local-drgn.py 随后自己完成 Ghidra 专属初始化,最后才调用 run_interactive(cmd.prog),核心调用链清晰可查:
def main():
append_paths()
from ghidradrgn import commands as cmd
cmd.ghidra_trace_connect(address=os.getenv('GHIDRA_TRACE_RMI_ADDR'))
cmd.ghidra_trace_create(start_trace=True)
cmd.ghidra_trace_txstart()
cmd.ghidra_trace_put_all()
cmd.ghidra_trace_txcommit()
cmd.ghidra_trace_activate()
drgn.cli.run_interactive(cmd.prog)
这里 append_paths() 先把 Debugger-rmi-trace 模块的 data/support 与 Python 包路径加入 sys.path,再通过环境变量 GHIDRA_TRACE_RMI_ADDR 与 GUI 建立的 RMI 通道完成 connect → create → txstart → put_all → txcommit → activate 的握手序列。文档给出的开发建议是:先实现一个尽量调用 connect、create、start 三个方法的脚本,并让 create 初期只做最少的工作,这样才能先把 arch.py 与 util.py 的调试问题暴露出来。
2.2 launcher 元数据注释语法
.sh 脚本本身是标准类 nix Shell,但 launcher 还包含一段元数据头,用于填充 GUI 中的启动器菜单与选项对话框。完整注释集合为:
#!行:指定 Shell;- Ghidra 许可声明;
#@title:启动器名称;#@desc:HTML 描述,显示在启动对话框中;#@menu-group:启动器分组;#@icon:图标;#@help:帮助文件与锚点;- 若干
#@arg变量(通常只有一个,用于命名可执行镜像); #@args:其余参数(若适用,传递给用户态目标);- 若干
#@env变量:被 Python 代码引用的环境变量。
#@env 的语法形式为:
#@env <Name>:<Type>[!]=<DefaultValue> <Label> <Description>
其中 ! 若出现则表示该选项必填。drgn 的 local-drgn.sh 没有使用 @arg/@args,但 gdb Agent 的 launcher 中示例非常丰富,例如 local-gdb.sh 中同时用到了 #@enum、#@arg、#@args 与多条 #@env:
#@enum StartCmd:str run start starti
#@arg :file "Image" "The target binary executable image, empty for no target"
#@args "Arguments" "Command-line arguments to pass to the target"
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
#@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."
2.3 sudo 与环境变量透传的两个坑
drgn 的大多数目标需要 sudo 权限,这带来两个容易踩坑的问题:
- wheel 安装位置混乱:
sudo下很容易误把用户级site-packages与系统级site-packages混用; - 环境变量必须显式透传:
sudo默认会重置环境,因此必须加-E参数,否则前面#@env定义的环境变量到不了 root 环境(见local-drgn.sh中被注释掉的sudo -E drgn ...一行,以及ptrace_scope的替代设置echo 0 > /proc/sys/kernel/yama/ptrace_scope)。
此外,使用 sudo 时交互 Shell 中打印的第一条消息将是密码提示,Agent 的输出解析逻辑必须容忍这一点。
3. 对象模型 schema:schema.xml 的角色与细节
schema.xml 为 Ghidra 的 Model 视图提供基本结构,让 Ghidra 能识别并定位用于填充 GUI 的各类接口。例如:Memory 接口标识了包含 MemoryRegion 项的容器,MemoryRegion 提供的信息用于填充 Memory 视图。关键接口包括 Process、Thread、Frame、Register、MemoryRegion、Module、Section 等——这些接口是“内置”于 Ghidra 的,使 GUI 能识别哪些对象提供特定信息与命令。
入门的最省事做法是克隆 dbgeng 的 schema 再按需修改;虽然后期要大量清理,但 schema 错误往往微妙且难以定位,先跑通再回头收拾是更稳妥的路径。同时需要修改 MANIFEST.in(drgn 中为 MANIFEST.in)以反映 schema 路径。
从 drgn 实际生成的 schema.xml 可以看到文件结构(其头部注释标明该文件由 build_xml 自动生成):
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
<attribute name="Id" schema="STRING" fixed="yes"/>
<interface name="Process"/>
<interface name="Aggregate"/>
<interface name="ExecutionStateful"/>
<element schema="VOID"/>
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes"/>
<attribute name="Memory" schema="Memory" required="yes" fixed="yes"/>
<attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes"/>
...
</schema>
文档中列出的 schema 编写规则,均可在此文件中找到对应物:
- 接口名必须正确:Process、Thread、Frame、Module、Section、Memory 等元素的名字可以任意取,但
<interface>名必须与内置接口一致; canonical标记:如果某元素需要作为默认搜索流程的一部分被遍历,其容器必须标记canonical(如ProcessContainer、ThreadContainer、ModuleContainer);Aggregate接口:若属性(attributes)需要被遍历,其父对象应带有Aggregate接口;elements与attributes:每个条目可以包含按 key 排序的同类型elements与任意类型的attributes;element条目描述所有元素的统一 schema,属性 schema 可用命名attribute显式声明,也可以用匿名条目给出默认值(通常是<attribute schema="VOID">或<attribute schema="ANY">);hidden=yes:隐藏条目,效果不言自明;- 特殊属性名:
_display定义 Model 树中条目的可见 ID;ADDRESS与RANGEschema 的属性标记为“可导航(navigable)”(如MemoryRegion的Range属性、Module的Range属性)。
一个实用技巧:用鼠标悬停(hover)即可查看 Model 视图中任意元素的 schema,这对定位 schema 遍历错误非常有帮助。
4. 构建逻辑:build.gradle 与 pyproject.toml
build.gradle 基本可以从 dbgeng 克隆,只需相应修改 eclipse.project.name。drgn 的 build.gradle 印证了文档的说法——主体只需要套用两个脚本:
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
apply from: "$rootProject.projectDir/gradle/hasPythonPackage.gradle"
apply plugin: 'eclipse'
eclipse.project.name = 'Debug Debugger-agent-drgn'
dependencies {
// Only for Help :/
api project(':Debugger-rmi-trace')
}
(此外它还 apply 了 javaProject.gradle 与 helpProject.gradle,后者纯粹因为“必须是一个 Help project”。)若需进一步定制,可参考 Ghidra 项目内的其他示例以及 Gradle 官方文档。
还有一个不直接属于构建逻辑但必须同步的项:pyproject.toml 中的版本号——按惯例使用 Ghidra 的版本号(见 pyproject.toml)。
5. Python 实现层:arch、util、commands、methods、hooks
5.1 arch.py:架构映射是难点
arch.py 是好的起点,因为大量初始逻辑依赖它。其“难就难在知道什么映射到什么”:
language_map把调试器自报的架构转换为 Ghidra 的语言集;- Ghidra 的语言再映射到“语言→编译器”映射表,进而把调试器自报的语言映射到 Ghidra 编译器;
- 某些组合不被允许,因为 Ghidra 对相应“语言-编译器”组合没有概念——例如 x86 语言永远不会映射到
default,所以必须存在x86_compiler_map并回退到其他编译器(drgn 中是gcc)。
对照 arch.py 源码可以完整看到上述设计:
# NOTE: This map is derived from the ldefs using a script
language_map: Dict[str, List[str]] = {
'AARCH64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'],
'ARM': ['ARM:BE:32:v8', 'ARM:BE:32:v8T', 'ARM:LE:32:v8', 'ARM:LE:32:v8T'],
...
'X86_64': ['x86:LE:64:default'],
'UNKNOWN': ['DATA:LE:64:default', 'DATA:LE:64:default'],
}
x86_compiler_map: Dict[Optional[str], str] = {
'Language.C': 'gcc',
}
compiler_map: Dict[str, Dict[Optional[str], str]] = {
'DATA:BE:64:': data64_compiler_map,
'DATA:LE:64:': data64_compiler_map,
'x86:LE:32:': x86_compiler_map,
'x86:LE:64:': x86_compiler_map,
...
}
compute_ghidra_language() 的选取逻辑也值得注意:先检查便利变量(auto 表示自动推导),然后取该架构的候选语言列表,按字节序过滤,并用“优先 :default 后缀、其次变体 ID 最短”的启发式排序选出第一个;若无匹配则回退到 DATA 语言。compute_ghidra_compiler() 则按前缀匹配 compiler_map 的语言键,最后对 x86 特判回退到 gcc。
5.2 util.py:版本信息与“当前选中”状态
arch.py 之后应尽早给 util.py 打一个初稿,因为版本信息在启动早期就会被用到。与当前项目无关的代码可以先不管,但至少要实现(或先伪造)selected_process、selected_thread、selected_frame 等方法。drgn 场景下通常只有一个 session、一个进程,最终还要决定 schema 里是否保留 Session。文档记录的初始做法是:session 与 process 默认 0,thread 默认 1(因为 0 在内核调试中是非法值);后来发现用户态调试用“attach 的 pid 与 prog.main_thread().tid”更合理,崩溃转储调试则用 prog.crashed_thread().tid。util.py 中的实现正是模块级全局变量配合 select_*/selected_* 访问器:
selected_pid = -1
selected_tid = -1
selected_level = -1
def selected_process() -> int:
return selected_pid
def select_thread(id: int) -> int:
global selected_tid
selected_tid = id
return selected_tid
5.3 commands.py:从树根向下实现 put 命令
当 arch.py 与 util.py 达到初稿水准后,通常从 Model 视图树根开始,向下逐层实现 commands.py 中各对象的 put 方法。Session 与 Process 定义较模糊(drgn 中各留一个即可),一般先做 Threads。
对调试器 API 中的每个迭代器,通常要实现两条命令:
- 一个内部方法做实际工作,如
put_threads();它由其他 Python 代码调用,调用方负责建立事务; - 一个可调用方法,如
ghidra_trace_put_threads(),把内部方法包裹进(可批量的)事务中;带ghidra_trace前缀的方法属于用户可自定义 CLI 命令集的一部分,因此必须自行建立事务。
内部方法的典型流程是:用容器 pattern、单条 key pattern 与组合 pattern(如 THREADS_PATTERN、THREAD_KEY_PATTERN、THREAD_PATTERN)拼出对象路径 → 从路径创建对应调试器对象的 trace 对象 → 插入 trace 数据库。pattern 由更底层的 pattern 逐级拼装,最终回到树根。drgn 的 commands.py 中可以看到这种拼装方式:
PROCESS_PATTERN = PROCESSES_PATH + PROCESS_KEY_PATTERN
THREADS_PATTERN = PROCESS_PATTERN + '.Threads'
THREAD_KEY_PATTERN = '[{tnum}]'
THREAD_PATTERN = THREADS_PATTERN + THREAD_KEY_PATTERN
测试通过后,就可以用 set_value 给基对象追加属性;非原子的属性则采用 create-populate-insert 三步:用 create_object 在路径扩展处创建对象、填充其子节点、再 insert。若填充子节点代价很高(很常见),可以选择延迟填充——先建一个占位节点、按需再生成,代价是要为这些节点额外编写 refresh 方法。
这里文档专门澄清了 create_object 与 insert 的语义区别,值得原文保留:trace 中的对象维护在一棵目录树里,允许链接(与反向链接),其可视化体现就是 Model 视图;树上操作遵循常规图操作语义。create_object 只创建节点而不创建任何边——连隐含的“父到子规范(canonical)边”也不创建;insert 才创建这条规范边。在那条边存在之前,对象不被视为“存活”,因此这条边的生命周期实际上编码了对象的生命周期。遵循 create-populate-insert 模式可以最小化需要处理的事件数量。
5.4 methods.py:refresh 方法与焦点语义
完成一条命令后有两个推进方向:继续实现树中其他对象的命令,或为已完成对象实现 methods.py 中对应的 refresh 方法。methods.py 同样依赖 pattern(通常通过 find_x_by_pattern 系列方法)来把路径匹配到 trace 对象;refresh 方法是否依赖 find_by 系列方法,取决于匹配命令是否需要参数——例如可以假设 selected_thread 与视图中的当前对象一致并借此定位节点;若 trace 对象能轻易对应到调试器对象,则可以强制方法匹配该节点;也可以反过来用该节点去设置 selected_thread。
焦点(focus)语义是调试器中一个复杂且极易混淆的概念,文档的界定值得逐字掌握:
- selected(选中):表示 GUI 当前的焦点,通常是用户在 Model 或关联视图中选中的节点,代表用户关心的进程/线程/帧。它还可能与*高亮(highlighted)*节点不同——单击产生高亮,双击才设置选中;
- current(当前):原生调试器自己对焦点的认知。这一概念还被 event 对象(如调试器断在哪个线程)与 current 对象(如正在检视哪个线程)的区分进一步复杂化;
- 数据流向:
current值从原生调试器向上推(push up)到 Ghidra GUI;selected值从 Ghidra 向下推(push down)到原生调试器。尽可能同步这两类值:新的 selected 应促使改变 current 对象集合,current 变化的事件也应改变 GUI 的 selected 对象集合——当然要注意避免形成往返循环。
refresh 方法(及其他方法)常带多种注解:
@REGISTRY.method:把方法暴露给 GUI,指定将执行的action与 GUI 弹出菜单中的display名称。Actions 可以是纯描述性的,也可以对应 GUI 的内置动作,如refresh以及step_into等控制方法;sch.Schema(惯例标注在第一个参数上):指明方法适用的节点;ParamDesc:描述参数类型与弹窗对话框中的标签。
取回所需参数后,refresh 方法把 commands.py 中的方法包裹在事务中调用。
drgn 的具体实现顺序是:线程、帧、寄存器(putreg)、局部变量,然后模块与段、内存与区域、环境,最后是进程;并用 drgn 的 read API 实现了 putmem。Symbols 也曾是候选,但整体填充符号代价过高,于是改为提供 retrieve_symbols 支持按 pattern 逐个添加符号——遗憾的是 drgn API 不支持通配符,文档作者也指出最终需要换一种策略。
5.5 hooks.py:事件回调与推送式架构
hooks.py 是原生调试器发出各类事件时的回调集合。drgn 目前没有事件系统,所以 hooks.py 中保留的是一组骨架方法——一方面可以把 GUI 的单步按钮当作“更新状态”的替代手段,另一方面 drgn 社区正在讨论实现更多控制功能。对真正实现 hooks.py 的开发者,挑战集中在事件循环上,尤其是需要在调试器与 repl 之间来回切换的场景;还要区分等待事件的控制命令与依赖回调但立即完成的命令。
经验法则是:向 Ghidra 推送(push)——Ghidra 异步发出请求,Agent 负责更新 trace 数据库。drgn 的 hooks.py 中 ProcessState.record() 就是一个标准的“停止时增量刷新”实现:首次停止时全量 put_processes()、put_environment(),之后按 threads/regions/modules 等脏标志位增量刷新,并对访问过的线程与帧去重(visited 集合),每个钩子(on_stop、on_cont、on_thread_selected 等)都在 trace.open_tx(...) 事务中批量提交。install_hooks()/remove_hooks() 则只是一对以 HOOK_STATE.installed 布尔量守卫的占位实现——这正是文档所说“骨架方法”的体现。
5.6 回头修订 schema
进入实现阶段后可能需要回头编辑 schema。drgn 的实例:既然永远不可能有多个 session,把 Processes 直接嵌到树根更干净(对照生成的 schema,根 DrgnRoot 的唯一必填属性就是 Processes);又因为不支持断点,断点相关条目可以安全删除。这类结构性调整会连锁要求修改 commands.py 与 methods.py 中的 pattern。
5.7 扩展功能
其他都完成后,可以考虑调试器专属的附加功能,放在 commands.py 或 methods.py 中均可。drgn 增加了 attach 方法,允许用户附加到额外的程序。
6. 单元测试
写单元测试最难的部分几乎总是让第一个测试跑起来;与 Python 文件一样,最容易的是 commands.py 的测试。drgn 依旧以 dbgeng 为模板,但必须修改 AbstractDrgnTraceRmiTest(位于 AbstractDrgnTraceRmiTest.java)中的 runThrowError(更具体地说是 execInPython)逻辑:由于 launcher 执行的是一个脚本,需要改成以脚本为参数的 ProcessBuilder 调用,而不是把脚本写入 stdin。顺路还可以从所有测试类中裁掉断点、watchpoint 等无关的辅助逻辑。
methods.py 的 JUnit 测试遵循类似模式,同样“跑通第一个最难”。drgn 上需要覆盖(override)waitForPass 与 waitForCondition 的超时;测试目标先用硬编码路径起步,之后还要在 execInDrgn 中动态改写 PREAMBLE。没有真正的 hooks.py 逻辑,自然也不需要 DrgnHooksTest。仓库中的 drgn 集成测试目录 agent/drgn 下现有 DrgnCommandsTest、DrgnConnectorsTest、DrgnMethodsTest、DrgnVersionTest 四个测试类,与文档描述一一对应。
两个实践细节值得记住:
- 测试用 gdb 的
gcore命令生成 core dump,作为测试输入; - 用户态与内核态调试都要求特权,测试环境里这不理想——这也是 drgn 用 core dump 路径做集成测试的动机。同时,IntegrationTest 的 build.gradle 需要修改以包含新的调试器 Python 包依赖。
7. 文档与扩展特性
所有新调试器的首要文档是 launcher 说明:目前这些信息集中存放于 TraceRmiLauncherServicePlugin.html(Debug/Debugger-rmi-trace 模块内)。细节要求:launcher 中的 #@help 位置必须与该 HTML 文件中的锚点标签一致,launcher 名称也必须一致(drgn 的 #@help drgn#attach 即对应 drgn 帮助页中的 attach 锚点)。
最后,当其余工作全部完成后,再考虑添加调试器专属的扩展功能。对 drgn,最终落地的就是允许附加到额外程序的 attach 方法——这印证了文档“扩展功能放在最后”的节奏建议:先把 connect/create/put/refresh 主干跑通,再谈特色功能。
8. 小结:新增一个 Agent 的检查清单
按文档脉络与 drgn 实例归纳,新增 Ghidra 调试器 Agent 的完整检查清单如下:
- 选型:目标调试器若本身是 Python 应用,走 dbgeng 式“Agent 主动驱动”模式;否则(gdb/lldb)走“启动调试器 + 加载插件”模式;
- launcher:从现有 Agent 的
.bat/.sh转换起步,补齐#@title/#@desc/#@menu-group/#@icon/#@help/#@arg(s)/#@env元数据;注意sudo -E与环境变量透传; - schema:克隆 dbgeng 的
schema.xml,确认内置接口名(Process/Thread/Frame/Register/MemoryRegion/Module/Section)、canonical容器与Aggregate接口,同步修改MANIFEST.in; - 构建:套用
distributableGhidraModule.gradle与hasPythonPackage.gradle,改eclipse.project.name,同步pyproject.toml版本号为 Ghidra 版本号; - Python 主干:
arch.py(语言/编译器映射,注意 x86 不可映射default)→util.py(版本信息、selected_* 状态)→commands.py(自树根向下put_*,ghidra_trace_*前缀方法自管事务,遵循 create-populate-insert)→methods.py(refresh 方法与@REGISTRY.method/sch.Schema/ParamDesc注解,同步 selected/current 双向焦点)→hooks.py(推送式事件回调); - 回头修订 schema(删除用不到的断点条目、调整层级并同步 pattern);
- 测试:修改
execInPython为脚本式ProcessBuilder调用、覆盖等待超时、必要时用gcore生成 core dump 规避特权要求,并在 IntegrationTest 的build.gradle中加入新包依赖; - 文档:更新 launcher 帮助页,保持
#@help锚点与 launcher 名称一致; - 扩展:最后再实现调试器专属功能(如 drgn 的
attach)。
掌握上述路径后,读者即可参照仓库中 Debugger-agent-drgn 的完整实现与 DebuggerIntegrationTest 的测试骨架,独立完成一个新调试器 Agent 的开发与验证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00