乐于分享
好东西不私藏

1. 从零构建 VS Code AI 编程agent助手 — 总览篇

1. 从零构建 VS Code AI 编程agent助手 — 总览篇

1. 从零构建 VS Code AI 编程助手 — 总览篇

难度:⭐⭐ | 核心素材:整个项目架构


这篇文章适合谁?

  • 想了解 AI 编程助手内部原理的开发者
  • 想自己写 VS Code 插件的 TypeScript 开发者
  • 想知道「AI 怎么调用工具」的好奇者

CodeHi 是什么?

CodeHi 是一个 VS Code 侧边栏插件,功能类似 Cursor/Cline——在编辑器里和 AI 对话,AI 能读文件、搜索代码、编辑文件、执行命令。

┌────────────────────────────┐│ VS Code 窗口               ││  ┌──────────┬────────────┐ ││  │          │  侧边栏     │ ││  │  编辑器   │ ┌────────┐ │ ││  │          │ │  对话回答 │ │ ││  │          │ ├────────┤ │ ││  │          │ │ 输入框  │ │ ││  │          │ │        │ │ ││  │          │ ├────────┤ │ ││  │          │ │  模型▼ │ │ ││  │          │ └────────┘ │ ││  └──────────┴────────────┘ │└────────────────────────────┘

技术栈一览

技术
文件
VS Code 扩展
TypeScript + VS Code API
extension.ts
sidebar.ts
LLM 集成
OpenAI SDK (流式 SSE)
client.ts
stream.ts
提供者管理
事件驱动 + 单例
manager.ts
工具系统
Facade + 命令分发
tools.ts
executor.tsdefinitions.ts
提示词构建
动态拼装 + YAML frontmatter
prompt.ts
上下文管理
正则替换 + 目录树生成
manager.ts
 (context)
前端 UI
纯 Vanilla JS + CSS
app.js
handlers.jspopover.jsstyle.css
Markdown 渲染
marked (umd)
CDN 内联
持久化
VS Code globalState
sidebar.ts

核心架构图

extension.ts (入口)    │    ├── ProviderManager (单例,事件驱动)    │   ├── getAllAvailableProviders()    │   ├── getEndpointConfig()    │   ├── addCustomProvider()    │   └── _onDidChange (EventEmitter)    │    ├── LLMClient    │   ├── chatAgentLoop()      ← 核心循环    │   │   ├── requestLLM()     → processStream()    │   │   └── executeToolCalls()    │   └── createClient()       → OpenAI 实例    │    ├── ContextManager    │   ├── getProjectContext()   → 目录树    │   ├── resolveReferences()  → @file 解析    │   └── getEditorContext()    │    ├── tools/    │   ├── tools.ts             → Facade + write 开关    │   ├── executor.ts          → 20+ 工具实现    │   └── definitions.ts       → ToolDefinition[]    │    ├── chat/    │   ├── prompt.ts            → buildSystemPrompt()    │   └── stream.ts            → processStream()    │    └── sidebar/        └── sidebar.ts           → SidebarView (消息路由+生命周期)            │            └── Webview (frontend/)                ├── app.js       → 主控制器                ├── handlers.js  → 消息处理路由表                ├── popover.js   → 模型选择 Popover                └── style.css    → 1809 行纯手写 CSS

一条消息的完整旅程

从用户输入到 AI 回答,一条消息经历的全部步骤:

1. 用户输入 "帮我优化 foo.ts"        │2. app.js sendMessage()   └─→ postMessage({ type: 'userInput', content: '...' })        │3. sidebar.ts onDidReceiveMessage   └─→ handleUserInput()        │4. contextManager.resolveReferences()   └─→ 解析 @引用,注入文件内容        │5. contextManager.getProjectContext()   └─→ 首条消息注入项目目录树        │6. buildSystemPrompt()   └─→ 动态拼装系统提示词(含写入规则/Provider身份/Skills)        │7. trimHistoryForLLM()   └─→ 瘦身历史消息        │8. llmClient.chatAgentLoop(messages)   └─→ 进入 Agent Loop        │   ┌──────────────────────────────┐   │ Agent Loop (最多 200 轮)     │   │                              │   │ 8a. requestLLM()             │   │  └─→ OpenAI SDK 流式请求     │   │      └─→ processStream()     │   │          └─→ 逐 chunk 推送   │   │              Webview         │   │                              │   │ 8b. LLM 返回 tool_calls      │   │  └─→ executeToolCalls()      │   │      └─→ executeToolCall()   │   │          └─→ switch(name)    │   │              ├─ read_file    │   │              ├─ search_code  │   │              ├─ write_file   │   │              └─ ...          │   │                              │   │ 8c. tool 结果追加到 messages │   │  └─→ 回到 8a 继续循环        │   │                              │   │ 8d. LLM 返回纯文本 → 退出    │   └──────────────────────────────┘        │9. 保存聊天历史到 globalState        │10. postMessage({ type: 'requestEnd', content })    └─→ Webview 显示最终答案

关键数字

指标
数值
总代码量
~3500 行 TypeScript + ~2500 行 JS/CSS
支持工具数
24 个(14 只读 + 10 写入)
支持 Provider
2 内置 + 无限自定义
Agent Loop 上限
200 轮
文件大小限制
1MB (read_file)
命令超时
最长 120 秒
HTTP 下载限制
10MB
历史瘦身
早期消息截断到 200 字符

设计原则

  1. 最小依赖
    :前端零框架(纯 Vanilla JS),后端只用 OpenAI SDK + json5 + marked
  2. 事件驱动
    :Provider 变更通过 EventEmitter 广播,UI 自动刷新
  3. 安全优先
    :写入开关 + 危险命令检测 + 文件大小限制
  4. KV Cache 友好
    :历史瘦身保持消息结构不变,最大化缓存命中
  5. 错误容忍
    :工具调用失败不崩溃,作为 tool result 返回给 LLM

下一篇:#2 VS Code 扩展的 Facade 模式实践