IntelliGit 项目个人博客(6)Agent Runtime、安全策略与配置系统落地
1 前言
上一篇博客介绍了 Agent 层的整体架构、LLM 客户端设计和工具注册系统。这篇记录剩余几个模块的实现细节:Agent Runtime 的执行循环、安全策略的设计思路、配置持久化的问题修复,以及 GlobalSettingsPanel 从占位符到完整功能的改造。
2 Agent Runtime:支持 Tool Call 的多轮执行循环
agentRuntime.ts 是整个 Agent 层的执行核心。它的职责是:接收任务描述,驱动 LLM 完成推理,处理 Tool Call 的多轮迭代,解析最终输出,并在任何环节出错时触发降级。
核心函数签名如下:
export async function runAgentWithFallback<T>(
config: LlmConfig | undefined,
task: AgentTask,
parseResult?: (rawOutput: string) => T | null
): Promise<AgentResult<T>>
泛型参数 T 是调用方期望的输出类型,parseResult 由调用方传入,负责把 LLM 的原始文本转换成结构化数据。这样设计的好处是 Runtime 本身不感知具体的输出格式,每个 P1 工作流用自己的 Schema 来解析,互不干扰。
执行流程分五步:
第一步,前置检查。 如果 config 为空或 apiKey 未填写,直接走降级路径返回模板结果,不尝试调用 LLM。这保证了在用户未配置 AI 的情况下,基础 Git 功能完全不受影响。
第二步,组装消息。 将 task.systemPrompt 和 task.userMessage 组装为标准的 messages 数组,连同 task.tools 里声明的工具定义一起发给 LLM。
第三步,Tool Call 循环。 LLM 可能在一次对话里多次调用工具,每次调用都需要执行对应工具、把结果追加到消息记录、再继续推理。循环在 finish_reason === 'stop' 时终止。
while (true) {
const response = await client.chat(messages, toolDefs)
if (response.finishReason === 'tool_calls') {
for (const call of response.toolCalls) {
const toolResult = await toolRegistry.execute(call.name, call.arguments)
messages.push({ role: 'tool', content: toolResult, toolCallId: call.id })
}
continue
}
// finish_reason === 'stop',退出循环
rawOutput = response.content
break
}
第四步,解析输出。 调用传入的 parseResult 函数处理 LLM 的原始文本。如果解析失败(返回 null),自动进入降级。
第五步,异常兜底。 整个执行过程包在 try-catch 里,任何未预期的异常都触发降级,而不是向上抛出导致界面报错。
3 三套 Prompt 模板
prompts/ 目录下针对三个 P1 工作流各维护一套 Prompt 模板,均支持参数渲染。
提交工作流 Prompt 接收 unified diff 作为输入,要求 LLM 输出符合 Conventional Commits 规范的结构化提交信息:
输入: unified diff
输出: { type, scope, subject, body, breaking }
System Prompt 里明确了输出格式要求,并给出了 feat / fix / refactor / chore 等类型的定义和使用场景,减少模型在类型判断上的歧义。
冲突管控 Prompt 接收三方内容(ancestor / ours / theirs)作为输入,要求 LLM 给出合并策略和建议代码:
输入: ancestor / ours / theirs 三方内容
输出: { strategy: take_ours | take_theirs | merge_both | manual, resolvedContent }
strategy 字段限定了四个枚举值,防止模型输出无法处理的策略描述。manual 表示模型认为无法自动解决,需要用户介入。
自然语言助手 Prompt 接收用户的自然语言输入,输出结构化的操作计划:
输入: 用户自然语言
输出: { intent, operations[{ command, args, riskLevel }], requiresWorkflow }
riskLevel 字段要求模型对每条 Git 命令做风险标注,这个结果会直接传入安全策略模块做二次验证。
4 安全策略:三级风险分级
safety.ts 实现了基于正则规则的命令风险分级,分三档:
| 级别 | 行为 | 典型示例 |
|---|---|---|
| safe | 直接执行 | git status、git log、git diff |
| high | 需用户二次确认 | git push --force、git reset --hard、git rebase |
| extreme | 默认阻止 | push --force 到 main/master、git clean -f |
export function checkCommandRisk(command: string): SafetyCheckResult {
for (const rule of EXTREME_RISK_RULES) {
if (rule.pattern.test(command)) {
return { riskLevel: 'extreme', reason: rule.reason, blocked: true }
}
}
for (const rule of HIGH_RISK_RULES) {
if (rule.pattern.test(command)) {
return { riskLevel: 'high', reason: rule.reason, blocked: false }
}
}
return { riskLevel: 'safe', blocked: false }
}
extreme 级别的判断优先于 high,避免规则覆盖顺序导致的漏判。blocked: true 的命令在 NLP 助手页面以"已阻止"状态记录,用户需要在设置中显式解锁才能执行。
安全策略在两个位置被调用:NLP 工作流在执行转译后的命令前调用,Agent Runtime 在处理 Tool Call 里的 Git 操作前调用。两处校验互相独立,不存在可以绕过其中一处的路径。
5 降级处理:LLM 不可用时的保底策略
fallback.ts 的设计原则是:AI 功能不可用时,不影响基础 Git 操作,同时给用户一个可用的默认结果,而不是显示错误状态。
按 taskType 分派三种降级结果:
commit.generateMessage:根据已暂存的文件数量生成格式化的占位提交信息,例如"chore: update 3 files"conflict.suggestResolution:返回strategy: manual,提示用户手动解决,不给出具体建议nl_assistant:对用户输入做关键词匹配,尝试识别基础意图(push / pull / commit / status),匹配失败时提示"无法解析,请直接使用 Git 命令"
降级结果在格式上和正常 AI 输出完全一致,上层代码不需要区分处理。
6 配置持久化的 Bug 修复
在接入 LLM 配置时,发现了一个已有的持久化逻辑问题:persistConfig 函数在写入时直接构造了一个新的 AppConfig 对象,没有读取当前存储的完整配置,导致每次保存仓库列表时会把 llmConfig 字段覆盖为空。
// 修改前:直接构造,丢失其他字段
const config: AppConfig = { repos, currentRepoPath }
// 修改后:先读取当前配置,再合并写入
const current = await loadConfig()
const config: AppConfig = { ...current, repos, currentRepoPath }
这类问题在功能模块相互独立开发时容易出现——各自写入同一份配置文件,但没有协调好合并逻辑。修复方式是在写入前先读取全量配置,用展开运算符合并,确保只更新自己负责的字段。
7 GlobalSettingsPanel 与 StatusBar 改造
GlobalSettingsPanel 从占位 UI 改为完整的配置界面,包含以下交互元素:
- Provider 选择(OpenAI 兼容 / Anthropic),切换时界面联动显示/隐藏 Base URL 输入项
- API Key 输入框,密码模式隐藏内容
- Base URL 输入项(OpenAI 兼容模式下显示),用于接入 DeepSeek 等兼容接口
- 模型名称输入框
- Temperature 滑块(0.0 ~ 2.0)和 Max Tokens 滑块
- 连接测试按钮,点击后调用
ping()验证 API Key 有效性,结果实时显示
StatusBar 的 AI 状态指示灯从硬编码的"API 已连接"改为动态状态,对应四种状态:
// unconfigured:用户未填写 API Key,灰点
// checking:正在验证连接,黄点
// ready:连接正常,绿点
// error:连接失败,红点
状态数据来源于 llmConfigStore,ping() 的结果写入 store,StatusBar 订阅变化自动更新,不需要额外的通信逻辑。
8 阶段小结
P0 Agent 框架完成后,项目的 AI 基础层具备了可用状态:LLM 多 Provider 支持、Tool Call 执行循环、三套工作流 Prompt、安全分级策略、结构化输出解析、完整降级兜底,以及配置界面和状态可视化。
下一步是推进 P1 工作流的接入——在这套框架上注册 Git 工具实现,把智能提交、冲突管控、自然语言助手三个功能从占位符变成真正可用的交互。
更多推荐
所有评论(0)