여기, 준비되었으니 dev.to의 body에 복사하세요:
모든 운명술 앱은 당신을 12개의 태양 속성 중 하나로 줄여줍니다. 진정한 운명술사들은 그런 식으로 일하지 않습니다 — 그들은 서양 운명술, 비디카 (조티시), 중국 바지, 수학술, 휴먼 디자인 등을 교차参照합니다. 그래서 저는 13개의 시스템에서 일일 예측을 합성하는 텔레그램 봇을 만들었습니다: 당신의 전체 출생일을 기반으로 합니다.
약 1개월 동안 서비스되고 있습니다. 아직 작지만 — 83명의 사용자 — 하지만 실제로 나에게 무언가를 가르쳐준 부분을 공유하고 싶습니다.
아키텍처: 13개 시스템을 결합하는 이유는 데이터 문제가 아니라 천문학 문제가 아닙니다
각 시스템은 별개의 계산기입니다. 서양 천문학은 장도(나사의 일람표와 Skyfield을 사용)가 필요합니다. 비대천문학은 티히(달의 날, 1-30)와 나크샤트라(27개의 달의 궤도)가 필요합니다. 바지는 태양기의 경계를 사용하여 일주요소를 할당합니다. 수학은 마스터 숫자 예외(11, 22, 33은 산술 이전에 줄이지 않음)와 함께 숫자 감소가 필요합니다.
각각은 자신만의 방식으로 까다롭습니다. 이들을 결합하면 흥미로운 오류 모드가 나타납니다: 일정을 기다리는 잠재적 버그들.
제가 좋아하는 것: 달일(월일) 변환 표에는 5개의 항목이 있었지만, _tithi_group(30)은 인덱스 5(Amavasya / 새 달)를 반환했습니다. 버그는 몇 주 동안 비활성 상태로 있었습니다. 그런데 새 달이 왔습니다:
day_label = _TITHI_DAY_LABEL[lang][group_idx]
# IndexError: list index out of range
세 가지 언어 모두 콘텐츠 생성이 중단되었습니다. 봇의 시작도 ensure_content(today)를 호출했으므로 크래시 루프에 진입했습니다. 그날 내가 배운 점은 두 가지입니다:
- 잠재적인 버그는 달력을 기다립니다.특정 천문학적 사건에만 실행되는 코드 경로는 그 경계 조건에서 명시적인 테스트가 필요합니다.
-
시작 핸들러는 프로세스를 중단해서는 안 됩니다.그들을 래핑해
try/except로봇이 살아있고 관리자가 여전히 진단 명령을 통해 내부를 살펴볼 수 있도록합니다.
LLM 비용 아키텍처: 한 Sentinel이 99%의 비용을 절약했습니다
봇은 Gemini를 사용하여 원시 템플릿 출력을 따뜻한 대화 언어로 재작성합니다. 일일, 월간, 연간 예측. 사용자별 재작성으로 비용은 사용자 수에 비례하여 선형적으로 증가합니다 - 나쁨.
하지만 일반 예측 (모든 사람이 받는 아침 브로드캐스팅)은 모든 사용자에게 동일합니다. 그래서 저는 보호대( Sentinel) 패턴을 사용합니다: user_id=0는 "공유 캐시 행"을 의미합니다. 하루에 처음으로 일간 LLM 재작성을 트리거하는 사용자는 캐시를 따뜻하게 합니다; 다른 모든 사람은 그것에서 읽습니다.
async def get_cached(session, user_id, date, lang, content_type):
row = await session.get(LLMOutputCache,
(user_id, content_type, 0, date, lang))
return row.text if row else None
이것은 5줄의 아이디어지만, 제 LLM 비용을 "불편하다"에서 "거의 눈에 띄지 않는다"로 줄였습니다. UTC 03:00에 cron을 미리 뜨게 하여 아무도 일어나기 전에 캐시를 채웁니다.
환각 방지 장치
Gemini는 당신의 시드에 없는 천문학적 사실을 만들어내기 위해 기뻐합니다. 시드에는 달이 언급되어 있지만, 재작성은 자신감 있게 베네스를 소개합니다. 천문학 봇으로서 그것은 재앙입니다 - 사용자들은 출력을 신뢰합니다.
제 보호 토큰은 두 텍스트를 모두 토큰화하고, 어떤 것이든 재작성을 거부합니다새로운 행성 이름입력에 없었던 내용이 출력에 나타납니다. 별자리 이름은 허용됩니다 (LLM은 종종 "스코피오 목성"을 자연스러운 비유로 추가하므로 그럴 수 있습니다), 하지만 실제 행성 추가는 거부하고 Groq로 되돌리고, 그 다음 평범한 템플릿으로 돌아갑니다.
new_planets = _extract_astro_tokens(rewritten) \
- _extract_astro_tokens(original)
new_planets &= _PLANET_TOKENS
if new_planets:
log.warning("hallucination guard fired: %s", new_planets)
return None # fall back
지니의 출력 중 약 2-3%가 이를 트리거합니다. 봇이 조용히 백업됩니다; 사용자는 절대로 쓰레기를 볼 수 없습니다.
자동 게시: 진실의 단일 출처
나는 매일 예보를 Telegram 채널, 인스타그램(4개의 PNG 슬라이드 카러셀) 및 Threads에 동일하게 게시합니다. 세 가지 다른 형식, 세 가지 다른 API, 하나의 소스 콘텐츠.
핵심 통찰: 캐시된 LLM 리라이트를 여러 표면 간에 공유합니다. IG 캡션은 llm_output_cache에서 user_id=0을 가져옵니다. 스레드의 메인 게시물은 동일한 캐시에서 가져오고 500자 미만인 가장 가까운 문장 경계에서 잘라냅니다. 추가 LLM 비용은 없으며 하나의 진실입니다.
main_text = await get_cached(0, today, lang, CONTENT_TYPE_DAILY)
if len(main_text) > 500:
head = main_text[:500]
for sep in (". ", "! ", "? "):
idx = head.rfind(sep)
if idx >= 200:
main_text = head[:idx+1].rstrip()
break
IG 슬라이드 렌더러는 별도의 Gemini 호출을 사용합니다.response_mime_type=application/json 압축된 문자 예산에 적합합니다 (슬라이드에는 시각적 제약이 있으며 PNG 렌더러는 준수해야 합니다). 하루에 한 번의 LLM 호출, Redis에서 24시간 캐싱됩니다.
메타 밴 (또는: 내가 잘못한 일)
이 부분을 되돌리고 싶습니다. 저는 다음과 같은 것을 가지고 있었습니다.
- 포스트당 참여 유도 Threads/IG 게시물마다: "응답을 남기고, 누군가에게 공유하세요" — 매일 동일한 문구입니다.
- 하루 5개 게시물 자기 답변 연쇄 (메인 게시물 + 숫자학 답변 + 바지 답변 + 조옥답변 + 링크 포함 CTA 답변).
- 기계적 완벽한 시간 : 매일마다 UTC 04:02 ±0.
이 중 하나하나가 텍스트북 스팸 신호입니다. 조합—자동 봇 게시, 동일한 참여꾀, 매일 자기 자신에 대한 답글 연쇄와 외부 링크—는 정확히 Meta의 도덕 체계가 벌점을 부과하기 위해 설계되었습니다.
영어 계정은 직접 비활성화되었습니다: "저희가 계정을 검토한 결과, 그것이 저희 커뮤니티 기준에 맞지 않는 것을 발견했습니다." 러시아 계정은 살아남았지만 은닉 제한(포스트는 API를 통해 게시되지만 계정은 검색/프로필에서 사라짐)을 받았습니다.
스팸 제거는 코드에서 간단했습니다:
- 포스트당 참여 유도 콘텐츠를 제거하고, "비오 비오에 링크"라는 부드러운 CTA만 유지했습니다
- 5자리 연쇄를 단일 예측 게시물로 자름
- cron에
jitter=14400초 (±4시간) 추가하여 게시물이 매일 다른 시간에 도착하도록 함
scheduler.add_job(
send_threads_post,
trigger="cron",
hour=10, minute=0,
jitter=14400, # ±4h — fires anywhere in 06:00-14:00 UTC daily
id="threads_post",
)
더 어려운 교훈: 메타 플랫폼의 자동 사회 게시물은 설계적으로 취약합니다. Meta는 순수 브로드캐스팅 봇 계정을 원하지 않습니다. 새로 만든 계정을 즉시 cron에 연결하면 다시 차단당하게 됩니다. 프로젝트에서 사회적 존재감이 중요하다면, 인간이 운영하는 경로만이 영속적인 것입니다.
1개월 후의 성실한 숫자
- 83 사용자
- DAU/MAU 비율: ~9% (건강한 기준은 20%+ — 보존은 제 실제 문제입니다)
- 프로필 완료율: 73.5% (온보딩 작업)
- 가장 많이 사용된 기능: 월간 예측 (높은 재참여율, 7명의 사용자가 한 주에 25번 열었다)
- 가장 적게 사용된 기능: 초대/리퍼럴 (30일에 1번 초대 — 코드 내 리퍼럴 메커니즘을 UI에 노출하지 않으면 그것이 아무것도 아니다)
- 유료 전환: 0 (아직 수익화를 강요하지 않았다)
과거의 나에게 하고 싶은 말
- 배포는 제품보다 어렵습니다. 저는 3주 안에 봇을 배포했습니다. 사람들이 이를 사용하게 만드는 것은 실제 작업이며, 전혀 다른 기술입니다.
- 지루한 인프라 결정은 복합됩니다. Sentinel 캐시, 환각 방지기, 관리자 진단 명령을 갖춘 도커화된 스택 — 이 중 어느 것도 멋지지 않습니다. 모두 시간을 아끼게 해줍니다.
- 당신을 사랑하지 않는 채널에 최적화하지 마세요. Meta의 자동 게시기 금지는 기능이지 버그가 아닙니다. 당신의 행동이 환영되는 채널을 위해 만드세요.
봇이 가동 중이며 무료입니다: t.me/CosmoCast_bot — 생일을 입력하면 예보를 받으세요.
댓글에서 LLM 비용 아키텍처, 환각 방지기, 자동 게시기 설정, 또는 Meta-ban 사후 분석에 대해 궁금한 점이 있으면 언제든지 답변해 기뻐요.










