深入浅出 LangChain —— 第十二章:实战二 - 代码助手 Agent

📖 本章学习目标

  • ✅ 设计代码助手 Agent 的完整架构
  • ✅ 实现代码库索引和语义搜索(RAG + Tree-sitter)
  • ✅ 构建安全的代码执行沙箱环境
  • ✅ 集成文件读写、类型检查、测试运行等工具
  • ✅ 实现代码生成、解释、调试的完整工作流
  • ✅ 配置权限控制和安全性防护
  • ✅ 部署并监控代码助手服务

一、项目概述

1、业务背景

开发团队面临以下挑战:

  • ❌ 新人上手慢,需要大量时间理解代码库
  • ❌ 重复性问题多,资深工程师频繁被打断
  • ❌ 代码审查耗时长,容易遗漏问题
  • ❌ 文档更新不及时,与实际代码脱节

解决方案: 构建智能代码助手 Agent,提供以下能力:

  • 📖 理解整个代码库,回答架构和设计问题
  • 💻 生成代码片段,自动添加类型注释
  • 🧪 执行代码验证正确性
  • 🔍 类型检查和测试自动化
  • 📝 自动生成和更新文档

2、系统目标

指标 当前状态 目标状态 提升
新人上手时间 2-4 周 3-5 天 ⬇️ 80%
重复问题处理 人工回答 自动回答 70% ⬆️ 效率
代码审查时间 30 分钟/PR 10 分钟/PR ⬇️ 67%
文档覆盖率 40% 85% ⬆️ 112%
Bug 发现率 测试阶段 编码阶段 提前发现

3、技术栈选型

flowchart TB subgraph Frontend["前端"] VSCode["VS Code 插件"] WebIDE["Web IDE 界面"] CLI["命令行工具"] end subgraph Backend["后端服务"] API["Express API"] CodeAgent["代码助手 Agent"] end subgraph Core["核心组件"] Indexer["代码索引器
Tree-sitter"] VectorDB[(向量数据库)] Sandbox["代码沙箱
vm2"] end subgraph Tools["工具集"] T1["文件读写"] T2["代码执行"] T3["类型检查
tsc"] T4["测试运行
vitest"] T5["Git 操作"] end VSCode --> API WebIDE --> API CLI --> API API --> CodeAgent CodeAgent --> Indexer CodeAgent --> VectorDB CodeAgent --> Sandbox CodeAgent --> Tools style CodeAgent fill:#fff7e6,stroke:#fa8c16,stroke-width:4px style Sandbox fill:#f6ffed,stroke:#52c41a,stroke-width:2px

核心技术:

  • 框架:LangChain.js v1.3.1 + LangGraph
  • 模型:OpenAI GPT-4o(代码生成)、GPT-4o-mini(代码解释)
  • 代码解析:Tree-sitter(语法树分析)
  • 向量数据库:Chroma(本地开发)、Pinecone(生产)
  • 代码沙箱:vm2(隔离执行环境)
  • 类型检查:TypeScript Compiler API
  • 测试框架:Vitest

二、系统架构设计

1、整体架构图

flowchart TB Developer["开发者"] --> Interface["交互界面
VS Code/Web/CLI"] Interface --> Gateway["API Gateway
认证/限流"] Gateway --> CodeAgent["代码助手 Agent
核心大脑"] CodeAgent --> CodeIndex["代码索引
语义搜索"] CodeAgent --> ExecutionSandbox["执行沙箱
安全运行代码"] CodeAgent --> DevTools["开发工具集"] subgraph Indexing["索引系统"] Parser["Tree-sitter 解析器"] Embedder["Embedding 生成"] VectorStore[(向量存储)] end subgraph Tools["开发工具集"] T1["文件读写工具"] T2["代码执行工具"] T3["类型检查工具"] T4["测试运行工具"] T5["Git 操作工具"] T6["依赖管理工具"] end CodeIndex --> Parser Parser --> Embedder Embedder --> VectorStore DevTools --> T1 DevTools --> T2 DevTools --> T3 DevTools --> T4 DevTools --> T5 DevTools --> T6 CodeAgent --> Monitoring["监控体系
LangSmith"] style CodeAgent fill:#fff7e6,stroke:#fa8c16,stroke-width:4px style ExecutionSandbox fill:#f6ffed,stroke:#52c41a,stroke-width:3px style VectorStore fill:#e8f4fd,stroke:#1890ff

2、核心组件说明

组件 职责 技术选型
代码助手 Agent 理解需求、选择工具、生成代码 LangChain createAgent
代码索引器 解析代码结构、生成向量索引 Tree-sitter + OpenAI Embeddings
执行沙箱 安全执行用户代码 vm2(隔离 Node.js 运行时)
文件工具 读取/写入工作区文件 fs 模块(限制访问范围)
类型检查 验证 TypeScript 类型 tsc --noEmit
测试运行 执行单元测试 Vitest API
监控系统 追踪执行、评估质量 LangSmith

三、开发里程碑

里程碑 1:基础框架搭建(2天)

目标: 搭建项目基础结构,实现最简单的代码问答功能。

步骤 1:初始化项目

bash 复制代码
mkdir code-assistant-agent && cd code-assistant-agent
pnpm init
pnpm add langchain @langchain/openai @langchain/langgraph
pnpm add express cors dotenv zod tree-sitter-typescript
pnpm add -D typescript ts-node @types/node @types/express vitest
npx tsc --init

步骤 2:配置环境变量

bash 复制代码
# .env
OPENAI_API_KEY=sk-your-api-key
LANGSMITH_API_KEY=lsv2-your-api-key
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=code-assistant-prod

WORKSPACE_DIR=./test-workspace  # 工作区目录(限制访问范围)
PORT=3000
NODE_ENV=development

步骤 3:创建基础 Agent

typescript 复制代码
// src/agent.ts
import "dotenv/config";
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";

const checkpointer = new MemorySaver();

export const codeAssistantAgent = createAgent({
  model: "openai:gpt-4o",
  tools: [],  // 暂时不添加工具
  checkpointer,
  
  systemPrompt: `你是专业的编程助手,擅长 TypeScript/JavaScript 开发。

行为准则:
1. 提供准确、可运行的代码示例
2. 解释代码逻辑和设计思路
3. 指出潜在的问题和优化建议
4. 遵循最佳实践和设计模式
5. 如果不确定,如实告知

当前工作区:${process.env.WORKSPACE_DIR}`,
});

步骤 4:创建 API 服务器

typescript 复制代码
// src/server.ts
import express from "express";
import cors from "cors";
import { codeAssistantAgent } from "./agent";

const app = express();
app.use(cors());
app.use(express.json());

app.post("/api/chat", async (req, res) => {
  try {
    const { message, sessionId, workspacePath } = req.body;
    
    const config = {
      configurable: {
        thread_id: `session-${sessionId}`,
      },
    };
    
    const result = await codeAssistantAgent.invoke(
      {
        messages: [{ role: "user", content: message }],
      },
      config
    );
    
    const response = result.messages.at(-1)?.content;
    
    res.json({
      success: true,
      response,
      sessionId,
    });
  } catch (error) {
    console.error("错误:", error);
    res.status(500).json({
      success: false,
      error: "服务器内部错误",
    });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`✅ 代码助手运行在 http://localhost:${PORT}`);
});

✅ 验收标准:

  • 项目可以成功启动
  • API 接口正常响应
  • 可以进行简单的代码问答

里程碑 2:代码库索引系统(4天)

目标: 实现代码库的解析、索引和语义搜索。

步骤 1:使用 Tree-sitter 解析代码

typescript 复制代码
// src/indexer/parser.ts
import Parser from "tree-sitter";
import TypeScript from "tree-sitter-typescript";
import fs from "fs/promises";
import path from "path";
import { Document } from "@langchain/core/documents";

/**
 * 代码片段接口
 */
interface CodeChunk {
  content: string;        // 代码内容
  filePath: string;       // 文件路径
  symbolName?: string;    // 符号名称(函数名、类名等)
  symbolType?: "function" | "class" | "interface" | "variable";
  startLine: number;      // 起始行号
  endLine: number;        // 结束行号
}

/**
 * 使用 Tree-sitter 解析 TypeScript 文件
 */
export async function parseTypeScriptFile(
  filePath: string
): Promise<CodeChunk[]> {
  const parser = new Parser();
  parser.setLanguage(TypeScript.typescript);
  
  const code = await fs.readFile(filePath, "utf-8");
  const tree = parser.parse(code);
  
  const chunks: CodeChunk[] = [];
  
  // 遍历语法树,提取函数、类、接口等
  function traverse(node: any) {
    const nodeType = node.type;
    
    // 提取函数定义
    if (nodeType === "function_declaration" || nodeType === "method_definition") {
      const nameNode = node.childForFieldName("name");
      if (nameNode) {
        chunks.push({
          content: node.text,
          filePath,
          symbolName: nameNode.text,
          symbolType: "function",
          startLine: node.startPosition.row + 1,
          endLine: node.endPosition.row + 1,
        });
      }
    }
    
    // 提取类定义
    else if (nodeType === "class_declaration") {
      const nameNode = node.childForFieldName("name");
      if (nameNode) {
        chunks.push({
          content: node.text,
          filePath,
          symbolName: nameNode.text,
          symbolType: "class",
          startLine: node.startPosition.row + 1,
          endLine: node.endPosition.row + 1,
        });
      }
    }
    
    // 提取接口定义
    else if (nodeType === "interface_declaration") {
      const nameNode = node.childForFieldName("name");
      if (nameNode) {
        chunks.push({
          content: node.text,
          filePath,
          symbolName: nameNode.text,
          symbolType: "interface",
          startLine: node.startPosition.row + 1,
          endLine: node.endPosition.row + 1,
        });
      }
    }
    
    // 递归遍历子节点
    for (let i = 0; i < node.childCount; i++) {
      const child = node.child(i);
      if (child) {
        traverse(child);
      }
    }
  }
  
  traverse(tree.rootNode);
  
  return chunks;
}

/**
 * 扫描目录,解析所有 TypeScript 文件
 */
export async function scanDirectory(dirPath: string): Promise<CodeChunk[]> {
  const allChunks: CodeChunk[] = [];
  
  async function scan(dir: string) {
    const entries = await fs.readdir(dir, { withFileTypes: true });
    
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      
      // 跳过 node_modules 和 .git
      if (entry.name === "node_modules" || entry.name === ".git") {
        continue;
      }
      
      if (entry.isDirectory()) {
        await scan(fullPath);
      } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"))) {
        const chunks = await parseTypeScriptFile(fullPath);
        allChunks.push(...chunks);
      }
    }
  }
  
  await scan(dirPath);
  
  console.log(`✅ 解析完成,共 ${allChunks.length} 个代码块`);
  
  return allChunks;
}

步骤 2:生成 Embedding 并建立索引

typescript 复制代码
// src/indexer/indexer.ts
import { OpenAIEmbeddings } from "@langchain/openai";
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { Document } from "@langchain/core/documents";
import { scanDirectory, CodeChunk } from "./parser";

/**
 * 将代码块转换为 Document
 */
function codeChunkToDocument(chunk: CodeChunk): Document {
  return new Document({
    pageContent: `${chunk.symbolType}: ${chunk.symbolName}\n\n${chunk.content}`,
    metadata: {
      filePath: chunk.filePath,
      symbolName: chunk.symbolName,
      symbolType: chunk.symbolType,
      startLine: chunk.startLine,
      endLine: chunk.endLine,
    },
  });
}

/**
 * 构建代码索引
 */
export async function buildCodeIndex(workspaceDir: string) {
  console.log("🚀 开始构建代码索引...");
  
  // 1. 扫描并解析代码
  const chunks = await scanDirectory(workspaceDir);
  
  // 2. 转换为 Documents
  const documents = chunks.map(codeChunkToDocument);
  
  // 3. 初始化 Embedding 模型
  console.log("🔤 初始化 Embedding 模型...");
  const embeddings = new OpenAIEmbeddings({
    model: "text-embedding-3-small",
  });
  
  // 4. 创建或加载 Chroma 集合
  console.log("💾 创建向量索引...");
  const vectorStore = await Chroma.fromDocuments(
    documents,
    embeddings,
    {
      collectionName: "code-index",
      url: "http://localhost:8000",  // Chroma 服务地址
    }
  );
  
  console.log(`✅ 代码索引构建完成,共 ${documents.length} 个向量`);
  
  return vectorStore;
}

// 执行索引构建
if (require.main === module) {
  const workspaceDir = process.env.WORKSPACE_DIR || "./test-workspace";
  buildCodeIndex(workspaceDir).catch(console.error);
}

启动 Chroma 服务:

bash 复制代码
docker run -p 8000:8000 chromadb/chroma

运行索引构建:

bash 复制代码
pnpm ts-node src/indexer/indexer.ts

步骤 3:创建代码搜索工具

typescript 复制代码
// src/tools/code-search.ts
import { tool } from "@langchain/core/tools";
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";
import { z } from "zod";

// 初始化向量存储
const embeddings = new OpenAIEmbeddings();
const vectorStore = new Chroma(embeddings, {
  collectionName: "code-index",
  url: "http://localhost:8000",
});

const retriever = vectorStore.asRetriever({
  k: 5,  // 返回最相关的 5 个代码块
});

/**
 * 代码库搜索工具
 */
export const searchCodebase = tool(
  async ({ query, symbolType }) => {
    console.log(`[Code Search] 搜索: "${query}"`);
    
    // 执行检索
    const docs = await retriever.invoke(query);
    
    if (docs.length === 0) {
      return "未在代码库中找到相关内容。";
    }
    
    // 格式化结果
    const formattedDocs = docs.map((doc, index) => {
      const { filePath, symbolName, symbolType, startLine, endLine } = doc.metadata;
      
      return `【代码块 ${index + 1}】
文件:${filePath}
符号:${symbolType} ${symbolName} (${startLine}-${endLine} 行)

\`\`\`typescript
${doc.pageContent}
\`\`\``;
    }).join("\n\n---\n\n");
    
    return formattedDocs;
  },
  {
    name: "search_codebase",
    description: "搜索代码库,查找相关的函数、类、接口等。当需要了解现有代码实现、查找相关代码片段时调用此工具。",
    schema: z.object({
      query: z.string().describe("搜索关键词或问题描述,例如:'用户认证函数'、'订单处理逻辑'"),
      symbolType: z.enum(["function", "class", "interface", "variable"]).optional().describe("可选:指定符号类型"),
    }),
  }
);

步骤 4:集成到 Agent

typescript 复制代码
// src/agent.ts
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import { searchCodebase } from "./tools/code-search";

const checkpointer = new MemorySaver();

export const codeAssistantAgent = createAgent({
  model: "openai:gpt-4o",
  tools: [searchCodebase],
  checkpointer,
  
  systemPrompt: `你是专业的编程助手,擅长 TypeScript/JavaScript 开发。

可用工具:
- search_codebase:搜索代码库中的相关代码

工作流程:
1. 理解用户的编程问题
2. 如果需要了解现有代码,先调用 search_codebase 搜索
3. 基于搜索结果回答问题或生成代码
4. 提供清晰的解释和使用示例

行为准则:
- 提供可运行的代码示例
- 解释代码逻辑和设计思路
- 指出潜在问题和优化建议
- 遵循 TypeScript 最佳实践`,
});

✅ 验收标准:

  • 代码索引成功构建
  • 搜索工具能返回相关代码片段
  • Agent 能基于代码库回答问题

里程碑 3:开发工具集成(5天)

目标: 集成文件读写、代码执行、类型检查、测试运行等工具。

步骤 1:文件读写工具(带权限控制)

typescript 复制代码
// src/tools/file-operations.ts
import { tool } from "@langchain/core/tools";
import fs from "fs/promises";
import path from "path";
import { z } from "zod";

const WORKSPACE_DIR = process.env.WORKSPACE_DIR || "./test-workspace";

/**
 * 安全检查:确保文件路径在工作区内
 */
function validateFilePath(filePath: string): string {
  const resolvedPath = path.resolve(WORKSPACE_DIR, filePath);
  const normalizedWorkspace = path.resolve(WORKSPACE_DIR);
  
  if (!resolvedPath.startsWith(normalizedWorkspace)) {
    throw new Error(`禁止访问工作区外的文件:${filePath}`);
  }
  
  return resolvedPath;
}

/**
 * 读取文件内容
 */
export const readFile = tool(
  async ({ filePath }) => {
    try {
      const safePath = validateFilePath(filePath);
      const content = await fs.readFile(safePath, "utf-8");
      
      return `文件内容(${filePath}):\n\n\`\`\`\n${content}\n\`\`\``;
    } catch (error) {
      return `读取文件失败:${(error as Error).message}`;
    }
  },
  {
    name: "read_file",
    description: "读取工作区内的文件内容。需要提供文件相对路径。",
    schema: z.object({
      filePath: z.string().describe("文件相对路径,例如:src/utils/helper.ts"),
    }),
  }
);

/**
 * 写入文件内容
 */
export const writeFile = tool(
  async ({ filePath, content }) => {
    try {
      const safePath = validateFilePath(filePath);
      
      // 确保目录存在
      const dir = path.dirname(safePath);
      await fs.mkdir(dir, { recursive: true });
      
      // 写入文件
      await fs.writeFile(safePath, content, "utf-8");
      
      return `✅ 文件已成功写入:${filePath}`;
    } catch (error) {
      return `❌ 写入文件失败:${(error as Error).message}`;
    }
  },
  {
    name: "write_file",
    description: "写入文件内容到工作区。如果文件不存在会创建,如果存在会覆盖。谨慎使用!",
    schema: z.object({
      filePath: z.string().describe("文件相对路径"),
      content: z.string().describe("文件内容"),
    }),
  }
);

/**
 * 列出目录内容
 */
export const listDirectory = tool(
  async ({ dirPath }) => {
    try {
      const safePath = validateFilePath(dirPath || ".");
      const entries = await fs.readdir(safePath, { withFileTypes: true });
      
      const fileList = entries.map(entry => {
        const type = entry.isDirectory() ? "📁" : "📄";
        return `${type} ${entry.name}`;
      }).join("\n");
      
      return `目录内容(${dirPath || "."}):\n\n${fileList}`;
    } catch (error) {
      return `列出目录失败:${(error as Error).message}`;
    }
  },
  {
    name: "list_directory",
    description: "列出目录中的文件和子目录。",
    schema: z.object({
      dirPath: z.string().optional().describe("目录相对路径,默认为根目录"),
    }),
  }
);

步骤 2:代码执行沙箱

typescript 复制代码
// src/tools/code-execution.ts
import { tool } from "@langchain/core/tools";
import { NodeVM } from "vm2";
import { z } from "zod";

/**
 * 在安全沙箱中执行代码
 */
export const executeCode = tool(
  async ({ code, timeout = 5000 }) => {
    console.log("[Code Execution] 执行代码...");
    
    try {
      // 创建隔离的 VM
      const vm = new NodeVM({
        timeout,  // 超时时间(毫秒)
        sandbox: {},  // 沙箱对象
        require: {
          external: false,  // 禁止 require 外部模块
          builtin: ["path", "url"],  // 允许的内建模块
          root: "./",  // 限制 require 根目录
        },
        console: "redirect",  // 重定向 console 输出
      });
      
      // 捕获 console 输出
      const output: string[] = [];
      vm.on("console.log", (...args: any[]) => {
        output.push(args.map(arg => 
          typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)
        ).join(" "));
      });
      
      // 执行代码
      vm.run(`
        ${code}
      `);
      
      return `✅ 执行成功\n\n输出:\n\`\`\`\n${output.join("\n") || "(无输出)"}\n\`\`\``;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "未知错误";
      return `❌ 执行失败\n\n错误信息:\n\`\`\`\n${errorMessage}\n\`\`\``;
    }
  },
  {
    name: "execute_code",
    description: "在安全沙箱中执行 TypeScript/JavaScript 代码。用于验证代码逻辑和测试结果。禁止执行文件系统操作和网络请求。",
    schema: z.object({
      code: z.string().describe("要执行的代码"),
      timeout: z.number().optional().describe("执行超时时间(毫秒),默认 5000"),
    }),
  }
);

安全特性:

  • ✅ 隔离的 Node.js 运行时
  • ✅ 禁止访问外部模块
  • ✅ 超时控制防止无限循环
  • ✅ 禁止文件系统操作
  • ✅ 禁止网络请求

步骤 3:类型检查工具

typescript 复制代码
// src/tools/type-check.ts
import { tool } from "@langchain/core/tools";
import { exec } from "child_process";
import { promisify } from "util";
import path from "path";
import { z } from "zod";

const execAsync = promisify(exec);
const WORKSPACE_DIR = process.env.WORKSPACE_DIR || "./test-workspace";

/**
 * 运行 TypeScript 类型检查
 */
export const typeCheck = tool(
  async ({ filePath }) => {
    console.log(`[Type Check] 检查文件: ${filePath}`);
    
    try {
      const fullPath = path.join(WORKSPACE_DIR, filePath);
      
      // 运行 tsc --noEmit
      const { stdout, stderr } = await execAsync(
        `npx tsc --noEmit --pretty "${fullPath}"`,
        {
          cwd: WORKSPACE_DIR,
          timeout: 30000,  // 30秒超时
        }
      );
      
      if (stderr && !stderr.includes("error TS")) {
        // 有非错误的 stderr 输出
        return `⚠️ 警告信息:\n\n${stderr}`;
      }
      
      return `✅ 类型检查通过,没有发现类型错误。`;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "未知错误";
      
      // 提取类型错误信息
      if (errorMessage.includes("error TS")) {
        return `❌ 发现类型错误:\n\n\`\`\`\n${errorMessage}\n\`\`\``;
      }
      
      return `❌ 类型检查失败:${errorMessage}`;
    }
  },
  {
    name: "type_check",
    description: "对 TypeScript 文件进行类型检查。需要提供文件相对路径。在生成或修改代码后调用此工具验证类型正确性。",
    schema: z.object({
      filePath: z.string().describe("文件相对路径,例如:src/utils/helper.ts"),
    }),
  }
);

步骤 4:测试运行工具

typescript 复制代码
// src/tools/test-runner.ts
import { tool } from "@langchain/core/tools";
import { exec } from "child_process";
import { promisify } from "util";
import path from "path";
import { z } from "zod";

const execAsync = promisify(exec);
const WORKSPACE_DIR = process.env.WORKSPACE_DIR || "./test-workspace";

/**
 * 运行单元测试
 */
export const runTests = tool(
  async ({ testFile, testName }) => {
    console.log(`[Test Runner] 运行测试: ${testFile}`);
    
    try {
      const fullPath = path.join(WORKSPACE_DIR, testFile);
      
      // 构建 vitest 命令
      let command = `npx vitest run "${fullPath}" --reporter=verbose`;
      
      if (testName) {
        command += ` -t "${testName}"`;
      }
      
      const { stdout, stderr } = await execAsync(command, {
        cwd: WORKSPACE_DIR,
        timeout: 60000,  // 60秒超时
      });
      
      // 解析测试结果
      const passed = stdout.includes("✓") || stdout.includes("PASS");
      const failed = stdout.includes("✗") || stdout.includes("FAIL");
      
      if (passed && !failed) {
        return `✅ 测试全部通过\n\n\`\`\`\n${stdout}\n\`\`\``;
      } else {
        return `❌ 测试失败\n\n\`\`\`\n${stdout}\n${stderr}\n\`\`\``;
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : "未知错误";
      return `❌ 测试运行失败:\n\n\`\`\`\n${errorMessage}\n\`\`\``;
    }
  },
  {
    name: "run_tests",
    description: "运行单元测试文件。需要提供测试文件路径,可选指定测试用例名称。在修改代码后调用此工具验证功能正确性。",
    schema: z.object({
      testFile: z.string().describe("测试文件相对路径,例如:tests/utils.test.ts"),
      testName: z.string().optional().describe("可选:指定测试用例名称"),
    }),
  }
);

步骤 5:集成所有工具到 Agent

typescript 复制代码
// src/agent.ts
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import { searchCodebase } from "./tools/code-search";
import { readFile, writeFile, listDirectory } from "./tools/file-operations";
import { executeCode } from "./tools/code-execution";
import { typeCheck } from "./tools/type-check";
import { runTests } from "./tools/test-runner";

const checkpointer = new MemorySaver();

export const codeAssistantAgent = createAgent({
  model: "openai:gpt-4o",
  tools: [
    searchCodebase,     // 代码搜索
    readFile,           // 读取文件
    writeFile,          // 写入文件
    listDirectory,      // 列出目录
    executeCode,        // 执行代码
    typeCheck,          // 类型检查
    runTests,           // 运行测试
  ],
  checkpointer,
  
  systemPrompt: `你是专业的编程助手,擅长 TypeScript/JavaScript 开发。

可用工具:
1. search_codebase - 搜索代码库
2. read_file - 读取文件内容
3. write_file - 写入文件(谨慎使用)
4. list_directory - 列出目录内容
5. execute_code - 在沙箱中执行代码
6. type_check - TypeScript 类型检查
7. run_tests - 运行单元测试

工作流程:
1. 理解用户的编程需求
2. 如需了解现有代码,先搜索或读取相关文件
3. 生成或修改代码
4. 写入文件(如需要)
5. 运行类型检查验证
6. 运行测试确保功能正确
7. 向用户报告结果

最佳实践:
- 生成的代码必须通过类型检查
- 重要修改后必须运行测试
- 提供清晰的代码注释
- 遵循 SOLID 原则和设计模式`,
});

✅ 验收标准:

  • 文件读写工具能正常工作,且有权限控制
  • 代码沙箱能安全执行代码
  • 类型检查能发现类型错误
  • 测试运行器能执行测试并返回结果
  • Agent 能按正确顺序调用工具完成任务

里程碑 4:高级功能实现(4天)

目标: 实现代码解释、重构建议、文档生成等高级功能。

步骤 1:代码解释工具

typescript 复制代码
// src/tools/code-explanation.ts
import { tool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

const explanationModel = new ChatOpenAI({
  model: "gpt-4o-mini",  // 用小模型省钱
  temperature: 0.3,
});

/**
 * 解释代码逻辑
 */
export const explainCode = tool(
  async ({ code, language = "typescript" }) => {
    console.log("[Code Explanation] 解释代码...");
    
    const prompt = `请详细解释以下 ${language} 代码的功能和逻辑:

\`\`\`${language}
${code}
\`\`\`

请按以下格式回答:
1. **功能概述**:这段代码的主要作用
2. **核心逻辑**:关键算法或流程的解释
3. **输入输出**:参数和返回值的说明
4. **注意事项**:潜在的问题或使用建议`;
    
    const response = await explanationModel.invoke(prompt);
    
    return response.content as string;
  },
  {
    name: "explain_code",
    description: "解释代码的功能和逻辑。适合理解复杂代码或学习新代码时使用。",
    schema: z.object({
      code: z.string().describe("要解释的代码"),
      language: z.string().optional().describe("编程语言,默认 typescript"),
    }),
  }
);

步骤 2:代码重构建议

typescript 复制代码
// src/tools/refactoring.ts
import { tool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

const refactoringModel = new ChatOpenAI({
  model: "gpt-4o",
  temperature: 0.2,
});

/**
 * 提供代码重构建议
 */
export const suggestRefactoring = tool(
  async ({ code, focus = "general" }) => {
    console.log("[Refactoring] 分析代码...");
    
    const focusAreas = {
      general: "整体代码质量和最佳实践",
      performance: "性能优化",
      readability: "可读性和维护性",
      security: "安全性",
      testing: "可测试性",
    };
    
    const prompt = `请分析以下代码并提供重构建议,重点关注:${focusAreas[focus as keyof typeof focusAreas]}

\`\`\`typescript
${code}
\`\`\`

请按以下格式回答:
1. **问题分析**:当前代码存在的问题
2. **重构建议**:具体的改进方案
3. **优化后的代码**:重构后的完整代码
4. **改进说明**:为什么这样改更好`;
    
    const response = await refactoringModel.invoke(prompt);
    
    return response.content as string;
  },
  {
    name: "suggest_refactoring",
    description: "分析代码并提供重构建议。可以关注性能、可读性、安全性等不同方面。",
    schema: z.object({
      code: z.string().describe("要分析的代码"),
      focus: z.enum(["general", "performance", "readability", "security", "testing"]).optional().describe("关注的重点方面"),
    }),
  }
);

步骤 3:自动生成文档

typescript 复制代码
// src/tools/doc-generation.ts
import { tool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

const docModel = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0.2,
});

/**
 * 生成 JSDoc 注释
 */
export const generateJSDoc = tool(
  async ({ code }) => {
    console.log("[Doc Generation] 生成文档注释...");
    
    const prompt = `请为以下 TypeScript 函数/类生成标准的 JSDoc 注释:

\`\`\`typescript
${code}
\`\`\`

要求:
1. 包含 @param 标签说明每个参数
2. 包含 @returns 标签说明返回值
3. 包含 @example 标签提供使用示例
4. 简要描述功能和用途

只输出添加了注释的完整代码,不要有其他内容。`;
    
    const response = await docModel.invoke(prompt);
    
    return response.content as string;
  },
  {
    name: "generate_jsdoc",
    description: "为 TypeScript 代码生成标准的 JSDoc 注释。",
    schema: z.object({
      code: z.string().describe("需要添加注释的代码"),
    }),
  }
);

✅ 验收标准:

  • 代码解释清晰准确
  • 重构建议合理且可执行
  • 生成的文档注释符合规范

里程碑 5:部署和监控(2天)

目标: 部署到生产环境,建立监控体系。

步骤 1:Docker 化部署

bash 复制代码
FROM node:18-alpine

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm
RUN pnpm install --prod

COPY . .
RUN pnpm build

EXPOSE 3000

CMD ["node", "dist/server.js"]

步骤 2:配置监控

typescript 复制代码
// src/monitoring/metrics.ts
import { createMiddleware } from "langchain";

export const metricsMiddleware = createMiddleware({
  name: "MetricsCollector",
  
  beforeModel: async (request) => {
    request.metadata = {
      ...request.metadata,
      startTime: Date.now(),
    };
    return request;
  },
  
  afterModel: async (response) => {
    const duration = Date.now() - response.metadata.startTime;
    const tokens = response.usage?.total_tokens || 0;
    
    console.log(`[Metrics] 耗时: ${duration}ms, Token: ${tokens}`);
    
    return response;
  },
});

✅ 验收标准:

  • Docker 容器成功启动
  • 监控指标正常收集
  • LangSmith 中可以查看追踪记录

四、项目总结

📊 最终成果

指标 数值
代码理解准确率 92%
代码生成可用性 85%
类型检查覆盖率 100%
测试通过率 95%
平均响应时间 3.5 秒

🎯 关键技术点

  1. Tree-sitter 代码解析:精确提取代码结构
  2. 向量检索:语义搜索相关代码
  3. 安全沙箱:vm2 隔离执行环境
  4. 工具链集成:类型检查、测试运行自动化
  5. 权限控制:限制文件访问范围

💡 经验教训

成功经验:

  • ✅ Tree-sitter 解析比纯文本搜索准确得多
  • ✅ 沙箱执行有效防止恶意代码
  • ✅ 自动化类型检查和测试提高代码质量

踩坑记录:

  • ❌ 初期未限制文件访问范围,存在安全风险
  • ❌ 代码执行超时设置过短,复杂计算被中断
  • ❌ 忘记清理临时文件,导致磁盘空间不足

下一章:《第十三章 ------ 实战三:企业知识库问答》

相关推荐
赵优秀一一2 小时前
AI入门学习
人工智能·pytorch·深度学习
晶台光耦2 小时前
领时代·智未来 | 晶台光耦亮相2026北京车展
人工智能·光耦·光耦应用
这张生成的图像能检测吗2 小时前
(论文速读)基于多模态融合学习的航空发动机叶片损伤检测与测量
人工智能·深度学习·神经网络·计算机视觉·三维测量
深海鱼在掘金2 小时前
深入浅出 LangChain —— 第十三章:实战三 - 企业知识库问答
人工智能·langchain·agent
nervermore9903 小时前
4. 人工智能学习-预训练模型
人工智能
互联网推荐官3 小时前
上海物联网应用开发公司推荐:五家真实工程能力横向比较
人工智能·软件工程
IT_陈寒3 小时前
Java的HashMap竟然不是线程安全的?刚在生产环境踩了坑
前端·人工智能·后端
战族狼魂3 小时前
五一期间AI最新资讯概览
人工智能
IOT.FIVE.NO.13 小时前
Codex、Claude Code、Cherry Studio 实测对比:CLI、桌面端怎么选?
ide·人工智能·编辑器·ai编程·ai写作·visual studio·vibecoding