autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
327 lines (326 loc) • 14.6 kB
JavaScript
/**
* KnowledgeModule — 知识 + 搜索 + 向量服务注册
*
* 负责注册:
* - knowledgeService, knowledgeGraphService, codeEntityGraph, confidenceRouter
* - searchEngine, vectorStore, indexingPipeline
* - discovererRegistry, enhancementRegistry, languageService, dimensionCopy
* - constitution, aiProvider, projectGraph
*/
import { DimensionCopy } from '#domain/dimension/DimensionCopy.js';
import { resolveProjectRoot } from '#shared/resolveProjectRoot.js';
import { getDiscovererRegistry } from '../../core/discovery/index.js';
import { getEnhancementRegistry } from '../../core/enhancement/index.js';
import { HnswVectorAdapter } from '../../infrastructure/vector/HnswVectorAdapter.js';
import { IndexingPipeline } from '../../infrastructure/vector/IndexingPipeline.js';
import { JsonVectorAdapter } from '../../infrastructure/vector/JsonVectorAdapter.js';
import { LifecycleEventRepository } from '../../repository/evolution/LifecycleEventRepository.js';
import { ProposalRepository } from '../../repository/evolution/ProposalRepository.js';
import { ConsolidationAdvisor } from '../../service/evolution/ConsolidationAdvisor.js';
import { ContentPatcher } from '../../service/evolution/ContentPatcher.js';
import { ContradictionDetector } from '../../service/evolution/ContradictionDetector.js';
import { DecayDetector } from '../../service/evolution/DecayDetector.js';
import { EnhancementSuggester } from '../../service/evolution/EnhancementSuggester.js';
import { KnowledgeMetabolism } from '../../service/evolution/KnowledgeMetabolism.js';
import { ProposalExecutor } from '../../service/evolution/ProposalExecutor.js';
import { RecipeLifecycleSupervisor } from '../../service/evolution/RecipeLifecycleSupervisor.js';
import { RedundancyAnalyzer } from '../../service/evolution/RedundancyAnalyzer.js';
import { StagingManager } from '../../service/evolution/StagingManager.js';
import { CodeEntityGraph } from '../../service/knowledge/CodeEntityGraph.js';
import { ConfidenceRouter } from '../../service/knowledge/ConfidenceRouter.js';
import { KnowledgeGraphService } from '../../service/knowledge/KnowledgeGraphService.js';
import { KnowledgeService } from '../../service/knowledge/KnowledgeService.js';
import { SourceRefReconciler } from '../../service/knowledge/SourceRefReconciler.js';
import { HybridRetriever } from '../../service/search/HybridRetriever.js';
import { SearchEngine } from '../../service/search/SearchEngine.js';
import { LanguageService } from '../../shared/LanguageService.js';
export function register(c) {
// ═══ Knowledge ═══
c.singleton('confidenceRouter', (ct) => new ConfidenceRouter({}, ct.get('qualityScorer')));
c.singleton('knowledgeService', (ct) => new KnowledgeService(ct.get('knowledgeRepository'), ct.get('auditLogger'), ct.get('gateway'), ct.get('knowledgeGraphService'), {
fileWriter: ct.get('knowledgeFileWriter'),
skillHooks: ct.get('skillHooks'),
confidenceRouter: ct.get('confidenceRouter'),
qualityScorer: ct.get('qualityScorer'),
eventBus: ct.services.eventBus ? ct.get('eventBus') : null,
edgeRepo: ct.get('knowledgeEdgeRepository'),
proposalRepo: ct.get('proposalRepository'),
}));
c.singleton('knowledgeGraphService', (ct) => new KnowledgeGraphService(ct.get('knowledgeEdgeRepository')));
c.singleton('codeEntityGraph', (ct) => {
const projectRoot = resolveProjectRoot(ct);
return new CodeEntityGraph(ct.get('codeEntityRepository'), ct.get('knowledgeEdgeRepository'), { projectRoot });
});
// ═══ Search + Vector ═══
c.singleton('searchEngine', (ct) => {
const aiProvider = ct.singletons.aiProvider || null;
const embedProvider = ct.singletons._embedProvider || aiProvider;
const vectorService = ct.services.vectorService ? ct.get('vectorService') : null;
return new SearchEngine(ct.get('database'), {
aiProvider: embedProvider,
vectorStore: ct.get('vectorStore'),
vectorService,
hybridRetriever: ct.get('hybridRetriever'),
crossEncoderReranker: null,
signalBus: ct.singletons.signalBus || null,
knowledgeRepo: ct.get('knowledgeRepository'),
sourceRefRepo: ct.get('recipeSourceRefRepository'),
});
}, { aiDependent: true });
c.singleton('vectorStore', (ct) => {
const projectRoot = resolveProjectRoot(ct);
const config = ct.singletons._config?.vector || {};
const adapter = config.adapter || 'auto';
// 根据配置选择适配器
if (adapter === 'json') {
const store = new JsonVectorAdapter(projectRoot);
store.initSync();
return store;
}
if (adapter === 'hnsw' || adapter === 'auto') {
try {
const hnsw = config.hnsw || {};
const persistence = config.persistence || {};
const store = new HnswVectorAdapter(projectRoot, {
M: hnsw.M,
efConstruct: hnsw.efConstruct,
efSearch: hnsw.efSearch,
quantize: config.quantize,
quantizeThreshold: config.quantizeThreshold,
flushIntervalMs: persistence.flushIntervalMs,
flushBatchSize: persistence.flushBatchSize,
});
store.initSync();
return store;
}
catch (err) {
// HNSW 初始化失败, 降级到 JSON — 记录警告便于排查
const logger = ct.singletons.logger || console;
logger.warn?.('[vectorStore] HNSW init failed, falling back to JsonVectorAdapter', {
error: err.message,
adapter,
});
const store = new JsonVectorAdapter(projectRoot);
store.initSync();
return store;
}
}
// 未知适配器, 默认 JSON
const store = new JsonVectorAdapter(projectRoot);
store.initSync();
return store;
});
c.singleton('indexingPipeline', (ct) => {
const aiProvider = ct.singletons.aiProvider || null;
const embedProvider = ct.singletons._embedProvider || aiProvider;
return new IndexingPipeline({
vectorStore: ct.get('vectorStore'),
aiProvider: embedProvider,
});
}, { aiDependent: true });
c.singleton('hybridRetriever', (ct) => {
const config = ct.singletons._config?.vector;
const hybrid = config?.hybrid || {};
return new HybridRetriever({
vectorStore: ct.get('vectorStore'),
rrfK: hybrid.rrfK || 60,
alpha: hybrid.alpha || 0.5,
});
});
// ═══ Discovery + Shared ═══
c.register('discovererRegistry', () => getDiscovererRegistry());
c.register('enhancementRegistry', () => getEnhancementRegistry());
c.register('languageService', () => LanguageService);
c.register('dimensionCopy', () => DimensionCopy);
c.register('constitution', () => c.singletons.constitution || null);
c.register('aiProvider', () => c.singletons.aiProvider || null);
c.register('projectGraph', () => c.singletons.projectGraph || null);
// ═══ Governance / Evolution ═══
c.singleton('sourceRefReconciler', (ct) => {
const projectRoot = resolveProjectRoot();
const sourceRefRepo = ct.get('recipeSourceRefRepository');
const knowledgeRepo = ct.get('knowledgeRepository');
return new SourceRefReconciler(projectRoot, sourceRefRepo, knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
});
});
c.singleton('stagingManager', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new StagingManager(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
});
});
c.singleton('decayDetector', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new DecayDetector(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
knowledgeEdgeRepo: ct.services.knowledgeEdgeRepository
? ct.get('knowledgeEdgeRepository')
: undefined,
sourceRefRepo: ct.services.recipeSourceRefRepository
? ct.get('recipeSourceRefRepository')
: undefined,
drizzle: ct.get('database').getDrizzle(),
});
});
c.singleton('contradictionDetector', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new ContradictionDetector(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
});
});
c.singleton('redundancyAnalyzer', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new RedundancyAnalyzer(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
});
});
c.singleton('enhancementSuggester', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new EnhancementSuggester(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
});
});
c.singleton('knowledgeMetabolism', (ct) => {
return new KnowledgeMetabolism({
contradictionDetector: ct.get('contradictionDetector'),
redundancyAnalyzer: ct.get('redundancyAnalyzer'),
decayDetector: ct.get('decayDetector'),
signalBus: ct.singletons.signalBus || undefined,
proposalRepository: ct.services.proposalRepository
? ct.get('proposalRepository')
: undefined,
});
});
c.singleton('proposalRepository', (ct) => {
const db = ct.get('database');
const drizzle = db.getDrizzle();
return new ProposalRepository(drizzle);
});
c.singleton('contentPatcher', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
const sourceRefRepo = ct.get('recipeSourceRefRepository');
return new ContentPatcher(knowledgeRepo, sourceRefRepo);
});
c.singleton('lifecycleEventRepository', (ct) => {
const db = ct.get('database');
const drizzle = db.getDrizzle();
return new LifecycleEventRepository(drizzle);
});
c.singleton('lifecycleSupervisor', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new RecipeLifecycleSupervisor(knowledgeRepo, {
signalBus: ct.singletons.signalBus || undefined,
lifecycleEventRepo: ct.services.lifecycleEventRepository
? ct.get('lifecycleEventRepository')
: undefined,
proposalRepo: ct.services.proposalRepository
? ct.get('proposalRepository')
: undefined,
});
});
c.singleton('proposalExecutor', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new ProposalExecutor(knowledgeRepo, ct.get('proposalRepository'), {
signalBus: ct.singletons.signalBus || undefined,
contentPatcher: ct.get('contentPatcher'),
supervisor: ct.get('lifecycleSupervisor'),
knowledgeEdgeRepo: ct.services.knowledgeEdgeRepository
? ct.get('knowledgeEdgeRepository')
: undefined,
});
});
c.singleton('consolidationAdvisor', (ct) => {
const knowledgeRepo = ct.get('knowledgeRepository');
return new ConsolidationAdvisor(knowledgeRepo);
});
}
/**
* 初始化知识服务(在容器初始化后调用)
* 绑定 EventBus → SearchEngine.refreshIndex() + recipe_source_refs 填充
*/
export function initializeKnowledgeServices(c) {
if (!c.services.eventBus || !c.services.searchEngine) {
return;
}
try {
const { EventBus } = await_import_EventBus();
const eventBus = c.get('eventBus');
const searchEngine = c.get('searchEngine');
// Bug 修复: BM25 索引与 Vector 索引一致性 — 将 knowledge:changed 事件绑定到 refreshIndex
eventBus.on('knowledge:changed', () => {
try {
searchEngine.refreshIndex();
}
catch {
/* refreshIndex failure is non-fatal */
}
});
// recipe_source_refs 填充:MCP 内提交新知识后同步更新桥接表
eventBus.on('knowledge:changed', (data) => {
try {
const d = data;
if (d.action === 'create' && d.entryId) {
void _populateSourceRefsForEntry(c, d.entryId);
}
}
catch {
/* sourceRef population failure is non-fatal */
}
});
}
catch {
/* EventBus/SearchEngine not available — skip binding */
}
}
/** EventBus 延迟引用(避免循环依赖) */
function await_import_EventBus() {
// EventBus 类型已经通过 container 解析,此处只用于 TS 类型
return {
EventBus: Object,
};
}
/**
* 从 knowledge_entries.reasoning 中提取 sources 并填充 recipe_source_refs 桥接表
* 使用 KnowledgeRepository + RecipeSourceRefRepository 类型安全 API
*/
async function _populateSourceRefsForEntry(c, entryId) {
try {
const knowledgeRepo = c.get('knowledgeRepository');
const sourceRefRepo = c.get('recipeSourceRefRepository');
const row = await knowledgeRepo.findSourceFileAndReasoning(entryId);
if (!row?.reasoning) {
return;
}
let sources = [];
try {
const reasoning = JSON.parse(row.reasoning);
sources = Array.isArray(reasoning.sources)
? reasoning.sources.filter((s) => typeof s === 'string' && s.length > 0)
: [];
}
catch {
return;
}
if (sources.length === 0) {
return;
}
const now = Date.now();
for (const sourcePath of sources) {
try {
sourceRefRepo.upsert({
recipeId: entryId,
sourcePath,
status: 'active',
verifiedAt: now,
});
}
catch {
/* table may not exist yet */
}
}
}
catch {
/* repos may not be registered yet */
}
}