之前在维护一套传统 HPC 数值计算模块时,我最大的感受是:代码完全能跑,结果也正确,但没有人愿意动它。牵一发动全身,循环里藏着几十年前的编程习惯,全局 COMMON block 把数据耦合得密密麻麻。直到引入 Agentic Workflow,把“理解老代码 -> 设计替换方案 -> 生成等价实现 -> 数值回归验证”这条链路自动化之后,才真正敢对一个核心模块动刀。
本文就以量子化学软件 GAMESS 中的双电子积分(Two-Electron Integral)模块为背景,完整拆解如何用 Agentic Workflow 对传统 HPC 代码做现代化改造。适合有一定 HPC 或科学计算背景、想用大模型辅助重构遗留系统的开发者阅读。读完你会理解双电子积分为什么难改、Agentic Workflow 如何分角色协作,以及怎么搭建一套可验证、可回退的改造流程。
1. 背景与核心概念
1.1 GAMESS 与双电子积分是什么
GAMESS(General Atomic and Molecular Electronic Structure System)是一款开源量子化学计算软件,用于计算分子的能量、几何结构优化、反应路径、光谱性质等。它的底层核心是求解薛定谔方程的近似解,常用方法是 Hartree-Fock(HF)和密度泛函理论(DFT)。
无论是 HF 还是 DFT,整个计算过程中最耗时、最核心的数值模块之一就是积分计算。特别是双电子积分(Two-Electron Integral),它描述的是两个电子之间的库仑排斥作用,数学形式为:
[ (ij|kl) = \iint \phi_i(\mathbf{r}_1)\phi_j(\mathbf{r}1) \frac{1}{r{12}} \phi_k(\mathbf{r}_2)\phi_l(\mathbf{r}_2) d\mathbf{r}_1 d\mathbf{r}_2 ]
其中 ( \phi_i ) 是基函数,( r_{12} ) 是两个电子之间的距离。这个积分在 SCF 迭代中会被反复调用。
理论上,双电子积分的数量随体系大小呈 ( O(N^4) ) 增长,实际程序中会利用对称性、积分 cutoff 和 Schwarz 不等式做大量剪枝,降到 ( O(N^2) ) 到 ( O(N^3) ) 的量级。尽管如此,它仍然是整个 SCF 计算中最大的热点。
1.2 为什么双电子积分模块是典型的 Legacy HPC 代码
传统意义上的 HPC 代码,并不只是“跑得慢”的代码,而是“在上一代硬件和编译器环境下写出来的、难以被现代工具链理解和优化”的代码。双电子积分模块是典型代表。
早期 Fortran 代码通常具备以下特征:
- 大量使用 COMMON block 保存全局状态,模块之间的隐式耦合非常强。
- 循环内出现 GOTO 跳转,控制流复杂。
- 使用固定维度数组,数组大小在编译期写死。
- 对现代 CPU 的向量化、寄存器分配、缓存局部性并不友好。
- 依赖特定 Fortran 编译器的优化开关,换一个编译器可能性能差异巨大。
这些代码的正确性经过几十年工程验证,但可读性、可维护性和性能都已经严重落后。替换它,等于在飞行中换引擎。
1.3 Agentic Workflow 如何切入
Agentic Workflow(代理式工作流)不是简单的“用大模型重写代码”,而是把一次大型重构任务拆成多个可验证的小步骤,由不同角色的 AI Agent 分别执行,并通过自动化的数值回归测试作为质量门禁。
在双电子积分模块的改造中,Agentic Workflow 可以拆成四个关键角色:
- 代码理解 Agent:解析 Fortran 源文件,输出函数依赖、变量作用域、全局状态访问情况。
- 转换 Agent:基于理解结果,把遗留 Fortran 函数等价翻译成现代语言(如 C++、CUDA 或 Python 原型)。
- 验证 Agent:构造随机输入、已知解析值、回归数据集,对比新旧实现的数值误差。
- 性能 Agent:对替换后的代码做 profile,确认性能不降级,并给出优化建议。
这四类 Agent 之间不是简单的串行关系,而是一个闭环:验证失败时,要把错误信息反馈给转换 Agent,让它重新生成实现。
2. 环境准备与版本说明
本文案例需要以下环境。版本不需要和你本机完全一致,核心是思路:
| 组件 | 说明 |
|---|---|
| 操作系统 | Linux(Ubuntu 22.04、Rocky Linux 9 或 HPC 集群均可) |
| Fortran 编译器 | gfortran 11+ 或 Intel ifx,用于编译遗留代码 |
| Python | 3.10+,用于编写验证脚本和 Agent 编排 |
| Python 依赖 | numpy、pytest |
| LLM 接口 | OpenAI 兼容 API,或本地部署 vLLM 等推理服务 |
| 静态分析工具 | ctags、tree-sitter-fortran,用于代码结构解析 |
| 性能分析工具 | perf、gprof,用于热点确认 |
| 版本管理 | Git,用于管理源码和 Agent 中间产物 |
需要说明的是,GAMESS 是一个非常庞大的真实科学计算软件,完整改造它需要大量工程配合。本文给出的双电子积分示例是教学化最小版本,保留真实积分的数学形式和数值特征,但把问题缩小到可演示、可验证的规模。理解了这个小闭环,再把它放大到真实模块只是工作量问题。
3. 核心原理拆解
3.1 双电子积分的数学结构与计算特征
考虑最简情况:四个基函数都是 s 型高斯函数:
[ \phi_a(\mathbf{r}) = \exp(-\alpha |\mathbf{r} - \mathbf{A}|^2) ]
其中 ( \alpha ) 是指数,( \mathbf{A} ) 是原子中心坐标。此时双电子积分 ( (ss|ss) ) 存在解析表达式。
首先,两个高斯函数的乘积仍然是一个高斯函数。对于中心在 ( \mathbf{A} )、指数为 ( \alpha ) 的高斯和中心在 ( \mathbf{B} )、指数为 ( \beta ) 的高斯,乘积的中心和指数为:
[ p = \alpha + \beta,\quad \mathbf{P} = \frac{\alpha \mathbf{A} + \beta \mathbf{B}}{p} ]
最终 ( (ss|ss) ) 的解析形式为:
[ (ss|ss) = \frac{2\pi^{5/2}}{p \cdot q \cdot \sqrt{p+q}} \cdot \exp\left(-\frac{\alpha\beta}{\alpha+\beta}|\mathbf{A}-\mathbf{B}|^2\right) \cdot \exp\left(-\frac{\gamma\delta}{\gamma+\delta}|\mathbf{C}-\mathbf{D}|^2\right) \cdot \exp\left(-\frac{pq}{p+q}|\mathbf{P}-\mathbf{Q}|^2\right) ]
从代码实现角度看,这个公式包含:
- 多次指数运算和幂运算,浮点开销高。
- 多组三维向量运算,遍历内存次数多。
- 中间变量多,编译器需要合理调度寄存器。
真实 GAMESS 中的积分模块远比这个复杂,因为它要处理 p、d、f 等高角动量基函数,还要利用对称性。但核心瓶颈特征一致,本文用 ( (ss|ss) ) 完全够了。
3.2 传统 Fortran 实现为什么会成为改造难点
下面是一段教学化的遗留 Fortran 实现,把它当成从老代码里抽出来的最小核心片段:
! legacy_ss_integral.f90 subroutine ss_integral(alpha, beta, gamma, delta, A, B, C, D, result) implicit none double precision, intent(in) :: alpha, beta, gamma, delta double precision, intent(in) :: A(3), B(3), C(3), D(3) double precision, intent(out) :: result double precision :: p, q, pref double precision :: P(3), Q(3) double precision :: AB2, CD2, PQ2 p = alpha + beta q = gamma + delta P(1) = (alpha * A(1) + beta * B(1)) / p P(2) = (alpha * A(2) + beta * B(2)) / p P(3) = (alpha * A(3) + beta * B(3)) / p Q(1) = (gamma * C(1) + delta * D(1)) / q Q(2) = (gamma * C(2) + delta * D(2)) / q Q(3) = (gamma * C(3) + delta * D(3)) / q AB2 = (A(1) - B(1)) ** 2 + (A(2) - B(2)) ** 2 + (A(3) - B(3)) ** 2 CD2 = (C(1) - D(1)) ** 2 + (C(2) - D(2)) ** 2 + (C(3) - D(3)) ** 2 PQ2 = (P(1) - Q(1)) ** 2 + (P(2) - Q(2)) ** 2 + (P(3) - Q(3)) ** 2 pref = 2.0d0 * 3.141592653589793d0 ** 2.5d0 / (p * q * sqrt(p + q)) result = pref * exp(-alpha * beta / p * AB2) & * exp(-gamma * delta / q * CD2) & * exp(-p * q / (p + q) * PQ2) end subroutine ss_integral这段代码看起来还比较规范,但真实遗留代码往往存在这些问题:
- P 和 Q 是局部数组,但真实代码中会被放进 COMMON block,供其他子程序共享。
- 大量计算用 GOTO 和递推公式完成,循环层数深,循环体内部还有条件分支。
- 数组访问顺序与缓存优化不匹配,现代 CPU 的 SIMD 向量化很难生效。
- 数学函数调用密集,编译器难以判断是否可以安全重排。
所以改造的核心目标不只是“换成 Python”,而是把难以向量化、难以维护、难以测试的 Fortran 代码,替换成结构清晰、可单元测试、可性能调优的现代实现。
3.3 Agentic Workflow 的任务分解模式
我们不需要让一个 Agent 从头到尾读完整个 GAMESS,那是错误思路。正确做法是把大规模任务拆成小粒度、可验证的子任务。
下面用文字流程表示 Agent 协作关系,注意在真实系统中这通常是一个带循环的 pipeline:
- 代码理解阶段:代码理解 Agent 接收源文件,输出函数清单、调用关系、全局变量访问表。这一步可以用 tree-sitter-fortran 做静态解析,减少 LLM 的幻觉风险。
- 热点识别阶段:性能 Agent 通过 gprof 或 perf 确认哪些函数是真正值得替换的热点。不要盲目重构所有代码。
- 等价转换阶段:转换 Agent 只针对一个函数生成替代实现,同时必须输出“转换说明”,包括数学公式来源、边界条件处理、浮点累加顺序的变化。
- 验证回归阶段:验证 Agent 运行数值测试,把误差结果反馈给转换 Agent。如果超过阈值,转换 Agent 需要重新生成实现。
- 集成部署阶段:替换后的代码通过验证后,才合入主工程,并保留旧实现作为回退开关。
这种设计的关键在于:每一步都有明确产出,并且每一步都是可验证的。Agent 的“自由发挥”被限制在一个非常窄的范围内。
4. 完整实战案例:从 Fortran 到 Python 等价转换
下面我们用一个最小但完整的案例演示整个 Agentic Workflow。案例会包含四个文件:
legacy_hpc_modernization/ ├── legacy_ss_integral.f90 # 遗留 Fortran 实现 ├── agentic_workflow.py # Agent 编排脚本 ├── modern_ss_integral.py # 转换 Agent 生成的等价实现 └── validate_conversion.py # 数值验证脚本4.1 编写遗留 Fortran 实现
先在legacy_ss_integral.f90中放入前面那段ss_integral子程序,并补充一个测试主程序,方便后续编译运行:
! legacy_ss_integral.f90 subroutine ss_integral(alpha, beta, gamma, delta, A, B, C, D, result) implicit none double precision, intent(in) :: alpha, beta, gamma, delta double precision, intent(in) :: A(3), B(3), C(3), D(3) double precision, intent(out) :: result double precision :: p, q, pref double precision :: P(3), Q(3) double precision :: AB2, CD2, PQ2 p = alpha + beta q = gamma + delta P(1) = (alpha * A(1) + beta * B(1)) / p P(2) = (alpha * A(2) + beta * B(2)) / p P(3) = (alpha * A(3) + beta * B(3)) / p Q(1) = (gamma * C(1) + delta * D(1)) / q Q(2) = (gamma * C(2) + delta * D(2)) / q Q(3) = (gamma * C(3) + delta * D(3)) / q AB2 = (A(1) - B(1)) ** 2 + (A(2) - B(2)) ** 2 + (A(3) - B(3)) ** 2 CD2 = (C(1) - D(1)) ** 2 + (C(2) - D(2)) ** 2 + (C(3) - D(3)) ** 2 PQ2 = (P(1) - Q(1)) ** 2 + (P(2) - Q(2)) ** 2 + (P(3) - Q(3)) ** 2 pref = 2.0d0 * 3.141592653589793d0 ** 2.5d0 / (p * q * sqrt(p + q)) result = pref * exp(-alpha * beta / p * AB2) & * exp(-gamma * delta / q * CD2) & * exp(-p * q / (p + q) * PQ2) end subroutine ss_integral program test_legacy implicit none double precision :: alpha, beta, gamma, delta double precision :: A(3), B(3), C(3), D(3) double precision :: val alpha = 1.0d0 beta = 0.5d0 gamma = 0.2d0 delta = 0.1d0 A = (/0.0d0, 0.0d0, 0.0d0/) B = (/0.0d0, 0.0d0, 1.0d0/) C = (/1.0d0, 0.0d0, 0.0d0/) D = (/0.5d0, 0.5d0, 0.0d0/) call ss_integral(alpha, beta, gamma, delta, A, B, C, D, val) print *, val end program test_legacy编译运行:
gfortran -O2 -o test_legacy legacy_ss_integral.f90 ./test_legacy你会发现输出的是一个双精度浮点数,这个值就是后续所有验证的基准之一。
4.2 用 Agent 编排脚本规划改造任务
下面用 Python 写一个最小化的 Agent 编排脚本。实际项目中你可能用 LangChain、LlamaIndex 或自研 pipeline,但核心结构相同:
# agentic_workflow.py from dataclasses import dataclass, field from typing import Callable @dataclass class AgentTask: name: str goal: str input_files: list output_files: list constraints: str = "" class Agent: def __init__(self, role: str, llm: Callable[[str], str]): self.role = role self.llm = llm def run(self, task: AgentTask) -> str: prompt = f""" 你是{self.role} Agent。 任务名称:{task.name} 目标:{task.goal} 输入文件:{', '.join(task.input_files)} 约束:{task.constraints} 请给出可执行的分析结论或代码,并说明理由。 """ return self.llm(prompt) def build_pipeline(llm: Callable[[str], str]): # 阶段 1:代码理解 analyzer = Agent("代码理解", llm) analyze_task = AgentTask( name="解析遗留积分函数", goal="列出 ss_integral 子程序的输入、输出、局部变量和潜在数值风险", input_files=["legacy_ss_integral.f90"], output_files=["analysis_report.md"], constraints="只做静态分析,不要修改代码", ) report = analyzer.run(analyze_task) print("[代码理解阶段]", report) # 阶段 2:等价转换 translator = Agent("等价转换", llm) translate_task = AgentTask( name="将 Fortran 转换为 Python", goal="生成 modern_ss_integral.py,保持数学公式完全一致", input_files=["legacy_ss_integral.f90", "analysis_report.md"], output_files=["modern_ss_integral.py"], constraints="必须保留原始公式,禁止修改数值算法,输出 float64", ) code = translator.run(translate_task) print("[等价转换阶段]", code) # 阶段 3:数值验证(实际项目里由 pytest 实现) verifier = Agent("数值验证", llm) verify_task = AgentTask( name="验证新旧实现一致性", goal="对比 Fortran 与 Python 输出,误差小于 1e-12", input_files=["modern_ss_integral.py"], output_files=["verification_report.md"], constraints="使用随机高斯指数和随机坐标,至少测试 1000 组", ) report = verifier.run(verify_task) print("[数值验证阶段]", report) if __name__ == "__main__": # 用本地函数模拟 LLM,实际替换为 OpenAI 兼容 API 或 vLLM def mock_llm(prompt: str) -> str: return "已收到任务,下一步输出由验证脚本完成。" build_pipeline(mock_llm)这个脚本展示了 Agent 之间如何传递上下文。关键点是每个 Agent 只看到它需要的那部分信息,不会一次把整个程序喂给大模型,减少了生成无关代码和出错的可能性。
4.3 转换 Agent 生成的现代实现
在真实流程中,转换 Agent 的输出可能是 C++ 或 CUDA。这里为了演示数值验证,先生成 Python 等价实现:
# modern_ss_integral.py import numpy as np def ss_integral(alpha, beta, gamma, delta, A, B, C, D): """等价实现 (ss|ss) 双电子积分。 对应遗留 Fortran 子程序 legacy_ss_integral.f90 中的 ss_integral。 全部计算使用 float64,确保数值精度。 """ p = alpha + beta q = gamma + delta P = (alpha * A + beta * B) / p Q = (gamma * C + delta * D) / q AB2 = np.dot(A - B, A - B) CD2 = np.dot(C - D, C - D) PQ2 = np.dot(P - Q, P - Q) pref = 2.0 * np.pi ** 2.5 / (p * q * np.sqrt(p + q)) return (pref * np.exp(-alpha * beta / p * AB2) * np.exp(-gamma * delta / q * CD2) * np.exp(-p * q / (p + q) * PQ2))这里要注意 numpy 向量运算和 Fortran 数组运算的语义一致。A - B是逐元素相减,np.dot对应 Fortran 中手写的平方和累加。
4.4 数值验证脚本
数值验证是整个 Agentic Workflow 的安全网。我们写一个脚本,把 Fortran 测试程序输出的基准值,与 Python 实现计算结果做对比:
# validate_conversion.py import subprocess import numpy as np from modern_ss_integral import ss_integral # 编译并运行 Fortran 测试程序 subprocess.run(["gfortran", "-O2", "-o", "test_legacy", "legacy_ss_integral.f90"], check=True) output = subprocess.check_output(["./test_legacy"], text=True) legacy_value = float(output.strip().split()[-1]) # 使用与 Fortran 测试程序完全相同的输入 alpha, beta, gamma, delta = 1.0, 0.5, 0.2, 0.1 A = np.array([0.0, 0.0, 0.0]) B = np.array([0.0, 0.0, 1.0]) C = np.array([1.0, 0.0, 0.0]) D = np.array([0.5, 0.5, 0.0]) modern_value = ss_integral(alpha, beta, gamma, delta, A, B, C, D) diff = abs(legacy_value - modern_value) print(f"Fortran 结果: {legacy_value:.16e}") print(f"Python 结果: {modern_value:.16e}") print(f"绝对误差: {diff:.4e}") if diff < 1e-12: print("[PASS] 数值一致性验证通过") else: print("[FAIL] 数值不一致,请检查转换过程")运行:
python validate_conversion.py预期输出类似:
Fortran 结果: 1.2345678901234567e+00 Python 结果: 1.2345678901234567e+00 绝对误差: 0.0000e+00 [PASS] 数值一致性验证通过实际数值会受到编译选项和浮点运算顺序的影响,但只要阈值设成1e-12,对这个简单的 ( (ss|ss) ) 积分来说是很稳妥的。
4.5 扩展为随机化回归测试
固定输入只能说明“这个点通过了”,不够。建议验证脚本改成随机生成 1000 组指数和坐标,做批量对比。这一步就是 Agent 验证 Agent 真正执行的任务:
# stochastic_validation.py import numpy as np from modern_ss_integral import ss_integral rng = np.random.default_rng(42) max_diff = 0.0 for _ in range(1000): alpha, beta, gamma, delta = rng.uniform(0.1, 2.0, size=4) A = rng.uniform(-1.0, 1.0, size=3) B = rng.uniform(-1.0, 1.0, size=3) C = rng.uniform(-1.0, 1.0, size=3) D = rng.uniform(-1.0, 1.0, size=3) # 这里应调用 Fortran 暴露的接口做对比, # 为演示方便,我们使用与 Python 相同算法路径的参考函数。 # 在实际项目中,通过 f2py 或 ctypes 把 Fortran 编译为共享库, # 再与本函数逐组对比。 p = alpha + beta q = gamma + delta P = (alpha * A + beta * B) / p Q = (gamma * C + delta * D) / q ref = (2.0 * np.pi**2.5 / (p * q * np.sqrt(p + q)) * np.exp(-alpha * beta / p * np.dot(A - B, A - B)) * np.exp(-gamma * delta / q * np.dot(C - D, C - D)) * np.exp(-p * q / (p + q) * np.dot(P - Q, P - Q))) val = ss_integral(alpha, beta, gamma, delta, A, B, C, D) max_diff = max(max_diff, abs(val - ref)) print(f"1000 组随机测试最大误差: {max_diff:.4e}") assert max_diff < 1e-12, "随机验证失败" print("[PASS] 随机化回归通过")这个脚本澄清了一个重要观点:在真实项目中,参考函数应当是“被替换的旧实现”,而不是“自己写的新实现”。否则验证就失去了意义。正确做法是把 Fortran 源码编译成共享库,测试脚本循环调用它作为 ground truth。
5. 常见问题与排查思路
实际落地的过程中,Agentic Workflow 不会一帆风顺。下面汇总我遇到的高频问题:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| Python 与 Fortran 数值误差过大 | 浮点运算顺序不同,或中间变量精度不一致 | 统一使用 float64,调整累加顺序,必要时用更高精度做中间计算 |
| Agent 生成的代码不是等价转换 | 转换 Agent 没有充分理解公式,或 prompt 缺少数值约束 | 在 prompt 中要求输出“转换说明”,人工审核公式来源 |
| 重构后性能不升反降 | 数据布局和循环结构不适配现代硬件 | 先用 perf 定位热点,分析内存访问模式,再考虑向量化或 GPU 化 |
| COMMON block 全局状态难以迁移 | 新实现无法访问遗留全局变量 | 先重构数据结构,把全局状态封装成 context 对象,再做函数替换 |
| 回归测试只覆盖少量输入 | 测试用例太少,偶然通过 | 使用随机化测试 + 真实分子体系测试集,纳入 CI |
| LLM 生成的代码风格不统一 | 缺少代码规范和模板约束 | 在 prompt 中嵌入项目代码风格指南,用 lint 工具强制检查 |
| 重构被无限期拖延 | 任务粒度太大,验证复杂 | 坚持“一次只替换一个函数”,每个函数都走完整验证闭环 |
这里面最值得强调的一条是:永远不要让 Agent 在没有验证闭环的情况下,“一口气”重写一个大模块。数值代码的正确性是靠测试撑起来的,不是靠生成模型的语言能力撑起来的。
6. 最佳实践与工程建议
6.1 先建立数值基线,再开始重构
在写任何 Agent prompt 之前,先把旧代码的测试基线建立起来。对科学计算代码来说,基线不只是几个数值结果,而是:
- 一组覆盖典型输入范围的回归测试数据。
- 一组随机化测试的随机种子和参数范围。
- 已知解析解或高精度参考值。
没有基线,Agent 生成的代码就缺少“正确”的定义。
6.2 一次只替换一个函数
双电子积分模块内部函数众多,彼此有依赖。正确的替换单位是“一个子程序/函数”,而不是“整个文件”。替换后立即跑回归,通过后再处理下一个依赖节点。
这不仅能降低排查复杂度,也能让 Git 历史更清晰。每次提交对应一个函数的等价替换,随时可以 revert。
6.3 把 Agent 的中间产物纳入版本管理
Agent 生成的分析报告、转换说明、验证报告,都是重构过程中非常有价值的资产。它们解释了“为什么这样改”,对后续维护者极其重要。
建议在项目仓库中增加一个migration/目录:
migration/ ├── analysis/ # 代码理解 Agent 报告 ├── design/ # 转换说明、数学公式说明 └── verification/ # 数值验证日志6.4 数值阈值要结合物理意义设计
双电子积分在量子化学中最终会进入哈密顿矩阵,影响能量到微乎其微的量级。如果你的替换代码在积分层面有1e-10的误差,对总能量的影响可能很容易被 SCF 收敛阈值吸收;但如果误差达到1e-6,就可能改变分子几何优化的结果。
建议设置两档阈值:
- 单元测试级:积分本身绝对误差小于
1e-12。 - 集成测试级:SCF 总能量误差小于
1e-8Hartree,且梯度误差低于指定阈值。
这也是为什么验证 Agent 需要知道物理背景,而不只是做“两个数是否相等”的比较。
6.5 保留旧实现作为回退开关
即使新实现通过了所有测试,也不要立刻删除旧代码。在正式替换后的头几个版本里,保留一个环境变量或编译宏,允许用户切回旧实现。
! 伪代码示意 #ifdef USE_LEGACY_INTEGRAL call ss_integral_legacy(...) #else call ss_integral_new(...) #endif这种做法在科学计算领域尤其重要。因为某些极端输入可能不在你的测试覆盖范围内,旧实现作为兜底方案,能避免整个计算任务失败。
6.6 人工审核的边界
Agentic Workflow 可以大幅提升重构效率,但不可能完全替代人工审核。特别是涉及数学公式的模块,必须有人确认:
- 生成代码中的公式与文献公式一致。
- 边界条件处理与旧代码一致。
- 数值误差的方向和大小符合预期。
建议在 pipeline 中设置“人工 Review 门禁”,Agent 生成的代码必须经过人工确认后才能合入主干。
7. 总结与动手建议
把双电子积分这个例子跑通之后,你其实已经掌握了一套通用的遗留 HPC 代码现代化方法:
- 先理解:用静态分析 + Agent 生成依赖报告。
- 再定位:用 profiler 找热点,不盲目重构。
- 后替换:一次一个函数,Agent 生成等价实现。
- 终验证:随机测试 + 物理阈值 + CI 回归。
对于 GAMESS 这类大型科学计算软件,真正的挑战往往不是“让 Agent 写出跑得通的代码”,而是“让 Agent 写出数值上与原实现等价的代码”。后者必须以严谨的测试闭环为前提。
如果你准备在自己的项目里动手,我的建议是先不要碰核心模块。找一块隔离程度高、有明确输入输出、市面上有参考实现的子程序,把它作为 Agentic Workflow 的试验田。跑通第一段完整 pipeline 后,再逐步扩大改造范围。这套流程比人工阅读几十万行 Fortran 代码,要高效得多。