IntelliGit第七期:智能提交工作流、AI 接入与设置面板黑屏修复
好的,下面是可以直接复制到 CSDN 的 Markdown 版本:
一、项目背景
IntelliGit 是一个面向 Git 工作流增强的桌面应用,技术栈主要包括 Electron、React、TypeScript、Ant Design、Zustand 和 Electron IPC。
本次开发沿三条主线推进:
- 补齐智能提交的完整执行链路——从 AI 生成到用户确认,再到成功 / 失败反馈
- 接入 OpenAI-compatible 协议,默认推荐 DeepSeek,支持 AI 分析变更与生成 commit message
- 定位并修复设置面板打开时的黑屏问题,顺带优化提交面板布局
项目的核心目标是把"智能提交"从可用推进到好用——让 AI 不只是生成一行 commit message,而是真正理解变更意图、帮助用户组织提交。
二、智能提交工作流
2.1 补齐完整的提交执行链路
之前的项目中,智能提交的"分析"和"生成"部分已经可以运行,但缺少完整的执行闭环:用户生成了 commit message 之后,没有清晰的路径走到"提交成功"这一步,失败了也不知道发生了什么。
本次把整个链路打通,完整流程如下:
查看变更 diff
↓
AI 分析变更分组
↓
选择分组,暂存所选文件
↓
AI 生成 Commit Message
↓
用户编辑 Commit Message(可选)
↓
用户点击"确认创建 Commit"
↓
提交前三重校验
↓
提交成功 → 显示短 hash,清空状态
提交失败 → 保留输入,展示错误信息
2.2 提交前三重校验
为了防止用户误触或在错误状态下提交,在执行 git commit 前增加了三层检查,任意一层不通过都会阻止提交并给出提示:
- 是否存在非空的 commit message
- 是否存在至少一个已暂存(staged)的文件
- 当前是否没有其他 Git 操作正在执行(防止并发冲突)
2.3 成功与失败的反馈设计
提交结果直接显示在面板内,不需要用户打开终端或查看 Git 日志:
- 提交成功:清空 commit message 输入框,清空智能分组状态,在面板内显示短 hash,如
ea39eb2 - 提交失败:保留用户已输入的 commit message,展示具体错误信息,方便用户修改后直接重试
2.4 提交面板布局重构
随着智能分组结果、commit message 输入框、确认按钮、反馈区域等内容逐渐增多,原有面板高度(156px)已严重不够用,内容互相挤压导致按钮看不到。
本次将父容器高度调整为 340px,并重新划分了面板内的布局层次:
智能工具栏
↓
分组分析结果(可折叠滚动区)
↓
Commit Message 输入框
↓
确认提交按钮
↓
提交反馈区域
分组结果区使用折叠面板展示,折叠时只显示"分析结果:N 个分组",展开后可以看到 AI 分析出的分组类型、摘要和涉及文件,不影响下方的提交操作区域。
涉及文件:
src/renderer/src/views/ChangesView/CommitPanel.tsxsrc/renderer/src/views/ChangesView/CommitPanel.module.csssrc/renderer/src/services/gitWorkflowService.tssrc/renderer/src/views/ChangesView/ChangesView.module.css
三、设置面板黑屏问题修复
3.1 问题现象
接入 AI 配置面板后,点击左侧"设置"按钮,界面直接变黑,主界面其他功能完全正常。控制台报错:
Maximum update depth exceeded
3.2 排查过程
第一步怀疑是 useEffect 内部的表单同步逻辑——设置面板中有这样一段常见模式:
useEffect(() => {
form.setFieldsValue(config) // config 变化 → 同步表单
}, [config]) // 表单变化 → state 更新 → 再次触发
移除这部分逻辑后,问题依然存在。继续往上追溯,最终定位到 Zustand store 的 selector 写法。
3.3 根本原因
原来的 selector 每次调用都返回一个新的对象字面量:
// 有问题的写法
const { config, status, error } = useStore(
state => ({ config: state.config, status: state.status, error: state.error })
)
// 每次调用都是新对象 → 引用永远不等 → 无限更新
在 React 19 / Zustand 的 useSyncExternalStore 机制下,React 通过引用比较判断 store 快照是否变化。每次调用都返回新对象,引用永远不相等,React 认为 store 持续变化,无限触发 re-render,最终栈溢出,表现为黑屏。
3.4 修复方案
将 selector 拆分为分别订阅各字段的独立调用:
// 修复后
const config = useLlmConfigStore(s => s.config)
const status = useLlmConfigStore(s => s.status)
const error = useLlmConfigStore(s => s.error)
// 每个 selector 返回基础值 → 值未变时引用稳定
每个 selector 返回稳定的基础值,只有对应字段真正发生变化时才会触发重新渲染。
3.5 增加 Fatal Error Fallback
为了避免下次再出现"纯黑屏但没有任何信息"的情况,在 renderer 入口增加了全局错误兜底页:如果 React 启动或运行时抛出未捕获的错误,页面会直接展示错误信息与完整堆栈,而不是纯黑屏,方便后续定位问题。
涉及文件:
src/renderer/src/viewModels/useGlobalSettingsPanelModel.tssrc/renderer/src/main.tsxsrc/renderer/src/layout/GlobalSettingsPanel/index.tsx
四、接入 OpenAI-compatible 协议
4.1 为什么优先接 OpenAI-compatible
没有直接绑定某一家厂商,而是优先支持 OpenAI-compatible 协议——这个协议已经成为事实标准,一次接入可以兼容绝大多数主流模型:
- OpenAI
- DeepSeek
- 通义千问(兼容模式)
- Moonshot / Kimi
- 智谱 GLM
- SiliconFlow
- 火山方舟
- 本地模型(Ollama、LM Studio、vLLM)
4.2 默认配置
考虑到国内用户的实际情况,OpenAI-compatible 模式的默认配置直接指向 DeepSeek:
Provider : OpenAI 兼容
Base URL : https://api.deepseek.com
Model : deepseek-chat
Temperature: 0.2
Max Tokens : 4096
用户只需填入自己的 DeepSeek API Key,即可直接使用智能提交能力。
4.3 Base URL 规范化
用户填写 Base URL 时,可能带或不带 /v1 后缀,不处理会拼出 /v1/v1/chat/completions 这样的错误地址。实现了规范化逻辑统一处理:
https://api.deepseek.com → https://api.deepseek.com/v1/chat/completions ✓
https://api.deepseek.com/v1 → https://api.deepseek.com/v1/chat/completions ✓
4.4 其他工程细节
- AI 请求统一设置 30 秒超时,超时后自动 fallback 到本地模板
- 错误日志中对 API Key 脱敏:
sk-abcdef...→sk-***,防止密钥泄露
涉及文件:
src/renderer/src/agent/llmClient.tssrc/renderer/src/layout/GlobalSettingsPanel/index.tsx
五、LLM 请求迁移到主进程代理
5.1 问题现象
配置好 DeepSeek 之后,点击"分析变更分组",控制台报:
Failed to fetch
这不是 API Key 错误,也不是模型名称问题,而是请求根本没有发出去。
5.2 原因分析
Electron 的 renderer 进程本质上运行在一个受限的浏览器环境中。直接在 renderer 里 fetch 第三方 API 会受到 CORS 策略和 Electron 安全沙箱的限制,导致请求被拦截。
5.3 解决方案
将 LLM 的 HTTP 请求从 renderer 进程迁移到主进程(main process)执行,renderer 通过 IPC 发起调用:
Renderer(构造请求参数)
↓ IPC channel: llm:proxy
Main Process(执行 fetch)
↓ HTTPS
DeepSeek / OpenAI API
↑ 响应结果原路返回
主进程没有浏览器 CORS 的限制,可以直接发起任意 HTTP 请求。这个架构的额外好处:
- API Key 不再出现在 renderer 的请求逻辑中,也不会出现在 DevTools 的 Network 面板
- 超时控制、重试、日志审计可以集中在主进程统一处理
- 后续如需对 AI 请求做频控或审计,只需改一处
涉及文件:
src/main/ipc/llmHandlers.tssrc/main/ipc/index.tssrc/preload/index.tssrc/shared/types/sidecar.tssrc/renderer/src/agent/llmClient.ts
六、AI 能力设计
6.1 Commit Message 生成
Prompt 设计思路
要求模型只输出 JSON,不带任何 Markdown 或解释文字,遵循 Conventional Commits 规范。type 从固定集合中选取,subject 使用中文短句,scope 根据文件路径自动推断。
支持的 type:feat / fix / refactor / style / docs / test / chore / perf / build / ci
目标输出示例:
{
"type": "fix",
"scope": "settings",
"subject": "修复设置面板打开黑屏",
"body": "",
"breaking": false
}
最终转换为:
fix(settings): 修复设置面板打开黑屏
输出解析与清洗
模型的输出不总是干净的 JSON,解析流程分三步:
- 从响应文本中提取 JSON 部分,去掉
```json等 Markdown 标记 - 用
JSON.parse解析,并对 schema(type、subject 等字段)做校验 - 任何环节失败则 fallback 本地模板,保证流程不中断
6.2 AI 变更分组
功能目标
一次开发往往同时修改了多个不相关的问题。AI 变更分组的目标是把这些变更按提交意图拆开,让用户可以分批提交。
例如同时改了设置面板黑屏修复、LLM 请求代理、CommitPanel 样式,AI 应拆分为三个分组并各自给出合适的 type 和摘要:
{
"groups": [
{
"type": "fix",
"scope": "settings",
"summary": "修复设置面板打开黑屏",
"files": [
"src/renderer/src/viewModels/useGlobalSettingsPanelModel.ts",
"src/renderer/src/layout/GlobalSettingsPanel/index.tsx"
]
},
{
"type": "feat",
"scope": "ai",
"summary": "通过主进程代理 LLM 请求",
"files": [
"src/main/ipc/llmHandlers.ts",
"src/renderer/src/agent/llmClient.ts"
]
}
]
}
Prompt 约束与输出清洗
Prompt 中加入了明确限制:只输出 JSON,不超过 5 个分组,files 必须来自 diff 中真实出现的路径,不允许编造文件。
输出后进行二次清洗:
- 过滤 diff 中不存在的文件路径
- 对文件列表去重
- 过滤空 summary 和空 files 的分组
- 最多保留 5 组,超出截断
- 结果不可用时 fallback,避免污染后续 Git 操作
6.3 降级策略
智能提交的核心原则:AI 只是辅助,任何情况下流程都不应该卡死。触发 fallback 的条件:
- 未配置 API Key
- 请求超时(超过 30 秒)
- 请求失败(网络错误等)
- 模型返回空内容
- 输出不是合法 JSON
- schema 校验失败
以上任意一种情况,均自动切换到本地模板生成 commit message,用户无感知,提交流程继续。
涉及文件:
src/renderer/src/agent/prompts/commit.tssrc/renderer/src/agent/outputParser.tssrc/renderer/src/services/smartCommitProvider.ts
七、安全设计
本次 AI 接入的核心原则:AI 只生成建议,人始终在决策回路中。
- Commit 前必须用户点击确认,不会自动 commit
- 不会自动 push,push 操作完全由用户发起
- AI 分组结果中的文件路径必须来自真实 diff,不允许模型编造
- API Key 错误信息脱敏,不出现在日志中
- Renderer 不直接请求 LLM,所有 AI 请求通过主进程 IPC 代理
- 模型输出经过 JSON 解析和 schema 校验后才会影响界面状态
八、当前项目进度
| 模块 | 状态 |
|---|---|
| Git 基础操作(status / diff / stage / commit / push / pull) | ✅ 已完成 |
| 部分暂存(行级 diff patch 选择) | ✅ 已完成 |
| 智能提交工作流(分组→暂存→生成→确认→反馈) | ✅ 基本闭环 |
| AI Commit Message 生成(OpenAI-compatible) | ✅ 已完成 |
| AI 变更分组 | ✅ 已完成 |
| 全局设置面板 + AI 配置项 | ✅ 已完成 |
| LLM 请求主进程 IPC 代理 | ✅ 已完成 |
| 智能冲突管控 | 🕐 待开始 |
| 自然语言 Git 助手 | 🕐 待推进 |
| 高危操作安全体系(force push 阻断、reset 二次确认) | 🕐 待推进 |
九、遇到的典型问题总结
问题一:设置面板黑屏
- 根因:Zustand selector 每次返回新对象,
useSyncExternalStore引用比较永远失败,无限触发 re-render - 修复:拆分 selector,分别订阅
config/status/error各字段
问题二:AI 请求 Failed to fetch
- 根因:Renderer 直接请求第三方 LLM API,受到 CORS 和 Electron 安全沙箱限制
- 修复:通过 Electron 主进程代理 LLM 请求,新增 IPC channel
llm:proxy
问题三:CommitPanel 中分组结果和提交按钮互相挤占
- 根因:提交面板高度太小(
156px),承载内容过多 - 修复:提高父容器高度至
340px,重新设计 CommitPanel 布局,分组结果使用可折叠滚动区域
问题四:AI 输出不可控
- 根因:模型可能返回 Markdown 标记、解释文本、非法 JSON 或编造文件路径
- 修复:Prompt 强约束 + JSON 提取 + schema 校验 + 文件路径过滤 + fallback 降级
十、下一步计划
AI 生成质量优化
- 调整 Commit Message prompt,控制 subject 长度
- 自动推断更准确的 scope
- 支持多语言配置
变更分组体验优化
- 分组结果可编辑,支持合并 / 拆分分组
- 一键暂存多个分组
- 支持分组后连续创建多个 commit
智能冲突管控
- 检测 merge conflict 状态,解析冲突块
- 展示 ours / theirs 对比
- AI 生成冲突解决建议,用户确认后写回文件
自然语言 Git 助手
- 自然语言输入 → 识别 Git 意图 → 生成操作计划 → 用户确认 → 执行 → 反馈结果
Safety 体系完善
- 高危操作识别,force push 阻断
- reset / rebase 二次确认
- AI 工具调用审计日志
目前 IntelliGit 的智能提交已经从"本地模板降级可用"推进到了"真实 AI 可用"的阶段,完整的提交工作流基本闭环。下一阶段的重点是提升 AI 输出质量,以及开始推进智能冲突管控方向。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)