📖 本章学习目标
- ✅ 设计代码助手 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
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
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 秒 |
🎯 关键技术点
- Tree-sitter 代码解析:精确提取代码结构
- 向量检索:语义搜索相关代码
- 安全沙箱:vm2 隔离执行环境
- 工具链集成:类型检查、测试运行自动化
- 权限控制:限制文件访问范围
💡 经验教训
成功经验:
- ✅ Tree-sitter 解析比纯文本搜索准确得多
- ✅ 沙箱执行有效防止恶意代码
- ✅ 自动化类型检查和测试提高代码质量
踩坑记录:
- ❌ 初期未限制文件访问范围,存在安全风险
- ❌ 代码执行超时设置过短,复杂计算被中断
- ❌ 忘记清理临时文件,导致磁盘空间不足