AI提效Android开发系列 · 第5/5篇(完结篇)
从需求到上线,用AI重塑Android开发全流程
第1篇:AI提效Android开发全景图:从需求到上线的AI工具链
第2篇:AI驱动需求梳理与Spec编写:让PRD自动变成技术方案
第3篇:AI编码提效实战:Skill、Rule与上下文工程
第4篇:AI Code Review:让每一行代码都有AI审查员
第5篇:AI Bug修复与测试生成:从崩溃日志到修复PR的自动化(本篇 · 完结)
周五晚上十一点半。电脑已经合上了,你正准备出门。手机震了一下------Bugly 告警推送:线上新版本 Top 1 Crash,影响用户数 3,200+,曲线还在往上走。
你叹了口气,重新打开电脑。堆栈一百多行,Java/Kotlin 混合调用链,横跨三个模块。你开始做那个最痛苦的事情------在脑子里当 debugger,一帧一帧地回溯执行路径,试图找到那个"不该是 null 但偏偏是 null"的鬼地方。
四十分钟后定位到了根因。又花二十分钟写修复。然后你看了一眼时间------凌晨十二点半了。要不要补个测试?算了,先提 hotfix MR,明天再说。
然后明天就没有了。"明天"永远不会来。这个修复就这样裸奔上线了,直到下次类似的问题再次出现。
这是「AI提效Android开发」系列的完结篇。前四篇走完了需求→编码→Review 的流程,这一篇补上最后一块拼图:Bug 修复和测试生成------看看 AI 能不能帮我们把"周五半夜救火"变成"周一早上审查 AI 的修复方案"。
一、修 Bug 的时间到底花在哪了
不着急上 AI。先拆解一下从"收到崩溃告警"到"修复代码合入"这个链路。我统计了团队最近两个月的 12 次线上 Crash 修复,时间分布大致是这样的:
• 告警识别 + 信息收集(15-30 分钟):看堆栈、查设备分布、翻用户反馈、确认影响范围
• 根因定位(30 分钟 - 3 小时):顺着堆栈看代码、追数据流、尝试复现
• 编写修复(15-45 分钟):写修复代码,通常改动不超过五个文件
• 验证 + 测试(30 分钟 - 1 小时):跑现有测试、手动验证、偶尔补个测试
• 提 MR + Review + 合入(1-4 小时):等人看、等人批、等 CI 跑完
一个反直觉的发现:写修复代码的时间只占总时间的 10-15%。绝大部分时间花在了"搞清楚问题在哪"和"等流程走完"上。
而这两个环节,恰好是 AI 最能发力的地方------分析能力强、不需要等人、可以同时做多件事。
二、AI 崩溃分析:不只是"把堆栈丢给 ChatGPT"
很多人试过把 Crash 堆栈直接丢给 ChatGPT 或 Claude,得到一个半对半错的分析,然后就放弃了:"AI 不靠谱嘛"。
问题不在 AI,在于你喂进去的信息太少了。一个堆栈只是冰山一角,AI 看着一堆 at com.example.app.xxx,它不知道这些代码长什么样、最近改了什么、项目架构是怎么设计的。
2.1 构建"崩溃上下文包"
真正有效的做法是给 AI 构建一个完整的"崩溃上下文包":
python
# crash_context_builder.py
def build_crash_context(
crash_stack: str,
project_root: str
) -> dict:
"""从堆栈中提取涉及的源文件,
打包完整分析上下文"""
# 1. 解析堆栈中所有本项目的调用帧
our_frames = extract_project_frames(
crash_stack,
package_prefix="com.example.app"
)
# 2. 读取涉及的源文件完整内容
source_files = {}
for frame in our_frames:
path = resolve_source_path(
frame.class_name, project_root
)
if path and path.exists():
source_files[str(path)] = \
path.read_text()
# 3. 找到相关的数据类和接口定义
# (崩溃经常涉及数据层)
related_types = find_related_types(
source_files, project_root
)
# 4. 最近 7 天相关文件的 git log
# (大部分线上 Crash 和最近提交有关)
recent_changes = get_recent_changes(
[f.file_path for f in our_frames],
days=7
)
return {
"crash_stack": crash_stack,
"source_files": source_files,
"related_types": related_types,
"recent_changes": recent_changes,
"project_rules": load_project_rules()
}
每一项都有明确的作用:
• source_files:让 AI 看到完整代码逻辑,不靠猜
• related_types:数据类定义------NPE 经常是因为某个字段声明为非空但运行时为空
• recent_changes:最近的 git diff,帮 AI 判断是不是回归 bug
• project_rules:CLAUDE.md 或编码规范,确保生成的修复代码风格正确
我实测过有无上下文包的分析准确率差异:只给堆栈的准确率约 45%,加上完整上下文后提升到约 80%。差距惊人。
2.2 让 AI 像侦探一样推理
prompt 不能只说"分析这个崩溃"。你得给 AI 一个推理框架,逼它一步步推导,而不是直接跳到结论:
diff
"""
你是 Android 崩溃分析专家。
请严格按以下框架分析:
### 1. 崩溃分类
- 异常类型(NPE/OOM/ANR 等)
- 精确触发位置(类名 + 行号)
- 影响范围(UI 线程/后台/全局)
### 2. 根因推理链(最重要)
用"因为→所以"的链式推理:
- 直接原因:哪个变量/状态异常
- 为什么异常:追溯赋值/初始化路径
- 触发条件:什么操作/时序/状态组合
- 是否回归:对照 recent_changes 判断
### 3. 修复方案(必须双方案)
- 方案A:修复根因
- 方案B:防御性保护
- 每个方案给具体代码 diff
### 4. 风险评估
- 修复的副作用和兼容性
- 是否需要灰度验证
### 5. 测试建议
- 必须覆盖的回归场景
"""
这里有两个设计意图:
**"根因推理链"**是核心。不让 AI 直接蹦答案,而是让它像侦探破案一样展示推理过程。这样你一看就知道它的分析对不对------如果推理链中间有一步不成立,整个结论就不可信。
**"方案A + 方案B"**是防御。很多时候根因修复不能一步到位(比如需要服务端配合),防御性保护至少能先止血。如果 AI 只给了一个方案,多半漏了什么。
2.3 实战:一个真实的 NPE 分析
来看一个真实案例。线上报了这样一个崩溃:
csharp
java.lang.NullPointerException:
Attempt to invoke virtual method
'java.lang.String
com.example.app.model.UserProfile
.getDisplayName()'
on a null object reference
at com.example.app.ui.chat
.ChatViewModel.formatMessage
(ChatViewModel.kt:142)
at com.example.app.ui.chat
.ChatViewModel$loadMessages$1
.invokeSuspend
(ChatViewModel.kt:87)
at kotlin.coroutines.jvm.internal
.BaseContinuationImpl.resumeWith
(ContinuationImpl.kt:33)
把堆栈 + 完整上下文包喂给 AI 后,它输出的分析:
AI 根因分析
直接原因 :ChatViewModel.kt 第 142 行调用 userProfile.displayName,但 userProfile 为 null。
推理链 :loadMessages()(第 87 行)调用 userRepository.getCurrentUser() 获取用户信息 → 该方法在用户 token 过期时返回 null → 三天前的 commit a3f7b2c 移除了 formatMessage 中原有的空检查 → commit message 写着"用户登录态下 profile 不可能为 null" → 但忽略了 token 刷新窗口期:用户 session 有效但 token 恰好过期。
触发条件:用户 token 在 loadMessages 执行期间过期,且 token 刷新请求尚未完成。在登录超过 2 小时的用户中有约 0.3% 概率触发。
回归判定:是。commit a3f7b2c 引入。
你注意这个分析的质量:它不仅定位到了 NPE 的直接位置,还追溯到了三天前某个 commit 移除空检查的根因,甚至分析了触发概率。这比人工 debug 快至少十倍,而且推理链可验证------你去看 commit a3f7b2c 就知道 AI 说得对不对。
2.4 Android Studio + Gemini AQI:IDE 里的原生体验
不想自建分析流程?Android Studio Panda 把这套能力产品化了。App Quality Insights 面板对接 Firebase Crashlytics,Gemini 直接在 IDE 里分析崩溃:
• 打开 AQI 面板 → 选中一个 Crash → 点击 "Show Gemini Insights"
• Gemini 生成崩溃摘要、可能的根因、推荐的修复方向
• 点击分析中提到的类/方法名,直接跳转到本地源码
体验确实不错,但有两个实际限制。第一,分析深度取决于 Crashlytics 上传的信息量------如果混淆后没上传 mapping 文件,Gemini 也看不懂堆栈。第二,它不了解你项目的架构约定和业务规则。
我的建议:简单的 NPE / ClassCastException 用 AQI + Gemini 快速定位,复杂的并发/生命周期/跨模块问题用自建分析流程。不冲突,各取所长。
三、AI 生成修复代码:diff 比完整文件靠谱
分析完根因,下一步是让 AI 写修复代码。这里有个反直觉的经验:让 AI 输出 diff 格式比输出完整文件靠谱得多。
原因是修复通常只改几行。让 AI 输出整个 300 行的文件,它可能"顺手"改了其他地方------有时候是优化(比如加个 TODO 注释),有时候是破坏(悄悄改了一个不相关的条件判断)。限定为 diff 格式可以精确控制变更边界。
python
"""
基于你的根因分析,生成修复代码。
## 输出规则
1. 使用 unified diff 格式
2. 只改必要的行,不做无关重构
3. 同时给出根因修复 + 防御性保护
4. Kotlin 风格:?.let / ?: / when
5. 遵循项目规范:{project_rules}
## 硬约束
- 不引入新依赖
- 不改变公开 API 签名
- 不破坏向后兼容
"""
继续上面那个 ChatViewModel 的例子,AI 生成的修复:
kotlin
// ChatViewModel.kt
// ── 方案A:根因修复 ──
// 确保 token 刷新完成后再取 profile
- val profile = userRepo.getCurrentUser()
+ val profile = userRepo
+ .getCurrentUserWithRefresh()
// ── 方案B:防御性保护 ──
// formatMessage 恢复空安全
private fun formatMessage(
msg: Message,
- profile: UserProfile
+ profile: UserProfile?
): FormattedMessage {
- val senderName = profile.displayName
+ val senderName = profile?.displayName
+ ?: "用户"
// ...
}
两个方案应该同时采用:方案A 解决根因(确保 token 刷新后再获取 profile),方案B 兜底(即使 profile 仍然为 null 也不崩溃)。这种"腰带 + 吊带"的修复策略,比单纯加个空判断要健壮得多。
四、端到端自动化:从告警到 PR
如果你想更进一步,可以搭建一个全自动 Pipeline:告警进来 → AI 分析 → AI 修复 → 自动跑测试 → 自动创建 MR。2026 年这已经不是 PPT------Claude Code 的自动修复 PR 功能、OpenClaw 的 cron 监控 + AI Agent 联动,都已经有成熟的实践案例。
ini
# auto_fix_pipeline.py
# 告警 → 分析 → 修复 → 测试 → PR
async def auto_fix_crash(
crash_report: CrashReport
):
# Step 1: 构建崩溃上下文包
context = build_crash_context(
crash_report.stack_trace,
project_root=PROJECT_ROOT
)
# Step 2: AI 分析根因
analysis = await analyze_crash(context)
if analysis.confidence < 0.7:
notify_human(
"AI 信心不足,需人工介入",
crash_report, analysis
)
return
# Step 3: 生成修复 diff
fix = await generate_fix(
analysis, context
)
# Step 4: 创建分支、应用修复
branch = f"ai-fix/{crash_report.id}"
create_branch(branch)
apply_diff(fix.diff, branch)
# Step 5: 生成回归测试
tests = await generate_regression_test(
analysis, fix, context
)
write_test_file(tests, branch)
# Step 6: 跑测试(最多自动修正 3 轮)
for attempt in range(3):
result = run_tests(branch)
if result.passed:
break
fix = await fix_test_failure(
result, context
)
apply_diff(fix.diff, branch)
# Step 7: 创建 MR
mr = create_merge_request(
branch=branch,
title=f"[AI-Fix] {analysis.summary}",
description=format_mr_desc(
analysis, fix, tests
),
labels=["ai-fix", "needs-review"]
)
# Step 8: 通知相关人
notify_team(crash_report, mr)
这个 Pipeline 有几个关键设计:
• 信心阈值:AI 分析的 confidence 低于 0.7 就不自动修复,直接通知人工。不确定的时候不瞎搞
• "测试不过就再修一轮":AI 生成的代码不一定一次过。让它看测试失败日志 → 自动修正,最多 3 轮
• MR 标签 needs-review:AI 修复的 MR 必须经过人类 Review 才能合入。这条底线不能破
• MR 描述包含完整分析:不只有 diff,还有根因推理链、触发条件、风险评估。Reviewer 一眼就能判断靠不靠谱
划重点:自动化的边界是"帮你干活",不是"替你决策"。AI 创建的 MR 可以自动跑 CI,但合入按钮只有人类能按。
五、AI 测试生成:告别"来不及写测试"
Android 团队的测试覆盖率低是公开的秘密。不是不想写,是真的没时间。尤其是修完 bug 的时候------赶着上线,测试下次再补吧。然后就永远没有下次了。
AI 测试生成解决的核心痛点是:把"从零写一个测试"变成"审查和微调一个测试"。前者心理门槛高、耗时长,后者顺手就做了。
5.1 针对 Bug 修复的回归测试
比通用单测生成更有价值的场景是:修完 Bug 后自动生成回归测试。目标很清晰------确保这个 bug 永远不会再出现。
python
"""
根据以下崩溃分析和修复,生成回归测试。
## 崩溃根因
{analysis.root_cause}
## 触发条件
{analysis.trigger_condition}
## 修复 Diff
{fix_diff}
## 测试规则
1. 必须包含一个"修复前会失败"的测试
(证明 bug 确实存在)
2. 覆盖所有已知触发条件
3. 覆盖修复引入的新代码分支
4. 使用项目测试框架:
JUnit 5 + MockK + Turbine
5. 命名:test_动作_条件_预期结果
6. mock SUT 的依赖,不 mock SUT 本身
"""
AI 生成的回归测试(还是前面那个 ChatViewModel):
kotlin
class ChatViewModelCrashRegressionTest {
private lateinit var viewModel:
ChatViewModel
private val userRepo:
UserRepository = mockk()
private val chatRepo:
ChatRepository = mockk()
@BeforeEach
fun setup() {
viewModel = ChatViewModel(
userRepo, chatRepo
)
}
@Test
fun formatMessage_profileNull_
returnsDefaultName() = runTest {
// 修复前这个测试会 NPE
val result = viewModel
.formatMessage(
testMessage, null
)
assertEquals(
"用户", result.senderName
)
}
@Test
fun loadMessages_tokenExpired_
triggersRefresh() = runTest {
coEvery {
userRepo
.getCurrentUserWithRefresh()
} coAnswers {
delay(100) // 模拟刷新耗时
testProfile
}
coEvery {
chatRepo.getMessages()
} returns testMessages
viewModel.loadMessages()
advanceUntilIdle()
// 确认调用了带刷新的版本
coVerify {
userRepo
.getCurrentUserWithRefresh()
}
}
@Test
fun loadMessages_concurrent_
noDuplicates() = runTest {
// 模拟两次并发调用
coEvery {
userRepo
.getCurrentUserWithRefresh()
} coAnswers {
delay(50)
testProfile
}
coEvery {
chatRepo.getMessages()
} returns testMessages
launch { viewModel.loadMessages() }
launch { viewModel.loadMessages() }
advanceUntilIdle()
// 验证消息列表无重复
val msgs = viewModel
.messages.first()
assertEquals(
msgs.distinctBy { it.id }.size,
msgs.size
)
}
}
注意第三个测试------AI 不仅测了修复点本身,还补了一个并发测试。因为我们在崩溃分析 prompt 里要求它识别触发条件(token 刷新窗口期的竞态),它就知道并发场景需要覆盖。Prompt 的分析深度决定了测试的覆盖质量。
5.2 小心"测了个寂寞"
AI 生成测试最常见的坑:它 mock 了被测对象本身,然后验证 mock 的返回值等于 mock 设置的值。等于自己出题自己答,测试全绿,但什么也没验证。
kotlin
// "测了个寂寞"
@Test
fun getUser_returnsUser() = runTest {
val expected = User("test")
coEvery {
userRepo.getUser()
} returns expected
val result = userRepo.getUser()
assertEquals(expected, result)
// ← 你验证了 MockK 能工作
// 不是验证你的业务代码
}
// 测真正的 ViewModel 逻辑
@Test
fun viewModel_loadsUser_updatesState() =
runTest {
coEvery {
userRepo.getUser()
} returns User("test")
viewModel.loadUser()
advanceUntilIdle()
// 验证 ViewModel 状态变化
assertEquals(
"test",
viewModel.uiState.value.userName
)
}
在 prompt 里必须加一条铁律:**"测试必须调用 SUT(被测系统)的真实方法,只 mock SUT 的外部依赖,绝不 mock SUT 本身。"**加了这条之后,"测了个寂寞"的概率大幅降低。
5.3 Meta TestGen-LLM 的启发
Meta 在 TestGen-LLM 论文中分享了几个关键发现:
• 覆盖率提升:在已有测试基础上,LLM 额外生成的测试平均提升 15.7% 行覆盖率
• 可编译率:67% 直接编译通过,一轮自动修复后提到 82%
• 人类评价:可编译的测试中,57% 被开发者评为"有用,愿意保留"
Meta 的核心 insight 是:别让 AI 从零开始,让它在已有测试上"改进"。给 AI 看你现有的测试风格和模式,让它补缺失场景,效果远好于凭空创造。这和我们第三篇讲的"上下文工程"是一个道理------AI 需要例子来学习你的风格。
六、变更影响分析:只跑该跑的测试
修完 Bug 跑全量测试?我们项目要 45 分钟。Hotfix 场景根本等不起。
AI 可以做变更影响分析------根据改了哪些代码,推断该跑哪些测试。
python
"""
分析以下变更的影响范围,
推荐精准测试集。
## 变更文件
{changed_files_with_diff}
## 模块依赖图
{module_deps}
## 分析维度
1. 直接影响:被改函数/类的测试
2. 上游影响:调用了被改代码的模块
3. 数据流:改了 data class → 序列化
/API/DB 是否受影响
4. UI 层:Composable 的参数/状态变了
## 输出
{
"must_run": ["必跑测试类"],
"should_run": ["建议跑的测试类"],
"estimated_minutes": 3
}
"""
在 CI 里集成精准测试,MR 级别的测试时间从 45 分钟缩短到 3-5 分钟:
bash
# CI:智能测试选择
smart-test:
stage: test
script:
# AI 分析影响范围
- python scripts/impact_analysis.py
--diff $CI_MERGE_REQUEST_DIFF_REF
> test_plan.json
# 核心测试(阻塞合并)
- ./run_tests.sh
--from test_plan.json
--tier must_run
# 扩展测试(不阻塞)
- ./run_tests.sh
--from test_plan.json
--tier should_run
--allow-failure
Reviewer 看到的 MR 状态是"核心测试 / 扩展测试运行中 ⏳",可以马上开始 Review,不用枯等。
七、踩坑实录:AI 修 Bug 的四个真实教训
用了两个月,踩的坑比想象中多。分享四个最痛的。
7.1 AI 最爱"加个空判断了事"
给 AI 一个 NPE,它的第一反应是加 ?.let 或 ?: return。技术上正确------不崩了。但如果根因是上游数据源没初始化,你只是把崩溃变成了"功能默默失效"。用户从"闪退"变成"点了没反应",体验可能更差。
对策:Prompt 里强制双方案。如果 AI 只给了防御性方案,追问"根因是什么?根因修复的方案是什么?"
7.2 ANR 分析准确率断崖式下降
Crash 分析准确率 80%,但 ANR 只有约 45%。原因是 ANR trace 包含所有线程的堆栈,动辄几千行,AI 容易"迷路"。而且 ANR 的根因经常不在主线程堆栈里------主线程被阻塞了,但阻塞它的锁可能在另一个线程。
对策:对 ANR trace 做预处理。只保留主线程 + 持有目标锁的线程 + Binder 调用链,并标注锁竞争关系。预处理脚本大概 50 行 Python:
python
def preprocess_anr_trace(
full_trace: str
) -> str:
threads = parse_threads(full_trace)
main = threads["main"]
# 找主线程等待的锁
blocked_on = extract_lock_wait(main)
if not blocked_on:
return main.raw # 没锁竞争
# 找持有该锁的线程
holder = find_lock_holder(
threads, blocked_on
)
# 精简输出
return f"""
== 主线程(BLOCKED)==
等待锁: {blocked_on}
{main.stack[:30]}
== 锁持有者: {holder.name} ==
{holder.stack[:30]}
== 锁竞争关系 ==
main --等待--> {blocked_on}
--持有者--> {holder.name}
"""
预处理后再喂给 AI,ANR 分析准确率从 45% 提升到约 68%。还是不如 Crash,但已经能用了。
7.3 混淆堆栈一定要先 retrace
Release 包的堆栈是混淆过的------a.b.c.d()。AI 对着这个什么也分析不出来。Pipeline 里必须在分析前加一步 retrace。
如果用 Bugly,它的 API 可以直接返回还原后的堆栈。如果用 Firebase Crashlytics,确保上传了 mapping.txt。这一步看起来简单,但如果忘了,整个 Pipeline 就废了。
7.4 业务逻辑 Bug 不要指望 AI
"用户充值 100 元但余额只加了 10 元"------这种 bug 堆栈是正常的,代码也没崩,只是业务逻辑算错了。AI 不知道"100 元应该加 100"这个业务规则,它能看出代码在做乘法而不是加法,但不知道哪个是对的。
明确边界:AI 擅长分析技术性 Bug(NPE、OOM、并发、类型错误),不擅长业务逻辑 Bug。后者还是得靠人。
八、实践数据:两个月的效果
分享试点两个月的真实数据。
AI Bug 修复 Pipeline 实践数据
• 处理 Crash 总数:34 个(Top 10 或影响用户 > 500)
• 根因分析准确率:27/34 ≈ 79%(NPE 类 92%,并发类 71%,业务类 40%)
• 修复直接可用:18/34 ≈ 53%(人类 Review 后无需修改直接合入)
• 修复需要微调:9/34 ≈ 26%(分析正确,代码需要小幅调整)
• 完全不可用:7/34 ≈ 21%(分析偏差或修复不合理)
• MR 创建速度:从告警到 MR 创建,平均 12 分钟(传统 3.5 小时)
• AI 生成回归测试保留率:约 71%
几个 takeaway:
• NPE 效果最好:因果链明确,AI 几乎不会错
• 并发/时序类次之:AI 能定位问题,但修复方案的线程安全性需要人工把关
• 业务逻辑 Bug 最差:AI 不懂业务语义
• 53% 直接可用是个好数字:超过一半的线上 Crash,从告警到可合入的 PR 只要十几分钟
九、系列总结:AI 提效的完整拼图
五篇文章,走完了一个 Android 功能从零到上线再到维护的全流程:
第1篇 · 全景图:建立"需求→编码→Review→发布→修复"五阶段模型,定义了每个环节的 AI 切入点
第2篇 · 需求→Spec:AI 把模糊 PRD 转化为结构化技术方案,"开会三天"变成"AI 出初稿 + 人工改半天"
第3篇 · AI 编码:Skill + Rule + 上下文工程三板斧,让 AI 写出"能合入"而不仅是"能跑"的代码
第4篇 · AI Review:AI 做第一个 Reviewer,扫机械性问题,人类专注架构和逻辑
第5篇 · Bug 修复:从崩溃告警到修复 PR 的全自动 Pipeline,十几分钟完成过去半天的活
如果让我用一句话总结:
AI 不是来取代开发者的。 是来让开发者只做"只有人类能做的事"。
格式化代码、扫空指针、生成样板测试、跑回归验证------这些事情人可以做,但不该花时间做。交给 AI,你的精力就能集中在产品设计、架构决策、用户体验、技术创新上。这些才是开发者真正的价值。
2026 年的 AI 工具远不完美。它会误报,会幻觉,会生成看起来对但实际不对的代码。但它已经能每天帮你省下 1-2 小时的重复劳动。这条线只会越来越陡------今年省两小时,明年可能省四小时。
不是"要不要用"的问题,是"怎么用好"的问题。
感谢读完这个系列的每一位。如果你在实践中有更好的经验或踩了不同的坑,欢迎留言交流。下个系列见。
AI提效Android开发系列 · 完结
从需求到上线,用AI重塑Android开发全流程
第1篇:AI提效Android开发全景图:从需求到上线的AI工具链
第2篇:AI驱动需求梳理与Spec编写:让PRD自动变成技术方案
第3篇:AI编码提效实战:Skill、Rule与上下文工程
第4篇:AI Code Review:让每一行代码都有AI审查员
第5篇:AI Bug修复与测试生成:从崩溃日志到修复PR的自动化(本篇)
--- 技术博客 · 叶聪路 ---