@heripo/research-radar 5.2.5 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -15
- package/dist/index.d.ts +43 -8
- package/dist/index.js +110 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Previously reported service metrics were $0.2–1 per issue and 15% CTR. These a
|
|
|
24
24
|
- Type-safe TypeScript with strict interfaces
|
|
25
25
|
- Provider pattern for swapping components (Crawling/Analysis/Content/Email)
|
|
26
26
|
- 74 active crawling targets across heritage agencies, museums, academic societies, filtered at runtime by robots.txt
|
|
27
|
-
-
|
|
27
|
+
- OpenAI GPT-6 defaults for every LLM task, with per-task model overrides
|
|
28
28
|
- Built-in retries, chain options, preview emails
|
|
29
29
|
|
|
30
30
|
**Links**: [Live service](https://heripo.app/research-radar/subscribe) • [Newsletter example](https://heripo.app/research-radar-newsletter-example.html) • [Core engine](https://github.com/heripo-lab/llm-newsletter-kit-core)
|
|
@@ -72,7 +72,7 @@ npm install @heripo/research-radar '@llm-newsletter-kit/core@~3.0.6'
|
|
|
72
72
|
|
|
73
73
|
**Requirements**: Node.js 24.15.0 or newer within 24.x, or >= 26.0.0, and an ESM application. Node.js 25 is not supported. The package exports `dist/index.js` and TypeScript declarations (`dist/index.d.ts`), with a JavaScript sourcemap. The core engine is a peer dependency; the current supported range is `~3.0.6`. The floor moved to 3.0.6 because the 나라장터 target runs its triage inside a crawl fetch that core times out, and before 3.0.6 core classified a timeout as non-retryable, so that target failed on the first attempt every run. 3.0.5 remains required for the self-verification retry cap the generation prompt relies on.
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
An OpenAI API key is always required and supplies all default models. The optional compatibility path for Anthropic or Google newsletter generation additionally requires that provider's key. Keys are passed explicitly to the library; load environment variables in your application.
|
|
76
76
|
|
|
77
77
|
## Quick Start
|
|
78
78
|
|
|
@@ -99,7 +99,6 @@ export async function runNewsletter(repositories: {
|
|
|
99
99
|
const newsletterId = await generateNewsletter({
|
|
100
100
|
...repositories,
|
|
101
101
|
openAIApiKey: apiKey,
|
|
102
|
-
contentGeneration: { provider: 'openai', apiKey },
|
|
103
102
|
logger: console,
|
|
104
103
|
});
|
|
105
104
|
|
|
@@ -163,18 +162,33 @@ Uses the **Provider-Service pattern** from `@llm-newsletter-kit/core`. See [core
|
|
|
163
162
|
|
|
164
163
|
## Configuration and models
|
|
165
164
|
|
|
166
|
-
Defaults below describe the checked-in code, not provider recommendations.
|
|
165
|
+
Defaults below describe the checked-in code, not provider recommendations. `openAIApiKey` is required and supplies every default model.
|
|
167
166
|
|
|
168
|
-
| Stage
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
| Content generation | Google | `gemini-3.1-pro-preview` |
|
|
167
|
+
| Stage | Provider | Default model |
|
|
168
|
+
| --------------------- | -------- | ------------- |
|
|
169
|
+
| Heritage bid triage | OpenAI | `gpt-6-luna` |
|
|
170
|
+
| Tag classification | OpenAI | `gpt-6-luna` |
|
|
171
|
+
| Image analysis | OpenAI | `gpt-6-sol` |
|
|
172
|
+
| Importance scoring | OpenAI | `gpt-6-sol` |
|
|
173
|
+
| Newsletter generation | OpenAI | `gpt-6-sol` |
|
|
176
174
|
|
|
177
|
-
|
|
175
|
+
Override any task independently with `models`. Every value is an OpenAI model ID:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
await generateNewsletter({
|
|
179
|
+
...repositories,
|
|
180
|
+
openAIApiKey,
|
|
181
|
+
models: {
|
|
182
|
+
heritageBidTriage: 'gpt-6-luna',
|
|
183
|
+
classifyTags: 'gpt-6-luna',
|
|
184
|
+
analyzeImages: 'gpt-6-sol',
|
|
185
|
+
determineImportance: 'gpt-6-sol',
|
|
186
|
+
generateNewsletter: 'gpt-6-sol',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The optional `contentGeneration: { provider, apiKey, model? }` compatibility path still supports Anthropic and Google for newsletter generation. `models.generateNewsletter` takes precedence when both are supplied.
|
|
178
192
|
|
|
179
193
|
[src/config/index.ts](./src/config/index.ts) defines Korean output (`outputLanguage: '한국어'`), the cultural heritage domain (`expertField: ['문화유산']`), brand name, `subscribePageUrl`, LLM `maxRetries: 5`, chain `stopAfterAttempt: 3`, and generation `temperature: 0.3`. Publication settings are `minimumArticleCountForIssue: 5` and `priorityArticleScoreThreshold: 8`; the core engine evaluates them. In the locked core 3.0.6 implementation, the count check skips **5 or fewer** candidates unless at least one has importance score >= 8. An empty candidate list is always skipped.
|
|
180
194
|
|
|
@@ -379,7 +393,7 @@ export const newsletterConfig: NewsletterConfig = {
|
|
|
379
393
|
- Replace Korean heritage sites with your domain sources
|
|
380
394
|
- Implement parsers in `src/parsers/`
|
|
381
395
|
|
|
382
|
-
**4. Switch content generation LLM provider** (
|
|
396
|
+
**4. Switch content generation LLM provider** (legacy compatibility):
|
|
383
397
|
|
|
384
398
|
Content generation supports **3 built-in providers** — just change `contentGeneration.provider`:
|
|
385
399
|
|
|
@@ -396,7 +410,7 @@ const contentGeneration: ContentGenerationConfig = {
|
|
|
396
410
|
};
|
|
397
411
|
```
|
|
398
412
|
|
|
399
|
-
|
|
413
|
+
Compatibility defaults: openai=`gpt-6-sol`, anthropic=`claude-sonnet-4-6`, google=`gemini-3.1-pro-preview`
|
|
400
414
|
|
|
401
415
|
To change analysis providers, update both `src/providers/analysis.provider.ts` (provider type and models) and `src/newsletter-generator.ts` (provider construction), using a compatible AI SDK provider. Also adapt domain-specific minimum-score rules in the analysis provider, output language and expert fields in config, package metadata, and the GitHub Actions runner/Slack settings for your fork.
|
|
402
416
|
|
package/dist/index.d.ts
CHANGED
|
@@ -203,9 +203,11 @@ interface NewsletterRepository {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
* Uses
|
|
206
|
+
* Uses the required OpenAI key for every default model. Each task accepts an
|
|
207
|
+
* OpenAI model-ID override, while the compatibility content-generation option
|
|
208
|
+
* can still select Anthropic or Google.
|
|
207
209
|
*
|
|
208
|
-
*
|
|
210
|
+
* `models.generateNewsletter` takes precedence over `contentGeneration`.
|
|
209
211
|
*/
|
|
210
212
|
|
|
211
213
|
/**
|
|
@@ -225,7 +227,7 @@ interface PreviewNewsletterOptions {
|
|
|
225
227
|
* Each provider uses a sensible default model that can be overridden.
|
|
226
228
|
*
|
|
227
229
|
* Default models:
|
|
228
|
-
* - openai: `gpt-
|
|
230
|
+
* - openai: `gpt-6-sol`
|
|
229
231
|
* - anthropic: `claude-sonnet-4-6`
|
|
230
232
|
* - google: `gemini-3.1-pro-preview`
|
|
231
233
|
*/
|
|
@@ -242,14 +244,35 @@ type ContentGenerationConfig = {
|
|
|
242
244
|
apiKey: string;
|
|
243
245
|
model?: string;
|
|
244
246
|
};
|
|
247
|
+
/**
|
|
248
|
+
* OpenAI model IDs used by each LLM task.
|
|
249
|
+
*
|
|
250
|
+
* Omitted values use the defaults from `llmConfig.models`. Content generation
|
|
251
|
+
* can still use a non-OpenAI provider through the legacy `contentGeneration`
|
|
252
|
+
* option; `models.generateNewsletter` takes precedence when both are supplied.
|
|
253
|
+
*/
|
|
254
|
+
interface NewsletterModelConfig {
|
|
255
|
+
heritageBidTriage?: string;
|
|
256
|
+
classifyTags?: string;
|
|
257
|
+
analyzeImages?: string;
|
|
258
|
+
determineImportance?: string;
|
|
259
|
+
generateNewsletter?: string;
|
|
260
|
+
}
|
|
245
261
|
/**
|
|
246
262
|
* Newsletter generator dependencies interface
|
|
247
263
|
*/
|
|
248
264
|
interface NewsletterGeneratorDependencies {
|
|
249
|
-
/** OpenAI API key
|
|
265
|
+
/** OpenAI API key used by every default model. */
|
|
250
266
|
openAIApiKey: string;
|
|
251
|
-
/**
|
|
252
|
-
|
|
267
|
+
/** OpenAI model overrides for every LLM task (optional). */
|
|
268
|
+
models?: NewsletterModelConfig;
|
|
269
|
+
/**
|
|
270
|
+
* Non-OpenAI content generation configuration (optional).
|
|
271
|
+
*
|
|
272
|
+
* Prefer `models.generateNewsletter`, which uses the required `openAIApiKey`.
|
|
273
|
+
* This remains available for Anthropic/Google compatibility.
|
|
274
|
+
*/
|
|
275
|
+
contentGeneration?: ContentGenerationConfig;
|
|
253
276
|
/** Task management repository */
|
|
254
277
|
taskRepository: TaskRepository;
|
|
255
278
|
/** Article management repository */
|
|
@@ -398,13 +421,17 @@ declare class TaskService implements TaskService$1<number> {
|
|
|
398
421
|
end(): Promise<void>;
|
|
399
422
|
}
|
|
400
423
|
|
|
424
|
+
interface AnalysisModels {
|
|
425
|
+
classifyTags: ReturnType<OpenAIProvider>;
|
|
426
|
+
analyzeImages: ReturnType<OpenAIProvider>;
|
|
427
|
+
determineImportance: ReturnType<OpenAIProvider>;
|
|
428
|
+
}
|
|
401
429
|
/**
|
|
402
430
|
* Analysis provider implementation
|
|
403
431
|
* - LLM-based article analysis
|
|
404
432
|
* - Tag classification, image analysis, importance scoring
|
|
405
433
|
*/
|
|
406
434
|
declare class AnalysisProvider implements AnalysisProvider$1 {
|
|
407
|
-
private readonly openai;
|
|
408
435
|
private readonly articleRepository;
|
|
409
436
|
private readonly tagRepository;
|
|
410
437
|
classifyTagOptions: {
|
|
@@ -421,6 +448,7 @@ declare class AnalysisProvider implements AnalysisProvider$1 {
|
|
|
421
448
|
}>;
|
|
422
449
|
};
|
|
423
450
|
constructor(openai: OpenAIProvider, articleRepository: ArticleRepository, tagRepository: TagRepository);
|
|
451
|
+
constructor(models: AnalysisModels, articleRepository: ArticleRepository, tagRepository: TagRepository);
|
|
424
452
|
/**
|
|
425
453
|
* Fetch articles that haven't been scored yet
|
|
426
454
|
* @returns Unscored articles awaiting analysis
|
|
@@ -612,6 +640,13 @@ declare const robotsExemptOrigins: readonly string[];
|
|
|
612
640
|
* LLM configuration
|
|
613
641
|
*/
|
|
614
642
|
declare const llmConfig: {
|
|
643
|
+
models: {
|
|
644
|
+
heritageBidTriage: string;
|
|
645
|
+
classifyTags: string;
|
|
646
|
+
analyzeImages: string;
|
|
647
|
+
determineImportance: string;
|
|
648
|
+
generateNewsletter: string;
|
|
649
|
+
};
|
|
615
650
|
maxRetries: number;
|
|
616
651
|
chainStopAfterAttempt: number;
|
|
617
652
|
generation: {
|
|
@@ -650,4 +685,4 @@ declare const llmConfig: {
|
|
|
650
685
|
declare const researchRadarPromptProvider: PromptProvider;
|
|
651
686
|
|
|
652
687
|
export { AnalysisProvider, ContentGenerateProvider, CrawlingProvider, DateService, TaskService, contentOptions, createCrawlingTargetGroups, generateNewsletter, generateWelcomeHTML, getSourceList, llmConfig, maximumImportanceScoreByDomain, newsletterConfig, researchRadarPromptProvider, robotsExemptOrigins };
|
|
653
|
-
export type { ArticleRepository, ContentGenerationConfig, ContentOptions, ExcavationReport, ExcavationReportSource, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, SourceGroup, SourceItem, TagRepository, TaskRepository, WelcomeTemplateOptions };
|
|
688
|
+
export type { ArticleRepository, ContentGenerationConfig, ContentOptions, ExcavationReport, ExcavationReportSource, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterModelConfig, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, SourceGroup, SourceItem, TagRepository, TaskRepository, WelcomeTemplateOptions };
|
package/dist/index.js
CHANGED
|
@@ -3200,6 +3200,13 @@ const robotsExemptOrigins = [
|
|
|
3200
3200
|
* LLM configuration
|
|
3201
3201
|
*/
|
|
3202
3202
|
const llmConfig = {
|
|
3203
|
+
models: {
|
|
3204
|
+
heritageBidTriage: 'gpt-6-luna',
|
|
3205
|
+
classifyTags: 'gpt-6-luna',
|
|
3206
|
+
analyzeImages: 'gpt-6-sol',
|
|
3207
|
+
determineImportance: 'gpt-6-sol',
|
|
3208
|
+
generateNewsletter: 'gpt-6-sol',
|
|
3209
|
+
},
|
|
3203
3210
|
maxRetries: 5,
|
|
3204
3211
|
chainStopAfterAttempt: 3,
|
|
3205
3212
|
generation: {
|
|
@@ -3310,46 +3317,86 @@ const DOMAIN_PRIORITY = `## 유산 영역 우선순위
|
|
|
3310
3317
|
판단이 애매할 때 고고학 기사는 고려 중인 점수대의 위쪽을, 자연유산·무형유산 기사는 아래쪽을 택한다.
|
|
3311
3318
|
영역은 tag1에 표기되어 있다. 단, 영역만으로 점수를 정하지는 않는다 — 사안 자체가 미미한 고고학 기사보다
|
|
3312
3319
|
중대한 문화유산 기사가 높은 점수를 받는 것이 옳다.`;
|
|
3313
|
-
const SUBJECT_OVER_INSTITUTION = `##
|
|
3320
|
+
const SUBJECT_OVER_INSTITUTION = `## 입찰·조달 공고를 발주 내용으로 판단
|
|
3321
|
+
|
|
3322
|
+
**이 절은 입찰·조달 공고에만 적용한다.** 사업을 발주한다는 것을 알리는 글, 즉 업무구분·
|
|
3323
|
+
추정가격·계약방법·입찰마감이 붙어 있는 공고가 대상이다. 보도자료, 공지사항, 학술행사
|
|
3324
|
+
안내, 채용 공고에는 적용하지 않는다. 그런 기사는 아래 규칙을 거치지 않고 점수 기준으로
|
|
3325
|
+
바로 평가한다.
|
|
3326
|
+
|
|
3327
|
+
특히 **이미 끝난 일을 전하는 보도는 여기에 해당하지 않는다.** "○○ 복원 완료", "○○
|
|
3328
|
+
보수 마치고 공개"처럼 수리·복원의 결과를 알리는 기사는 유산 소식이므로 사안의 무게대로
|
|
3329
|
+
평가한다. 아래에서 1점을 주는 것은 그 일을 **하겠다고 발주하는 공고**이지, 그 일이
|
|
3330
|
+
끝났다는 소식이 아니다.
|
|
3314
3331
|
|
|
3315
3332
|
유산 기관이 낸 공고라도 **내용이 유산 업무가 아니면 낮게 준다.** 기관명은 단서일 뿐 근거가 아니다.
|
|
3316
3333
|
|
|
3334
|
+
**두 가지를 차례로 본다.**
|
|
3335
|
+
|
|
3336
|
+
1. **대상이 유산인가.** 제목이나 본문에 유산이라는 근거가 없으면 유산 업무가 아니다.
|
|
3337
|
+
조달 정보만 있고 무엇을 다루는지 알 수 없으면 유산으로 짐작하지 않는다.
|
|
3338
|
+
2. **하는 일이 조사·연구·기록인가, 고치고 관리하는 시공인가.** 앞의 것은 독자에게
|
|
3339
|
+
새로 알려줄 내용이 있고, 뒤의 것은 시공 발주다. 대상이 아무리 중요한 유산이어도
|
|
3340
|
+
시공이면 1점이다.
|
|
3341
|
+
|
|
3317
3342
|
**유산 업무 — 정상 채점**
|
|
3318
3343
|
|
|
3319
|
-
-
|
|
3320
|
-
- 학술연구, 유물
|
|
3321
|
-
-
|
|
3322
|
-
(예: "달천철장 홍보관 무장애 관광 콘텐츠 개발 용역")
|
|
3344
|
+
- 발굴조사, 시굴조사, 표본·입회·지표조사, 매장유산 조사
|
|
3345
|
+
- 학술연구, 유물 조사·분석, 기록화, 정밀실측, 보고서·자료집 발간
|
|
3346
|
+
- **유물(동산유산) 보존처리** — 불상, 괘불도, 전적, 박물관 소장자료
|
|
3323
3347
|
- **유산 정보를 다루는 데이터베이스·아카이브·디지털화의 구축과 유지보수**
|
|
3324
|
-
|
|
3325
|
-
"AI 학습데이터 구축 기반 아카이브 자료 디지털화")
|
|
3348
|
+
- 전시·해설의 기획과 **콘텐츠 개발** — 무엇을 보여주고 어떻게 설명할지 정하는 일
|
|
3326
3349
|
- **유산 교육 프로그램의 기획·운영** (예: "평창올림픽 유산교육 프로그램 기획·운영 대행용역")
|
|
3350
|
+
— 운영 대행이라는 형식 때문에 행사 운영으로 보지 않는다. 가르치는 내용이 있으면 교육이다.
|
|
3351
|
+
- **발굴과 직결된 유적 복원·정비** — 발굴조사 결과에 따른 유적 복원, 발굴지 복토,
|
|
3352
|
+
고분군·폐사지 정비처럼 조사에서 이어지는 시공
|
|
3327
3353
|
|
|
3328
3354
|
**유산 업무가 아닌 것 → 발주처가 국가유산청·국립박물관이어도 1점**
|
|
3329
3355
|
|
|
3356
|
+
- **건조물·구조물의 수리와 보수공사** — 지붕보수, 단청, 초가이엉잇기, 성벽·석축 보수,
|
|
3357
|
+
담장·울타리 정비. 전통기법을 쓰더라도 시공이다.
|
|
3358
|
+
- **주변·부대시설 정비, 탐방로·진입로 정비, 조성 사업**
|
|
3359
|
+
- **수목·조경 관리와 병해충 방제** — 재선충 방제, 위험수목 정비, 경관림 복원, 식재
|
|
3360
|
+
- **종합정비계획·관리계획 수립**, 실시설계, **공사 감리**
|
|
3330
3361
|
- 소방·전기·냉난방·창호·정보통신 같은 건물 설비와 그 유지보수, 청사 환경개선
|
|
3331
3362
|
- **구조안전진단**, 건물 철거와 폐기물 처리
|
|
3332
3363
|
- 기관 홈페이지, 사무용 전산 시스템
|
|
3333
|
-
- 차량 임차, **운송·운반**
|
|
3364
|
+
- 차량 임차, **운송·운반**
|
|
3334
3365
|
- 홍보물·영상 제작
|
|
3335
3366
|
- **전시를 물리적으로 만들고 옮기고 설치하는 일** — 전시설계, 전시물·실감영상 제작·설치,
|
|
3336
|
-
전시품 포장·운송
|
|
3367
|
+
전시품 포장·운송
|
|
3337
3368
|
- 축제·행사의 운영과 그 부대 용역(무대, 홍보, 교통·안전관리)
|
|
3369
|
+
- **대상이 유산이 아닌 조사·연구** — 자연환경·생태·수질·보호구역 실태조사, 생태지도 제작.
|
|
3370
|
+
조사라는 형식이 아니라 무엇을 조사하는지가 기준이다.
|
|
3338
3371
|
|
|
3339
3372
|
발주처가 유산 기관이라는 이유로 2-3점으로 올리지 않는다. 유산 업무가 아니면 1점이다.
|
|
3340
3373
|
|
|
3341
|
-
**갈리는
|
|
3342
|
-
|
|
3343
|
-
1.
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3374
|
+
**갈리는 지점**
|
|
3375
|
+
|
|
3376
|
+
1. **시공이냐 조사냐**: "창원 웅천읍성 서쪽성벽 보수공사", "부평향교 동무 동재 지붕보수공사",
|
|
3377
|
+
"옥천 송림사 요사채 단청공사", "서산 해미읍성 진남문 주변 석축정비공사"는 모두 1점이다.
|
|
3378
|
+
대상은 지정유산이지만 하는 일이 시공이다. 반대로 "구)경주역 폐철도부지 매장유산
|
|
3379
|
+
시굴조사"는 같은 땅을 파도 조사이므로 정상 채점한다.
|
|
3380
|
+
2. **복원의 예외**: 발굴조사에서 이어지는 복원·복토·유적 정비는 시공이어도 포함한다.
|
|
3381
|
+
어디서 무엇이 나왔고 그래서 무엇을 되살리는지가 고고학 소식이기 때문이다.
|
|
3382
|
+
건조물을 고쳐 쓰는 수리와는 다르다.
|
|
3383
|
+
3. **수목·방제**: "양산 통도사 일원 재선충병 예방 지상방제", "여주 영릉과 영릉 소나무재선충병
|
|
3384
|
+
예방나무주사", "남양주 홍릉과 유릉 위험수목 정비", "서울 헌릉과 인릉 역사경관림 복원 정비"는
|
|
3385
|
+
모두 1점이다. 왕릉과 사찰의 나무를 돌보는 일이지 유산을 조사하는 일이 아니다.
|
|
3386
|
+
4. **계획과 감리**: "안동 백운정 산불피해 복원 종합정비계획 수립", "경주 인왕동사지 석탑복원
|
|
3387
|
+
공사 책임감리", "정선 고성리 산성 성곽 보수공사 실시설계"는 모두 1점이다. 시공을 준비하고
|
|
3388
|
+
관리하는 일은 시공에 딸린다.
|
|
3389
|
+
5. **대상 불명**: "2026년 사천시 보호구역 실태조사"는 무엇의 보호구역인지 본문에 없다.
|
|
3390
|
+
"독도 수중 생태지도(닭바위) 제작"은 해양 생태 조사다. 둘 다 1점이다. 조사라는 말에
|
|
3391
|
+
끌려가지 말고, 유산을 조사한다는 근거가 실제로 있는지 본다.
|
|
3392
|
+
6. **전산**: 유산 자료를 담고 보여주고 보존하는 시스템이면 유산 업무이고, 기관을 운영하기
|
|
3393
|
+
위한 전산이면 아니다.
|
|
3394
|
+
7. **전시**: 무엇을 보여줄지 정하는 일은 유산 업무이고, 만들고 옮기고 설치하는 일은 아니다.
|
|
3395
|
+
8. **프로그램**: 유산을 가르치고 해설하는 프로그램은 유산 업무이고, 축제·기념행사를
|
|
3348
3396
|
진행하는 일은 아니다.
|
|
3349
3397
|
|
|
3350
|
-
|
|
3351
|
-
"
|
|
3352
|
-
발주처가 사찰이어도 명백한 유산 조사이고, "안양암 아미타괘불도 보존처리",
|
|
3398
|
+
반대쪽 예: "팔만대장경 P-XRF 분석"은 발주처가 사찰이어도 명백한 유산 조사이고,
|
|
3399
|
+
"안양암 아미타괘불도 보존처리", "국립농업박물관 소장 박물관자료 보존처리",
|
|
3353
3400
|
"중요동산문화유산 기록화 3D 스캐닝", "소장유물 복제", "소장품도록 발간"은
|
|
3354
3401
|
유물을 직접 다루므로 정상 채점한다.`;
|
|
3355
3402
|
const EMPLOYMENT_FILTER = `## 채용 공고 판별
|
|
@@ -3401,7 +3448,9 @@ function temporalRule$1(minimumScore) {
|
|
|
3401
3448
|
독자는 입찰에 참여하려고 이 소식을 보는 것이 아니라 **어디서 어떤 조사가 시작되는지** 알려고 본다.
|
|
3402
3449
|
"○○ 유적 정밀발굴조사 용역 발주"는 그 자체로 고고학계 소식이다.
|
|
3403
3450
|
이런 공고는 수의계약으로 나와 마감이 공고 다음 날인 경우가 많아, 마감으로 거르면 정작 알려야 할 조사가 사라진다.
|
|
3404
|
-
|
|
3451
|
+
유물 보존처리와 발굴 연계 복원 발주도 같게 본다. 다만 입찰 참여 조건이나 마감을 강조해
|
|
3452
|
+
쓰지는 않는다. **수리·보수정비 발주는 여기에 넣지 않는다.** 위 발주 판정에서 이미 1점이므로
|
|
3453
|
+
이 예외로 되살리면 앞뒤가 어긋난다.
|
|
3405
3454
|
- 마감·일정 언급이 전혀 없으면 이 규칙은 적용하지 않는다.
|
|
3406
3455
|
`;
|
|
3407
3456
|
}
|
|
@@ -3789,26 +3838,44 @@ const researchRadarPromptProvider = {
|
|
|
3789
3838
|
* - Tag classification, image analysis, importance scoring
|
|
3790
3839
|
*/
|
|
3791
3840
|
class AnalysisProvider {
|
|
3792
|
-
openai;
|
|
3793
3841
|
articleRepository;
|
|
3794
3842
|
tagRepository;
|
|
3795
3843
|
classifyTagOptions;
|
|
3796
3844
|
analyzeImagesOptions;
|
|
3797
3845
|
determineScoreOptions;
|
|
3798
|
-
constructor(
|
|
3799
|
-
this.openai = openai;
|
|
3846
|
+
constructor(modelsOrOpenAI, articleRepository, tagRepository) {
|
|
3800
3847
|
this.articleRepository = articleRepository;
|
|
3801
3848
|
this.tagRepository = tagRepository;
|
|
3849
|
+
const models = typeof modelsOrOpenAI === 'function'
|
|
3850
|
+
? {
|
|
3851
|
+
classifyTags: modelsOrOpenAI(llmConfig.models.classifyTags),
|
|
3852
|
+
analyzeImages: modelsOrOpenAI(llmConfig.models.analyzeImages),
|
|
3853
|
+
determineImportance: modelsOrOpenAI(llmConfig.models.determineImportance),
|
|
3854
|
+
}
|
|
3855
|
+
: modelsOrOpenAI;
|
|
3802
3856
|
this.classifyTagOptions = {
|
|
3803
|
-
model:
|
|
3857
|
+
model: models.classifyTags,
|
|
3804
3858
|
};
|
|
3805
3859
|
this.analyzeImagesOptions = {
|
|
3806
|
-
model:
|
|
3860
|
+
model: models.analyzeImages,
|
|
3807
3861
|
};
|
|
3808
3862
|
this.determineScoreOptions = {
|
|
3809
|
-
model:
|
|
3863
|
+
model: models.determineImportance,
|
|
3810
3864
|
minimumImportanceScoreRules: [
|
|
3811
|
-
//
|
|
3865
|
+
// Archaeology-only boards: minimum score 6. Everything they carry
|
|
3866
|
+
// concerns the field, so the floor keeps routine-looking posts from
|
|
3867
|
+
// being scored as if they came from a general board. That includes the
|
|
3868
|
+
// association's 사업공고 board (`bussopen`), which despite the name is
|
|
3869
|
+
// not a tender board: across everything collected from it, not one post
|
|
3870
|
+
// is an 입찰 공고. It announces 지원사업, 조사기관 모집 and 선정결과,
|
|
3871
|
+
// 발굴현장 안전컨설팅 and 교육 — programme news for the field.
|
|
3872
|
+
//
|
|
3873
|
+
// `ipcopen` is the actual tender board and deliberately carries no
|
|
3874
|
+
// floor. Tenders are judged by what the work is: a 지표조사 발주 is
|
|
3875
|
+
// archaeology news, while the 전기·통신·소방 공사 and 감리용역 posted
|
|
3876
|
+
// alongside it are construction. A floor of 6 cannot express the 1 that
|
|
3877
|
+
// policy calls for, so it pinned exactly the notices the importance
|
|
3878
|
+
// prompt is meant to drop.
|
|
3812
3879
|
{
|
|
3813
3880
|
targetUrl: 'https://www.kras.or.kr/?r=kras&m=bbs&bid=notice',
|
|
3814
3881
|
minScore: 6,
|
|
@@ -3853,10 +3920,6 @@ class AnalysisProvider {
|
|
|
3853
3920
|
targetUrl: 'https://www.kaah.kr/bussopen',
|
|
3854
3921
|
minScore: 6,
|
|
3855
3922
|
},
|
|
3856
|
-
{
|
|
3857
|
-
targetUrl: 'https://www.kaah.kr/ipcopen',
|
|
3858
|
-
minScore: 6,
|
|
3859
|
-
},
|
|
3860
3923
|
{
|
|
3861
3924
|
targetUrl: 'https://www.seamuse.go.kr/resources/academiccultural/list/1',
|
|
3862
3925
|
minScore: 6,
|
|
@@ -5054,7 +5117,7 @@ function createContentGenerationModel(config) {
|
|
|
5054
5117
|
switch (config.provider) {
|
|
5055
5118
|
case 'openai': {
|
|
5056
5119
|
const provider = createOpenAI({ apiKey: config.apiKey });
|
|
5057
|
-
return provider(config.model ?? 'gpt-
|
|
5120
|
+
return provider(config.model ?? 'gpt-6-sol');
|
|
5058
5121
|
}
|
|
5059
5122
|
case 'anthropic': {
|
|
5060
5123
|
const provider = createAnthropic({ apiKey: config.apiKey });
|
|
@@ -5070,6 +5133,10 @@ function createNewsletterGenerator(dependencies) {
|
|
|
5070
5133
|
const openai = createOpenAI({
|
|
5071
5134
|
apiKey: dependencies.openAIApiKey,
|
|
5072
5135
|
});
|
|
5136
|
+
const modelIds = {
|
|
5137
|
+
...llmConfig.models,
|
|
5138
|
+
...dependencies.models,
|
|
5139
|
+
};
|
|
5073
5140
|
const dateService = new DateService(dependencies.publishDate);
|
|
5074
5141
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
5075
5142
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch, dependencies.excavationReportSource, dependencies.logger, dependencies.publicDataApiKey,
|
|
@@ -5077,7 +5144,7 @@ function createNewsletterGenerator(dependencies) {
|
|
|
5077
5144
|
// reach per-article scoring is decided here, 100 titles per request. The
|
|
5078
5145
|
// deterministic filter stays behind it as the fallback.
|
|
5079
5146
|
createHeritageBidTriage({
|
|
5080
|
-
model: openai(
|
|
5147
|
+
model: openai(modelIds.heritageBidTriage),
|
|
5081
5148
|
onFallback: (reason, batchSize) => {
|
|
5082
5149
|
dependencies.logger?.info({
|
|
5083
5150
|
event: 'crawl.g2b.triage.fallback',
|
|
@@ -5085,7 +5152,11 @@ function createNewsletterGenerator(dependencies) {
|
|
|
5085
5152
|
});
|
|
5086
5153
|
},
|
|
5087
5154
|
}));
|
|
5088
|
-
const analysisProvider = new AnalysisProvider(
|
|
5155
|
+
const analysisProvider = new AnalysisProvider({
|
|
5156
|
+
classifyTags: openai(modelIds.classifyTags),
|
|
5157
|
+
analyzeImages: openai(modelIds.analyzeImages),
|
|
5158
|
+
determineImportance: openai(modelIds.determineImportance),
|
|
5159
|
+
}, dependencies.articleRepository, dependencies.tagRepository);
|
|
5089
5160
|
// Inject display date from DateService into template options
|
|
5090
5161
|
const templateOptions = dependencies.templateOptions
|
|
5091
5162
|
? {
|
|
@@ -5104,7 +5175,11 @@ function createNewsletterGenerator(dependencies) {
|
|
|
5104
5175
|
};
|
|
5105
5176
|
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
5106
5177
|
}
|
|
5107
|
-
const contentModel =
|
|
5178
|
+
const contentModel = dependencies.models?.generateNewsletter
|
|
5179
|
+
? openai(modelIds.generateNewsletter)
|
|
5180
|
+
: dependencies.contentGeneration
|
|
5181
|
+
? createContentGenerationModel(dependencies.contentGeneration)
|
|
5182
|
+
: openai(modelIds.generateNewsletter);
|
|
5108
5183
|
const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
5109
5184
|
return new GenerateNewsletter({
|
|
5110
5185
|
contentOptions: resolvedContentOptions,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@heripo/research-radar",
|
|
3
3
|
"private": false,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.3.1",
|
|
6
6
|
"description": "AI-driven intelligence for Korean cultural heritage. This package serves as both a ready-to-use newsletter service and a practical implementation example for the LLM-Newsletter-Kit.",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|