news 2026/10/13 9:49:53

AI自动修Bug实战:从一句Bug描述到复现、根因定位、最小修复与回归验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI自动修Bug实战:从一句Bug描述到复现、根因定位、最小修复与回归验证

AI自动修Bug实战:从一句Bug描述到复现、根因定位、最小修复与回归验证

AI编程 Agent 智能体 Bug修复 软件测试 自动化测试 Python 代码审查 软件工程 持续集成

一个 Bug 只有一句自然语言描述时,AI 能不能真正完成“复现—定位—修复—验证”,而不是只生成一段看似合理的代码?本文用一个可运行的金额舍入缺陷,从零搭建 Repair Agent 闭环:先把用户描述编译成 BugSpec,再生成稳定失败的回归测试;随后通过仓库地图、符号检索、失败栈和最小实验建立根因证据,用 Diff 预算约束补丁范围,最后依次执行原始复现、边界测试、模块测试和全量回归。示例中,旧实现 7 个测试有 2 个失败,修复后 7/7 通过。文章进一步给出状态机、工具接口、停止条件、权限护栏、失败回退和 CI/PR 接入方式,让 AI 修 Bug 从“代码生成演示”变成可复现、可审计、可回滚的工程流程。

1. 一个“只差 1 分钱”的 Bug,为什么值得做成完整闭环

凌晨的订单告警里出现一条投诉:原价 199.90 元的商品打 85 折后显示 169.91 元,但业务规则要求金额按“分”四舍五入,正确结果应该是 169.92 元。

这类 Bug 对人类开发者并不难。真正困难的是:如果只把这句话交给 AI,它怎样证明自己不是“碰巧改对”?

●它必须先把用户描述变成可执行的验收条件,而不是直接猜代码。

●它必须在补丁前制造一个稳定失败,否则“测试通过”没有证明力。

●它必须给出根因证据,而不是只说“可能是浮点精度”。

●它必须控制修改半径,避免顺手重构导致新的回归面。

●它必须用补丁后的全量回归结果,证明修复没有破坏已有行为。

所以,AI 自动修 Bug 的核心问题不是“模型会不会写代码”,而是能不能把一次修复变成一条有入口、有证据、有门禁、有退出条件的工程流水线。

图1AI自动修复闭环:每一步都必须留下可审计证据

2. 先看最终结果:旧实现 5/7 通过,修复后 7/7 通过

验证项

输入/条件

期望

旧实现

修复后

用户报告场景

19990 分,85%

16992 分

失败:16991

通过

0.5 舍入边界

101 分,50%

51 分

失败:50

通过

无折扣

12345 分,100%

12345 分

通过

通过

零折扣

12345 分,0%

0 分

通过

通过

整分结果

20000 分,85%

17000 分

通过

通过

非法价格

-1 分,85%

抛 ValueError

通过

通过

非法折扣

100 分,101%

抛 ValueError

通过

通过

这里最重要的不是“7/7”这个数字,而是验证集合同时覆盖了原始现象、舍入边界、正常路径和输入不变量。只有原始用例转绿还不够,因为针对单个样本写特判,同样可以让一个测试通过。

3. 最小工程:先把实验环境缩到任何人都能复现

为了把重点放在闭环本身,示例只依赖 Python 标准库。目录如下:

bugfix-demo/

├─ app/

│ ├─ __init__.py

│ └─ pricing.py

├─ tests/

│ └─ test_pricing.py

└─ repair_agent.py

示例适用于 Python 3.11+;核心 Decimal 与 unittest API 在 Python 3.x 中长期稳定。真实项目若换成 pytest、Java/JUnit、Go testing 或前端测试框架,闭环结构不变,只需要替换工具适配层。

4. 第一步不是搜代码:先把自然语言编译成 BugSpec

Issue 往往把现象、输入、期望、猜测甚至情绪混在一起。Repair Agent 的第一步应该是抽取“可验证事实”,并把未知信息显式保留为 unknown,而不是自行补全。

图2从一句自然语言投诉到结构化BugSpec

{

"module_hint": "pricing",

"entry_symbol": "final_price",

"input": {

"price_cent": 19990,

"discount_percent": 85

},

"actual": 16991,

"expected": 16992,

"invariants": [

"金额最小单位为分",

"0.5 边界按 ROUND_HALF_UP 处理",

"price_cent 不允许为负数",

"discount_percent 必须位于 0..100"

],

"acceptance": [

"用户报告场景由红转绿",

"舍入边界测试通过",

"全量回归通过"

]

}

BugSpec 不是为了把 JSON 写得漂亮,而是为了统一后续所有动作的目标:测试生成看它、根因判断看它、补丁评审看它、最终验收仍然看它。长链路 Agent 最怕目标在多轮推理中漂移,结构化契约就是第一道防漂移机制。

5. 第二步:没有稳定的“红”,就不允许改生产代码

原始错误实现如下:

def final_price(price_cent: int, discount_percent: int) -> int:

if price_cent < 0:

raise ValueError("price_cent must be >= 0")

if not 0 <= discount_percent <= 100:

raise ValueError("discount_percent must be in [0, 100]")

return int(price_cent * (discount_percent / 100))

先写测试,不改函数:

import unittest

from app.pricing import final_price

class PricingTest(unittest.TestCase):

def test_reported_case(self):

self.assertEqual(final_price(19990, 85), 16992)

def test_round_half_up(self):

self.assertEqual(final_price(101, 50), 51)

def test_no_discount(self):

self.assertEqual(final_price(12345, 100), 12345)

def test_zero_discount(self):

self.assertEqual(final_price(12345, 0), 0)

def test_exact_integer(self):

self.assertEqual(final_price(20000, 85), 17000)

def test_negative_price_rejected(self):

with self.assertRaises(ValueError):

final_price(-1, 85)

def test_invalid_discount_rejected(self):

with self.assertRaises(ValueError):

final_price(100, 101)

执行:

python -m unittest discover -s tests -v

补丁前的关键结果:

test_reported_case ............. FAIL

test_round_half_up ............. FAIL

test_no_discount ............... ok

test_zero_discount ............. ok

test_exact_integer ............. ok

test_negative_price_rejected ... ok

test_invalid_discount_rejected . ok

Ran 7 tests

FAILED (failures=2)

第二个失败用例非常关键:101 分 × 50% 的数学结果是 50.5 分,业务规则要求进位到 51 分。它把问题从“199.90 这个输入有问题”提升成“整个函数的舍入语义有问题”。

6. 第三步:用证据漏斗定位,而不是把整个仓库塞给模型

图3证据漏斗:搜索空间逐层收缩

层级

Agent 动作

产物

停止/继续条件

仓库地图

读取目录、测试入口、构建配置

模块地图

找到 pricing 业务域

关键词检索

price、discount、cent、amount

候选文件集合

候选仍多则继续

符号检索

final_price 定义与引用

定义/调用者

锁定执行入口

失败栈

读取 failing test stack

真实调用路径

与 BugSpec 对齐

局部读取

目标函数上下文

可疑表达式

提出可证伪假设

最小实验

单独计算边界输入

假设证据

能解释全部失败才进入 PATCH

这种定位方式解决两个现实问题:一是减少 Token 和上下文噪声;二是让“根因”可以追溯。模型不能因为看到一个可疑函数就直接修改,它需要先解释为什么这个函数能够同时解释已知失败。

7. 根因分析:真正的问题是“截断”与“业务舍入规则”不一致

图4旧实现的截断语义与业务要求的四舍五入语义

很多人看到金额 Bug 会第一时间说“不要用 float”。这个方向没有错,但如果只停在这里,分析仍然不够准确。对本例而言,即使中间结果恰好是 16991.5,`int(16991.5)` 也会得到 16991,因为对正数它执行向零截断。

因此可以形成一条完整证据链:

●用户报告:19990 × 85% 期望 16992,旧实现得到 16991。

●边界实验:101 × 50% 期望 51,旧实现得到 50。

●源码表达式:`int(...)` 明确执行截断,而业务要求 HALF_UP。

●根因结论:金额计算的数值表示与舍入策略没有被显式建模。

这比“可能存在浮点精度问题”更强,因为它是可以被反驳、被验证、被代码直接修正的工程结论。

8. 第四步:最小修复——把金额语义写进代码,而不是针对样本打补丁

修复实现:

from decimal import Decimal, ROUND_HALF_UP

def final_price(price_cent: int, discount_percent: int) -> int:

if price_cent < 0:

raise ValueError("price_cent must be >= 0")

if not 0 <= discount_percent <= 100:

raise ValueError("discount_percent must be in [0, 100]")

amount = (

Decimal(price_cent)

* Decimal(discount_percent)

/ Decimal(100)

)

return int(

amount.quantize(

Decimal("1"),

rounding=ROUND_HALF_UP

)

)

图5最小补丁:只改变金额计算语义

为什么不用 `round()`?因为金融/计费代码最重要的是把舍入规则显式写出来。Python 内置 `round()` 使用 bankers rounding(ties-to-even)语义,而这里的业务规则明确要求 HALF_UP。代码应该表达业务,而不是让读者猜运行时默认规则。

9. 第五步:验证要像金字塔,不能只跑刚新增的那个测试

图6四层验证:从原始复现到全量回归

层级

目的

命令示例

原始复现

证明用户报告场景由红转绿

python -m unittest ...test_reported_case -v

边界测试

证明根因修复而非样本特判

python -m unittest ...test_round_half_up -v

模块测试

检查 pricing 邻近行为

python -m unittest tests.test_pricing -v

全量回归

发现跨模块副作用

python -m unittest discover -s tests -v

修复后的实际结果:

test_reported_case ............. ok

test_round_half_up ............. ok

test_no_discount ............... ok

test_zero_discount ............. ok

test_exact_integer ............. ok

test_negative_price_rejected ... ok

test_invalid_discount_rejected . ok

Ran 7 tests

OK

必须明确证据边界:7/7 通过只证明当前测试集合覆盖的行为成立,并不等于“系统绝对没有其他缺陷”。成熟 Agent 应把“已验证什么”和“没有验证什么”同时写进报告。

10. 把流程固化成 Repair Agent 状态机

图7Repair Agent状态机:验证结果控制状态转换

state = "PARSE"

while state not in {"DONE", "HUMAN"}:

if state == "PARSE":

bug = llm.to_bug_spec(issue_text)

state = "REPRODUCE"

elif state == "REPRODUCE":

repro = llm.generate_repro_test(bug, repo_map)

result = tools.run_tests(repro)

state = "LOCALIZE" if result.failed_as_expected else "HUMAN"

elif state == "LOCALIZE":

evidence = tools.collect_evidence(

keywords=bug.keywords,

failing_stack=result.stack,

symbol=bug.entry_symbol,

)

hypothesis = llm.propose_root_cause(bug, evidence)

state = "PATCH" if tools.can_verify(hypothesis) else "HUMAN"

elif state == "PATCH":

patch = llm.generate_patch(

bug=bug,

evidence=evidence,

max_files=2,

max_changed_lines=80,

)

state = "VERIFY"

elif state == "VERIFY":

report = tools.verify(patch)

if report.all_green:

state = "DONE"

elif report.attempts >= 3:

state = "HUMAN"

else:

tools.rollback_patch()

state = "LOCALIZE"

关键点在于:`DONE` 不是模型可以自由输出的一句话,而是由验证工具的结构化结果触发。这样即使模型非常自信,也不能越过门禁。

11. Agent 到底需要哪些工具

第一版不要追求“万能 Shell”。越通用的工具越难控制权限,也越难让输出结构化。六类窄接口已经足够覆盖大部分低风险缺陷:

工具

输入

输出

权限

repo_map

仓库根目录

目录、语言、测试入口

只读

search_code

关键词/正则

文件、行号、片段

只读

find_symbol

符号名

定义、引用、调用者

只读

read_file

路径 + 行范围

局部源码

只读

run_tests

测试选择器

退出码、日志、失败栈

执行

apply_patch

统一 Diff

修改后的隔离工作区

受限写入

show_diff

基线与当前工作区

文件级差异

只读

rollback

当前补丁

恢复干净基线

受控写入

工具输出尽量结构化。例如测试工具不要只返回一大段 stdout,而应额外返回 `exit_code`、`failed_tests`、`stack`、`duration`。结构化结果越多,模型越少需要从日志文本中猜状态。

12. Prompt 只负责约束推理,真正的权限必须由程序控制

你是代码修复 Agent。

目标:

1. 复现用户报告的 Bug;

2. 基于仓库证据定位根因;

3. 生成最小补丁;

4. 完成定向测试与全量回归。

硬约束:

- 没有稳定失败,不修改生产代码;

- 根因必须引用失败栈、源码或最小实验;

- 默认最多修改 2 个生产文件、80 行;

- 不删除测试,不弱化断言,不关闭 CI;

- 测试未全绿,不得宣告修复完成;

- 涉及数据迁移、权限、安全边界、公共 API 破坏时升级人工;

- 连续 3 轮失败后停止自动循环。

注意:Prompt 里的规则不能替代沙箱、文件白名单和命令过滤。模型“知道不应该删除测试”和系统“根本不给它删除测试的权限”是两个完全不同的安全等级。

13. 六道工程护栏:让 Agent 有能力,但没有无限权限

图8自动修复的权限与风险护栏

建议把风险控制放在模型外部:隔离工作区保证可回滚;只读优先降低误写概率;写入白名单控制修改半径;危险命令 deny-by-default;Diff 预算阻止修复漂移;人工升级负责处理模型不应该独立决定的问题。

尤其不要允许 Agent 为了“让 CI 变绿”修改测试断言、跳过测试或关闭 lint。对自动修复系统而言,测试是裁判,Agent 不能同时拥有修改裁判规则的无限权限。

14. 失败分支同样重要:复现不了时,正确答案不是编一个测试

情况

正确行为

错误行为

缺少输入数据

列出缺失字段,请求最小样本

自行编造生产数据

环境差异

记录 OS、版本、配置、依赖差异

默认本地等于线上

间歇性失败

重复运行并记录触发频率/条件

一次没失败就判定不存在

外部依赖不可用

使用明确的 mock 或升级人工

静默跳过依赖

补丁引入新失败

回滚,保留失败证据

继续叠加更多修改

连续多轮失败

停止并生成证据包

无限循环直到“看起来成功”

一个可靠 Agent 的价值不仅体现在成功修复,还体现在失败时能准确说清楚“停在哪里、缺什么、下一步需要谁决定”。

15. 如何防止“测试投机”:别让 Agent 学会只为测试写代码

自动修复还有一个典型风险:模型观察到测试输入后,写出能通过测试但不满足真实业务语义的补丁。例如针对 19990 和 85 直接返回 16992。

可以用三类机制抑制这种行为:

●测试多样化:原始用例之外加入边界、等价类和不变量测试。

●隐藏验证:CI 中保留 Agent 不可见的回归测试,避免完全针对公开断言优化。

●语义审查:要求补丁解释“为什么修改能推广到同类输入”,并检查是否出现硬编码样本值。

对于金额、时间、权限、并发等高风险逻辑,还可以加入属性测试(property-based testing)或差分测试,让验证从几个例子升级为一组性质。

16. 一个更接近真实项目的 repair_agent.py

下面给出一个不依赖具体大模型 SDK 的骨架。`planner` 可以替换为任意支持结构化输出的模型,`tools` 则封装仓库操作。

from dataclasses import dataclass

from enum import Enum, auto

class State(Enum):

PARSE = auto()

REPRODUCE = auto()

LOCALIZE = auto()

PATCH = auto()

VERIFY = auto()

DONE = auto()

HUMAN = auto()

@dataclass

class Budget:

max_files: int = 2

max_changed_lines: int = 80

max_attempts: int = 3

class RepairAgent:

def __init__(self, planner, tools, budget=Budget()):

self.planner = planner

self.tools = tools

self.budget = budget

self.attempts = 0

def run(self, issue_text: str):

bug = self.planner.to_bug_spec(issue_text)

repro = self.planner.make_repro(bug, self.tools.repo_map())

before = self.tools.run_tests(repro.selector)

if not before.failed_as_expected:

return self.tools.handoff("无法稳定复现", bug, before)

while self.attempts < self.budget.max_attempts:

self.attempts += 1

evidence = self.tools.localize(

bug=bug,

failing_stack=before.stack,

)

patch = self.planner.make_patch(

bug=bug,

evidence=evidence,

budget=self.budget,

)

if not self.tools.patch_within_budget(patch, self.budget):

return self.tools.handoff("补丁超出预算", bug, evidence)

self.tools.apply_patch(patch)

report = self.tools.verify_all()

if report.all_green:

return self.tools.build_report(

bug=bug,

evidence=evidence,

patch=patch,

report=report,

)

self.tools.rollback()

return self.tools.handoff("超过自动修复轮次", bug, evidence)

这个骨架刻意把“模型能力”和“工程控制”分开:planner 负责生成候选计划,tools 负责执行事实动作,RepairAgent 负责状态和门禁。以后更换模型时,安全边界不需要跟着重写。

17. 上下文工程:大型仓库不要一次性读取全部源码

上下文层

包含什么

何时加载

L0 仓库地图

目录、语言、构建命令、测试入口

任务开始时一次

L1 任务切片

关键词命中、失败栈、目标符号

复现后加载

L2 邻接上下文

调用者、被调用者、邻近测试

定位阶段按需

L3 扩展证据

配置、接口、历史提交、覆盖率

证据不足时再加载

这类分层比“扩大上下文窗口”更重要。窗口再大也不等于信息选择正确。Repair Agent 应该像调试者一样逐步取证,而不是像搜索引擎一样把所有相关文本一次性塞进提示词。

18. 团队落地:第一阶段的目标应是自动生成可评审 PR

图9Issue → Repair Agent → CI → PR的团队级闭环

在真实团队中,最稳妥的自治边界通常不是“自动合并”,而是“自动生成高质量 PR”。Agent 负责把机械但耗时的工作做完:复现、搜索、定位、补丁、测试、报告;人负责业务判断和最终合并。

PR描述可以固定包含:

●BugSpec:输入、实际、期望、不变量。

●复现证据:失败测试名、命令、关键失败日志。

●根因:文件、符号、表达式和为什么能解释全部失败。

●补丁:修改文件、统一 Diff、修改语义。

●验证:定向、边界、模块、全量回归结果。

●风险:没有覆盖的路径、外部依赖、版本边界。

●回滚:恢复到哪个提交或如何撤销补丁。

19. 哪些 Bug 适合自动闭环,哪些应该尽早交给人

类型

自动化适配度

原因

纯函数/算法边界错误

高

输入输出清晰,容易构造失败测试

解析、格式化、校验错误

高

可用样例和不变量验证

强测试覆盖的业务逻辑

较高

回归证据充分

依赖外部服务的集成问题

中

需要稳定 mock 或测试环境

并发、竞态、性能抖动

中低

复现成本高,证据易受环境影响

数据库迁移/数据修复

低

副作用大、回滚复杂

鉴权/支付/安全边界

低

风险高,应强制人工决策

架构级重构

低

目标开放,难以用有限测试定义完成

自动化程度应该由“风险 × 可验证性”决定,而不是由模型看起来有多聪明。低风险、强测试、明确验收的任务可以更自治;高风险、弱测试、开放目标的任务应该更早升级人工。

20. 修复报告:最终产物应该长什么样

{

"status": "fixed",

"reproduction": {

"failed_before_patch": true,

"failed_tests": [

"test_reported_case",

"test_round_half_up"

]

},

"root_cause": {

"symbol": "app.pricing.final_price",

"reason": "int() 截断与 HALF_UP 业务舍入规则不一致"

},

"patch": {

"files_changed": 1,

"strategy": "Decimal + ROUND_HALF_UP"

},

"verification": {

"targeted": "pass",

"module": "pass",

"full_regression": "7/7 pass"

},

"remaining_risk": [

"当前示例未覆盖多币种规则",

"真实生产系统应按币种配置最小货币单位"

]

}

这份报告比“已修复”三个字更有价值,因为它可以被 PR、CI、审计系统和下一次 Agent 任务继续消费。

21. 从这个 1 分钱 Bug,可以抽象出一套通用修复协议

阶段

输入

必须产物

门禁

解析

Issue/告警

BugSpec

验收条件可执行

复现

BugSpec + 仓库

失败测试 + 失败栈

补丁前稳定失败

定位

失败证据

根因假设 + 源码证据

能解释全部已知失败

修复

根因证据

最小 Diff

不超预算、不越权限

验证

补丁

分层测试报告

全量回归通过

交付

全部证据

PR/修复报告

风险与回滚明确

这套协议与语言、框架、模型无关。你可以把 Python 换成 Java,把 unittest 换成 JUnit,把本地仓库换成 CI worktree,把单 Agent 换成 planner + executor;只要这六个阶段和门禁仍然存在,系统就保留了可审计性。

22. 最后:AI 修 Bug 的终点不是“代码变了”,而是“证据闭合了”

判断一个自动修复系统是否真正可用,可以只问四个问题:为什么相信 Bug 被复现?为什么相信这里是根因?为什么相信这个补丁是必要且足够的?为什么相信它没有破坏已有行为?

对应的工程答案分别应该是:失败测试、定位证据、最小 Diff、回归报告。

当这四份证据能够自动产生、彼此对齐,并且状态机不允许模型绕过它们时,AI 才真正从“帮你写几行代码”进入软件工程闭环。低风险缺陷可以因此被大幅自动化;高风险问题即使不能自动修,也能在交给人之前完成复现、收集证据和缩小范围。

真正值得自动化的,从来不只是最后那一下提交代码,而是从一句模糊的 Bug 描述,到一个可验证、可解释、可回滚结论之间的全部工程动作。

附录 A 完整测试文件

import unittest

from app.pricing import final_price

class PricingTest(unittest.TestCase):

def test_reported_case(self):

self.assertEqual(final_price(19990, 85), 16992)

def test_round_half_up(self):

self.assertEqual(final_price(101, 50), 51)

def test_no_discount(self):

self.assertEqual(final_price(12345, 100), 12345)

def test_zero_discount(self):

self.assertEqual(final_price(12345, 0), 0)

def test_exact_integer(self):

self.assertEqual(final_price(20000, 85), 17000)

def test_negative_price_rejected(self):

with self.assertRaises(ValueError):

final_price(-1, 85)

def test_invalid_discount_rejected(self):

with self.assertRaises(ValueError):

final_price(100, 101)

if __name__ == "__main__":

unittest.main()

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 9:47:58

WPF 集成 ActiViz 9.3:D3D11 互操作解决空域问题与透明叠加

简介&#xff1a;这份资源面向在 .NET 6.0 环境下使用 WPF 开发三维可视化应用的开发者&#xff0c;重点解决长期困扰 WPF 与 DirectX 混合渲染的空域&#xff08;Airspace&#xff09;问题。通过集成 ActiViz 9.3 窗口控件并结合 DirectX 互操作&#xff0c;程序实现了三维渲染…

作者头像 李华
网站建设 2026/10/13 9:47:02

从无状态到有状态:AGENTS.md 与 Memory 工程实践指南

1. 从无状态到有状态&#xff1a;AI 编程范式转换的底层逻辑1.1 为什么“无状态模型”正在成为瓶颈过去两年&#xff0c;大多数人用 AI 写代码的方式其实非常原始&#xff1a;打开一个对话框&#xff0c;把需求敲进去&#xff0c;模型吐出一段代码&#xff0c;复制粘贴&#xf…

作者头像 李华
网站建设 2026/10/13 9:44:02

基于数字孪生的一键顺控五防校验系统落地方案

简介&#xff1a;基于数字孪生技术的一键顺控/五防校验系统PPT课件&#xff0c;面向电力自动化与智慧变电站领域的工程师、运维及项目管理人员&#xff0c;适用于技术汇报、方案评审和专题培训等场景。内容完整覆盖系统背景、工作原理、系统特点、系统组成与工程服务五大模块&a…

作者头像 李华
网站建设 2026/10/13 9:43:18

学习ESP32-GPIO

文章目录前言一、了解开发板电路二、了解芯片的启动配置三、了解芯片内部GPIO的部分电路四、明确目的看数据手册五、代码编写1.main主函数编写2.led.c模块的编写3.led.h模块的编写4.led模块CMakeLists.txt文件的编写5.main主函数的CMakeLists.txt文件编写六、我踩的一些坑七、总…

作者头像 李华
网站建设 2026/10/13 9:41:25

Spring Boot 3.4 整合 Swagger 与 MyBatis-Plus 实践

这件事我得先讲个背景。前阵子帮某朋友的旧项目做升级&#xff0c;技术栈从 Spring Boot 2.7 直接跳到 3.4&#xff0c;本来以为只是换几个依赖版本的事&#xff0c;结果文档工具和 ORM 这一层就卡了两天。网上随便一搜&#xff0c;十有八九还是 Springfox 配 Boot 2 的老教程&…

作者头像 李华
网站建设 2026/10/13 9:40:28

VS2005+Access老组合实战:WinForm增删改查与避坑指南

简介&#xff1a;这份资源是Visual Studio 2005环境下Access数据库开发的经典案例配套光盘&#xff0c;面向正在学习C#与Access数据库结合开发的高校学生、培训学员及初级开发者&#xff0c;帮助解决从建库建表到窗体交互的完整实践问题。压缩包为zip格式&#xff0c;整体约11.…

作者头像 李华