CodeWeaver / ARCHITECTURE.md
ใ……ใ…Žใ…‡
Initial commit for Hugging Face Spaces
ea80cdc

A newer version of the Gradio SDK is available: 6.2.0

Upgrade

CodeWeaver ์•„ํ‚คํ…์ฒ˜ (์‹ค์ œ ์ฝ”๋“œ ๊ธฐ์ค€)

์ด ๋ฌธ์„œ๋Š” ํ˜„์žฌ ์ €์žฅ์†Œ์˜ CodeWeaver๊ฐ€ ์–ด๋–ค ์ˆœ์„œ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์›๋ฆฌ๊ฐ€ ๋ฌด์—‡์ธ์ง€(์ƒํƒœ/๋ผ์šฐํŒ…/๋ณ‘๋ ฌํ™”/์บ์‹œ)๋ฅผ ์ฝ”๋“œ์™€ 1:1๋กœ ์ •ํ•ฉ๋˜๊ฒŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ๊ตฌ์„ฑ ์š”์†Œ

  • UI: Gradio ์ฑ„ํŒ… UI (CodeWeaver/ui/app.py)
    • ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ AgentState๋กœ ํฌ์žฅํ•œ ๋’ค agent.ainvoke(..., config={"configurable": {"thread_id": ...}})๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜(๊ทธ๋ž˜ํ”„): LangGraph StateGraph (CodeWeaver/src/agent/graph.py)
    • START โ†’ create_plan๋กœ ์ง„์ž… ํ›„, ์งˆ๋ฌธ ์œ ํ˜•/๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ๋ถ„๊ธฐํ•ฉ๋‹ˆ๋‹ค.
    • ์ฒดํฌํฌ์ธํŒ…: MemorySaver ์‚ฌ์šฉ(์Šค๋ ˆ๋“œ/์„ธ์…˜ ๋‹จ์œ„ ์ƒํƒœ ์œ ์ง€).
  • ๋…ธ๋“œ ๊ตฌํ˜„: (CodeWeaver/src/agent/nodes.py)
    • ์งˆ๋ฌธ ๋ถ„์„, ์บ์‹œ ์กฐํšŒ, ์˜๋„ ๋ถ„๋ฅ˜, 3์†Œ์Šค ๋ณ‘๋ ฌ ๊ฒ€์ƒ‰, ๊ฒฐ๊ณผ ํ‰๊ฐ€/๋ฆฌํŒŒ์ธ, ํ•„ํ„ฐ๋ง/์š”์•ฝ, ๋‹ต๋ณ€ ์ƒ์„ฑ, ๋‹ค์ค‘ ์งˆ๋ฌธ ๊ฒฐํ•ฉ ๋“ฑ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒํƒœ ๋ชจ๋ธ(Reducer ํฌํ•จ): (CodeWeaver/src/agent/state.py)
    • search_results๋Š” Annotated[List[SearchResult], add]๋กœ ๋ณ‘๋ ฌ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์ž๋™ ๋ณ‘ํ•ฉ๋ฉ๋‹ˆ๋‹ค.
    • intermediate_steps, multi_answers๋Š” ๋ฆฌ์…‹ ํ† ํฐ์„ ์ง€์›ํ•˜๋Š” ์ปค์Šคํ…€ reducer๋กœ, ์ฒดํฌํฌ์ธํŒ…/์Šค๋ ˆ๋“œ ์œ ์ง€ ์‹œ ์ด์ „ ํ„ด์˜ ๋ˆ„์ ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์บ์‹œ(Vector DB): Qdrant Cloud (CodeWeaver/src/vector_db/qdrant_client.py)
    • ์ž„๋ฒ ๋”ฉ์€ ๋กœ์ปฌ BAAI/bge-m3(sentence-transformers)๋กœ ์ƒ์„ฑ, Qdrant์— ์ €์žฅ/๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฒ€์ƒ‰ ์†Œ์Šค: (CodeWeaver/src/tools/search_tools.py)
    • Stack Overflow(๊ณต์‹ StackExchange API), GitHub Code Search API, Tavily(๊ณต์‹๋ฌธ์„œ ๋„๋ฉ”์ธ ์ œํ•œ) ์‚ฌ์šฉ.

์‚ฌ์šฉ์ž ์ œ๊ณต ๊ทธ๋ž˜ํ”„์™€์˜ ์ •ํ•ฉ์„ฑ

์‚ฌ์šฉ์ž๊ป˜์„œ ์ œ๊ณตํ•œ Mermaid ๊ทธ๋ž˜ํ”„๋Š” ์ด ํ”„๋กœ์ ํŠธ์˜ ์˜๋„์™€ ๋Œ€๋ถ€๋ถ„ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

์ผ์น˜ํ•˜๋Š” ๋ถ€๋ถ„(ํ•ต์‹ฌ ํŒŒ์ดํ”„๋ผ์ธ)

  • create_plan์—์„œ single_topic / multiple_questions(2๊ฐœ) / too_many(3+) ๋ถ„๊ธฐ
  • ๋‹จ์ผ ์งˆ๋ฌธ(ํ˜น์€ ๋‹จ์ผ ์ฃผ์ œ)์—์„œ:
    • analyze_question โ†’ check_cache โ†’ (hit๋ฉด return_cached_answer) / (miss๋ฉด classify_intent)
    • classify_intent ์ดํ›„ 3์†Œ์Šค ๊ฒ€์ƒ‰์„ Send API๋กœ ๋ณ‘๋ ฌ ์‹คํ–‰(fan-out)ํ•˜๊ณ  collect_results์—์„œ fan-in
    • evaluate_results โ†’ (ํ•„์š” ์‹œ refine_search 1ํšŒ) โ†’ filter_and_score โ†’ summarize_results โ†’ generate_answer
  • evaluate_results๊ฐ€ ๋ถ€์กฑํ•˜๋ฉด refine_search โ†’ classify_intent๋กœ ์ตœ๋Œ€ 1ํšŒ ๋ฃจํ”„

์‹ค์ œ ์ฝ”๋“œ์—์„œ ์ถ”๊ฐ€/๋ณ€ํ˜•๋œ ๋ถ€๋ถ„(์ค‘์š”)

  1. clarification(๋ณด์ถฉ ์š”์ฒญ) ์ „์šฉ ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌ
  • analyze_question ๊ฒฐ๊ณผ๊ฐ€ clarification์ด๋ฉด
    • ์บ์‹œ/๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ 
    • generate_with_history๋กœ ๋ฐ”๋กœ ๋‹ต๋ณ€ํ•˜๊ณ  ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
  1. multiple_questions fan-out์€ analyze_question๋กœ ์ง์ ‘ ๋“ค์–ด๊ฐ€์ง€ ์•Š์Œ

์‚ฌ์šฉ์ž ๊ทธ๋ž˜ํ”„๋Š” โ€œdynamic์—์„œ Send๋กœ analyze_question์„ 2๋ฒˆ ํ˜ธ์ถœโ€ ํ˜•ํƒœ์— ๊ฐ€๊น์ง€๋งŒ, ์‹ค์ œ ๊ตฌํ˜„์€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

  • ์‹ค์ œ ๊ตฌํ˜„์€ fanout_multi_questions๊ฐ€ Send("run_single_question_worker", child_state)๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์ด์œ : outer graph์—์„œ ์งˆ๋ฌธ 2๊ฐœ๋ฅผ ๋™์‹œ์— ๋™์ผ ํŒŒ์ดํ”„๋ผ์ธ(analyze/cache/intent/โ€ฆ)์œผ๋กœ ๋Œ๋ฆฌ๋ฉด
    • question_type, cached_result ๊ฐ™์€ **scalar ์ฑ„๋„(state ํ•„๋“œ)**์ด ๋ณ‘๋ ฌ ์—…๋ฐ์ดํŠธ ์ถฉ๋Œ์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ worker ๋‚ด๋ถ€์—์„œ ๋ณ„๋„์˜ โ€˜๋‹จ์ผ ์งˆ๋ฌธ ๊ทธ๋ž˜ํ”„โ€™๋ฅผ ์‹คํ–‰ํ•˜๊ณ ,
    • outer graph์—๋Š” reducer ์ฑ„๋„์ธ multi_answers๋งŒ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ถฉ๋Œ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์‹คํ–‰ ํ๋ฆ„(์ฝ”๋“œ ๊ธฐ์ค€)

1) UI โ†’ Agent ์‹คํ–‰(์—”ํŠธ๋ฆฌ)

CodeWeaver/ui/app.py์—์„œ:

  • ์ž…๋ ฅ ๋ฌธ์ž์—ด message๋ฅผ AgentState(user_question=..., messages=[HumanMessage(...)], ...)๋กœ ๋งŒ๋“ค๊ณ 
  • thread_id๋ฅผ config={"configurable":{"thread_id": thread_id}}๋กœ ์ „๋‹ฌํ•˜์—ฌ agent.ainvoke() ์‹คํ–‰
    • MemorySaver๊ฐ€ thread_id ๋‹จ์œ„๋กœ ์ƒํƒœ๋ฅผ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค.

2) ๋ฉ”์ธ ๊ทธ๋ž˜ํ”„(Top-level) ํ๋ฆ„

CodeWeaver/src/agent/graph.py ๊ธฐ์ค€ ๋ฉ”์ธ ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

graph TD
    startNode[START] --> createPlan[create_plan]

    createPlan -->|single_topic| analyzeQuestion[analyze_question]
    createPlan -->|multiple_questions_2| initiateDynamic[initiate_dynamic_search]
    createPlan -->|too_many_3plus| tooMany[handle_too_many_questions]

    tooMany --> endNode[END]

    analyzeQuestion -->|clarification| withHistory[generate_with_history]
    withHistory --> endNode

    analyzeQuestion -->|new_topic_or_independent| checkCache[check_cache]
    checkCache -->|hit| returnCached[return_cached_answer]
    returnCached --> endNode

    checkCache -->|miss| classifyIntent[classify_intent]

    classifyIntent --> searchSO[search_stackoverflow]
    classifyIntent --> searchGH[search_github]
    classifyIntent --> searchDocs[search_official_docs]

    searchSO --> collect[collect_results]
    searchGH --> collect
    searchDocs --> collect

    collect --> evalNode[evaluate_results]
    evalNode -->|needs_refinement_and_lt1| refine[refine_search]
    refine --> classifyIntent

    evalNode -->|sufficient_or_ge1| searchSubgraph[search_subgraph]
    searchSubgraph --> generateAnswer[generate_answer]
    generateAnswer --> routeAfterGen[route_after_generate]
    routeAfterGen -->|single| endNode
    routeAfterGen -->|multi| combine[combine_answers]
    combine --> endNode

    initiateDynamic --> fanout[fanout_multi_questions]
    fanout --> worker[run_single_question_worker]
    worker --> combine

3) create_plan: ์งˆ๋ฌธ ๊ฐœ์ˆ˜/ํ˜•ํƒœ ํŒ๋ณ„ + โ€œ3๊ฐœ ์ด์ƒโ€ ํ•˜๋“œ ๊ฐ€๋“œ

create_plan_node๋Š” ์ž…๋ ฅ์„ ์•„๋ž˜ 3๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.

  • single_topic: ํ•˜๋‚˜์˜ ์ฃผ์ œ๋ฅผ ๋‹ค์–‘ํ•œ ๊ด€์ ์œผ๋กœ ๋ฌป๋Š” ํ˜•ํƒœ
  • multiple_questions: ๋…๋ฆฝ ์งˆ๋ฌธ 2๊ฐœ
  • too_many: ๋…๋ฆฝ ์งˆ๋ฌธ 3๊ฐœ ์ด์ƒ

์ถ”๊ฐ€๋กœ, LLM ๋ถ„๋ฅ˜์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋‹ค์Œ ์กฐ๊ฑด์ด๋ฉด ๊ฒฐ์ •๋ก ์ ์œผ๋กœ too_many๋กœ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฌผ์Œํ‘œ๊ฐ€ 3๊ฐœ ์ด์ƒ
  • ๋˜๋Š” โ€œ์งˆ๋ฌธ ํ›„๋ณดโ€๊ฐ€ 3๊ฐœ ์ด์ƒ(์ค„๋ฐ”๊ฟˆ/๋ฒˆํ˜ธ/๊ตฌ๋ถ„์ž ๋“ฑ์œผ๋กœ ์ถ”์ •)

๋˜ํ•œ ์ฒดํฌํฌ์ธํŒ… ์ƒํƒœ ๋ˆ„์ ์„ ๋ง‰๊ธฐ ์œ„ํ•ด, ๋งค ์‹คํ–‰ ์‹œ์ž‘ ์‹œ multi_answers๋ฅผ ๋ฆฌ์…‹ ํ† ํฐ์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

4) analyze_question: ์งˆ๋ฌธ ํƒ€์ž…(clarification/new_topic/independent) + ์บ์‹œ ์ ๊ฒฉ์„ฑ ํŒ๋‹จ

analyze_question_node๊ฐ€ LLM์œผ๋กœ ์•„๋ž˜ ๊ฐ’์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

  • question_type: clarification | new_topic | independent
  • should_cache: ์บ์‹œ ์ €์žฅ ์—ฌ๋ถ€
  • canonical_question: ์บ์‹œ์šฉ ์ •๊ทœํ™” ์งˆ๋ฌธ(should_cache=true์ผ ๋•Œ)

๋ผ์šฐํŒ…์€ graph.py์˜ route_after_analysis์—์„œ:

  • clarification โ†’ generate_with_history (๊ฒ€์ƒ‰/์บ์‹œ ์ƒ๋žต)
  • ๋‚˜๋จธ์ง€ โ†’ check_cache

5) ์บ์‹œ(check_cache / return_cached_answer)

check_cache_node๋Š” Qdrant์—์„œ ์œ ์‚ฌ ์งˆ๋ฌธ์„ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

  • ์ž„๋ฒ ๋”ฉ: ๋กœ์ปฌ BAAI/bge-m3 (1024์ฐจ์›)
  • ์ž„๊ณ„๊ฐ’: cosine score 0.85 ์ด์ƒ์ด๋ฉด hit๋กœ ๊ฐ„์ฃผ

hit๋ฉด return_cached_answer_node๊ฐ€ ์ €์žฅ๋œ ๋‹ต๋ณ€์„ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

6) ์˜๋„ ๋ถ„๋ฅ˜(classify_intent)

classify_intent_node๊ฐ€ ์งˆ๋ฌธ์„ debugging | learning | code_review๋กœ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฐ’์€ ๊ฒ€์ƒ‰ ๊ฐœ์ˆ˜ ๋“ฑ ์ผ๋ถ€ ์ •์ฑ…์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค(์˜ˆ: StackOverflow๋Š” debugging์ด๋ฉด ๋” ๋งŽ์ด ๊ฐ€์ ธ์˜ด).

7) ๋ณ‘๋ ฌ ๊ฒ€์ƒ‰(fan-out) โ†’ ์ˆ˜์ง‘(fan-in)

classify_intent ์ดํ›„ conditional edge ํ•จ์ˆ˜๊ฐ€ Send(...) 3๊ฐœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

  • search_stackoverflow_node
  • search_github_node
  • search_official_docs_node

๊ฐ ๋…ธ๋“œ๋Š” {"search_results": [..]}๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , AgentState.search_results์˜ reducer(add)๊ฐ€ ์ด๋ฅผ ์ž๋™ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

collect_results_node๋Š” ๋ณ‘ํ•ฉ๋œ ์ด ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜๋งŒ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค.

8) ๊ฒฐ๊ณผ ํ‰๊ฐ€(evaluate_results)์™€ ์ฟผ๋ฆฌ ๋ฆฌํŒŒ์ธ(refine_search)

evaluate_results_node๋Š” ๋‹ค์Œ ๊ธฐ์ค€์œผ๋กœ โ€œ๊ฐœ์„  ํ•„์š”โ€๋ฅผ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฒฐ๊ณผ ๊ฐœ์ˆ˜ < 2 โ†’ ๊ฐœ์„  ํ•„์š”
  • (relevance_score๊ฐ€ ์žˆ๋‹ค๋ฉด) ํ‰๊ท  ์ ์ˆ˜ < 0.5 โ†’ ๊ฐœ์„  ํ•„์š”

refine_search_node๋Š” LLM์ด MORE_SPECIFIC | MORE_GENERAL | TRANSLATE ์ „๋žต์„ ์„ ํƒํ•ด ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€: refinement_count < 1์ผ ๋•Œ๋งŒ 1ํšŒ ํ—ˆ์šฉ
  • ์žฌ๊ฒ€์ƒ‰์„ ์œ„ํ•ด search_results๋ฅผ ๋นˆ ๋ฆฌ์ŠคํŠธ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  classify_intent๋กœ ๋˜๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

9) search_subgraph: ํ•„ํ„ฐ๋ง + ์š”์•ฝ

๋ฉ”์ธ ๊ทธ๋ž˜ํ”„์—๋Š” search_subgraph๊ฐ€ โ€œํ•˜๋‚˜์˜ ๋…ธ๋“œโ€์ฒ˜๋Ÿผ ๋ถ™์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • filter_and_score: ์ตœ์†Œ ๊ธธ์ด/URL ์กฐ๊ฑด์œผ๋กœ ํ•„ํ„ฐ ํ›„, ์ƒ์œ„ ์ผ๋ถ€์— ๋Œ€ํ•ด ๊ด€๋ จ๋„ ์ ์ˆ˜ ๋ถ€์—ฌ
  • summarize_results: ๊ฐ ๊ฒฐ๊ณผ๋ฅผ 2~3๋ฌธ์žฅ์œผ๋กœ ์š”์•ฝ

10) generate_answer: ๋‹ต๋ณ€ ์ƒ์„ฑ + (์กฐ๊ฑด๋ถ€) ์บ์‹œ ์ €์žฅ

generate_answer_node๋Š” ์˜๋„์— ๋”ฐ๋ผ ํ…œํ”Œ๋ฆฟ์„ ๋ฐ”๊ฟ” ์ตœ์ข… ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์บ์‹œ ์ €์žฅ ์ •์ฑ…:

  • question_type๊ฐ€ new_topic ๋˜๋Š” independent์ด๊ณ  should_cache๊ฐ€ true์ด๋ฉด ์ €์žฅ
  • clarification์€ ์ €์žฅํ•˜์ง€ ์•Š์Œ(๋ผ์šฐํŒ…์ƒ ๋ณดํ†ต ์—ฌ๊ธฐ๋กœ ์˜ค์ง€ ์•Š์ง€๋งŒ ๋ฐฉ์–ด์ ์œผ๋กœ ์ฒดํฌ)

11) ๋‹ค์ค‘ ์งˆ๋ฌธ(multiple_questions) ์ฒ˜๋ฆฌ ์›๋ฆฌ

๋‹ค์ค‘ ์งˆ๋ฌธ์˜ ํ•ต์‹ฌ์€ โ€œouter graph๋Š” ์ถฉ๋Œ ์—†์ด orchestration๋งŒ, ์‹ค์ œ ํŒŒ์ดํ”„๋ผ์ธ์€ worker ๋‚ด๋ถ€์—์„œ ์‹คํ–‰โ€์ž…๋‹ˆ๋‹ค.

ํ๋ฆ„

  • create_plan(case=multiple_questions) โ†’ initiate_dynamic_search (์ค€๋น„)
  • fanout_multi_questions(conditional edge)์ด ์งˆ๋ฌธ 2๊ฐœ๋ฅผ ๊ฐ๊ฐ run_single_question_worker๋กœ Send
  • run_single_question_worker_node ๋‚ด๋ถ€์—์„œ ๋‹จ์ผ ์งˆ๋ฌธ์šฉ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ณ„๋„ compile/์‹คํ–‰
  • worker ๊ฒฐ๊ณผ๋Š” multi_answers์— append(reducer๋กœ ๋ณ‘ํ•ฉ)
  • ๋ชจ๋“  worker๊ฐ€ ๋๋‚˜๋ฉด combine_answers_node๊ฐ€ Markdown์œผ๋กœ ๊ฒฐํ•ฉ

์™œ worker๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

outer graph์—์„œ ๋™์ผํ•œ state๋ฅผ ๋ณต์ œํ•ด analyze_question๋ถ€ํ„ฐ ๋™์‹œ์— ๋Œ๋ฆฌ๋ฉด, scalar ์ฑ„๋„(question_type, cached_result ๋“ฑ)์ด ์„œ๋กœ ๋ฎ์–ด์“ฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์‹ค์ œ ๊ตฌํ˜„์€:

  • worker ๋‚ด๋ถ€์—์„œ ๋‹จ์ผ ์งˆ๋ฌธ ๊ทธ๋ž˜ํ”„๋ฅผ ๋Œ๋ฆฌ๊ณ 
  • outer state์—๋Š” reducer ์ฑ„๋„์ธ multi_answers๋งŒ ์—…๋ฐ์ดํŠธ

์ด ๋ฐฉ์‹์œผ๋กœ ๋ณ‘๋ ฌ ์‹คํ–‰ ์•ˆ์ •์„ฑ์„ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜(์‹คํ–‰์— ํ•„์š”ํ•œ ์‹ค์ œ ๊ฐ’)

ํ•„์ˆ˜:

  • GOOGLE_API_KEY: Gemini ํ˜ธ์ถœ(langchain-google-genai)
  • QDRANT_URL, QDRANT_API_KEY: Qdrant Cloud ์บ์‹œ
  • TAVILY_API_KEY: ๊ณต์‹ ๋ฌธ์„œ ๊ฒ€์ƒ‰(Tavily)

์„ ํƒ:

  • GITHUB_TOKEN: GitHub API rate limit ์™„ํ™”(์—†์œผ๋ฉด 60 req/hr ์ˆ˜์ค€)
  • LANGCHAIN_TRACING_V2, LANGCHAIN_API_KEY: LangSmith ํŠธ๋ ˆ์ด์‹ฑ(์„ ํƒ)