К основному содержанию
правило

langchain-js — правило для Cursor: что делает и как установить

Правило задаёт практики для LLM-приложений на langchain-js: модульность, типы, схемы zod, конфигурацию и тестирование.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/langchain-js.mdc
Открыть на GitHub

Что делает langchain-js

Правило советует разносить агентов, инструменты, модели и цепочки по отдельным модулям. Для интерфейсов инструментов нужны явные типы и схемы zod, а секреты, параметры моделей и адреса сервисов следует хранить в переменных окружения.

В рекомендациях также есть шаблон для инструментов с валидацией входных данных и подход к RAG через VectorStoreRetriever, RunnableSequence и MongoDB Atlas Vector Search. Файлы с правилом применяются к **/*.{js,jsx}. В примерах используются @langchain/openai, @langchain/mongodb, mongodb, VoyageEmbeddings и dotenv; без OPENAI_API_KEY конфигурация модели из примера не заработает.

Какие задачи решает

  • разнести агента и инструменты по модулям
  • описать входные данные инструмента схемой zod
  • убрать API-ключи из исходного кода
  • собрать RAG-цепочку с поиском по MongoDB Atlas
  • проверить структуру LLM-приложения на langchain-js

Примеры запросов агенту

  • Проверь JavaScript-файлы проекта по правилам для langchain-js и укажи нарушения модульности.
  • Разнеси агента и его инструменты по отдельным модулям, добавь явные типы и схемы zod.
  • Перепиши этот RAG-код через VectorStoreRetriever и RunnableSequence, не хардкодь секреты.

Как установить langchain-js

Вручную: положить файлы

.cursor/rules/langchain-js.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о langchain-js

Как установить langchain-js в Cursor?

Создай файл .cursor/rules/langchain-js.mdc и помести в него содержимое правила.

Что делает langchain-js?

Правило помогает организовать приложения на langchain-js: разделять компоненты, типизировать инструменты, валидировать входные данные и хранить настройки в переменных окружения.

Какие файлы обрабатывает правило?

Оно применяется к файлам с расширениями .js и .jsx, заданным шаблоном **/*.{js,jsx}.

Исходный текст

langchain-js.mdc · CC0-1.0
---
description: This guide provides opinionated, actionable best practices for building robust and maintainable LLM applications with langchain-js, emphasizing modularity, type safety, observability, and testing.
globs: **/*.{js,jsx}
---
# langchain-js Best Practices

Building LLM-powered applications with `langchain-js` requires a disciplined approach to ensure maintainability, performance, and reliability. This guide outlines our team's definitive best practices.

## 1. Code Organization and Structure

Always prioritize modularity and explicit typing. Break down complex logic into small, reusable components.

### 1.1. Modular Components & Single Responsibility

Each LangChain component (Agent, Tool, Chain, Model) should reside in its own file or a dedicated module, adhering to the Single Responsibility Principle.

❌ **BAD: Monolithic file**
```javascript
// src/agent.js
import { ChatOpenAI } from "@langchain/openai";
import { createAgent, tool } from "langchain";
import { z } from "zod";

const getStockPrice = tool(async ({ ticker }) => { /* ... */ }, { /* ... */ });
const getNews = tool(async ({ query }) => { /* ... */ }, { /* ... */ });

const model = new ChatOpenAI({ temperature: 0.7 });
const agent = createAgent({
  model,
  tools: [getStockPrice, getNews],
});

export async function runFinancialAgent(input) {
  return agent.invoke(input);
}
```

✅ **GOOD: Modular, reusable components**
```typescript
// src/tools/getStockPrice.ts
import { tool } from "langchain";
import { z } from "zod";

export const getStockPriceTool = tool(
  async ({ ticker }: { ticker: string }) => {
    // Simulate API call
    if (ticker === "AAPL") return "$170.00";
    return "Price not found.";
  },
  {
    name: "get_stock_price",
    description: "Get the current stock price for a given ticker symbol.",
    schema: z.object({
      ticker: z.string().describe("The stock ticker symbol (e.g., AAPL)"),
    }),
  }
);

// src/tools/getNews.ts
import { tool } from "langchain";
import { z } from "zod";

export const getNewsTool = tool(
  async ({ query }: { query: string }) => {
    // Simulate API call
    return `Latest news for ${query}: Market is up!`;
  },
  {
    name: "get_news",
    description: "Get the latest news for a given query.",
    schema: z.object({
      query: z.string().describe("The news query"),
    }),
  }
);

// src/agents/financialAgent.ts
import { ChatOpenAI } from "@langchain/openai";
import { createAgent } from "langchain";
import { getStockPriceTool } from "../tools/getStockPrice";
import { getNewsTool } from "../tools/getNews";

const model = new ChatOpenAI({ temperature: 0.7 });

export const financialAgent = createAgent({
  model,
  tools: [getStockPriceTool, getNewsTool],
});

// src/index.ts (or wherever the agent is invoked)
import { financialAgent } from "./agents/financialAgent";

async function main() {
  const result = await financialAgent.invoke({
    messages: [{ role: "user", content: "What's the price of AAPL and the latest market news?" }],
  });
  console.log(result);
}

main();
```

### 1.2. Explicit TypeScript Types

Always use explicit types for prompts, models, and tool interfaces. Avoid `any`.

❌ **BAD: Implicit types, no schema validation**
```javascript
const myTool = tool((input) => { /* ... */ }, { name: "my_tool" });
```

✅ **GOOD: Explicit types with Zod schema**
```typescript
import { tool } from "langchain";
import { z } from "zod";

const MyToolInputSchema = z.object({
  param1: z.string().describe("Description for param1"),
  param2: z.number().optional().describe("Description for param2"),
});

export const myTypedTool = tool(
  async (input: z.infer<typeof MyToolInputSchema>) => {
    console.log(`Received: ${input.param1}, ${input.param2}`);
    return "Processed successfully";
  },
  {
    name: "my_typed_tool",
    description: "A tool that takes typed input.",
    schema: MyToolInputSchema,
  }
);
```

### 1.3. Configuration Management

Store API keys, model parameters, and external service URLs in environment variables. Never hardcode secrets.

❌ **BAD: Hardcoded API key**
```javascript
const model = new ChatOpenAI({
  openAIApiKey: "sk-YOUR_HARDCODED_KEY",
});
```

✅ **GOOD: Environment variables**
```typescript
// .env
// OPENAI_API_KEY=sk-YOUR_OPENAI_KEY
// LANGSMITH_API_KEY=ls__YOUR_LANGSMITH_KEY
// MONGODB_URI=mongodb+srv://...

// src/config.ts
import "dotenv/config"; // Ensure dotenv is loaded early

export const OPENAI_API_KEY = process.env.OPENAI_API_KEY!;
export const LANGSMITH_API_KEY = process.env.LANGSMITH_API_KEY!;
export const MONGODB_URI = process.env.MONGODB_URI!;

// src/models/chatModel.ts
import { ChatOpenAI } from "@langchain/openai";
import { OPENAI_API_KEY } from "../config";

export const chatModel = new ChatOpenAI({
  openAIApiKey: OPENAI_API_KEY,
  temperature: 0.7,
  modelName: "gpt-4o",
});
```

## 2. Common Patterns and Anti-patterns

Leverage LangChain's core abstractions effectively.

### 2.1. Robust Tool Definitions

Always define tools with `zod` schemas for clear input validation and better agent reasoning.

❌ **BAD: Undefined tool schema**
```javascript
const simpleTool = tool((input) => `Processed ${input}`, { name: "simple_tool" });
```

✅ **GOOD: Tool with Zod schema**
```typescript
import { tool } from "langchain";
import { z } from "zod";

export const calculateSumTool = tool(
  async ({ a, b }: { a: number; b: number }) => (a + b).toString(),
  {
    name: "calculate_sum",
    description: "Calculates the sum of two numbers.",
    schema: z.object({
      a: z.number().describe("The first number"),
      b: z.number().describe("The second number"),
    }),
  }
);
```

### 2.2. Retrieval-Augmented Generation (RAG)

For knowledge retrieval, use `VectorStoreRetriever` within a `RunnableSequence`.

```typescript
// src/retrievers/documentRetriever.ts
import { MongoDBAtlasVectorSearch } from "@langchain/mongodb";
import { VoyageEmbeddings } from "@langchain/community/embeddings/voyage";
import { MongoClient } from "mongodb";
import { MONGODB_URI } from "../config";

const client = new MongoClient(MONGODB_URI);
const collection = client.db("langchain_db").collection("documents");

export async function getVectorStoreRetriever() {
  const embeddings = new VoyageEmbeddings();
  const vectorStore = new MongoDBAtlasVectorSearch(embeddings, {
    collection,
    indexName: "vector_index", // Your Atlas Vector Search index name
    textKey: "content", // Field containing the text
    embeddingKey: "embedding", // Field containing the vector embedding
  });
  return vectorStore.asRetriever();
}

// src/chains/ragChain.ts
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { formatDocumentsAsString } from "langchain/util/document";
import { chatModel } from "../models/chatModel";
import { getVectorStoreRetriever } from "../retrievers/documentRetriever";

const RAG_PROMPT_TEMPLATE = `
You are an AI assistant for answering questions about documents.
Use the following context to answer the question.
If you don't know the answer, just say that you don't know.
Context: {context}
Question: {question}
`;

export async function createRagChain() {
  const retriever = await getVectorStoreRetriever();
  const ragPrompt = PromptTemplate.fromTemplate(RAG_PROMPT_TEMPLATE);

  return RunnableSequence.from([
    RunnablePassthrough.assign({
      context: async (input: { question: string }) => {
        const docs = await retriever.invoke(input.question);
        return formatDocumentsAsString(docs);
      },
    }),
    ragPrompt,
    chatModel,
    new StringOutputParser(),
  ]);
}

// src/index.ts
import { createRagChain } from "./chains/ragChain";

async function runRagExample() {
  const ragChain = await createRagChain();
  const question = "What are the best practices for MongoDB Atlas?";
  const answer = await ragChain.invoke({ question });
  console.log(answer);
}

runRagExample();
```

### 2.3. Streaming for Responsiveness

Implement streaming for LLM responses to improve user experience, especially for long generations.

```typescript
// src/models/streamingChatModel.ts
import { ChatOpenAI } from "@langchain/openai";
import { OPENAI_API_KEY } from "../config";

export const streamingChatModel = new ChatOpenAI({
  openAIApiKey: OPENAI_API_KEY,
  temperature: 0.7,
  modelName: "gpt-4o",
  streaming: true, // Enable streaming
});

// src/index.ts
import { streamingChatModel } from "./models/streamingChatModel";

async function streamResponse() {
  const stream = await streamingChatModel.stream("Tell me a long story about a space cat.");
  for await (const chunk of stream) {
    process.stdout.write(chunk.content);
  }
  console.log("\n--- Stream Finished ---");
}

streamResponse();
```

### 2.4. Error Handling and Retries

Wrap external LLM and API calls with robust retry logic to handle transient failures. Use a library like `p-retry`.

❌ **BAD: No retry logic**
```typescript
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI();
try {
  await model.invoke("...");
} catch (error) {
  console.error("LLM call failed:", error); // Fails on first error
}
```

✅ **GOOD: Retry logic with exponential backoff**
```typescript
import { ChatOpenAI } from "@langchain/openai";
import pRetry from "p-retry";
import { OPENAI_API_KEY } from "../config";

const model = new ChatOpenAI({ openAIApiKey: OPENAI_API_KEY });

async function reliableLLMCall(prompt: string) {
  return pRetry(
    async () => {
      console.log("Attempting LLM call...");
      return await model.invoke(prompt);
    },
    {
      retries: 5,
      minTimeout: 1000, // 1 second
      maxTimeout: 10000, // 10 seconds
      onFailedAttempt: (error) => {
        console.warn(`Attempt ${error.attemptNumber} failed. Retrying...`);
      },
    }
  );
}

// Usage
reliableLLMCall("Generate a creative story about a robot chef.")
  .then((res) => console.log(res))
  .catch((err) => console.error("Final LLM call failed after retries:", err));
```

## 3. Performance Considerations

Optimize for speed and resource usage.

### 3.1. Asynchronous Callbacks

For non-serverless environments, set `LANGCHAIN_CALLBACKS_BACKGROUND=true` to process LangSmith traces asynchronously, reducing latency. In serverless, ensure it's `false` or unset for traces to complete before function exit.

```bash
# In your development/production environment (non-serverless)
export LANGCHAIN_CALLBACKS_BACKGROUND=true

# In your serverless environment (e.g., AWS Lambda)
# Ensure this is NOT set to true, or explicitly set to false if needed.
# export LANGCHAIN_CALLBACKS_BACKGROUND=false
```

### 3.2. Efficient Vector Search

When using vector stores, leverage metadata filtering to narrow down search space *before* vector similarity search, if applicable.

```typescript
// Example: Filtering by document source
const retriever = vectorStore.asRetriever({
  filter: {
    source: "internal_docs",
  },
});
```

## 4. Common Pitfalls and Gotchas

Avoid these common mistakes to prevent headaches.

### 4.1. Ignoring LangSmith for Observability

Not integrating with LangSmith means flying blind. Every chain, agent step, and retrieval call should be traced.

❌ **BAD: No LangSmith setup**
```bash
# Missing environment variables
# LANGSMITH_TRACING=true
# LANGSMITH_API_KEY=...
```

✅ **GOOD: LangSmith enabled**
```bash
# Set these in your .env or deployment environment
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=<your-api-key>
export LANGSMITH_PROJECT=<your-project-name> # Optional, but recommended
export LANGSMITH_WORKSPACE_ID=<your-workspace-id> # If using multiple workspaces
```
With these set, LangChain automatically instruments your runs.

### 4.2. Over-privileged Agents

Agents should operate with the principle of least privilege. Limit tool access and permissions.

❌ **BAD: Agent with access to sensitive file system operations**
```typescript
import { tool } from "langchain";
import * as fs from "fs/promises";
import { z } from "zod";

// This tool can delete any file! DANGEROUS!
const deleteFileTool = tool(
  async ({ path }: { path: string }) => {
    await fs.unlink(path);
    return `Deleted ${path}`;
  },
  {
    name: "delete_file",
    description: "Deletes a file at the given path.",
    schema: z.object({ path: z.string() }),
  }
);
// ... agent given deleteFileTool
```

✅ **GOOD: Sandboxed or read-only tools**
```typescript
import { tool } from "langchain";
import * as fs from "fs/promises";
import { z } from "zod";
import path from "path";

const SAFE_DIR = "/app/data/safe_uploads"; // Define a safe, restricted directory

const readFileSafeTool = tool(
  async ({ filename }: { filename: string }) => {
    const filePath = path.join(SAFE_DIR, filename);
    if (!filePath.startsWith(SAFE_DIR)) { // Prevent path traversal
      throw new Error("Access denied: Cannot read outside safe directory.");
    }
    return await fs.readFile(filePath, "utf-8");
  },
  {
    name: "read_safe_file",
    description: "Reads a file from the designated safe directory.",
    schema: z.object({ filename: z.string() }),
  }
);
// ... agent given readFileSafeTool, NOT deleteFileTool
```

## 5. Testing Approaches

Treat testing as a first-class concern.

### 5.1. Unit Testing with `FakeLLM` / `MockChatModel`

Isolate and test individual components by mocking LLM responses.

```typescript
// src/chains/greetingChain.ts
import { PromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence } from "@langchain/core/runnables";
import { BaseChatModel } from "@langchain/core/language_models/chat_models";

export function createGreetingChain(model: BaseChatModel) {
  const prompt = PromptTemplate.fromTemplate("Say hello to {name}.");
  return RunnableSequence.from([prompt, model, new StringOutputParser()]);
}

// src/chains/greetingChain.test.ts
import { test, expect, vi } from "vitest";
import { FakeChatModel } from "@langchain/core/utils/testing";
import { createGreetingChain } from "./greetingChain";

test("createGreetingChain generates correct greeting", async () => {
  const mockModel = new FakeChatModel({});
  mockModel.predict = vi.fn((input) => Promise.resolve(`Hello, ${input.split(" ")[3]}!`)); // Mock response based on prompt

  const chain = createGreetingChain(mockModel);
  const result = await chain.invoke({ name: "Alice" });

  expect(mockModel.predict).toHaveBeenCalledWith("Human: Say hello to Alice.");
  expect(result).toBe("Hello, Alice!");
});
```

### 5.2. Integration Testing with LangSmith

Run end-to-end tests against a dedicated sandbox LangSmith project to verify real-world behavior and catch regressions.

```typescript
// .env.test
// LANGSMITH_TRACING=true
// LANGSMITH_API_KEY=ls__YOUR_TEST_API_KEY
// LANGSMITH_PROJECT=my-app-integration-tests

// src/integration.test.ts
import { test, expect } from "vitest";
import { createRagChain } from "./chains/ragChain"; // Your actual RAG chain
import { Client } from "langsmith"; // For querying test results

// Ensure LangSmith is configured for this test run via .env.test
// or by setting process.env variables directly in test setup.

test("RAG chain answers question correctly with real LLM and retriever", async () => {
  const ragChain = await createRagChain(); // Uses real LLM and vector store
  const question = "What is the capital of France?";
  const answer = await ragChain.invoke({ question });

  expect(answer).toContain("Paris"); // Basic assertion
  // For more advanced validation, inspect the trace in LangSmith manually
  // or programmatically using the LangSmith SDK if needed.
});
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «ИИ и ML» →
mongodb-search-and-ai mongodb Помогает настроить полнотекстовый, векторный или гибридный поиск в MongoDB Atlas. Подбирает структуру индексов и строит запросы под ваши коллекции. CX Codex +1 5 800установок langchain-rag langchain-ai Даёт ИИ-агенту готовые шаблоны и код для сборки RAG-пайплайнов на LangChain в Python и TypeScript: от загрузки документов до векторного поиска. CX Codex +1 17 500установок agents-langchain Orchestra Research Скилл помогает собирать приложения на LangChain с агентами, цепочками и RAG. Пригодится для чат-ботов, поиска по документам и tool calling. 968установок langchain-development Mindrally Задаёт агенту правила разработки на LangChain и LangGraph: композиция цепочек, агенты, память, RAG. Пригодится, когда проект на Python растёт и код надо держать в одном стиле. CX Codex +1 681установок langchain-orchestration manutej Скилл-справочник по LangChain: цепочки, агенты, память, RAG и оркестрация. Пригодится, когда агент пишет код на LangChain и нужно, чтобы он держался рабочих паттернов. CX Codex +1 451установок langchain-python официальный github Правило для GitHub Copilot, которое подсказывает агенту паттерны LangChain: Runnable, LCEL, чат-модели, ретриверы и векторные хранилища. Пригодится, если вы пишете на Python с LangChain. ★ 40 478звёзд репо

Из того же набора240

Весь набор →
lightgbm Инструкция для Cursor по разработке моделей LightGBM в Python: настройка LGBMClassifier, воспроизводимость и категориальные признаки без лишнего кодирования. ★ 3 580звёзд репо laravel Правило направляет Cursor при работе с PHP-кодом Laravel: помогает выносить логику из контроллеров и избегать типичных ошибок с запросами и безопасностью. ★ 3 580звёзд репо langgraph Правило для Cursor с практиками LangGraph: как раскладывать узлы, состояния и подграфы в Python-проекте. ★ 3 580звёзд репо langchain Задаёт стандарты сборки LLM-приложений на Python: заставляет Cursor использовать LCEL, агентов на базе LangGraph и типизацию Pydantic вместо устаревших цепочек. ★ 3 580звёзд репо kubernetes Правило для Cursor задаёт, как писать и раскладывать по файлам манифесты Kubernetes: метки, ресурсы, пробы, Pod Security Standards. ★ 3 580звёзд репо kivy Направляет Cursor на правильную работу с Kivy: отделяет вёрстку в файлы .kv, подключает KivyMD и настраивает реактивные свойства вместо обычных атрибутов Python. ★ 3 580звёзд репо keras Правило задаёт практики для Keras 3: структуру проекта, построение моделей и обучение. Пригодится, если агент пишет или меняет Python-код для моделей. ★ 3 580звёзд репо junit Правило для Cursor задаёт стиль юнит-тестов на JUnit 5 в Java-проектах: имена, структура, параметризация. Пригодится, когда тесты пишет агент и хочется единый вид. ★ 3 580звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой