IntelliGit 项目个人博客(5)Agent 框架

发布时间:2026/5/19 22:03:39

IntelliGit 项目个人博客(5)Agent 框架 1 这一阶段的背景队友在前几周完成了前端核心视图的实现——Changes View、History View、仓库管理、布局系统Go Sidecar 后端的 branch / commit / diff / merge / remote / staging 等操作也已全部到位。需求文档里定义的智能化特性——AI 辅助提交、冲突智能管控、自然语言 Git 操作——在这个阶段还未完成。这一阶段我的工作是完成项目规划文档里定义的P0: Agent 框架与配置集成为后续三个 P1 智能工作流智能提交、冲突管控、自然语言助手搭建可复用的底层基础。2 本阶段完成工作在src/renderer/src/agent/下建立完整 Agent 基础层覆盖 LLM 客户端、工具注册、Prompt 模板、安全策略、输出解析、降级处理、运行时核心七个模块扩展全局配置类型支持多 LLM Provider 配置与持久化实现 GlobalSettingsPanel 完整配置界面替换原有占位 UI修复配置持久化逻辑中的覆盖问题将 StatusBar 的 AI 状态指示灯改为动态状态显示3 架构设计为什么要单独建一个 Agent 层在动手之前有一个问题需要先想清楚AI 能力以什么形式接入现有代码最直接的做法是在需要 AI 的地方直接调用 LLM API比如在提交面板的AI 生成按钮里写一段 fetch 调用拿到结果填入输入框。这样实现最快但有几个明显的问题三个 P1 工作流都需要调用 LLM每处各自实现意味着错误处理、降级逻辑、安全校验都要重复写LLM 的输出是非结构化文本每个调用方都要自己做解析容错处理很容易遗漏Provider 切换从 OpenAI 换到 DeepSeek需要改动多处代码。所以我们决定先建一个独立的 Agent 层把 LLM 调用、工具注册、Prompt 管理、安全策略、输出解析、降级处理统一封装P1 工作流只需要描述任务不需要关心底层细节。最终agent/目录的结构如下agent/ types.ts # 核心类型定义 llmClient.ts # LLM HTTP 客户端 toolRegistry.ts # Tool 注册与调用 prompts/ # Prompt 模板管理 safety.ts # 风险分级策略 outputParser.ts # 结构化输出解析 fallback.ts # 降级处理 agentRuntime.ts # Runtime 核心 index.ts # 统一导出每个文件职责单一P1 工作流接入时只需要调用agentRuntime.ts暴露的接口内部细节对上层透明。4 LLM 客户端不引入 SDK直接用 fetch 实现LLM 客户端llmClient.ts的设计原则是尽量轻目前没有引入第三方 SDK直接用fetch实现支持两套协议OpenAI 兼容协议覆盖 OpenAI 官方、DeepSeek、通义千问、以及本地部署的模型只要兼容/v1/chat/completions接口格式// POST /v1/chat/completions // Authorization: Bearer {apiKey}Anthropic 协议对应 Claude 系列模型// POST /v1/messages // x-api-key: {apiKey} // anthropic-version: 2023-06-01两套协议实现为两个独立的 Client 类对外暴露统一接口export interface LlmClient { chat(messages: AgentMessage[], tools?: ToolDefinition[]): PromiseLlmResponse ping(): Promisevoid } export function createLlmClient(config: LlmConfig): LlmClient { if (config.provider anthropic) return new AnthropicClient(config) return new OpenAICompatibleClient(config) }用工厂函数统一创建上层代码不感知 Provider 差异。ping()方法用于连接检测发送最小请求验证 API Key 有效性结果写入llmConfigStore在 StatusBar 里实时反映。配置类型定义在src/shared/types/sidecar.ts中作为前后端共享的类型契约export type LlmProvider openai | anthropic export interface LlmConfig { provider: LlmProvider apiKey: string baseUrl?: string // OpenAI 兼容模式下可覆盖用于接入国产模型 modelName: string temperature?: number maxTokens?: number }baseUrl字段的设计值得说一下。OpenAI 兼容协议现在已经是国内大模型的事实标准DeepSeek、通义千问都支持。通过允许覆盖baseUrl用户可以在不改任何代码的情况下切换到这些模型只需要在设置界面填入对应的 API 地址即可。5 工具注册系统Tool Call 是让 LLM 与应用功能交互的标准机制。Agent 层预声明了 13 个 Git 工具定义P1 工作流在接入时注入具体的执行逻辑export const GIT_TOOL_NAMES { GET_STATUS: git.getStatus, GET_DIFF: git.getDiff, GET_STAGED_DIFF: git.getStagedDiff, STAGE_FILE: git.stageFile, STAGE_ALL: git.stageAll, CREATE_COMMIT: git.createCommit, GET_CONFLICT_FILES: git.getConflictFiles, GET_TRIPLET_CONTENT: git.getTripletContent, APPLY_PATCH: git.applyPatch, CONTINUE_MERGE: git.continueMerge, ABORT_MERGE: git.abortMerge, // ... }P0 阶段只声明定义不注册实现——因为实现依赖 Sidecar IPC 调用属于各 P1 工作流自己的职责。P1 接入时只需要两步// 第一步注册工具实现 toolRegistry.register({ definition: GIT_TOOL_DEFINITIONS[git.stageFile], execute: async ({ filePath }) { /* 调用 sidecar */ } }) // 第二步调用 Agent const result await runAgentWithFallback( getCurrentLlmConfig(), { taskType: commit.generateMessage, systemPrompt: COMMIT_SYSTEM_PROMPT, userMessage: renderCommitMessagePrompt(diff), tools: [git.getStagedDiff] }, (raw) parseStructured(raw, COMMIT_MESSAGE_SCHEMA) )这种声明在 P0实现在 P1的分层设计让 Agent 框架本身不依赖具体的 Git 操作实现可以独立测试和维护。6 处理 LLM 输出的不确定性LLM 的输出是自然语言文本即便在 Prompt 里明确要求返回 JSON模型有时仍会在 JSON 前后附加说明文字或者把 JSON 包在 markdown 代码块里。outputParser.ts实现了三层提取逻辑export function extractJson(text: string): unknown { // 第一层提取 json ... 代码块内容 // 第二层提取第一个完整的 { ... } 块 // 第三层整体尝试 JSON.parse }三层兜底能覆盖绝大多数模型的输出格式差异。在此基础上针对三个 P1 工作流预置了五个 SchemaCommitMessage/CommitGroups/ConflictRisk/ConflictResolve/NlIntentP1 接入时直接用parseStructured(raw, SCHEMA)验证并转换输出不需要各自处理格式问题。7 心得体会这次工作和之前几周有明显不同——之前主要是理解已有代码、跑通通信链路这次是在相对空白的地方从零建立一套新的基础设施。设计 Agent 层的过程中对接口先于实现这件事有更具体的体会。Tool 注册系统的工具定义和执行实现分开Output Parser 的 Schema 和解析逻辑分开Fallback 的 taskType 和降级策略分开——这些分离看起来在 P0 阶段没有立竿见影的收益但它们决定了 P1 工作流能否真正独立推进而不是每个人都在改同一层代码。

相关新闻