LabHub
学习 学习路径 课程

AI 智能体 — 是图不是模型

明明在跑,看起来却像卡住了

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

같은 그래프를 다섯 가지 방식으로 흘려보내며 무엇이 언제 나오는지 직접 센다. 갱신만 모아 마지막 상태를 다시 세워 보고, 흘려보낸 마지막 상태가 invoke 의 답과 같은지 대조한다.

왜 중요한가

에이전트에서 흘려보낼 것은 토큰만이 아니다. 모델을 마지막에 한 번 부르는 그래프라면 토큰 스트리밍을 붙여도 앞의 침묵은 그대로다. 사용자에게 필요한 것은 "지금 어느 단계인지" 와 "무엇이 새로 정해졌는지" 이고, 그것은 모델이 아니라 그래프가 알고 있다. 모드마다 단위가 다르다. updates노드 단위라 나란히 도는 노드 둘이 같은 수퍼스텝에 있어도 이벤트가 따로 나오고, values수퍼스텝 단위라 그 둘의 결과가 합쳐진 뒤 한 번 나온다. debug 는 수퍼스텝 번호까지 알려 준다. 이 차이를 모르면 "왜 두 번 나오지" 를 버그로 오해한다. 갱신만 모아 상태를 다시 세우려면 리듀서를 알아야 한다. 그래프 안에서는 리듀서가 해 주던 일을 바깥에서는 직접 해야 하고, 이어 붙이는 열쇠를 덮어쓰면 발자국이 한 칸만 남는다. 그리고 상태에 남기고 싶지 않은 진행 표시는 custom 으로 내보낸다. 상태에 넣으면 체크포인트에 실리고 재개할 때 옛 표시가 되살아난다. 채점기는 여러분이 적어 둔 설명을 믿지 않는다. 여러분의 모듈을 실제로 불러 매번 다른 회의록으로 돌려 보고, 이벤트의 개수·순서·모양을 채점기가 따로 센 값과 대조한다. 시간은 재지 않는다.

단계

  1. /root/work/agstream/stream.pyState·노드 넷(split·keypoints·actions·composebuild_graph()·start(text)·run_values(text) 를 만드세요. split 다음에 keypointsactions 가 나란히 돌고, 둘 다 compose 로 모입니다.
  2. run_updates(text) 를 더해 stream_mode="updates" 의 이벤트를 그대로 목록으로 돌려주게 하세요.
  3. REDUCERS·rebuild(text)·rebuild_report(text) 를 더해 갱신만 모아 마지막 상태를 다시 세우고, 그것이 values 의 마지막과 같은지 대조하게 하세요.
  4. run_debug(text) 를 더해 {수퍼스텝번호: [노드이름...정렬됨]} 을 돌려주게 하세요.
  5. run_multi(text)mode_sequence(text) 를 더해 stream_mode=["updates", "values"] 의 결과를 다루게 하세요.
  6. composewriter: StreamWriter 를 받아 조각 두 개를 내보내게 하고, run_custom(text) 를 더하세요.
  7. compare_invoke(text) 를 더해 흘려보낸 마지막 상태와 invoke 의 답이 같은지, 가장 먼저 도착하는 노드가 무엇인지, 이벤트가 몇 개인지를 한 번에 돌려주게 하세요.
  8. /root/work/agstream/minutes.txt 에 잴 회의록을 저장하고, 그것으로 재어 /root/work/agstream/stream_report.json/root/work/agstream/stream_report.md 에 기록하세요.

참고

상태 전체가 단계마다 나온다

/root/work/agstream/stream.pyState·노드 넷·build_graph()·start(text)·run_values(text) 를 만드세요. split 다음에 keypointsactions 가 나란히 돌고 둘 다 compose 로 모입니다.

stream(입력, stream_mode="values") 가 주는 것을 그대로 목록으로 돌려주면 됩니다. 맨 앞에 입력 상태가 한 번 나온다는 점을 눈으로 확인하세요. 나란히 도는 두 노드의 결과는 합쳐진 뒤 한 이벤트로 나옵니다.

갱신은 노드마다 하나씩

run_updates(text) 를 더해 stream_mode="updates" 의 이벤트를 가공하지 말고 그대로 목록으로 돌려주게 하세요.

이벤트 하나가 {노드이름: 갱신} 입니다. 나란히 도는 노드 둘이 같은 수퍼스텝에 있어도 이벤트는 따로 나옵니다 — 그래서 values 보다 이벤트 수가 많습니다. 몇 개가 나오는지 직접 세어 보세요.

갱신만으로 상태를 다시 세운다

REDUCERS = {"trace": "append"}rebuild(text) 를 더하세요. start(text) 에서 시작해 updates 이벤트를 차례로 적용한 딕셔너리를 돌려줍니다.

그래프 안에서는 리듀서가 해 주던 일을 바깥에서는 직접 해야 합니다. 전부 덮어쓰면 이어 붙이는 열쇠에서 마지막 한 칸만 남습니다. 그런데 리듀서를 제대로 흉내 내도 완전히 같아지지는 않습니다 — 두 trace 를 나란히 찍어 보고 어디가 다른지, 왜 그런지 직접 확인하세요. 둘 다 매번 같은 답을 주지만 서로 다릅니다.

몇 번째 단계에서 무엇이 함께 도는가

run_debug(text) 를 더해 {수퍼스텝번호: [노드이름...정렬됨]} 을 돌려주게 하세요. event["type"] == "task" 인 것만 봅니다.

debug 모드는 노드마다 tasktask_result 두 이벤트를 냅니다. task 쪽의 step 이 수퍼스텝 번호이고 payload["name"] 이 노드 이름입니다. 나란히 도는 노드들이 같은 번호를 갖는지 확인하세요 — 그것이 이 모드를 쓰는 이유입니다.

여러 모드를 함께 듣는다

run_multi(text)mode_sequence(text) 를 더하세요. stream_mode=["updates", "values"] 를 주면 (모드이름, 값) 튜플이 나옵니다.

목록으로 모드를 주면 이벤트마다 어느 모드의 것인지가 앞에 붙습니다. 두 모드의 이벤트가 어떤 순서로 섞여 나오는지 직접 보세요 — 한 모드를 다 준 뒤 다른 모드를 주는 것이 아닙니다. mode_sequence 는 그 순서를 이름만 뽑아 보여 줍니다.

상태에 남기지 않고 보여 준다

composewriter: StreamWriter 를 두 번째 인자로 받아 {"stage": "compose", "points": 개수}{"stage": "compose", "todos": 개수} 를 차례로 내보내게 하고, run_custom(text) 를 더하세요.

노드 함수의 두 번째 인자 이름을 writer 로 두고 StreamWriter 로 주석하면 LangGraph 가 채워 줍니다. writer(...) 로 넣은 것은 상태에 남지 않고 듣는 쪽으로만 갑니다 — 진행 표시를 상태에 넣으면 체크포인트에 실리고 재개할 때 되살아납니다.

흘려보낸 마지막과 한 번에 받은 답

compare_invoke(text) 를 더해 {"same": 참거짓, "first_node": 문자열, "values_events": 정수, "updates_events": 정수} 를 돌려주게 하세요.

values 의 마지막 이벤트가 invoke() 의 답과 같다면, 화면은 스트리밍으로 그리고 기록은 따로 invoke 로 남기는 식으로 두 번 돌릴 이유가 없습니다. first_nodeupdates 의 첫 이벤트에 담긴 노드 이름입니다 — 사용자에게 가장 먼저 보여 줄 수 있는 것이 무엇인지 알려 줍니다.

무엇을 언제 보여 줄지 정리한다

/root/work/agstream/minutes.txt 에 잴 회의록을 저장하세요(결정 사항과 할 일이 각각 한 줄 이상, 빈 줄도 하나). 그 회의록으로 재어 /root/work/agstream/stream_report.jsonvalues_events·updates_events·first_node·supersteps·mode_sequence·custom_chunks·rebuild_same·stream_order·merged_order 를, /root/work/agstream/stream_report.md## 다섯 가지 방식이 각각 무엇을 주나 ## 갱신만으로 상태를 다시 세우려면 ## 화면에 먼저 보여 줄 수 있는 것 ## 현장에서 무엇을 고르나 네 절로 쓰세요.

전부 아래 회의록으로 돌려 얻은 값입니다. superstepsrun_debug 의 답을 문자열 열쇠로 바꿔 담고, rebuild_same·stream_order·merged_orderrebuild_report 의 답 그대로입니다. 숫자를 지어내지 말고 세어서 넣으세요.