autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
210 lines (209 loc) • 8.79 kB
JavaScript
/**
* 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();