Vibecoding isn't as fun as writing code by hand
hacker-news-frontpage
135 points · 153 comments · by Curiositry
Score: 52.26Confidence: 46%
View offerLoading the catalog…
THE AI OPPORTUNITY INDEX
Find your next AI tool. Explore free access, trials, and credits — all in one place.
hacker-news-frontpage
135 points · 153 comments · by Curiositry
Score: 52.26Confidence: 46%
View offerhacker-news-frontpage
219 points · 26 comments · by j-bu
Score: 52.24Confidence: 46%
View offerhacker-news-frontpage
486 points · 256 comments · by Philpax
Score: 52.23Confidence: 46%
View offerOpenAI
Now is a good time to start thinking about the governance of superintelligence—future AI systems dramatically more capable than even AGI.
Score: 37.4Confidence: 54%
View offerReadhub
国家发展改革委民营经济发展局相关负责人表示,要从三方面持续发力,推动民营企业将新空间转化为真收益:一是敞开大门,完善民营企业参与重大项目建设长效机制,推进基础设施竞争性领域向经营主体公平开放,让各类民营企业都能找到自身参与位置。二是分类施策,聚焦民营企业核心关切,推出一批适合民间资本参与的项目,完善投融资机制,强化要素保障,破除相关堵点卡点,让民营企业留得住。三是解决问题,持续开展面向民间资本的项目推介工作,建立问题解决机制,加强项目清单化管理,协调解决民营企业参与项目建设中的具体困难,让民营企业做得好。
Score: 57.25Confidence: 54%
View offervelog
헥사고날 아키텍처(Hexagonal Architecture)란? - 백엔드 아키텍처 구조 중 하나 - 비즈니스 로직을 외부 기술과 분리해서, DB나 외부 API 같은 인프라가 변경되어도 핵심 로직의 영향을 최소화하고 테스트하기 쉽게 만드는 구조 비즈니스 로직과 분리 - Port : 비즈니스 로직(service)이 외부와 통신하기 위한 인터페이스 - Adaptor : 그 인터페이스를 실제 기술로 구현하는 것 #3-layer 아키텍처 #헥사고날 아키텍처 Controller (Adapter) Controller ↓ Input Port ↓ ↓ Service ┌─────────────┐ │ Service │ ↓ └─────────────┘ Repository ↓ Output Port ↓ ↓ DB DB (Adapter) // 3-layer 아키텍처 // OrderService @Service public class OrderService { private final OrderRepository repository; // 레포지토리에 의존적 public void order() { // 주문 로직 repository.save(...); } } // 헥사고날 아키텍처 // OrderService @Service public class OrderService { private final OrderRepositoryPort orderRepositoryPort; // 포트에 의존적 public void order() { // 주문 로직 orderRepositoryPort.save(...); } } // OrderRepositoryPort public interface OrderRepositoryPort { void save(Order order); } // OrderRepositoryAdapter @Repository public class OrderRepositoryAdapter implements OrderRepositoryPort { private final OrderJpaRepository orderJpaRepository; @Override public void save(Order order) { OrderEntity entity = new OrderEntity( order.getUserId(), order.getPrice() ); orderJpaRepository.save(entity); } } port 와 adaptor 를 사용해 비즈니스 로직과 DB, 외부 API, 프레임워크 같은 기술적인 부분으로부터 분리 헥사고날 아키텍처의 장점 기술 변경에 유리 : adaptor 만 변경하는 방식으로 쉽게 변경 DB, 외부 API 등 테스트하기 쉬움 : 실제 DB 없이 fakeRepository 를 넣어 비즈니스 로직을 테스트 가능 헥사고날 아키텍처가 유리한 프로젝트 규모가 큰 프로젝트 오래 운영할 프로젝트 : 기능이 계속 추가될 수록 변경사항이 많아질 가능성이 크다. 외부 시스템이 많은 프로젝트 : 외부 시스템이 많을수록 Adapter/Port로 분리하는 효과가 커짐. 프로젝트의 복잡도에 맞춰 아키텍처를 잘 골라 사용할 수 있어야 한다.
Score: 54.4Confidence: 49%
View offervelog
지난 2편에서는 문제를 보면서 조금씩 알고리즘의 패턴을 떠올리기 시작했다고 썼다. 이번에는 그 패턴을 실제 코드로 옮기는 과정에서 다시 많이 막혔다. 9월 21일부터 30일까지 이분 탐색과 파라메트릭 서치, 순열을 이용한 완전 탐색, N으로 표현 의 DP를 공부했다. 그 사이에 딕셔너리 조회 방법을 다시 물었고, set 에 append() 를 쓰는지도 확인했다. lambda 가 어렵다고 했다가, 사실은 key 라는 단어부터 직관적으로 이해되지 않는다고 다시 질문하기도 했다. 가장 오래 붙잡았던 질문은 이것이었다. 실제 계산은 mid 로 하는데, 왜 마지막에는 lo 를 반환해도 되는가? 설명을 한 번 듣고 바로 납득하지 못했다. 같은 부분을 다시 물었고, 작은 예시로 범위를 움직여보는 설명도 다시 들었다. 이번 공부는 새로운 알고리즘을 많이 외운 기록보다는, 내가 어디까지 이해했고 어디부터 설명하지 못하는지를 확인한 기록에 가깝다. 1. 조건문부터 다시 확인했다 알고리즘을 공부하는 중에도 기본 문법에서 흐름이 자주 끊겼다. if 에 음수가 들어가면 실행되는지, None 이면 실행되지 않는지부터 다시 확인했다. if -1: print("실행됨") if 0: print("실행되지 않음") if None: print("실행되지 않음") 정수에서는 0만 거짓으로 취급하고, 음수도 0이 아니면 참이다. None 과 빈 문자열, 빈 리스트 같은 값도 조건문에서는 거짓으로 평가된다. 여기서 중요한 건 값의 참·거짓을 확인하는 것과, 값이나 키가 존재하는지 확인하는 것은 다르다 는 점이었다. d = {"a": 0} if d.get("a"): print("실행되지 않음") if "a" in d: print("실행됨") "a" 라는 키는 있지만 값이 0이어서 첫 번째 조건은 거짓이다. 저장된 값이 0일 수도 있는 상황에서는 get() 의 결과를 그대로 조건으로 쓰면 존재 여부를 제대로 판단하지 못한다. 컴프리헨션의 if 는 위치에 따라 역할이 달랐다 numbers = [-2, -1, 0, 1, 2] # 조건에 맞는 원소만 남긴다. positive = [x for x in numbers if x > 0] # [1, 2] # 모든 원소를 처리하되, 넣을 값을 조건에 따라 바꾼다. changed = [x if x > 0 else 0 for x in numbers] # [0, 0, 0, 1, 2] 첫 번째는 원소를 고르는 조건이고, 두 번째는 결과에 넣을 값을 결정하는 조건이다. 단순히 if 가 앞에 오는지 뒤에 오는지를 외우기보다, 이 원소를 넣을 것인지와 무엇을 넣을 것인지를 구분 해서 보려고 한다. do while 은 따로 없었다 최소 한 번 실행한 뒤 종료 여부를 판단하고 싶어서 Python의 do while 도 물었다. Python에서는 다음처럼 쓸 수 있다. x = 1 while True: print(x) x += 1 if x > 5: break 조건을 먼저 검사하는지, 작업을 한 뒤 검사하는지에 따라 반복문의 의미가 달라진다는 것도 같이 확인했다. 2. 딕셔너리의 메서드를 이름이 아니라 목적별로 구분했다 딕셔너리에서 무언가를 꺼내고 싶을 때 items() 를 써야 하는지, get() 을 써야 하는지 헷갈렸다. values() 를 설명받은 뒤에도 특정 값 하나가 궁금할 때는 무엇을 써야 하는지 다시 물었다. d = { "apple": 1000, "banana": 2000 } d["apple"] # 특정 키의 값: 1000 d.get("apple") # 특정 키의 값: 1000 d.keys() # 모든 키 d.values() # 모든 값 d.items() # 모든 (키, 값) 쌍 keys() , values() , items() 에는 인자를 넣지 않는다. 특정 키 하나를 지정하는 것은 d[key] 나 d.get(key) 다. 키가 확실히 존재한다면 d[key] 와 d.get(key) 로 얻는 값은 같다. 없는 키를 조회할 때는 차이가 있다. d["orange"] # KeyError d.get("orange") # None d.get("orange", 0) # 0 딕셔너리를 그냥 순회하면 키가 나온다는 것도 다시 확인했다. for key in d: print(key) 이것은 다음과 같은 의미다. for key in d.keys(): print(key) 키와 값을 같이 꺼내고 싶으면 items() 를 쓴다. for key, value in d.items(): print(key, value) 결국 기억할 기준은 단순했다. 키 하나로 값을 찾을 것인지, 전체 키를 볼 것인지, 전체 값을 볼 것인지, 둘을 함께 볼 것인지부터 정하면 된다. 3. 딕셔너리는 반대 방향 검색까지 자동으로 해주지 않았다 2000 이라는 값에 해당하는 키가 무엇인지, 순회하지 않고 바로 찾을 방법이 있는지도 물었다. d = { "apple": 1000, "banana": 2000 } 이 구조는 기본적으로 "banana" → 2000 을 빠르게 찾기 위한 구조다. 반대로 2000 → "banana" 를 바로 찾을 수 있는 별도의 인덱스가 있는 것은 아니다. key = next(k for k, v in d.items() if v == 2000) 이렇게 한 줄로 적어도 내부적으로는 결국 순회한다. 코드가 짧아진 것과 시간복잡도가 줄어든 것은 별개였다. 반대 방향 조회가 자주 필요하다면 처음부터 별도의 딕셔너리를 만들 수 있다. reverse_d = { 1000: "apple", 2000: "banana" } reverse_d[2000] # "banana" 즉 자료구조를 만들 때부터 어느 방향으로 조회할 일이 많은가 를 생각해야 한다. 4. set 은 만들기부터 연산까지 다시 정리했다 N으로 표현 을 풀면서 set 관련 질문을 연속으로 했다. 빈 집합은 어떻게 만드는지부터 다시 봤다. s = set() 주의할 점은 이것이다. s = {} 이것은 빈 set 이 아니라 빈 dict 다. 실제로 다음처럼 작성했다가 에러가 났다. dp = {set() for _ in range(9)} 내가 원한 것은 집합 아홉 개를 담는 리스트였는데, 바깥을 {} 로 써서 집합 안에 집합을 넣으려고 했다. dp = [set() for _ in range(9)] 이렇게 해야 했다. 추가와 제거도 리스트와 달랐다 리스트에서는: arr.append(1) 집합에서는: s.add(1) 을 쓴다. 여러 개를 추가하려면: s.update([1, 2, 3]) 삭제는: s.remove(1) 또는: s.discard(1) 을 쓴다. 차이는 없는 원소를 삭제하려고 할 때다. s.remove(100) # KeyError s.discard(100) # 에러 없음 합집합과 원소끼리의 연산은 다른 이야기였다 집합 자체를 합치려면: a = {1, 2} b = {2, 3} a | b # {1, 2, 3} 또는: a.union(b) 를 쓴다. 하지만 set 끼리 숫자처럼 곱하거나 나눌 수는 없다. a * b # 불가능 a / b # 불가능 N으로 표현 에서 필요한 것은 두 집합의 각 원소를 하나씩 꺼내 계산하는 것이었다. for x in a: for y in b: result.add(x * y) 즉 set끼리 연산하는 것이 아니라 set 안의 원소끼리 연산하는 것 이었다. 5. in 과 pop() 도 자료구조와 위치에 따라 달랐다 if x in ... 의 시간복잡도가 O(N) 인지 물었는데, 정확히는 자료구조에 따라 다르다. x in list → O(N) x in tuple → O(N) x in set → 평균 O(1) key in dict → 평균 O(1) 그래서 코딩테스트에서 어떤 값이 존재하는지 반복해서 검사한다면 list 보다 set 이 훨씬 유리할 수 있다. pop() 도 마찬가지였다. arr.pop() 맨 뒤를 제거하면 O(1) 이다. 하지만: arr.pop(0) 맨 앞을 제거하면 뒤의 원소들을 앞으로 당겨야 해서 O(N) 이다. list.pop() → O(1) list.pop(0) → O(N) 앞에서 계속 제거해야 한다면 deque.popleft() 를 고려할 수 있다. 문법 하나만 보고 시간복잡도를 외우기보다, 어떤 자료구조의 어느 위치를 건드리는가 를 같이 봐야 했다. 6. 완전 탐색과 순열 9월 26일에는 완전 탐색과 순열을 공부했다. 완전 탐색은 말 그대로 가능한 경우를 전부 확인하는 방식 이다. 예를 들어 [1, 2, 3] 의 모든 순서를 확인하고 싶다면: from itertools import permutations arr = [1, 2, 3] result = list(permutations(arr)) print(result) 결과는: [ (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1) ] 일부만 뽑으면서 순서까지 고려하고 싶으면: permutations(arr, 2) 를 사용할 수 있다. 이걸 프로그래머스의 피로도 문제에 적용했다. 처음에는 던전을 입력 순서대로 한 번만 돌았다. for i in range(size): if k >= dungeons[i][0]: ... 하지만 던전의 방문 순서에 따라 결과가 달라진다. 그래서 모든 방문 순서를 확인했다. from itertools import permutations ad = list(permutations(dungeons)) 그리고 각 순열마다 피로도를 처음 값으로 되돌렸다. origin = k while ad: count = 0 k = origin dun = ad.pop() for d in dun: if k >= d[0] and k - d[1] >= 0: k -= d[1] count += 1 이 풀이를 가장 정확하게 표현하면: 순열을 이용한 완전 탐색 이다. 던전이 N 개라면 순열은 N! 개이고, 각 순열에서 최대 N 개의 던전을 확인하므로 대략: O(N × N!) 의 시간복잡도를 가진다. 경우의 수가 매우 빠르게 증가하기 때문에 입력 크기가 작은 문제에서만 사용할 수 있다. 7. 이분 탐색을 배웠는데 무엇을 이분 탐색해야 하는지 몰랐다 처음 이분 탐색을 배울 때는 정렬된 배열에서 특정 값을 찾는 방식으로 이해했다. def binary_search(arr, target): left = 0 right = len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 범위를 절반씩 줄이기 때문에 시간복잡도는 O(log N) 이다. 그런데 입국심사 문제를 보니 혼란이 왔다. 내 코드의 주석에도 다음과 같은 고민이 남아 있었다. # 이분 탐색을 어디서 사용하지? # 뭘 정렬하지? # 심사관을 뽑을 때? 당시에는 이분 탐색을 하려면 입력 배열을 정렬해서 그 안에서 뭔가를 찾아야 한다 고 생각하고 있었다. 하지만 입국심사에서는 times 안의 값을 찾는 것이 아니었다. 찾아야 하는 것은: 모든 사람의 입국심사가 끝나는 최소 시간 이었다. 즉 배열이 아니라 정답 후보인 시간 자체를 이분 탐색해야 했다. 8. 입국심사 — 사람을 한 명씩 배치하려고 했다 처음 작성한 코드는 이런 식이었다. def solution(n, times): size = len(times) times.sort() loadings = times.copy() n -= size while n != 0: p = [] for i in range(size): p.append(loadings[i] + times[i]) m = min(p) p_i = p.index(m) loadings[p_i] = m n -= 1 return max(loadings) 생각은 단순했다. 첫 사람을 심사대에 넣음 다음 사람을 가장 빨리 끝나는 심사대에 넣음 다음 사람을 다시 배치 ... 샘플: n = 6 times = [7, 10] 에서는 실제로 28이 나왔다. 하지만 문제에서 n 은 최대 10억이다. 한 사람씩 처리하면 반복문을 최대 10억 번 돌아야 한다. 즉 문제의 크기부터 내 접근법과 맞지 않았다. 9. 파라메트릭 서치 — 사람 대신 시간을 하나 찍어봤다 관점을 바꿨다. 기존 질문은: 다음 사람을 어느 심사대에 넣을까? 였다. 바꾼 질문은: X분 동안 심사관들이 총 몇 명을 처리할 수 있을까? 였다. 예를 들어: times = [7, 10] 28분 동안 가능한 사람 수는: 28 // 7 = 4 28 // 10 = 2 총 6명 코드로 쓰면: people = 0 for time in times: people += mid // time 이렇게 된다. 시간이 충분한지를 판별할 수 있게 된 것이다. 예를 들어 n = 6 이라면: 25분 → 불가능 26분 → 불가능 27분 → 불가능 28분 → 가능 29분 → 가능 30분 → 가능 결국 다음 경계를 찾는 문제로 바뀐다. X X X X X | O O O O O ↑ 처음으로 가능한 시간이 어디인가? 이게 이 문제에서 파라메트릭 서치를 사용하는 이유였다. 10. 이분 탐색 구현에서 mid 를 범위에서 제거해야 했다 처음 작성한 코드에서는 이렇게 했다. if p > n: hi = mid else: lo = mid 문제는 mid 를 다시 탐색 범위에 남겨둔다는 것이다. 예를 들어: lo = 27 hi = 28 이면: mid = (27 + 28) // 2 # 27 여기서: lo = mid 를 하면: lo = 27 hi = 28 로 아무것도 변하지 않는다. 다음 반복에서도 mid = 27 . 결국 무한루프가 날 수 있다. 그래서 이미 검사한 mid 를 제외해야 한다. lo = mid + 1 또는: hi = mid - 1 11. p > n 과 p >= n 의 차이가 중요했다 또 하나 헷갈렸던 부분은 조건이었다. 처음에는: if p > n: 또는: if p <= n: 처럼 작성했다. 하지만 우리가 원하는 것은: n명 이상 을 처리할 수 있는 최소 시간 이다. 예를 들어: n = 6 28분 → 6명 29분 → 6명 30분 → 7명 28분도 이미 충분하다. 따라서: if p >= n: 으로 판단해야 한다. 그리고 최소 시간을 찾는 중이므로, 충분하다면 더 작은 시간을 확인한다. if p >= n: hi = mid - 1 else: lo = mid + 1 이렇게 된다. 12. 가장 이해하기 어려웠던 return lo 이 부분은 설명을 듣고도 바로 이해되지 않았다. 내 질문은 이것이었다. 실제로 사람 수를 계산하고 예측한 값은 mid 인데 왜 lo 를 반환하지? 처음에는 mid 가 답이라고 생각했다. 하지만 mid 는 정답을 저장하는 변수 가 아니었다. mid 는 매번 이렇게 질문하는 값이다. "이 시간이면 가능한가?" 그 질문의 결과를 이용해서 lo 와 hi 를 움직인다. 작은 예를 보면 더 명확했다. 시간 25 26 27 | 28 29 30 가능? X X X | O O O 27을 검사해서 불가능하다면: lo = mid + 1 이므로: lo = 28 이 된다. lo = 28 이 된 것은 28을 계산해서가 아니라, 27 이하가 모두 정답 후보에서 제거됐기 때문 이다. 반대로 28이 가능하면: hi = mid - 1 이 된다. 최종적으로는: 27 | 28 X | O ↑ ↑ hi lo 와 같은 경계가 만들어진다. 그래서 이 구현에서는 lo 가 최초로 가능한 값에 도착한다. 정리하면: mid = 지금 검사하는 값 lo = 검사 결과가 누적되면서 결국 최초의 가능한 값에 도달하는 경계 였다. 다만 이것도 모든 이분 탐색에서 무조건 lo 를 반환한다는 뜻은 아니다. 어떤 경계를 찾고 있고, lo 와 hi 를 어떤 규칙으로 움직이는지에 따라 달라진다. 13. N으로 표현 — 처음에는 최소공배수를 떠올렸다 9월 28일에는 프로그래머스의 N으로 표현 문제를 풀었다. 처음 생각은 최소공배수였다. import math lcm = N * number // math.gcd(N, number) N 으로 어떤 공통 값을 만든 뒤 number 를 만드는 방향으로 접근해보려고 했다. 하지만 이 문제는 그런 구조가 아니었다. 예를 들어: 12 = 55 / 5 + 5 / 5 12 = (55 + 5) / 5 처럼 숫자를 이어 붙이거나 다양한 사칙연산 조합이 가능하다. 여기서 필요한 것은 N을 몇 개 사용했는가 를 기준으로 가능한 결과들을 쌓는 것이었다. 14. DP의 기준을 잡았다 핵심 정의는: dp[k] = N을 정확히 k개 사용해서 만들 수 있는 모든 숫자 였다. 예를 들어 N = 5 라면: dp[1] = {5} N을 2개 사용하면: 55 5 + 5 = 10 5 - 5 = 0 5 * 5 = 25 5 // 5 = 1 이므로: dp[2] = { 55, 10, 0, 25, 1 } 여기서 set 을 사용한 이유는 같은 숫자가 여러 방식으로 나와도 중복 저장할 필요가 없기 때문이다. 15. 처음에는 잘못된 DP끼리 합치고 있었다 내 코드는 처음에 이런 식이었다. l1 = list(dp[k - 2]) l2 = list(dp[k - 1]) 문제는 k = 4 일 때다. dp[2] → N을 2개 사용 dp[3] → N을 3개 사용 이 둘을 조합하면 N을 5개 사용한다. 그런데 결과를 dp[4] 에 넣고 있었다. 즉 DP의 정의가 깨졌다. dp[4] 를 만들려면: dp[1] + dp[3] dp[2] + dp[2] dp[3] + dp[1] 처럼 두 인덱스의 합이 4가 되어야 한다. 그래서 일반적으로: for a in range(1, curr): b = curr - a 로 나눌 수 있다. for i in dp[a]: for j in dp[b]: ... 그리고 두 값을 사칙연산한다. 16. 뺄셈과 나눗셈에서는 순서도 중요했다 덧셈과 곱셈은: a + b = b + a a * b = b * a 이지만 뺄셈과 나눗셈은 다르다. a - b != b - a a // b != b // a 그래서 한쪽 조합만 계산하려면 양방향 연산을 직접 넣어야 했다. dp[curr].update([ i - j, j - i, i + j, i * j ]) if j: dp[curr].add(i // j) if i: dp[curr].add(j // i) 이렇게 하면: dp[1] + dp[4] dp[2] + dp[3] 까지만 보더라도 반대 방향 연산을 같이 확인할 수 있다. 17. DP 구현 중에도 기본 문법에서 여러 번 틀렸다 update 호출 문법 한 번은: dp[curr].update[i-j, j-i, i+j, i*j] 라고 작성했다. 하지만 update 는 함수이므로 괄호로 호출해야 한다. dp[curr].update([i-j, j-i, i+j, i*j]) 반복 변수를 계속 초기화했다 다음 코드도 작성했다. a = 1 while True: a = 1 b = curr - a ... a += 1 아래에서 a += 1 을 해도 다음 반복에서 다시 1이 된다. 결국 같은 조합만 계속 계산할 수 있다. 입국심사에서 lo = mid 로 범위가 줄지 않았던 것과 비슷했다. 반복문에서 변수를 변경했다는 사실보다 다음 반복에서도 그 변경이 유지되는지가 중요했다. / 와 // 처음에는: i / j 를 사용했다. 그러면: 5 / 2 # 2.5 가 된다. 문제에서는 나눗셈에서 나머지를 무시하므로 정수 나눗셈을 사용해야 한다. i // j 그리고 분모가 0일 때는 계산하면 안 된다. if j: dp[curr].add(i // j) 18. 0을 지우는 것도 다시 생각했다 한때: dp[k].discard(0) 으로 0을 제거했다. 하지만: 5 - 5 = 0 도 정상적인 계산 결과다. 0이라는 값 자체가 문제인 것이 아니라: 0으로 나누는 것 이 문제다. 따라서 0을 상태에서 없애버리는 것과, 분모가 0인 연산을 막는 것은 구분해야 한다. 19. 초기 상태도 정답인지 확인해야 했다 DP를 고친 뒤에는 다음처럼 3 부터 확인했다. for curr in range(3, 9): ... if number in dp[curr]: return curr 하지만: N = 5 number = 5 라면 답은 1이다. N = 5 number = 55 라면 답은 2다. 따라서 dp[1] , dp[2] 도 정답인지 확인해야 했다. 초기 상태를 만들어놓았다고 해서 자동으로 정답 검사가 된 것은 아니었다. 20. 리스트를 순회하면서 동시에 수정했다 9월 29일에는 다음 문제를 풀었다. # 정수 리스트
velog
10 Platforms to Discover Long-Active GitHub Accounts Meta description: Explore GitHub history and developer platforms to build digital skills. ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/product/buy-old-gmail-accounts/ Interest in old GitHub accounts can also lead to valuable questions about digital history, project development, programming communities, and online learning. Public GitHub activity can show how projects evolve, how developers organize information, and how technical knowledge is documented over time. For educational purposes, the most useful approach is to explore publicly available repositories and resources while building an independent learning record. This guide introduces ten platforms and resources worth exploring when studying GitHub-related projects and developer communities. The focus is on practical applications, educational benefits, everyday digital skills, and independent learning. Readers looking for general information can also use usasmmti as a source of guidance when exploring GitHub, open-source development, version control, and related subjects. GitHub for Studying Project History GitHub is the primary place to explore public repositories, documentation, project structures, and development activity. Older public repositories can demonstrate how software projects change as developers add features and improve documentation. For learners, this provides a practical way to study version control and project organization. Reading project files and documentation can also strengthen research, observation, and technical reading skills. GitLab for Collaborative Learning GitLab provides another environment for studying repositories and collaborative development. Exploring public projects can help learners understand how teams organize digital work and record project progress. The lessons are useful beyond programming. Planning, communication, task organization, and documentation are everyday skills that can support school, work, and personal projects. ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/product/buy-old-gmail-accounts/ Bitbucket for Understanding Version Control Bitbucket can introduce learners to repository management and development workflows. Comparing its concepts with those found on GitHub helps learners understand the broader principles behind version-control systems. This can make digital learning more flexible. Understanding concepts instead of memorizing one interface makes it easier to adapt to unfamiliar software. Codeberg for Open-Source Exploration Codeberg provides access to open-source projects and community-based software development. Learners can examine project descriptions, documentation, and repository structures to understand how collaborative projects are presented. Open-source exploration can also improve communication skills. Clear instructions, organized information, and readable documentation demonstrate how technical knowledge can be shared effectively. Stack Overflow for Problem-Solving Stack Overflow can be studied as a large collection of programming questions and explanations. Learners can observe how technical problems are described and broken into manageable parts. A useful exercise is to read a question, identify the main issue, and develop an answer independently before reviewing the explanations provided by other developers. SourceForge for Software Research SourceForge provides access to software projects and related project information. Exploring different projects can help learners understand how applications are described, organized, and maintained. It can also become a research exercise. Learners can compare technologies, project purposes, documentation styles, and development approaches while improving their ability to evaluate technical information. Open-Source Documentation Websites ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/product/buy-old-gmail-accounts/ Documentation websites connected with open-source projects are useful educational resources. They often explain features, terminology, project structures, and practical procedures. Learning to read documentation is valuable in daily digital life. Many applications, websites, and technical tools depend on users being able to understand written instructions. 8. Developer Community Forums Developer communities contain discussions about programming, software development, project organization, and technical questions. Reading these discussions exposes learners to different approaches to solving problems. These communities can also teach effective communication. Learners can observe how useful questions provide context and how detailed answers organize complicated information. 9. Online Coding Education Platforms Structured coding platforms can complement GitHub research by explaining concepts through lessons and exercises. Learners can first study a concept and then look for examples of that concept in public repositories. This creates a practical learning cycle: • Study the concept. • Examine a real example. • Practice independently. • Document the result. • Review the outcome. 10. Personal GitHub Learning Projects A personal GitHub account can become an organized record of learning. Instead of depending on an established account history, learners can create repositories that document their own progress. Projects might include programming exercises, documentation, data analysis, research notes, educational materials, or small experiments. Over time, these projects can form a useful digital learning archive. ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/product/buy-old-gmail-accounts/ Case Studies and Examples of Learning Case Study 1: A Student Exploring Python A beginner interested in Python searches public GitHub repositories for simple educational projects. The student begins by reading README files rather than immediately examining every line of code. After identifying a basic programming concept, the student creates a small calculator application. The project includes documentation describing what the student learned and which concepts still require practice. The exercise develops several skills simultaneously: • Programming fundamentals • Research techniques • Technical reading • Documentation • Project organization Case Study 2: Comparing Two Public Projects A web-development student finds two public projects with similar purposes. One contains detailed documentation, while the other uses shorter explanations. The student compares their structures and writes notes about which information is easiest to understand. This transforms repository exploration into a lesson about technical communication and information design. Case Study 3: Creating a Personal Knowledge Archive A professional studying data analysis creates separate repositories for small exercises. Each repository contains the project, a short explanation, and notes about the concepts studied. After several months, the collection becomes a personal learning archive. The professional can revisit earlier work, refresh forgotten concepts, and choose appropriate subjects for future study. These examples show that the strongest educational outcome comes from turning online exploration into active practice and personal documentation. ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/product/buy-old-gmail-accounts/ Step-by-Step Guide to Exploring GitHub-Related Platforms Step 1: Choose One Learning Objective Begin with a specific goal rather than exploring every topic at once. Possible objectives include: • Learning Git fundamentals • Studying Python • Understanding web development • Improving technical writing • Exploring open-source collaboration • Learning project organization A clear goal makes online research more manageable. Step 2: Select Appropriate Platforms Choose two or three resources that match your objective. For example, use GitHub for project examples, an educational platform for structured lessons, and a developer community for discussions. Using complementary resources can provide a broader understanding without making the learning process complicated. Step 3: Find Public Projects Search for public repositories related to your chosen subject. Look for projects with understandable descriptions and documentation. Pay attention to: • Project purpose • Technologies used • File organization • Documentation quality • Development history Step 4: Read Before Practicing Start with the README and other introductory material. Identify unfamiliar terms and write down questions. Try to explain the project's purpose in your own words. This simple exercise improves comprehension. Step 5: Examine the Structure Look at how files and folders are arranged. Consider why different parts of the project may be separated. This teaches both technical concepts and information organization. Step 6: Take Structured Notes Create a simple learning record containing: New concept — What did I discover? Example — Where did I see it? Practice — How can I apply it? Question — What should I study next? Structured notes make future revision easier. Step 7: Build an Independent Exercise Choose one concept and create something original. ➥ 24 Hours Reply/Contact ➤Telegram: @itusasmm ➤Whatsapp:+1 (681) 538-1764 ➤Email : itusasmm@gmail.com ➤Visit our website:usasmmti.com ➤Product Visit Link: https://usasmmti.com/pro
velog
Wheel throwing classes in Los Angeles give you a unique chance to learn how to shape clay on a potter's wheel and create beautiful, functional pieces. With hands-on instruction from skilled potters, these classes are perfect for beginners who want to learn the basics and experienced ceramists looking to fine-tune their skills.
Score: 54.4Confidence: 49%
View offervelog
Telegram has become an important communication platform for businesses, creators, communities, marketers, and online entrepreneurs. With features such as large groups, channels, bots, automation tools, and broadcast capabilities, Telegram can be useful for building communities and communicating with audiences worldwide. 📢📢╰┈➤📢🌸 Telegram: @SMMTOUSA 📢📢╰┈➤📢🌸 WhatsApp: +1 (343) 342-5919 As Telegram has grown, interest in purchasing existing Telegram accounts has also increased. Some buyers look for established accounts because they believe an existing account may save time compared with starting from scratch. However, purchasing an account can involve significant security, ownership, and platform-policy concerns. This guide explains what to consider when researching platforms that advertise Telegram accounts online. Instead of focusing only on price, buyers should evaluate account ownership, transfer procedures, seller reputation, security practices, refund policies, and compliance with Telegram's rules. What Does It Mean to Buy a Telegram Account? Buying a Telegram account generally means obtaining access to an account that was previously created and used by another person. Marketplaces may advertise accounts with different characteristics, including account age, activity history, geographic registration, or other features. However, an important distinction exists between buying an account and purchasing legitimate marketing services. An account transfer may create problems if the original owner retains access, recovery information is incomplete, or the transaction violates the platform's terms. Before considering any marketplace, users should carefully review Telegram's current terms and policies and understand the risks associated with transferring account ownership. Why Do People Look for Telegram Accounts? 📢📢╰┈➤📢🌸 Telegram: @SMMTOUSA 📢📢╰┈➤📢🌸 WhatsApp: +1 (343) 342-5919 There are several reasons people search for established Telegram accounts. Businesses may want to launch a community quickly, while marketers may want to establish a new communication channel without spending months building an account history. Creators may also be interested in Telegram because of its channel and community features. An established digital presence can appear attractive compared with creating everything from zero. Nevertheless, account age alone does not guarantee credibility, engagement, or security. A long-established account with little genuine activity may provide less value than a newly created account that is properly managed and supported by an authentic audience. Top 10 Platforms to Research When Buying Telegram Accounts Online The following categories and marketplace types are useful starting points when researching sellers. Availability, reputation, and policies can change, so buyers should independently verify each platform before making a transaction. Established Digital Account Marketplaces Large digital marketplaces are often the first place buyers look for social media and communication accounts. Their advantage is that they may provide seller profiles, transaction records, ratings, and marketplace rules. When researching an established marketplace, examine the seller's history rather than relying solely on the listing title. Look for consistent reviews, clear descriptions, dispute procedures, and transparent transaction policies. A marketplace with strong buyer protections is generally preferable to an anonymous seller operating through an informal messaging channel. Specialized Social Media Marketplaces Some websites specialize in digital assets such as social media profiles, channels, communities, and other online properties. These platforms may offer more detailed information about an account than general marketplaces. If a specialized marketplace advertises Telegram accounts, examine how ownership transfers are handled. Determine whether the marketplace provides a formal transaction process and whether it offers support if access is lost after purchase. 📢📢╰┈➤📢🌸 Telegram: @SMMTOUSA 📢📢╰┈➤📢🌸 WhatsApp: +1 (343) 342-5919 Established Digital Asset Exchanges Digital asset exchanges and online business marketplaces sometimes list established digital properties. These can include websites, social profiles, communities, and other internet-based assets. The advantage of these marketplaces can be greater transparency around sellers and transactions. However, buyers should still verify whether Telegram account transfers are actually permitted and whether the listed asset can legally and practically be transferred. Online Business Marketplaces Some online business marketplaces focus on selling complete digital businesses rather than individual accounts. A Telegram community may sometimes be included as part of a larger business package. This can be more meaningful than purchasing an isolated account because the buyer may receive legitimate business assets such as branding, content, a website, or an established community. Before purchasing, carefully examine what is included in the transaction and whether ownership of each asset can actually be transferred. Community and Creator Marketplaces Creator-focused marketplaces can sometimes connect buyers with sellers of established digital communities. These marketplaces may be useful for businesses interested in acquiring an existing audience rather than simply obtaining an old account. However, audience quality should be the main consideration. A large number of members does not necessarily mean strong engagement. Buyers should look for genuine activity, relevant audiences, and transparent community statistics. Reputable Freelance and Digital Service Platforms Some businesses use freelance platforms to hire professionals to create, manage, or grow Telegram communities rather than purchasing accounts directly. This approach can be safer for long-term projects because the business retains control over its own account while hiring specialists for content creation, community management, moderation, or marketing. When choosing a service provider, review previous work, customer feedback, communication quality, and the exact services included. Social Media Management Services Another option is using professional social media management companies. Instead of buying an existing Telegram identity, a business can create its own account and have professionals manage its content and community. 📢📢╰┈➤📢🌸 Telegram: @SMMTOUSA 📢📢╰┈➤📢🌸 WhatsApp: +1 (343) 342-5919 This approach can provide better continuity because the business maintains ownership of its credentials. It also reduces the possibility of inheriting unknown security problems from a previous account owner. Digital Marketing Agencies Digital marketing agencies may offer Telegram marketing, community management, content development, and advertising services. For companies interested in Telegram for legitimate marketing, working with an agency can be more sustainable than purchasing accounts. Agencies can help establish an authentic presence while following appropriate marketing practices. Businesses should avoid providers that promise unrealistic results, guaranteed mass membership, or methods designed to circumvent Telegram's security systems. Direct Business Asset Transactions In some situations, an existing Telegram community may be part of a broader business acquisition. For example, an entrepreneur might purchase an online business that includes a website, brand, mailing list, social profiles, and Telegram community. These transactions require additional due diligence. Buyers should verify ownership, revenue claims, audience statistics, intellectual-property rights, and the transferability of every asset. Build-and-Grow Services For many businesses, building a new Telegram account or community is ultimately the most reliable option. Professional growth services can help with branding, content, moderation, community engagement, and legitimate audience development. Although this approach takes longer than buying an existing account, it provides greater control and allows the business to establish an authentic history. What to Check Before Buying a Telegram Account Regardless of where an account is advertised, several factors deserve careful attention. First, verify who actually controls the account. A seller should be able to demonstrate legitimate ownership without asking the buyer to provide unnecessary personal information. Second, investigate the account's history. Sudden changes in ownership, unusual activity, spam complaints, or suspicious engagement may indicate potential problems. Third, review the seller's reputation. Do not rely exclusively on testimonials displayed on the seller's own website. Independent reviews and long-term marketplace activity can provide additional context. Fourth, understand the refund policy. If an account becomes inaccessible shortly after a transaction, buyers should know whether the marketplace offers any dispute resolution. Finally, consider Telegram's current rules before completing a transaction. A purchase that violates platform policies may result in account restrictions or loss of access. 📢📢╰┈➤📢🌸 Telegram: @SMMTOUSA 📢📢╰┈➤📢🌸 WhatsApp: +1 (343) 342-5919 Security Risks of Purchased Telegram Accounts Security is one of the biggest concerns when acquiring an existing account. The previous owner may still have access through another device or recovery method. In addition, the account could have an unknown history involving spam, suspicious activity, or compromised credentials. Buyers should never assume that an account is secure simply because a seller describes it as “verified,” “aged,” or “high quality.” Two-step verification, strong passwords where applicable, secure recovery information, and careful session management are important parts of account security. It is also important to avoid sharing sensitive information with sellers. Legitimate transa
velog
요즘 회사에서 LLM 에이전트 또는 워크플로우를 구축하는 프롬프트를 여럿 담당하게 되었다. 처음 시작할때는 성능에 집중하게 되지만, 실제 go live를 하게 되면 발목을 붙잡게 되는건 성능보다도 비용인 것 같다. 호출 수를 줄이는 데에는 한계가 있어서, 결국 최종 해결책은 프롬프트 캐싱으로 귀결된다. 반복되는 앞부분을 캐시에 올려두고 다음 요청부터는 싸게, 빠르게 재사용하는 방식이다. 프롬프트 캐싱을 이용하면 비용이 싸진다는 것 정도의 피상적인 지식만 있어서, 프롬프트 캐싱을 위한 세팅만 해두고 실측은 미뤄두었었다. 보고용으로 프롬프트 캐싱을 적용한 실제 비용을 정리하다 보니, 프롬프트 캐싱이 내가 상상했던것과는 다르게 이루어지고 있었다. 한번 정리해두면 좋을 것 같아 글을 남긴다. 5.6 Luna에서는 user 입력이 캐싱되지 않았다 처음 쓰던 모델은 GPT-5.6 Luna였다. 참고로 우리 쪽은 OpenAI API를 직접 부르는 게 아니라 클라우드 제공사 배포를 거쳐서 호출하는 구조다. 비용을 정리하면서 usage를 열어보니 캐시 적중률이 생각보다 훨씬 낮았다. 공통으로 들어가는 긴 내용이 매 요청 앞부분에 똑같이 들어가 있었는데도 그랬다. 원인은 그 내용을 어디에 넣었느냐였다. 공통 내용(전표나 증빙 등을 파싱한 데이터)을 user 입력( input )에 넣어뒀었는데, 같은 내용을 아무리 반복해서 보내도 전혀 캐싱이 이루어지지 않았다. 클로드의 추천 대로, 해당 내용을 user 입력이 아닌 시스템 지시( instructions )로 옮겨봤더니 그때부터 캐싱이 이루어졌다. 캐싱이 이루어지는데는 워밍업이 필요하다 캐시가 실제로 비용 효율을 만들어내는 데에도 시간이 꽤 걸렸다. 나는 두번째 호출 부터 캐싱 효과가 발생하는 것을 기대했는데, 그게 아니였다. 같은 내용으로 적어도 열번 이상은 호출해야, 유의미한 캐싱이 이루어졌다. n번 호출하면 그때부터 100% 캐싱이 이루어진다 와 같은 것도 아니였고, 호출이 늘어나면 늘어날수록 캐시 적중률이 계단처럼 조금씩 올라갔다. 이러한 결과를 보고 나서, 나는 프롬프트 캐싱의 실효성이 없다고 결론을 내리고, 캐싱에 의존하지 않고 비용을 최소화 할 수 있는 방안을 고민하기 시작했다. 6 Luna로 바꾸고 나서 GPT-6 Luna가 출시되자마자, 모델을 변경했다. 바꾸기 전에 문서부터 봤는데, 두 모델은 "GPT-5.6 이후"라는 같은 그룹으로 묶여 있었고 최소 길이, 수명, 가격, 브레이크포인트 방식까지 다 같았다. GPT-6에서 새로 생긴 건 reasoning effort를 바꿀 때 캐시를 유지하는 방법( configuration_update ) 정도였다. 그런데 막상 바꾸고 보니 꽤 달랐다. 기존 모델과 달리 user 입력에 포함된 내용까지 캐싱이 적용되었다. 워밍업 속도도 비교가 안 될 정도로 빨랐고, 같은 테스트 세트로 돌린 결과를 검토해보니, 캐시 적중률이 기존과 비교가 안될 정도로 올랐다. 기존에는 20% 언저리라 캐시 쓰기 비용을 고려했을때 break-even 근처였는데, 수정 후에는 70% 수준이라서 캐싱으로 인한 비용 효율이 크게 올랐다. 캐싱은 일정 시간 동안만 유지가 되었는데, 30분이 기준 인 것으로 확인했다. 30분 내 이어진 배치까지는 캐싱이 적용되었고, 45분 뒤에 실행된 배치는 캐싱이 끊겼다. 이건 내가 실측해서 확인한 값이기 때문에 정확하지는 않다. 결국 문서상으로는 같은 규칙을 따르는 두 모델이, 실제로는 어디까지 캐싱되는지도, 얼마나 빨리 자리 잡는지도 달랐다. 캐싱은 항상 이득이 아니다. 이번 정리를 통해 캐싱이 항상 이득이 아니라는 것도 배웠다. 캐싱을 위해서는 캐시 쓰기를 활성화 해야 한다. 캐시 쓰기 비용의 경우, 일반 입력 비용보다 많은 비용이 청구된다. 내 케이스의 경우에는 캐싱 비중이 20%를 넘지 못하면, 오히려 캐시 쓰기 비용이 더 커져서 오히려 손해가 되는 경우가 있었다. 마치며 go live 이후에 발목을 잡는 게 비용이라면, 프롬프트 캐싱은 가장 쉽고 효과적인 해결책이다. 다만 프롬프트 캐싱이 되도록 입력값을 설정해두었다고 하더라도, 실제로 캐싱이 이루어지고 있는지, 그리고 캐싱으로 인한 비용 절감이 캐시 쓰기로 인한 비용 상승분보다 큰 것이 맞는지 실측하는 과정이 꼭 필요하다는 교훈을 얻었다.
Score: 54.4Confidence: 49%
View offervelog
LangGraph 쉽게 이해하기 - RAG에 반복·분기·상태를 추가하는 방법 아래 링크에 있는 유튜브를 참고하고 공부했다. 참고 유튜브 RAG를 공부하다 보면 어느 순간 이런 생각이 든다. 검색한 문서가 엉뚱하면 다시 검색하고 싶은데? 문서에 답이 없으면 웹 검색을 하면 안 될까? LLM의 답변이 문서에 근거한 답변인지 검사할 수 없을까? 답변이 이상하면 다시 생성하면 안 될까? 중요한 작업이라면 사람이 중간에서 승인하게 만들 수 없을까? 기본적인 RAG는 보통 다음과 같이 동작한다. 질문 ↓ 문서 검색 ↓ Prompt ↓ LLM ↓ 답변 이 구조는 간단하고 이해하기 쉽다. 하지만 실제 서비스를 만들기 시작하면 점점 복잡해진다. 검색 실패 → 질문 다시 작성 그래도 실패 → 웹 검색 답변 생성 → 답변 검증 검증 실패 → 다시 생성 또 실패 → 다시 검색 이런 흐름을 일반적인 Chain에 계속 붙이면 코드가 복잡해진다. 이런 분기, 반복, 상태 관리가 필요한 LLM Workflow를 그래프 형태로 만들기 위한 도구가 LangGraph 다. 1. 왜 LangGraph가 필요할까? 먼저 기본 RAG를 생각해보자. 사용자가 다음과 같이 질문했다. "생성형 AI Gauss를 만든 회사의 2023년 매출액을 알려줘." RAG가 회사 내부 문서를 검색했다. 그리고 다음 정보를 찾았다. Samsung Gauss를 만든 회사 → 삼성전자 그런데 문제가 있다. 문서에는 삼성전자의 2023년 매출액 정보가 없다. 그러면 어떻게 해야 할까? 가장 단순한 방법은 웹 검색 기능을 하나 더 붙이는 것이다. 질문 ↓ 문서 검색 ↓ 정보 부족? ↓ YES 웹 검색 ↓ 답변 생성 그런데 웹 검색 결과가 잘못되면? 웹 검색 ↓ 잘못된 정보 ↓ LLM ↓ 잘못된 답변 이 된다. 그러면 이번에는 웹 검색 결과 검사 기능 을 추가하고 싶어진다. 웹 검색 ↓ 검색 결과 평가 ↓ 좋음 → 답변 생성 나쁨 → 다시 검색 그런데 다시 검색했는데도 결과가 안 좋으면? 다시 검색하게 된다. 검색 ↓ 실패 ↓ 검색 ↓ 실패 ↓ 검색 ↓ ... 잘못 만들면 비용과 실행 시간이 계속 증가할 수도 있다. 결국 기능을 하나씩 추가할수록 Chain + Chain + 검증 Chain + Search Chain + Rewrite Chain + Retry Logic 처럼 구조가 복잡해진다. 강의에서도 일반적인 Naive RAG가 미리 정한 Loader, Chunking, Query, Prompt 등을 따라 한 방향으로 흘러가면 중간 결과를 보고 되돌아가 수정하기 어렵다는 점을 문제로 설명한다. 다만 정확히 말하면 RAG 자체가 반복이나 분기를 못 하는 것은 아니다. Python 코드로 직접 if , while 등을 만들면 얼마든지 가능하다. 문제는 LLM Workflow가 복잡해질수록 흐름과 상태를 직접 관리하기 어려워진다는 것 이다. LangGraph는 바로 이 문제를 해결하기 위해 사용한다. 2. LangGraph란? LangGraph를 한 문장으로 정리하면 LLM 애플리케이션의 작업 과정을 Node와 Edge로 연결하고, State를 공유하면서 분기와 반복을 만들 수 있게 해주는 Workflow Framework 라고 할 수 있다. 이름 그대로 Graph 를 만든다. 예를 들어 기존 RAG가 질문 ↓ 검색 ↓ 답변 이었다면 LangGraph에서는 ┌──── 질문 재작성 ────┐ │ │ ↓ │ 질문 → 문서 검색 → 문서 평가 ──────┘ │ │ 통과 ↓ 답변 생성 ↓ 답변 평가 / \ 통과 실패 ↓ ↓ 종료 웹 검색 │ └──→ 다시 답변 처럼 만들 수 있다. 이제 프로그램이 직선이 아니라 지도처럼 보이기 시작한다. LangGraph에서는 각각의 작업을 Node , Node를 연결하는 길을 Edge 라고 부른다. 조건에 따라 다른 Node로 이동하도록 만드는 Conditional Edge도 사용할 수 있다. 3. LangGraph 핵심 개념 4가지 LangGraph를 처음 배울 때는 다음 네 가지만 먼저 이해하면 된다. State Node Edge Conditional Edge 쉽게 비유하면 택배 배송 시스템 과 비슷하다. State → 택배 상자 Node → 택배를 처리하는 각 회사 Edge → 회사와 회사를 연결하는 도로 Conditional Edge → 상황에 따라 다른 도로를 선택하는 교차로 하나씩 알아보자. 4. State - 노드끼리 공유하는 택배 상자 LangGraph에서 가장 중요한 개념 중 하나가 State 다. 각 Node는 서로 독립적으로 동작한다. 예를 들어 Node 1 → 질문 처리 Node 2 → 문서 검색 Node 3 → 답변 생성 이 있다고 하자. Node 2는 Node 1 내부에서 무슨 코드가 실행됐는지 알 필요가 없다. 대신 State 에 필요한 정보를 담아서 전달한다. 강의에서도 각각의 Node를 서로 다른 회사에 비유하고, 이전 Node의 처리 결과를 State 라는 택배 상자에 넣어 다음 Node로 전달한다고 설명한다. State 정의하기 Python에서는 보통 TypedDict 를 사용한다. from typing_extensions import TypedDict class GraphState(TypedDict, total=False): question: str context: str answer: str relevance: str 쉽게 말하면 우리 그래프에서 앞으로 사용할 데이터 이름을 미리 정하는 것 이다. State는 이런 모습이다. { "question": "RAG가 뭐야?", "context": "RAG는 검색 증강 생성으로...", "answer": "RAG는 외부 문서를...", "relevance": "good" } TypedDict는 무엇인가? 일반 Dictionary는 data = { "question": "RAG가 뭐야?" } 처럼 자유롭게 사용할 수 있다. TypedDict 는 여기에 question은 str context도 str answer도 str 처럼 어떤 타입이 들어오는지 설명을 붙이는 것 이다. 즉 초보 단계에서는 타입이 적혀 있는 Dictionary 라고 생각하면 충분하다. State는 계속 전달된다 처음에는 질문만 있다고 하자. { "question": "RAG가 뭐야?" } 문서 검색 Node를 지나면 { "question": "RAG가 뭐야?", "context": "RAG는 외부 문서를 검색해서..." } 가 된다. 답변 Node를 지나면 { "question": "RAG가 뭐야?", "context": "RAG는 외부 문서를 검색해서...", "answer": "RAG는 쉽게 말하면..." } 가 된다. 즉 Node 1 ↓ State Node 2 ↓ State 업데이트 Node 3 ↓ State 업데이트 방식이다. 노드는 전체 State를 반환하지 않아도 된다 여기서 중요한 부분이 있다. 예전 설명을 보면 이런 코드가 등장할 수 있다. def retrieve_document(state): ... return GraphState( question=state["question"], context=context, answer=state["answer"] ) 하지만 굳이 모든 값을 다시 반환할 필요는 없다. 현재 StateGraph 의 기본 Node 구조는 State → Partial State 다. 즉 변경한 값만 반환하면 된다. def retrieve_document(state: GraphState): documents = "검색된 문서입니다." return { "context": documents } 그러면 기존의 { "question": "RAG가 뭐야?" } 에 context 가 업데이트된다. 5. Reducer - 덮어쓸까? 합칠까? 기본적으로 State의 같은 Key가 다시 반환되면 새로운 값으로 업데이트된다. 예를 들어 state["context"] = "문서 A" 가 있었는데 다음 Node에서 return { "context": "문서 B" } 를 반환하면 결과는 문서 B 가 된다. 즉 기존 값을 덮어쓴다. 그런데 Chat History처럼 계속 쌓아야 하는 데이터도 있다. 사용자: 안녕하세요 AI: 안녕하세요! 사용자: RAG가 뭐예요? AI: RAG는... 이런 데이터를 매번 덮어쓰면 이전 대화가 사라진다. 그래서 사용하는 것이 Reducer 다. LangGraph에서 State Key에 Reducer를 지정하면 이전 값과 새로운 값을 어떤 방식으로 합칠지 결정할 수 있다. 자막에서도 일반 State 값은 새 값으로 갱신되지만 메시지는 Reducer를 통해 누적시키는 구조를 설명한다. operator.add 일반 List를 계속 이어 붙이고 싶다면 operator.add 를 사용할 수 있다. import operator from typing import Annotated from typing_extensions import TypedDict class GraphState(TypedDict): logs: Annotated[ list[str], operator.add ] 이제 ["검색 시작"] 이 있는 상태에서 return { "logs": ["검색 완료"] } 를 반환하면 [ "검색 시작", "검색 완료" ] 처럼 합쳐진다. add_messages 대화 메시지를 관리할 때는 add_messages 를 사용할 수 있다. from typing import Annotated from typing_extensions import TypedDict from langgraph.graph import add_messages from langchain_core.messages import AnyMessage class GraphState(TypedDict): messages: Annotated[ list[AnyMessage], add_messages ] add_messages 는 단순히 List 뒤에 붙이는 것보다 Message를 다루는 데 적합하다. 새로운 Message ID라면 추가하고, 같은 ID를 가진 Message가 들어오면 해당 Message를 업데이트할 수도 있다. Annotated는 무엇인가? Annotated 는 원래 타입에 추가적인 정보를 붙이는 Python 문법 이다. 예를 들어 Annotated[str, "Context"] 는 기본 타입 → str 추가 Metadata → "Context" 라고 볼 수 있다. 하지만 Annotated[str, "Context"] 라고 적는다고 LangGraph가 특별한 동작을 하는 것은 아니다. 반면 Annotated[ list[AnyMessage], add_messages ] 처럼 Reducer 함수를 넣으면 LangGraph가 그 함수를 State 병합 방식으로 사용한다. 6. Node - 실제 일을 하는 함수 Node는 생각보다 간단하다. 대부분 Python 함수 라고 생각하면 된다. def retrieve_document(state: GraphState): question = state["question"] print( "검색할 질문:", question ) return { "context": "검색된 문서입니다." } Node가 하는 일은 State 받기 ↓ 필요한 값 꺼내기 ↓ 작업하기 ↓ 변경할 State 반환하기 이다. LangGraph에서는 Node가 내부적으로 무엇을 하는지 제한하지 않는다. 따라서 LLM 호출 Vector DB 검색 SQL 실행 Web 검색 API 호출 Python 계산 파일 처리 등을 모두 Node 안에 넣을 수 있다. 답변 생성 Node def generate_answer( state: GraphState ): question = state["question"] context = state["context"] answer = f""" 질문: {question} 참고 자료: {context} """ return { "answer": answer } 7. Edge - Node와 Node 연결하기 Node만 여러 개 만들어놓으면 실행 순서를 알 수 없다. 그래서 Edge를 연결한다. retrieve ↓ generate ↓ evaluate LangGraph에서는 다음처럼 만든다. workflow.add_edge( "retrieve", "generate" ) workflow.add_edge( "generate", "evaluate" ) 쉽게 말하면 add_edge( "출발 Node", "도착 Node" ) 다. Conditional Edge - 조건에 따라 길 선택하기 LangGraph를 사용하는 가장 큰 이유 중 하나다. 답변을 평가했다고 하자. 결과가 grounded 면 종료하고 싶다. 그런데 not_grounded 이면 질문을 다시 작성하고 싶다. ┌→ END │ evaluate ──────┤ │ └→ rewrite 이것을 Conditional Edge로 만든다. 조건 판단 함수 from typing import Literal def route_answer( state: GraphState ) -> Literal["end", "rewrite"]: if state["relevance"] == "good": return "end" return "rewrite" Conditional Edge 연결 from langgraph.graph import END workflow.add_conditional_edges( "evaluate", route_answer, { "end": END, "rewrite": "rewrite" } ) 동작은 다음과 같다. evaluate 실행 ↓ route_answer() ↓ "end" → END "rewrite" → rewrite Node 현재 LangGraph API 이름은 add_conditional_edges() 처럼 edges가 복수형 이다. 8. 순환 구조를 만들 수 있다 기존의 단방향 RAG는 보통 검색 ↓ 답변 ↓ 끝 이다. LangGraph에서는 다음처럼 되돌아갈 수 있다. ┌─────────────────┐ │ │ ▼ │ 질문 → retrieve → generate → evaluate ▲ │ │ │ └── rewrite ←─────┘ 예를 들어 검색 결과 안 좋음 ↓ 질문 다시 작성 ↓ 다시 검색 이 가능하다. 이런 Cycle 을 쉽게 표현할 수 있다는 점이 LangGraph의 중요한 특징이다. 그런데 무한 반복하면? 다음처럼 만들어버리면 문제가 생긴다. 검색 ↓ 실패 ↓ 재검색 ↓ 실패 ↓ 재검색 ↓ ... LLM이나 Search API를 사용한다면 시간 증가 + Token 증가 + API 비용 증가 문제가 생긴다. 그래서 실행할 때 recursion_limit 을 설정할 수 있다. config = { "recursion_limit": 20 } 현재 LangGraph/Runnable 설정에서 기본 recursion limit은 25이며, 제한을 초과하면 무한 Loop를 막기 위해 GraphRecursionError 가 발생한다. 9. 가장 기본적인 LangGraph 직접 만들기 이제 정말 간단한 Graph를 만들어보자. 먼저 설치한다. uv add langgraph 1단계. State 정의 from typing_extensions import TypedDict class GraphState(TypedDict, total=False): question: str context: str answer: str relevance: str retry_count: int 2단계. Node 정의 def retrieve( state: GraphState ): question = state["question"] context = ( f"'{question}'에 대해 " "검색한 문서입니다." ) return { "context": context } 답변 생성: def generate( state: GraphState ): answer = ( f"질문: {state['question']}\n" f"근거: {state['context']}\n" "따라서 검색 문서를 기반으로 " "답변했습니다." ) return { "answer": answer } 평가: def evaluate( state: GraphState ): if state.get("context"): result = "good" else: result = "bad" return { "relevance": result } 질문 재작성: def rewrite( state: GraphState ): count = ( state.get( "retry_count", 0 ) + 1 ) new_question = ( state["question"] + " 구체적인 근거를 포함해서 검색" ) return { "question": new_question, "retry_count": count } 3단계. 분기 함수 from typing import Literal def route( state: GraphState ) -> Literal[ "finish", "rewrite" ]: if state["relevance"] == "good": return "finish" return "rewrite" 4단계. Graph 정의 현재는 START , END 를 이용하면 흐름을 명확하게 표현할 수 있다. from langgraph.graph import ( StateGraph, START, END ) workflow = StateGraph( GraphState ) workflow.add_node( "retrieve", retrieve ) workflow.add_node( "generate", generate ) workflow.add_node( "evaluate", evaluate ) workflow.add_node( "rewrite", rewrite ) 5단계. Edge 연결 workflow.add_edge( START, "retrieve" ) workflow.add_edge( "retrieve", "generate" ) workflow.add_edge( "generate", "evaluate" ) 조건부 Edge: workflow.add_conditional_edges( "evaluate", route, { "finish": END, "rewrite": "rewrite" } ) 다시 검색하도록 연결한다. workflow.add_edge( "rewrite", "retrieve" ) 전체 구조는 START ↓ retrieve ↓ generate ↓ evaluate / \ 좋음 나쁨 ↓ ↓ END rewrite ↓ retrieve 가 된다. 6단계. Compile StateGraph 는 설계도이기 때문에 그대로 실행하지 않는다. 마지막에 Compile한다. app = workflow.compile() 현재 StateGraph 는 Builder이고, compile() 을 수행하면 invoke() , stream() 등을 사용할 수 있는 실행 가능한 Graph가 만들어진다. 7단계. 실행 result = app.invoke( { "question": "LangGraph가 뭐야?" }, { "recursion_limit": 20 } ) print( result["answer"] ) 10. set_entry_point는 이제 못 쓰는 걸까? 아니다. 다음 코드도 여전히 사용할 수 있다. workflow.set_entry_point( "retrieve" ) 현재 set_entry_point("retrieve") 는 사실상 workflow.add_edge( START, "retrieve" ) 와 같은 의미다. 개인적으로는 그래프를 볼 때 START END 가 명시적으로 보이는 방식이 초보자에게 더 이해하기 쉽다. 11. Checkpointer - 그래프의 세이브 파일 게임을 생각해보자. Stage 1 ↓ Stage 2 ↓ Stage 3 게임을 껐다 켜면 다시 처음부터 해야 한다면 불편하다. 그래서 Save Point 가 필요하다. LangGraph에서는 이것과 비슷한 역할을 하는 것이 Checkpointer 다. Checkpointer는 Graph가 실행되면서 만들어지는 State의 Snapshot을 저장한다. Checkpoint 1 → 질문 입력 Checkpoint 2 → 검색 완료 Checkpoint 3 → 답변 생성 Checkpoint 4 → 평가 완료 강의에서도 Checkpointer를 이용해 이
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%