mcpField
L
langchain-workflow-builder

langchain-workflow-builder

github.com/patricio0312revエージェントワークフロー63258 回インストール41 週間前に更新
リポジトリ

langchain-workflow-builder について

LLMアプリケーションを構築する開発者向けのLangChainスキルです。チェーン、会話メモリ、ツールとエージェント、RAG、ストリーミング、本番環境の設定を扱います。

主な機能

  1. 01合成可能なチェーンパターン
  2. 02会話メモリの選択肢
  3. 03カスタムツールとエージェント
  4. 04RAGによる文書検索
  5. 05ストリーミング応答
  6. 06本番環境の設定方法

活用シーン

  1. 01複数段階のLLMワークフロー構築
  2. 02会話間でのコンテキスト保持
  3. 03カスタムツールを使うエージェント作成
  4. 04文書からの回答検索

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/patricio0312rev/skills --skill langchain-workflow-builder

インストール後、Claude Code で/langchain-workflow-builderを入力して読み込まれたか確認します。

詳細

LangChain ワークフロービルダー

チェーン、エージェント、検索拡張生成を活用して、強力な LLM アプリケーションを構築します。

コアワークフロー

  1. LangChain のセットアップ:インストールして設定します
  2. チェーンの作成:処理パイプラインを構築します
  3. メモリの追加:会話のコンテキストを保持します
  4. ツールの定義:エージェントの機能を拡張します
  5. RAG の実装:ナレッジ検索を追加します
  6. デプロイ:本番環境に対応したセットアップを行います

インストール

npm install langchain @langchain/openai @langchain/community

基本的なチェーン

シンプルな LLM チェーン
// chains/simple.ts
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

const model = new ChatOpenAI({
  modelName: 'gpt-4-turbo-preview',
  temperature: 0.7,
});

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant that {task}.'],
  ['human', '{input}'],
]);

const chain = prompt.pipe(model).pipe(new StringOutputParser());

// Usage
const result = await chain.invoke({
  task: 'summarizes text concisely',
  input: 'Summarize this article: ...',
});
シーケンシャルチェーン
// chains/sequential.ts
import { RunnableSequence } from '@langchain/core/runnables';

// Chain 1: Extract key points
const extractChain = ChatPromptTemplate.fromMessages([
  ['system', 'Extract the key points from the following text.'],
  ['human', '{text}'],
]).pipe(model).pipe(new StringOutputParser());

// Chain 2: Summarize key points
const summarizeChain = ChatPromptTemplate.fromMessages([
  ['system', 'Create a brief summary from these key points.'],
  ['human', '{keyPoints}'],
]).pipe(model).pipe(new StringOutputParser());

// Combined chain
const fullChain = RunnableSequence.from([
  {
    keyPoints: extractChain,
    originalText: (input) => input.text,
  },
  {
    summary: summarizeChain,
    keyPoints: (input) => input.keyPoints,
  },
]);

const result = await fullChain.invoke({ text: 'Long article...' });
// { summary: '...', keyPoints: '...' }
分岐チェーン
// chains/branching.ts
import { RunnableBranch } from '@langchain/core/runnables';

const classifyChain = ChatPromptTemplate.fromMessages([
  ['system', 'Classify the query as: question, complaint, or feedback'],
  ['human', '{query}'],
]).pipe(model).pipe(new StringOutputParser());

const questionChain = ChatPromptTemplate.fromMessages([
  ['system', 'Answer this question helpfully.'],
  ['human', '{query}'],
]).pipe(model).pipe(new StringOutputParser());

const complaintChain = ChatPromptTemplate.fromMessages([
  ['system', 'Respond empathetically to this complaint.'],
  ['human', '{query}'],
]).pipe(model).pipe(new StringOutputParser());

const feedbackChain = ChatPromptTemplate.fromMessages([
  ['system', 'Thank the user for their feedback.'],
  ['human', '{query}'],
]).pipe(model).pipe(new StringOutputParser());

const routingChain = RunnableSequence.from([
  {
    classification: classifyChain,
    query: (input) => input.query,
  },
  RunnableBranch.from([
    [(input) => input.classification.includes('question'), questionChain],
    [(input) => input.classification.includes('complaint'), complaintChain],
    feedbackChain, // Default
  ]),
]);

メモリと会話

バッファーメモリ
// memory/conversation.ts
import { BufferMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';

const memory = new BufferMemory({
  returnMessages: true,
  memoryKey: 'history',
});

const chain = new ConversationChain({
  llm: model,
  memory,
  prompt: ChatPromptTemplate.fromMessages([
    ['system', 'You are a helpful assistant.'],
    new MessagesPlaceholder('history'),
    ['human', '{input}'],
  ]),
});

// Conversation maintains context
await chain.invoke({ input: 'My name is Alice' });
await chain.invoke({ input: 'What is my name?' }); // Remembers Alice
ウィンドウメモリ
// memory/window.ts
import { BufferWindowMemory } from 'langchain/memory';

const memory = new BufferWindowMemory({
  k: 5, // Keep last 5 exchanges
  returnMessages: true,
  memoryKey: 'history',
});
要約メモリ
// memory/summary.ts
import { ConversationSummaryMemory } from 'langchain/memory';

const memory = new ConversationSummaryMemory({
  llm: model,
  memoryKey: 'history',
});
// Summarizes conversation to save tokens
Redis による永続メモリ
// memory/redis.ts
import { BufferMemory } from 'langchain/memory';
import { RedisChatMessageHistory } from '@langchain/community/stores/message/redis';

const memory = new BufferMemory({
  chatHistory: new RedisChatMessageHistory({
    sessionId: `user:${userId}:session:${sessionId}`,
    client: redisClient,
    ttl: 3600, // 1 hour
  }),
  returnMessages: true,
  memoryKey: 'history',
});

ツールとエージェント

カスタムツールの定義
// tools/custom.ts
import { DynamicTool, DynamicStructuredTool } from '@langchain/core/tools';
import { z } from 'zod';

// Simple tool
const searchTool = new DynamicTool({
  name: 'search',
  description: 'Search the web for information',
  func: async (query: string) => {
    const results = await searchAPI.search(query);
    return JSON.stringify(results);
  },
});

// Structured tool with schema
const calculatorTool = new DynamicStructuredTool({
  name: 'calculator',
  description: 'Perform mathematical calculations',
  schema: z.object({
    expression: z.string().describe('Mathematical expression to evaluate'),
  }),
  func: async ({ expression }) => {
    try {
      const result = eval(expression); // Use safer math parser in production
      return String(result);
    } catch {
      return 'Error: Invalid expression';
    }
  },
});

// Database query tool
const dbQueryTool = new DynamicStructuredTool({
  name: 'query_database',
  description: 'Query the database for user or order information',
  schema: z.object({
    table: z.enum(['users', 'orders', 'products']),
    filter: z.record(z.string()).optional(),
    limit: z.number().default(10),
  }),
  func: async ({ table, filter, limit }) => {
    const results = await db[table].findMany({
      where: filter,
      take: limit,
    });
    return JSON.stringify(results);
  },
});
エージェントの作成
// agents/react.ts
import { createReactAgent, AgentExecutor } from 'langchain/agents';
import { pull } from 'langchain/hub';

// Get standard ReAct prompt
const prompt = await pull('hwchase17/react');

// Create agent
const agent = await createReactAgent({
  llm: model,
  tools: [searchTool, calculatorTool, dbQueryTool],
  prompt,
});

// Create executor
const executor = new AgentExecutor({
  agent,
  tools: [searchTool, calculatorTool, dbQueryTool],
  verbose: true,
  maxIterations: 5,
});

// Run agent
const result = await executor.invoke({
  input: 'What is the square root of the number of users in our database?',
});
OpenAI Functions エージェント
// agents/openai-functions.ts
import { createOpenAIFunctionsAgent, AgentExecutor } from 'langchain/agents';

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant with access to tools.'],
  new MessagesPlaceholder('chat_history'),
  ['human', '{input}'],
  new MessagesPlaceholder('agent_scratchpad'),
]);

const agent = await createOpenAIFunctionsAgent({
  llm: new ChatOpenAI({ modelName: 'gpt-4-turbo-preview' }),
  tools: [searchTool, calculatorTool],
  prompt,
});

const executor = new AgentExecutor({
  agent,
  tools: [searchTool, calculatorTool],
  memory: new BufferMemory({
    returnMessages: true,
    memoryKey: 'chat_history',
  }),
});

RAG パイプライン

ドキュメントの読み込み
// rag/loader.ts
import { DirectoryLoader } from 'langchain/document_loaders/fs/directory';
import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { TextLoader } from 'langchain/document_loaders/fs/text';
import { JSONLoader } from 'langchain/document_loaders/fs/json';

const loader = new DirectoryLoader('./documents', {
  '.pdf': (path) => new PDFLoader(path),
  '.txt': (path) => new TextLoader(path),
  '.json': (path) => new JSONLoader(path),
});

const docs = await loader.load();
テキストの分割
// rag/splitter.ts
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
  separators: ['\n\n', '\n', ' ', ''],
});

const chunks = await splitter.splitDocuments(docs);
ベクトルストア
// rag/vectorstore.ts
import { OpenAIEmbeddings } from '@langchain/openai';
import { PineconeStore } from '@langchain/pinecone';
import { Pinecone } from '@pinecone-database/pinecone';

const embeddings = new OpenAIEmbeddings();

const pinecone = new Pinecone();
const index = pinecone.Index(process.env.PINECONE_INDEX!);

// Create vector store from documents
const vectorStore = await PineconeStore.fromDocuments(chunks, embeddings, {
  pineconeIndex: index,
  namespace: 'documents',
});

// Or connect to existing
const existingStore = await PineconeStore.fromExistingIndex(embeddings, {
  pineconeIndex: index,
  namespace: 'documents',
});
RAG チェーン
// rag/chain.ts
import { createRetrievalChain } from 'langchain/chains/retrieval';
import { createStuffDocumentsChain } from 'langchain/chains/combine_documents';

const retriever = vectorStore.asRetriever({
  k: 5, // Return top 5 results
  searchType: 'similarity',
});

const prompt = ChatPromptTemplate.fromMessages([
  [
    'system',
    `Answer questions based on the following context. If the answer is not in the context, say so.

Context: {context}`,
  ],
  ['human', '{input}'],
]);

const combineDocsChain = await createStuffDocumentsChain({
  llm: model,
  prompt,
});

const ragChain = await createRetrievalChain({
  retriever,
  combineDocsChain,
});

const result = await ragChain.invoke({
  input: 'What is our refund policy?',
});
// { answer: '...', context: [Document, Document, ...] }
会話型 RAG
// rag/conversational.ts
import { createHistoryAwareRetriever } from 'langchain/chains/history_aware_retriever';

const contextualizePrompt = ChatPromptTemplate.fromMessages([
  ['system', 'Given chat history and question, reformulate as standalone question.'],
  new MessagesPlaceholder('chat_history'),
  ['human', '{input}'],
]);

const historyAwareRetriever = await createHistoryAwareRetriever({
  llm: model,
  retriever,
  rephrasePrompt: contextualizePrompt,
});

const qaPrompt = ChatPromptTemplate.fromMessages([
  ['system', 'Answer based on context:\n\n{context}'],
  new MessagesPlaceholder('chat_history'),
  ['human', '{input}'],
]);

const questionAnswerChain = await createStuffDocumentsChain({
  llm: model,
  prompt: qaPrompt,
});

const conversationalRagChain = await createRetrievalChain({
  retriever: historyAwareRetriever,
  combineDocsChain: questionAnswerChain,
});

ストリーミング

// streaming/stream.ts
const stream = await chain.stream({ input: 'Tell me a story' });

for await (const chunk of stream) {
  process.stdout.write(chunk);
}

// With callbacks
const result = await chain.invoke(
  { input: 'Tell me a story' },
  {
    callbacks: [
      {
        handleLLMNewToken(token: string) {
          process.stdout.write(token);
        },
      },
    ],
  }
);

本番環境のセットアップ

// config/langchain.ts
import { ChatOpenAI } from '@langchain/openai';

export const model = new ChatOpenAI({
  modelName: process.env.OPENAI_MODEL || 'gpt-4-turbo-preview',
  temperature: 0.7,
  maxTokens: 4096,
  timeout: 60000,
  maxRetries: 3,
  cache: true,
});

// With rate limiting
import Bottleneck from 'bottleneck';

const limiter = new Bottleneck({
  minTime: 100, // 10 requests per second
  maxConcurrent: 5,
});

export async function invokeWithRateLimit(chain: any, input: any) {
  return limiter.schedule(() => chain.invoke(input));
}

ベストプラクティス

  1. LCEL を使用する:合成しやすい LangChain Expression Language を活用します
  2. メモリを戦略的に追加する:コンテキストとトークンのバランスを取ります
  3. ツールの説明を明確に定義する:エージェントが適切に選択できるようにします
  4. ドキュメントを適切にチャンク分割する:検索に最適化します
  5. ストリーミングを使用する:長い応答の UX を向上させます
  6. 埋め込みをキャッシュする:API コストを削減します
  7. トークンの使用量を監視する:使用状況を追跡して最適化します
  8. エラーを適切に処理する:再試行とフォールバックを行います

出力チェックリスト

LangChain の実装には、次の項目をすべて含めてください。

  • 再試行を含むモデル設定
  • 変数を含むプロンプトテンプレート
  • LCEL によるチェーンの合成
  • 会話のコンテキストを保持するメモリ
  • 明確な説明を含むツール
  • 反復回数の上限を設定したエージェント
  • 適切なチャンク分割を行う RAG
  • UX のためのストリーミング
  • エラー処理
  • 本番環境向けのレート制限

コメント