@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
146 lines (137 loc) • 4.4 kB
text/typescript
import { RSSFetcher, RSSFeedConfig, RSSDocument } from "./rss/rssFetcher";
import { extractMainContent, ExtractorOptions } from "./web/contentExtractor";
import {
WebpageSummarizer,
SummarizerOptions,
SummarizationResult,
} from "./summarizer/summarizer";
import {
QueryExpander,
QueryExpanderOptions,
QueryType,
} from "./query/queryExpander";
export interface AgentcRAGConfig {
rssFeeds: RSSFeedConfig[];
extractor?: ExtractorOptions;
summarizer?: SummarizerOptions & { apiKey: string };
queryExpander?: QueryExpanderOptions;
logLevel?: "info" | "warn" | "error" | "debug";
onLog?: (level: string, msg: string, meta?: any) => void;
onMetric?: (event: string, data?: any) => void;
}
export class AgentcRAG {
private rssFetcher: RSSFetcher;
private summarizer: WebpageSummarizer;
private queryExpander: QueryExpander;
private config: AgentcRAGConfig;
private log: (level: string, msg: string, meta?: any) => void;
private metric: (event: string, data?: any) => void;
constructor(config: AgentcRAGConfig) {
this.config = config;
this.rssFetcher = new RSSFetcher(config.rssFeeds);
this.summarizer = new WebpageSummarizer(
config.summarizer?.apiKey || "",
config.summarizer
);
this.queryExpander = new QueryExpander(config.queryExpander);
this.log =
config.onLog ||
((level, msg, meta) => {
if (
!config.logLevel ||
config.logLevel === "info" ||
level !== "info"
) {
// eslint-disable-next-line no-console
console.log(`[${level}]`, msg, meta || "");
}
});
this.metric = config.onMetric || (() => {});
}
/**
* RSS → Content → Summary 전체 파이프라인 실행
*/
public async runPipeline() {
this.log("info", "Starting RAG pipeline");
for (const feedCfg of this.config.rssFeeds) {
try {
const parser = new RSSFetcher([feedCfg]);
// 실제 서비스에서는 스케줄러가 아닌 단발성 fetch로 처리
const feed = await parser["parser"].parseURL(feedCfg.url);
for (const item of feed.items) {
const doc: RSSDocument = parser["toDocument"](item, feedCfg.url);
this.log("debug", "Fetched RSS item", doc);
const mainContent = extractMainContent(
doc.content,
this.config.extractor
);
this.log("debug", "Extracted main content", { mainContent });
const summary: SummarizationResult = await this.summarizer.summarize(
mainContent,
{ ...doc }
);
this.log("info", "Summarized content", { summary });
this.metric("summary_generated", { feed: feedCfg.url });
// TODO: 벡터스토어 저장 등 후처리
}
} catch (err) {
this.log("error", "Pipeline error", err);
this.metric("pipeline_error", { feed: feedCfg.url, error: err });
}
}
this.log("info", "RAG pipeline completed");
}
/**
* 질의 확장 및 재구성
*/
public expandQuery(query: string): {
type: QueryType;
expanded: string;
reformulated: string;
} {
const type = this.queryExpander.classify(query);
const expanded = this.queryExpander.expand(query, type);
const reformulated = this.queryExpander.reformulate(expanded);
this.log("debug", "Query expanded", {
query,
type,
expanded,
reformulated,
});
this.metric("query_expanded", { query, type });
return { type, expanded, reformulated };
}
/**
* 피드백 기록
*/
public feedback(
original: string,
reformulated: string,
success: boolean,
notes?: string
) {
this.queryExpander.feedback(original, reformulated, success, notes);
this.metric("feedback", { original, reformulated, success });
}
/**
* 피드백 전체 조회
*/
public getFeedbacks() {
return this.queryExpander.getFeedbacks();
}
}
/**
* 사용 예시:
*
* const rag = new AgentcRAG({
* rssFeeds: [{ url: 'https://example.com/rss', interval: '0 * * * *' }],
* summarizer: { apiKey: 'sk-xxx' },
* logLevel: 'info',
* onLog: (level, msg, meta) => { ... },
* onMetric: (event, data) => { ... },
* });
*
* await rag.runPipeline();
* const q = rag.expandQuery('AI?');
* rag.feedback('AI?', q.reformulated, true, '정확한 답변');
*/