Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PyCon mini 東海 2025「個人ではじめるマルチAIエージェント入門 〜LangCh...

Avatar for komo_fr komo_fr
November 07, 2025

PyCon mini 東海 2025「個人ではじめるマルチAIエージェント入門 〜LangChain × LangGraphでアイデアを形にするステップ〜」

PyCon mini 東海 2025の発表資料です。

発表概要:
https://tokai.pycon.jp/2025/#session-talk-2

GitHubリポジトリ:
https://github.com/komo-fr/easy-literature-agents

X(旧Twitter):
@komo_fr (https://x.com/komo_fr

Avatar for komo_fr

komo_fr

November 07, 2025
Tweet

More Decks by komo_fr

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ w 5PNPLP'VSVLJ w 9ʢ5XJUUFSʣ!LPNP@GS w ۽ຊਆಸ઒ͷڌ఺ੜ׆ w ॴଐגࣜձࣾϏʔϓϥ΢υ w

    1ZUIPOΛ࢖ͬͨσʔλαΠΤϯε෼໺ͷٕज़ࢧԉ΍ɺγεςϜ։ൃ ͳͲʹैࣄ
  2. ࠷ۙͷϚΠϒʔϜ w --.Λ࢖ͬͨখ͞ͳझຯϓϩάϥϜΛ࡞Δ͜ͱ w Ի੠΍ը૾ʹ൓Ԡͯ͠ಈ͘3BTQCFSSZ1J1JDPΧʔ w จֶ࡞඼Λখֶ೥ੜ޲͚ʹϦϥΠτ͢ΔϚϧνΤʔδΣϯτͷϓ ϩάϥϜ ࠓ೔ͷൃද಺༰ w

    ࢼ࡞͢ΔதͰײͨͭ͡·͖ͣ΍ɺࢼͨ͠޻෉ w -BOH$IBJO΍-BOH(SBQIͷجຊػೳͷ࢖͍Ͳ͜Ζ ΰʔϧ w -BOH$IBJO΍-BOH(SBQIͰԿ͕Ͱ͖Δͷ͔Կͱͳ͘ΠϝʔδΛ௫Ή w ʮࣗ෼΋Կ͔࡞ͬͯΈΑ͏ʂʯͱࢥ͑ΔΑ͏ʹͳΔʢͱ͍͍ͳʣ
  3. Ξ΢τϥΠϯ w ԿΛ࡞ͬͨͷ͔ w Ͳ͏࡞ͬͨͷ͔ w 4UFQ࠷ॳͷϓϩτλΠϓΛ࡞Δ w 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ w

    4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ w ·ͱΊ εϥΠυ಺ͷαϯϓϧίʔυ͸ɺ࣮ࡍͷ࣮૷ʢ(JU)VCܝࡌ൛ʣΛ؆ུԽͨ͠΋ͷͰ͢ɻ ࣗ࡞ͷΫϥεఆٛͳͲΛলུ͠ɺओཁͳॲཧͷྲྀΕΛத৺ʹ঺հ͍ͯ͠·͢ɻ εϥΠυͷαϯϓϧίʔυʹ͍ͭͯ
  4. ͖͔͚ͬ w ࠷ۙϚϧνΤʔδΣϯτ͕࿩୊͚ͩͲɺ͍·͍ͪΠϝʔδ͕͔ͭΊͳ͍ʜ w ϚϧνΤʔδΣϯτෳ਺ͷ"*ΤʔδΣϯτ͕ڠௐͯ͠λεΫΛਐΊΔγεςϜ w ͓ษڧ͕ͯΒɺࣗ෼ͷؔ৺ͷ͋Δ෼໺Ͱ࣮ࡍʹ࡞ͬͯΈΑ͏ w ֶੜ࣌୅ʹࣇಐจֶαʔΫϧʹॴଐ w

    👉จֶ࡞඼Λখֶɺ೥ੜ޲͚ͷ؆୯ͳจষʹม׵͢ΔϓϩάϥϜΛ࡞ͬͯ ΈΑ͏ w จֶ࡞඼தֶɾߴߍͷڭՊॻʹࡌ͍ͬͯͨΑ͏ͳ୹ฤΛ૝ఆ w ྫ தౡರʮࢁ݄هʯɺև઒ཾ೭հʮཏੜ໳ʯ
  5. ྫதౡರʮࢁ݄هʯ w ੲͷதࠃͷࢻਓ͕ϓϥΠυΛ͜͡ΒͤͯދʹͳΔ࿩ ݪจ ͔͠͠ɺจ໊͸༰қʹ༲Βͣɺੜ׆͸೔Λஞʬ͓ʭ ͏ͯۤ͘͠ͳΔɻ ཥ௃͸઴ʬΑ͏΍ʭ͘য᪮ʬ͠ΐ͏ͦ͏ʭʹۦΒ Εͯདྷͨɻ ͜ͷࠒʬ͜Ζʭ͔Βͦͷ༰๴ʬΑ͏΅͏ʭ΋ቆࠁ ʬ͠ΐ͏͘͜ʭͱͳΓɺ೑མͪࠎʛलʬͻ͍ʭͰɺ

    ؟ޫͷΈెʬ͍ͨͣʭΒʹᖲʑʬ͚͍͚͍ʭͱ͠ ͯɺિʬ͔ͭʭͯਐ࢜ʹొୈʬͱ͏͍ͩʭͨ͠ࠒ ͷ๛๹ʬ΄͏͖ΐ͏ʭͷඒগ೥ͷးʬ͓΋͔͛ʭ ͸ɺԿॲʬͲ͜ʭʹٻΊΑ͏΋ͳ͍ɻ "*ΤʔδΣϯτʹΑΔϦϥΠτ Ͱ΋ɺཥ௃ʬΓͪΐ͏ʭ͞Μͷࢻʬ͠ʭ͸ɺͳ ͔ͳ͔ΈͱΊΒΕ·ͤΜͰͨ͠ɻ͓ۚʬ͔Ͷʭ ΋ͳ͘ͳΓɺՈ଒ʬ͔ͧ͘ʭ΋͝͸Μ͕৯ʬͨʭ ΂ΒΕͳ͘ͳΓ·ͨ͠ɻ ͩΜͩΜɺཥ௃ʬΓͪΐ͏ʭ͞Μ͸ΠϥΠϥ͠ ͖ͯ·ͨ͠ɻ ͦͯ͠ɺإʬ͔͓ʭ΋΍ͤͯɺ໨ʬΊʭ͚͕ͩΩ ϥΩϥͱ͜Θ͘ޫʬͻ͔ʭΔΑ͏ʹͳΓ·͠ ͨɻΉ͔͠ͷݩؾʬ͛Μ͖ʭͳإʬ͔͓ʭͱ͸ɺ ·͕ͬͨͪͬͯ͘͠·͍·ͨ͠ɻ
  6. ࣮૷ͷΠϝʔδʢͭ໨ͷϦϥΠτΤʔδΣϯτʣ from langchain.chat_models import init_chat_model from langchain_core.prompts import ChatPromptTemplate …ུ…

    # LLMΛॳظԽ͢Δ llm = init_chat_model(model="gpt-4.1", temperature=0.3) # ϓϩϯϓτͷςϯϓϨʔτΛఆٛ͢Δ prompt = ChatPromptTemplate( [("system", REWRITER_PROMPT), # γεςϜϓϩϯϓτ ("human", "# ݪจ\n{text}")]) # ੨ۭจݿͷݪߘςΩετ # ϓϩϯϓτͱLLMΛͭͳ͗ɺνΣʔϯΛߏங͢Δ chain = prompt | llm # ݪจͷݪߘΛ༩࣮͑ͯߦ͠ɺϦϥΠτͷ݁ՌΛऔಘ͢Δ result = chain.invoke({"text": text}) γεςϜϓϩϯϓτ ͋ͳͨ͸খઆΛখֶߍ1-2೥ੜ޲͚ʹϦϥΠ τ͢Δઐ໳ՈͰ͢ɻ ҎԼͷࢦࣔʹैͬͯจষΛॻ͖׵͑ͯͩ͘͞ ͍ɿ 1. ௕͍จ͸୹͘෼ׂ͍ͯͩ͘͠͞ 2. ೉͍͠ݴ༿͸қ͍͠ݴ༿ʹஔ͖׵͑ͯ͘ ͍ͩ͞ …ུ… 4UFQ࠷ॳͷϓϩτλΠϓΛ࡞Δ ϞσϧΛࢦఆ
  7. ࣮૷ͷΠϝʔδʢখֶੜϨϏϡΞʔΤʔδΣϯτʣ # LLMΛॳظԽ͢Δ llm = init_chat_model(model="gpt-4.1", temperature=0.7) # ϓϩϯϓτͷςϯϓϨʔτΛఆٛ͢Δ prompt

    = ChatPromptTemplate( [(“system", CHILD_REVIEWER_PROMPT), # γεςϜϓϩϯϓτ ("human", "{text}")]) # ϦϥΠτΤʔδΣϯτ͕ग़ྗͨ͠ୈ1ߘ # ϓϩϯϓτͱLLMΛͭͳ͗ɺνΣʔϯΛߏங͢Δ chain = prompt | llm # ݪจͷݪߘΛ༩࣮͑ͯߦ͠ɺϦϥΠτͷ݁ՌΛऔಘ͢Δ result = chain.invoke({"text": text}) γεςϜϓϩϯϓτ ͋ͳͨ͸খֶ1-2೥ੜͷࢹ఺ͰจষΛධՁ͢Δઐ໳ՈͰ͢ɻҎԼͷ3ͭͷ؍఺ ͔ΒจষΛධՁ͠ɺϑΟʔυόοΫΛฦ͍ͯͩ͘͠͞ɻ ධՁͷ؍఺ɿ 1. Ή͔͍ͣ͠ݴ༿ɿখֶ1-2೥ੜʹ͸ཧղ͕೉͍͠ݴ༿΍දݱΛϦετ Ξοϓɻ೉͍͠ॱͰฒ΂Δɻ 2. ௕͗͢ΔจɿҰ౓ͰಡΉͷ͕೉͍͠௕͍จΛϦετΞοϓɻ௕͍ॱͰฒ ΂Δɻ …ུ… w ಉ͡Α͏ʹ࣮૷ 4UFQ࠷ॳͷϓϩτλΠϓΛ࡞Δ
  8. ࠷ॳͷ՝୊ w ಡΈ΍͍͢จষ͕ͩɺ؆ܿ͗ͯ͢৘ॹ͕ͳ͍ʢͨͩͷʮ͋Β͢͡ʯΈ͍ͨʜʣ w ྫʮཏੜ໳ʯͰԼਓ͕࿝ംͱग़ձͬͯ਎͙ΔΈണ͙·Ͱͷల։͕εϐʔσΟա ͗ͯɺԼਓͷᷤ౻͕શવײ͡ΒΕͳ͍ w ରԠ w ϓϩϯϓτΛؤுΔ

    w ʮ෺ޠͷల։͕ٸʹͳΓ͗͢ͳ͍Α͏ʹɺඞཁʹԠͯ͡ొ৔ਓ෺ͷʮ৺ͷ ੠ʯΛೖΕ͍ͯͩ͘͞ʯ w ରԠ w ݪจͷҹ৅తͳϑϨʔζΛ࢒ͨ͢ΊͷΤʔδΣϯτΛ௥Ճ͢Δ 4UFQ࠷ॳͷϓϩτλΠϓΛ࡞Δ
  9. ՝୊ΤʔδΣϯτ͕ग़ྗ͢Δσʔλͷߏ଄͕೺Ѳ͠ʹ͍͘ w ॳظ֤ΤʔδΣϯτͷग़ྗΛ+40/ܗࣜͷจࣈྻͰฦ͢Α͏ࢦࣔ w ϓϩϯϓτ಺Ͱσʔλߏ଄Λఆ͍ٛͯ͠ΔΑ͏ͳঢ়ଶʜ w *%&ͷิ׬͕ޮ͔ͣɺΩʔ໊΍σʔλܕ͕೺Ѳͮ͠Β͍ খֶੜϨϏϡΞʔΤʔδΣϯτͷϓϩϯϓτʢҰ෦ʣ ʲॏཁʳඞͣҎԼͷΩʔΛ࣋ͭܗࣜͷ **JSON**

    Ͱग़ྗ͍ͯͩ͘͠͞ɿ - Ωʔ"difficult_words": Ή͔͍ͣ͠ݴ༿ͷϦετʢྫ: ["Ή͔ͣ͠ ͍ݴ༿1", "Ή͔͍ͣ͠ݴ༿2"]ʣ - Ωʔ"long_sentences": ௕͗͢ΔจͷϦετʢྫ: ["௕͗͢Δจ1", "௕͗͢Δจ2"]ʣ - Ωʔ"unclear_parts": Θ͔Γʹ͍͘෦෼ͷϦετʢྫ: ["Θ͔Γʹ ͍͘෦෼1", "Θ͔Γʹ͍͘෦෼2"]ʣ ग़ྗྫ { "difficult_words": [ "ӡ໋ʢ͏ΜΊ͍ʣ", “໘Өʢ͓΋͔͛ʣ", ...], "long_sentences": [ “΅͘͸ɺࣗ෼ʬ͡ͿΜʭ͕͍͢͝ਓʬͻͱʭͰͳ͍͜ͱΛ͜Θ͕ͬ ͨͷͰɺҰੜʬ͍ͬ͠ΐ͏ʭ͚ΜΊ͍͕Μ͹ͬͯɺΈ͕͜͏ͱ͸͠· ͤΜͰͨ͠ɻ", ...], "unclear_parts": [ “ཥ௃ʬΓͪΐ͏ʭ͕Ͳ͏ͯ͠ދʬͱΒʭʹͳͬͨͷ͔ɺ͸͖ͬΓ ͱͨ͠ཧ༝͕ॻ͔Ε͍ͯ·ͤΜɻ", ...] } 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  10. 1ZEBOUJDʹΑΔग़ྗߏ଄Λ໌ࣔ͠ɺ1ZUIPOΦϒδΣΫτʹม׵ ग़ྗͷߏ଄Λ1ZEBOUJDΛ࢖ͬͯఆٛ ΤʔδΣϯτͷग़ྗߏ଄Λࢦఆ͢Δʢwith_structured_output()ʣ from pydantic import BaseModel, Field class ChildFeedback(BaseModel):

    # খֶੜϨϏϡΞʔΤʔδΣϯτͷग़ྗ݁Ռ difficult_words: List[str] = Field(default_factory=list, description="Ή͔͍ͣ͠ݴ༿ͷϦετ") long_sentences: List[str] = Field(default_factory=list, description="௕͗͢ΔจͷϦετ") unclear_parts: List[str] = Field(default_factory=list, description="Θ͔Γʹ͍͘෦෼ͷϦετ") # LLMʢGPT-4.1ʣΛॳظԽ͢Δ llm = init_chat_model(model="gpt-4.1", temperature=0.3) # LLMͷग़ྗΛChildFeedbackͷܗࣜͰड͚औΔΑ͏ʹઃఆ͢Δ llm = llm.with_structured_output(ChildFeedback) w 1ZEBOUJDͰσʔλߏ଄Λఆٛ͠ɺwith_structured_output Ͱࢦఆ ࣗಈͰ1ZUIPOΦϒδΣΫτʹม׵ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  11. ՝୊ΤʔδΣϯτશମͷߏ଄΍ঢ়ଶ͕Θ͔Γʹ͍͘ w -BOH$IBJO͚ͩͩͱɺΤʔδΣϯτͷ෼ذ΍߹ྲྀͳͲͷશମͷߏ଄͕ݟ͑ͳ͍ w தؒ݁Ռʢୈߘ΍ϨϏϡʔ݁ՌͳͲʣ͕ؔ਺ͷҾ਺Ͱόϥ͚ͯ౉͞ΕΔ # ҹ৅తͳϑϨʔζͷநग़ notable_lines = run_highlight_executor(input_text)

    # ϦϥΠτΤʔδΣϯτ͕ϦϥΠτ rewritten_text = run_rewriter(input_text) # খֶੜϨϏϡΞʔʹΑΔϨϏϡʔ child_feedback = run_child_reviewer(rewritten_text) # େਓϨϏϡΞʔʹΑΔϨϏϡʔ adult_feedback = run_adult_reviewer(rewritten_text) # վળఏҊAgentͷ࣮ߦ suggester_output = run_suggester(input_text, rewriten_text, ...ུ...) # ϨϏϡʔ൓өAgentͷ࣮ߦ final_text = run_suggestion_applier(input_text, rewritten_text, ...ུ...) -BOH$IBJO͚ͩͰ࡞Δ৔߹ͷΠϝʔδ 7 ΤʔδΣϯτΛ࣮ߦ͢Δؔ਺Λॱ൪ʹݺͿ ࣮ࡍͷΤʔδΣϯτͷߏ଄ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  12. ࣮૷άϥϑͷॳظԽɾεςʔτͷఆٛ # WorkflowStateΛঢ়ଶͱͯ࣋ͭ͠άϥϑΛॳظԽ graph_builder = StateGraph(WorkflowState) class WorkflowState(BaseModel): original_text: str

    = Field(..., description="ݩͷςΩετ") model: str = Field(..., description="Ϟσϧ໊") # ֤ΤʔδΣϯτͷೖग़ྗͰ࢖͏σʔλΛ࣋ͨͤΔ highlight_output: HighlightExtractorOutput = Field(default_factory=HighlightExtractorOutput, description="ݪߘΛ΋ͱʹநग़ͨ͠ҹ৅తͳจ΍ηϦϑ") rewritten_sentence: RewrittenSentence = Field(default_factory=RewrittenSentence, description="ϦϥΠτ͞ΕͨςΩετͷৄࡉ") child_feedback: ChildFeedbackDict = Field(default_factory=ChildFeedback, description="খֶੜͷϨϏϡΞʔAgent͔ΒͷϑΟʔυόοΫ") adult_feedback: AdultFeedbackDict = Field(default_factory=AdultFeedback, description="େਓͷϨϏϡΞʔAgent͔ΒͷϑΟʔυόοΫ") …ུ… ϫʔΫϑϩʔΛఆٛ͢ΔάϥϑͷॳظԽ ঢ়ଶʢεςʔτʣͷߏ଄Λఆٛ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  13. ࣮૷ϊʔυʢΤʔδΣϯτʣͷ௥Ճ graph_builder.add_node("ϑϨʔζநग़Agent", _extract_highlight_node) graph_builder.add_node("ϦϥΠτAgent", _rewrite_text_node) graph_builder.add_node("ࢠڙϨϏϡΞʔAgent", _review_child_node) graph_builder.add_node("େਓϨϏϡΞʔAgent", _review_adult_node) graph_builder.add_node("վળఏҊAgent",

    _suggest_improvements_node) graph_builder.add_node("ϨϏϡʔ൓өAgent", _apply_suggestions_node) ֤ΤʔδΣϯτΛϊʔυͱͯ͠௥Ճ ϊʔυΛࣝผ͢ΔͨΊͷ໊લ ϊʔυͰ࣮ߦ͢ΔॲཧΛॻ͍ͨؔ਺ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  14. graph_builder.add_node("ϑϨʔζநग़Agent", _extract_highlight_node) graph_builder.add_node("ϦϥΠτAgent", _rewrite_text_node) graph_builder.add_node("ࢠڙϨϏϡΞʔAgent", _review_child_node) graph_builder.add_node("େਓϨϏϡΞʔAgent", _review_adult_node) graph_builder.add_node("վળఏҊAgent", _suggest_improvements_node)

    graph_builder.add_nodee(“ϨϏϡʔ൓өAgent", _apply_suggestions_node) ֤ΤʔδΣϯτΛϊʔυͱͯ͠௥Ճ ϊʔυΛࣝผ͢ΔͨΊͷ໊લ ϊʔυͰ࣮ߦ͢ΔॲཧΛॻ͍ͨؔ਺ ࣮૷ϊʔυʢΤʔδΣϯτʣͷ௥Ճ খֶੜϨϏϡΞʔͷϊʔυͰ࣮ߦ͢Δॲཧ def _review_child_node(state: WorkflowState) -> dict: # LLMͷॳظԽ΍ϓϩϯϓτͷఆٛ llm = init_chat_model(...ུ...).with_structured_output(ChildFeedback) prompt = ChatPromptTemplare(...ུ...) # νΣʔϯΛߏங + ࣮ߦ chain = prompt | llm child_feedback = chain.invoke(state.rewritten_sentence.rewritten_text) return {"child_feedback": child_feedback} Ҿ਺Ͱঢ়ଶʢεςʔτʣΛड͚औΔ εςʔτ͔ΒɺୈߘͷσʔλΛऔಘ ໭Γ஋ͰεςʔτΛߋ৽ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  15. # Τοδͷఆٛ graph_builder.add_edge("ϑϨʔζநग़Agent", "ϦϥΠτAgent") graph_builder.add_edge("ϦϥΠτAgent", "ࢠڙϨϏϡΞʔAgent") graph_builder.add_edge("ϦϥΠτAgent", "େਓϨϏϡΞʔAgent") graph_builder.add_edge("ࢠڙϨϏϡΞʔAgent", "վળఏҊAgent")

    graph_builder.add_edge("େਓϨϏϡΞʔAgent", "վળఏҊAgent") graph_builder.add_edge("վળఏҊAgent", "ϨϏϡʔ൓өAgent") # ։࢝ϊʔυͱऴྃϊʔυͷઃఆ graph_builder.set_finish_point("ϑϨʔζநग़Agent") graph_builder.set_finish_point("ϨϏϡʔ൓өAgent") # ίϯύΠϧ graph = graph_builder.compile() Τοδͷ௥Ճ ࣮૷ϊʔυؒͷ઀ଓʢΤοδʣ ࣮ߦՄೳͳάϥϑ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ
  16. ࣮૷άϥϑͷ࣮ߦ # εςʔτͷॳظঢ়ଶΛ࡞੒ state = WorkflowState(original_text=original_text, model="gpt-4.1") # ϫʔΫϑϩʔͷάϥϑΛ࣮ߦ result

    = graph.invoke(state) άϥϑͷ࣮ߦ 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ w invoke()ͰΤʔδΣϯτΛॱʹ࣮ߦ͢Δ
  17. ධՁ΍ίϝϯτͷه࿥ w -BOH4NJUIʹ͸ɺϢʔβ͔ΒͷϑΟʔυόοΫΛऩू͢Δػೳ͕͋Δ w -BOH4NJUIͷClientΛ࡞Γɺcreate_feedback()ΛݺͿ from langsmith import Client ...ུ...

    vote = input("ධՁΛೖྗ͍ͯͩ͘͠͞(good/so-so/bad): ") comment = input("ίϝϯτΛೖྗ͍ͯͩ͘͠͞: ") client = Client() client.create_feedback(run_id, key="quality_vote", value=vote, comment=comment) ϑΟʔυόοΫͷऩू 4UFQָʹ։ൃͰ͖ΔΑ͏ʹ͢Δ -BOH4NJUIʹධՁͱίϝϯτΛૹ৴
  18. ࣮૷3FUSJFWFSͱ࣭໰จͷ࡞੒ 5BWJMZΛ࢖ͬͨ8FC৘ใͷऔಘ४උ from langchain_community.retrievers import TavilySearchAPIRetriever from langchain_core.runnables import RunnablePassthrough

    # TavilyݕࡧAPIͷretrieverΛॳظԽʢ࠷େ10݅ͷݕࡧ݁ՌΛऔಘʣ retriever = TavilySearchAPIRetriever(k=10) # ݕࡧΫΤϦΛ࡞੒ʢஶऀͱ࡞඼λΠτϧ͔Β໊ݴΛ୳͢ʣ question = f"""{author}ͷʮ{title}ʯͷ໊ݴɺ໊୆ࢺɺ༗໊ͳϑϨʔζΛબΜͰ͍ͩ͘͞ɻ ಛʹɺSNSͰ༗໊ͳϑϨʔζ΍ɺωοτϛʔϜʹͳ͍ͬͯΔ͔Ͳ͏͔΋ߟྀʹೖΕ͍ͯͩ͘͞ɻ""" # LLMΛॳظԽ & ϓϩϯϓτςϯϓϨʔτΛ࡞੒ llm = init_chat_model(model=model_name, temperature=0). llm = llm.with_structured_output(HighlightExtractorOutput) prompt = ChatPromptTemplate([ ("system", HIGHLIGHT_WITH_WEB_EXTRACTOR_PROMPT), ("human", "# ஶऀ໊: {author}\n# λΠτϧ: {title} # ίϯςΩετ\n{context}"), ]) ༩͑ΒΕͨίϯςΩετ͚ͩʹج͍ͮͯ ϑϨʔζΛநग़͢ΔΑ͏ɺϓϩϯϓτͰࢦࣔ DPOUFYUʹ5BWJMZͰݕࡧͨ݁͠ՌΛೖΕΔ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  19. ࣮૷ݕࡧΛ࣮ߦͯ͠ɺ݁ՌΛϓϩϯϓτʹ౉͢ νΣʔϯͷ૊Έཱͯͱ࣮ߦ # questionʹج͍ͮͯWebݕࡧΛߦ͍ɺͦͷ݁ՌΛcontextͱͯ͠ϓϩϯϓτʹ౉͢Α͏ # νΣʔϯΛ૊ΈཱͯΔ chain = ( RunnablePassthrough.assign(context=(lambda

    x: x["question"]) | retriever) | prompt | llm ) # νΣʔϯΛ࣮ߦ໊ͯ͠ݴΛநग़͢Δ highlighted_executor_output = chain.invoke({"question": question,
 ɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹɹ "author": author,
 "title": title}) ࣭໰จΛRVFTUJPO͔ΒऔΓग़͠ɺ SFUSJFWFSͰݕࡧ࣮ߦͯ͠ɺ ݁ՌΛDPOUFYUʹׂΓ౰ͯͯ࣍ͷQSPNQUʹ౉͢ wRunnablePassthrough.assign wೖྗσʔλΛอ࣋͠ͳ͕Βɺ৽͍͠σʔλʢ͜͜Ͱ͸contextʣΛ࣍ͷॲཧʹ࣋ͪӽͤΔίϯϙʔ ωϯτ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  20. औಘͨ͠ϑϨʔζʹ࠷΋ྨࣅ͢ΔݪจΛಛఆ w TFOUFODFUSBOTGPSNFSΛ࢖ͬͯҎԼΛϕΫτϧԽ  ݪจΛจͣͭ෼ׂͨ͠ςΩετ  "*ΤʔδΣϯτ͕8FC৘ใΛݩʹநग़ͨ͠ʮҹ৅తͳϑϨʔζʯ w ίαΠϯྨࣅ౓Λܭࢉͯ͠ɺ࠷΋ࣅ͍ͯΔݪจΛಛఆ w

    ࣅ͍ͯΔϑϨʔζ͕΋ͷ͕ͳ͍ʢྨࣅ౓Ҏ্ͷϑϨʔζ͕ͳ͍ʣ৔߹͸ഁغ Ұ൪͍ۙݪจ ྨࣅ౓ "*ΤʔδΣϯτ͕நग़ͨ͠ϑϨʔζ ಛఆͨ݁͠Ռ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  21. ࣮૷ਓ͕ؒհೖ͢ΔͨΊͷϊʔυΛఆٛ from langgraph.types import interrupt ...ུ... def _human_review_edit_node(state: WorkflowState) ->

    dict: result = interrupt({"task": "ఏҊͷऔࣺબ୒Λ͍ͯͩ͘͠͞ɻ"}) edited_suggester_output = result["edited_suggestions"] return {"suggester_output": edited_suggester_output} w interrupt()Ͱάϥϑͷ࣮ߦΛҰ࣌தஅ͠ɺ֎෦ೖྗΛ଴ͭ άϥϑͷ࣮ߦΛҰ࣌தஅ͠ɺ֎෦ೖྗΛऔಘ͢Δ ฤूޙͷఏҊͰεςʔτΛߋ৽ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  22. ࣮૷ޙ͔Β࠶։Ͱ͖ΔΑ͏ઃఆ graph_builder = StateGraph(WorkflowState) # ϊʔυͷ௥Ճ ...ུ... graph_builder.add_node("ਓؒʹΑΔνΣοΫ", _human_review_edit_node) ...ུ...

    # Τοδͷఆٛ ...ུ... graph_builder.add_edge("վળఏҊAgent", "ਓؒʹΑΔνΣοΫ") graph_builder.add_edge("ਓؒʹΑΔνΣοΫ", "ϨϏϡʔ൓өAgent") ...ུ... checkpointer = InMemorySaver() # ޙͰ࠶։Ͱ͖ΔΑ͏ʹঢ়ଶΛอଘՄೳʹ graph = graph_builder.compile(checkpointer=checkpointer) w InMemorySaverΛ࢖ͬͯɺ࣮ߦதͷάϥϑͷঢ়ଶΛอଘͰ͖ΔΑ͏ʹ͢Δ ͋ͱͰ࠶։Ͱ͖ΔΑ͏ʹɺঢ়ଶΛอଘՄೳʹ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  23. ࣮૷άϥϑͷதஅͱ࠶։ from langgraph.types import Command ...ུ... # ࠶։࣌ʹඞཁͳࣝผࢠΛ࡞੒ɾઃఆ config =

    {"configurable": {"thread_id": uuid.uuid4()}} result = graph.invoke(state, config=config) # interrept()ʹ౸ୡͨ͠λΠϛϯάͰॲཧ͕தஅ͢ΔͷͰɺݱࡏͷঢ়ଶΛऔಘ state = WorkflowState.model_validate(result) edited_sugesstions = _human_review_edit(state) # ࢒ΓͷΤʔδΣϯτͷॲཧΛ࠶։ resumed_result = graph.invoke( Command(resume={"edited_suggestions": edited_sugesstions}), config=config ) ୯७ʹɺඪ४ೖྗͰ֤ఏҊʹ͍ͭͯ0,/(Λೖྗͤ͞ɺ 0,ͩͬͨఏҊ͚ͩΛ࢒ͯ͠ฦؔ͢਺ ࣮ߦ࣌ʹɺฤूࡁΈͷఏҊΛάϥϑͷεςʔτʹ౉ͯ͠ ߋ৽͢Δ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  24. ՝୊දݱʹʮ೔ຊޠͷඒ͠͞ʯ΍ʮ৺஍Α͍ϦζϜײʯ͕͚͍ܽͯΔ և઒ͷʮཏੜ໳ʯΛΤʔδΣϯτʹϦϥΠτͤͨ݁͞Ռ ͋Δ೔ͷ ༦ํʬΏ͏͕ͨʭͷ͜ͱͰ͢ɻͻͱΓͷ Լਓʬ͛ʹΜʭ͕ɺཏੜ໳ʬΒ͠ΐ͏΋Μʭͷ Լʬͨ͠ʭͰɺӍʬ͋Ίʭ͕ ΍ΉͷΛ ·͍ͬͯ·ͨ͠ɻ޿ʬͻΖʭ͍໳ʬ΋ΜʭͷԼʹ͸ɺ͜ͷ உʬ͓ͱ͜ʭ͔͠ ͍·ͤΜɻେ͖ͳ

    ·Δ͍ ͸͠Βʹɺ͖Γ͗Γ͕͢ ͽΐΜͱ ͱ·͍ͬͯ·͢ɻ և઒ͷʮ஖ᥨͷࢳʯͷݪจ ͋Δ೔ͷࣄͰ͍͟͝·͢ɻޚऍ༷ᷟʬ͓͠Ό͔͞·ʭ͸ۃָͷ࿇஑ʬ͸͍͚͢ʭͷ;ͪΛɺಠΓͰ ͿΒͿΒޚา͖ʹͳ͍ͬͯΒͬ͠Ό͍·ͨ͠ɻ஑ͷதʹ࡙͍͍ͯΔ࿇ʬ͸͢ʭͷՖ͸ɺΈΜͳۄͷ Α͏ʹ·ͬനͰɺͦͷ·Μதʹ͋Δۚ৭ʬ͖Μ͍Ζʭͷࣵʬ͍ͣʭ͔Β͸ɺԿͱ΋Ӡ͑ͳ͍޷ʬΑʭ ͍೏ʬʹ͓͍ʭ͕ɺઈؒʬͨ͑·ʭͳ͋ͨ͘Γ΁ᷓʬ͋;ʭΕͯډΓ·͢ɻۃָ͸ஸ౓ேͳͷͰ͝ ͍͟·͠ΐ͏ɻ w ಉ͡࡞ऀ͕ॻ͍ͨಐ࿩ͷݪจͱൺֱ͢ΔͱɺϦζϜײ΍৘ॹ͕ҧ͏ʜʁ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  25. ࣮૷ΤοδͰϧʔτΛ෼ذͤ͞Δ # Τοδͷఆٛ …ུ… graph_builder.add_edge("ਓؒʹΑΔνΣοΫ", "ϨϏϡʔ൓өAgent") # ৚݅ʹΑͬͯ࣍ͷભҠઌͷϊʔυΛܾΊΔΑ͏ΤοδΛఆٛ graph_builder.add_conditional_edges( “ϨϏϡʔ൓өAgent",

    # ભҠݩͷϊʔυ # ৚݅ʮ໛฿ݩͷจষ͕͋Δ͔ʁʯ lambda state: state.style_source_text is not None, { True: "จମ໛฿Agent", # ͋Δ৔߹͸ɺจମ໛฿ΤʔδΣϯτͷϊʔυ΁ False: END, # ͳ͍৔߹͸ऴྃϊʔυ΁ }, ) w add_conditional_edge()Ͱɺ৚݅ʹΑͬͯભҠઌͷϊʔυΛม͑Δ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  26. จମ໛฿ΤʔδΣϯτͷద༻ྫ ద༻લ ద༻ޙ ͋Δ೔ͷ ༦ํʬΏ͏͕ͨʭͷ͜ͱͰ͢ɻ ͻͱΓͷ Լਓʬ͛ʹΜʭ͕ɺཏੜ໳ʬΒ ͠ΐ͏΋Μʭͷ Լʬͨ͠ʭͰɺӍʬ͋Ίʭ ͕

    ΍ΉͷΛ ·͍ͬͯ·ͨ͠ɻ ޿ʬͻΖʭ͍໳ʬ΋ΜʭͷԼʹ͸ɺ͜ͷ உʬ͓ͱ͜ʭ͔͠ ͍·ͤΜɻ େ͖ͳ ·Δ͍ ͸͠Βʹɺ͖Γ͗Γ͕͢ ͽΐΜͱ ͱ·͍ͬͯ·͢ɻ ͋Δ೔ͷ༦ํʬΏ͏͕ͨʭͷ͜ͱͰ͍͟͝ ·ͨ͠ɻ ͻͱΓͷԼਓʬ͛ʹΜʭ͕ɺཏੜ໳ʬΒ͠ΐ ͏΋Μʭͷେ͖ͳ໳ʬ΋ΜʭͷԼͰɺͬ͡ ͱӍʬ͋Ίʭͷ΍ΉͷΛ଴ʬ·ʭ͓ͬͯΓ ·ͨ͠ɻ ͋ͨΓʹ͸ɺ͜ͷஉʬ͓ͱ͜ʭͷ΄͔ɺͩ ΕͻͱΓ࢟ʬ͕ͨ͢ʭ͸͍͟͝·ͤΜɻ ੺ʬ͔͋ʭ͍৭ʬ͍Ζʭͷ͸͛ͨଠʬ;ͱʭ ͍பʬ͸͠Βʭʹ͸ɺ͖Γ͗Γ͕͢Ұͽ ͖ɺ੩ʬͣ͠ʭ͔ʹͱ·͓ͬͯΓ·͢ɻ w ʮཏੜ໳ʯͷϦϥΠτ݁Ռʹɺʮ஖ᥨͷࢳʯݪจͷจମΛద༻ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ
  27. จମ໛฿ΤʔδΣϯτͷద༻ྫ ద༻લ ద༻ޙ ͋Δ೔ͷ ༦ํʬΏ͏͕ͨʭͷ͜ͱͰ͢ɻ ͻͱΓͷ Լਓʬ͛ʹΜʭ͕ɺཏੜ໳ʬΒ ͠ΐ͏΋Μʭͷ Լʬͨ͠ʭͰɺӍʬ͋Ίʭ ͕

    ΍ΉͷΛ ·͍ͬͯ·ͨ͠ɻ ޿ʬͻΖʭ͍໳ʬ΋ΜʭͷԼʹ͸ɺ͜ͷ உʬ͓ͱ͜ʭ͔͠ ͍·ͤΜɻ େ͖ͳ ·Δ͍ ͸͠Βʹɺ͖Γ͗Γ͕͢ ͽΐΜͱ ͱ·͍ͬͯ·͢ɻ ͋Δ೔ͷ༦ํʬΏ͏͕ͨʭͷ͜ͱͰ͍͟͝ ·ͨ͠ɻ ͻͱΓͷԼਓʬ͛ʹΜʭ͕ɺཏੜ໳ʬΒ͠ΐ ͏΋Μʭͷେ͖ͳ໳ʬ΋ΜʭͷԼͰɺͬ͡ ͱӍʬ͋Ίʭͷ΍ΉͷΛ଴ʬ·ʭ͓ͬͯΓ ·ͨ͠ɻ ͋ͨΓʹ͸ɺ͜ͷஉʬ͓ͱ͜ʭͷ΄͔ɺͩ ΕͻͱΓ࢟ʬ͕ͨ͢ʭ͸͍͟͝·ͤΜɻ ੺ʬ͔͋ʭ͍৭ʬ͍Ζʭͷ͸͛ͨଠʬ;ͱʭ ͍பʬ͸͠Βʭʹ͸ɺ͖Γ͗Γ͕͢Ұͽ ͖ɺ੩ʬͣ͠ʭ͔ʹͱ·͓ͬͯΓ·͢ɻ w ʮཏੜ໳ʯͷϦϥΠτ݁Ռʹɺʮ஖ᥨͷࢳʯݪจͷจମΛద༻ 4UFQΤʔδΣϯτʹΑΔϦϥΠτͷ࣭Λ্͛Δ