UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

210 lines (209 loc) 8.79 kB
/** * LangChain / Agent Enhancement Pack * 条件: { languages: ['python'], frameworks: ['langchain'] } * * 覆盖 LLM Agent 开发生态: * - LangChain Chain / Agent / Tool * - RAG Pipeline (Retriever → Splitter → Embedding → VectorStore) * - Prompt Template / Output Parser * - LlamaIndex Query Engine * - 多轮对话 Memory * - Streaming / Callback */ import { EnhancementPack } from './EnhancementPack.js'; class LangChainEnhancement extends EnhancementPack { get id() { return 'python-langchain'; } get displayName() { return 'LangChain / Agent Enhancement'; } get conditions() { return { languages: ['python'], frameworks: ['langchain'] }; } getExtraDimensions() { return [ { id: 'langchain-chain-scan', label: 'Chain / Agent 分析', guide: 'LangChain Chain 拓扑分析 — LCEL (RunnableSequence | RunnableParallel) 链路、Agent 工具注册、AgentExecutor 配置、工具函数实现 (@tool decorator)', tierHint: 2, knowledgeTypes: ['architecture', 'code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-langchain-chains', description: 'LangChain chain topology, LCEL pipelines and agent tool registrations (auto-generated by enhancement)', }, }, { id: 'langchain-rag-scan', label: 'RAG Pipeline 分析', guide: 'RAG 检索增强生成管道分析 — Document Loader / Text Splitter / Embedding Model / VectorStore (Chroma/FAISS/Pinecone) 选型、Retriever 配置 (search_type/k/score_threshold)、Reranking 策略', tierHint: 2, knowledgeTypes: ['architecture', 'code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-langchain-rag', description: 'RAG pipeline — document loading, splitting, embedding and vector retrieval (auto-generated by enhancement)', }, }, { id: 'langchain-prompt-scan', label: 'Prompt / Output 模式分析', guide: 'Prompt 工程分析 — PromptTemplate / ChatPromptTemplate 结构、Few-shot 示例管理、Output Parser (JSON/Pydantic/Structured)、System Message 设计模式', tierHint: 2, knowledgeTypes: ['code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-langchain-prompts', description: 'Prompt templates, few-shot examples and output parser configurations (auto-generated by enhancement)', }, }, ]; } getGuardRules() { return [ { ruleId: 'langchain-prompt-injection', category: 'safety', dimension: 'file', severity: 'warning', languages: ['python'], pattern: /(?:PromptTemplate|ChatPromptTemplate)[\s\S]*?(?:f['"]|\.format\s*\()/, message: 'Prompt 中直接拼接用户输入可能导致 Prompt Injection — 使用 input_variables 参数化', }, { ruleId: 'langchain-no-bare-invoke', category: 'correctness', dimension: 'file', severity: 'info', languages: ['python'], pattern: /\.invoke\s*\([^)]*\)\s*(?![\s\S]*?(?:try|except|catch))/, message: 'Chain/Agent invoke 应包含错误处理 — LLM 调用可能超时/限流/返回异常格式', }, { ruleId: 'langchain-token-budget', category: 'performance', dimension: 'file', severity: 'info', languages: ['python'], pattern: /max_tokens\s*=\s*(?:None|0)/, message: '建议设置合理的 max_tokens 限制 — 防止意外高额 API 费用', }, { ruleId: 'langchain-hardcoded-api-key', category: 'safety', dimension: 'file', severity: 'error', languages: ['python'], pattern: /(?:api_key|openai_api_key|anthropic_api_key)\s*=\s*['"][^'"]+['"]/i, message: 'API Key 不应硬编码在代码中 — 使用环境变量 (os.environ) 或 .env 文件', }, ]; } detectPatterns(astSummary) { const patterns = []; // ── Chain / Runnable classes ── for (const cls of astSummary.classes || []) { if (cls.superclass && /Runnable|Chain|BaseTool|BaseRetriever/.test(cls.superclass)) { patterns.push({ type: 'langchain-chain', className: cls.name, line: cls.line, confidence: 0.9, }); } } // ── @tool decorated functions ── for (const m of astSummary.methods || []) { if (m.decorators?.some((d) => /@tool/.test(d))) { patterns.push({ type: 'langchain-tool', methodName: m.name, line: m.line, confidence: 0.95, }); } } // ── Agent/RAG setup functions ── for (const m of astSummary.methods || []) { if (!m.className && m.name) { const nameLower = m.name.toLowerCase(); if (nameLower.includes('create_agent') || nameLower.includes('build_chain') || nameLower.includes('setup_rag') || nameLower.includes('create_retriever') || nameLower.includes('get_llm') || nameLower.includes('create_chain') || nameLower.includes('build_graph')) { patterns.push({ type: 'langchain-setup', methodName: m.name, line: m.line, confidence: 0.8, }); } } } // ── Callback / Handler classes ── for (const cls of astSummary.classes || []) { if (cls.superclass && /CallbackHandler|BaseCallbackHandler/.test(cls.superclass)) { patterns.push({ type: 'langchain-callback', className: cls.name, line: cls.line, confidence: 0.9, }); } } // ── Output parser classes ── for (const cls of astSummary.classes || []) { if (cls.superclass && /OutputParser|BaseOutputParser/.test(cls.superclass)) { patterns.push({ type: 'langchain-output-parser', className: cls.name, line: cls.line, confidence: 0.9, }); } } // ── LangGraph nodes/edges ── for (const m of astSummary.methods || []) { if (!m.className && m.name) { const nameLower = m.name.toLowerCase(); if (nameLower.includes('node') || nameLower.includes('should_continue') || nameLower.includes('route_')) { patterns.push({ type: 'langgraph-node', methodName: m.name, line: m.line, confidence: 0.5, }); } } } // ── LangChain ecosystem imports ── const lcImports = (astSummary.imports || []).filter((imp) => imp.includes('langchain') || imp.includes('langgraph') || imp.includes('langsmith') || imp.includes('llama_index') || imp.includes('chromadb') || imp.includes('pinecone') || imp.includes('faiss') || imp.includes('openai') || imp.includes('anthropic')); if (lcImports.length > 0) { patterns.push({ type: 'langchain-ecosystem-usage', importCount: lcImports.length, confidence: 0.85, }); } return patterns; } } export const pack = new LangChainEnhancement();