From 30a049c5e1e83f6349d4cdb5312557c56859edba Mon Sep 17 00:00:00 2001 From: cooney Date: Fri, 5 Jun 2026 17:58:54 +0900 Subject: [PATCH] =?UTF-8?q?=EB=9E=AD=EC=B2=B4=EC=9D=B8=20=EC=97=90?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=ED=8A=B8=20=EC=8B=AC=ED=99=94=20-=20RouterCh?= =?UTF-8?q?ain,=20RunnableBranch,=20SequentialChain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026 상 삼성E&A 직무기술서.pdf | Bin .../2026 상 삼성SDS 직무기술서.pdf | Bin .../2026 상 삼성디스플레이 직무기술서.pdf | Bin .../2026 상 삼성전자 직무기술서.pdf | Bin .../2026 상 삼성증권 직무기술서 .pdf | Bin .../2026 상 제일기획 직무기술서.pdf | Bin .../length.bin | Bin 400 -> 400 bytes ollama/db/chroma/chroma.sqlite3 | Bin 1511424 -> 1511424 bytes ollama/db/company.db | Bin 0 -> 12288 bytes ollama/langchain-agent.ipynb | 1468 +++++++++++++++-- ollama/langchain-agent2.ipynb | 700 ++++++++ ollama/rag_company.py | 10 +- ollama/research_report_20260605_131335.md | 4 + ollama/research_report_20260605_144723.md | 77 + ollama/research_report_20260605_144803.md | 73 + 15 files changed, 2192 insertions(+), 140 deletions(-) rename ollama/data/{ => 직무기술서}/2026 상 삼성E&A 직무기술서.pdf (100%) rename ollama/data/{ => 직무기술서}/2026 상 삼성SDS 직무기술서.pdf (100%) rename ollama/data/{ => 직무기술서}/2026 상 삼성디스플레이 직무기술서.pdf (100%) rename ollama/data/{ => 직무기술서}/2026 상 삼성전자 직무기술서.pdf (100%) rename ollama/data/{ => 직무기술서}/2026 상 삼성증권 직무기술서 .pdf (100%) rename ollama/data/{ => 직무기술서}/2026 상 제일기획 직무기술서.pdf (100%) create mode 100644 ollama/db/company.db create mode 100644 ollama/langchain-agent2.ipynb create mode 100644 ollama/research_report_20260605_131335.md create mode 100644 ollama/research_report_20260605_144723.md create mode 100644 ollama/research_report_20260605_144803.md diff --git a/ollama/data/2026 상 삼성E&A 직무기술서.pdf b/ollama/data/직무기술서/2026 상 삼성E&A 직무기술서.pdf similarity index 100% rename from ollama/data/2026 상 삼성E&A 직무기술서.pdf rename to ollama/data/직무기술서/2026 상 삼성E&A 직무기술서.pdf diff --git a/ollama/data/2026 상 삼성SDS 직무기술서.pdf b/ollama/data/직무기술서/2026 상 삼성SDS 직무기술서.pdf similarity index 100% rename from ollama/data/2026 상 삼성SDS 직무기술서.pdf rename to ollama/data/직무기술서/2026 상 삼성SDS 직무기술서.pdf diff --git a/ollama/data/2026 상 삼성디스플레이 직무기술서.pdf b/ollama/data/직무기술서/2026 상 삼성디스플레이 직무기술서.pdf similarity index 100% rename from ollama/data/2026 상 삼성디스플레이 직무기술서.pdf rename to ollama/data/직무기술서/2026 상 삼성디스플레이 직무기술서.pdf diff --git a/ollama/data/2026 상 삼성전자 직무기술서.pdf b/ollama/data/직무기술서/2026 상 삼성전자 직무기술서.pdf similarity index 100% rename from ollama/data/2026 상 삼성전자 직무기술서.pdf rename to ollama/data/직무기술서/2026 상 삼성전자 직무기술서.pdf diff --git a/ollama/data/2026 상 삼성증권 직무기술서 .pdf b/ollama/data/직무기술서/2026 상 삼성증권 직무기술서 .pdf similarity index 100% rename from ollama/data/2026 상 삼성증권 직무기술서 .pdf rename to ollama/data/직무기술서/2026 상 삼성증권 직무기술서 .pdf diff --git a/ollama/data/2026 상 제일기획 직무기술서.pdf b/ollama/data/직무기술서/2026 상 제일기획 직무기술서.pdf similarity index 100% rename from ollama/data/2026 상 제일기획 직무기술서.pdf rename to ollama/data/직무기술서/2026 상 제일기획 직무기술서.pdf diff --git a/ollama/db/chroma/007b3d0a-071a-41ad-bdfc-b628142a46bd/length.bin b/ollama/db/chroma/007b3d0a-071a-41ad-bdfc-b628142a46bd/length.bin index 964e9469330886231439d9ddbf84f51ec17b0c48..8380f230771a48d937ce0709962b742003858b07 100644 GIT binary patch literal 400 zcmY#jVu<0bWMEK$(hUB51`Hq&Q5OUfhk_oU2YK^DhgZ`N-D zn+sL-{|6I_lK((}A^_#=fa;sU&5#4qA1rgf2t+eOUiU0QATp9RL6T literal 400 zcmcb>%+PYPo`K;4lx83j+&jW`45W`(U=LNu2&E?!g^GfhFg{cmPQ9NFVg82#xP){8 zSRb5!bK(M!JC;X4L{6SKrvu_Mo`HxnK@}Offl diff --git a/ollama/db/chroma/chroma.sqlite3 b/ollama/db/chroma/chroma.sqlite3 index 3d272bf6800d4df828210d44e6d7bd040cbde897..fc65bf9084c7de2d473cdaa0c31c374dee881e28 100644 GIT binary patch delta 138 zcmZoz5YwVA5=Lukv*evLh&Am#vKP9Ww2Vs0Sj0b*Vt<^y7WAQk{(K_C_aVqqW_0b- CB`@Rv diff --git a/ollama/db/company.db b/ollama/db/company.db new file mode 100644 index 0000000000000000000000000000000000000000..01db02df113f590520e141313ab0a216aa56ba9a GIT binary patch literal 12288 zcmeI&ze~eF6bJCTT-64Y6FOuH9Jr*kxHx6AO1BJEyEw^NpAwp1O|D`_H;aFyyQARl zMGR=6yUO>-@!nnDUEs4l$i-O{8&bD)I*oY{!;OI_<(n{w?2> zcG=7*(8I@E7^b0J@te3!2_U_bx@ z5P$##AOHafKmY;|fB*y_@b?5<(P2GjqD!xEohFTldQUEI(s`U71Wo_X \u001B[39m\u001B[32m17\u001B[39m result = agent.invoke(\n\u001B[32m 18\u001B[39m {\n\u001B[32m 19\u001B[39m \"messages\" : [\n\u001B[32m 20\u001B[39m {\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/main.py:3880\u001B[39m, in \u001B[36mPregel.invoke\u001B[39m\u001B[34m(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, control, version, **kwargs)\u001B[39m\n\u001B[32m 3877\u001B[39m chunks.append(chunk)\n\u001B[32m 3878\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 3879\u001B[39m \u001B[38;5;66;03m# v1: collect interrupts from updates stream\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m3880\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mchunk\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mstream\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 3881\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3882\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3883\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mcontext\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mcontext\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3884\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 3885\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m[\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mupdates\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m]\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01melse\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\n\u001B[32m 3886\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3887\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3888\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43moutput_keys\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43moutput_keys\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3889\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minterrupt_before\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43minterrupt_before\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3890\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minterrupt_after\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43minterrupt_after\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3891\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mdurability\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mdurability\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3892\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mcontrol\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mcontrol\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3893\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3894\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 3895\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 3896\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mlen\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mchunk\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m2\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/main.py:2936\u001B[39m, in \u001B[36mPregel.stream\u001B[39m\u001B[34m(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, control, subgraphs, debug, version, **kwargs)\u001B[39m\n\u001B[32m 2934\u001B[39m \u001B[38;5;28;01mfor\u001B[39;00m task \u001B[38;5;129;01min\u001B[39;00m loop.match_cached_writes():\n\u001B[32m 2935\u001B[39m loop.output_writes(task.id, task.writes, cached=\u001B[38;5;28;01mTrue\u001B[39;00m)\n\u001B[32m-> \u001B[39m\u001B[32m2936\u001B[39m \u001B[30;43m\u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43m_\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mrunner\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtick\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 2937\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m[\u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mloop\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtasks\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mnot\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mwrites\u001B[39;49m\u001B[30;43m]\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2938\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mtimeout\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mstep_timeout\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2939\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mget_waiter\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mget_waiter\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2940\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mloop\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43maccept_push\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2941\u001B[39m \u001B[30;43m\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 2942\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;03m# emit output\u001B[39;49;00m\n\u001B[32m 2943\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01myield from\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43m_output\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 2944\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2945\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m (...)\u001B[39m\u001B[32m 2951\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m_state_mapper\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2952\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 2953\u001B[39m loop.after_tick()\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/_runner.py:207\u001B[39m, in \u001B[36mPregelRunner.tick\u001B[39m\u001B[34m(self, tasks, reraise, timeout, retry_policy, get_waiter, schedule_task)\u001B[39m\n\u001B[32m 205\u001B[39m scheduled_error_handler = \u001B[38;5;28;01mFalse\u001B[39;00m\n\u001B[32m 206\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m207\u001B[39m \u001B[30;43mrun_with_retry\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 208\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 209\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 210\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mconfigurable\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43m{\u001B[39;49m\n\u001B[32m 211\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mCONFIG_KEY_CALL\u001B[39;49m\u001B[30;43m:\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mpartial\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 212\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m_call\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 213\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mweakref\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mref\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 214\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 215\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mfutures\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mweakref\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mref\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfutures\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 216\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 217\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43msubmit\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43msubmit\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 218\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 219\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m}\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 220\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 221\u001B[39m \u001B[38;5;28mself\u001B[39m.commit(t, \u001B[38;5;28;01mNone\u001B[39;00m)\n\u001B[32m 222\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m exc:\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/_retry.py:585\u001B[39m, in \u001B[36mrun_with_retry\u001B[39m\u001B[34m(task, retry_policy, configurable)\u001B[39m\n\u001B[32m 583\u001B[39m task.writes.clear()\n\u001B[32m 584\u001B[39m \u001B[38;5;66;03m# run the task\u001B[39;00m\n\u001B[32m--> \u001B[39m\u001B[32m585\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mtask\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mproc\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtask\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 586\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m ParentCommand \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 587\u001B[39m ns: \u001B[38;5;28mstr\u001B[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/_internal/_runnable.py:684\u001B[39m, in \u001B[36mRunnableSeq.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 682\u001B[39m \u001B[38;5;66;03m# run in context\u001B[39;00m\n\u001B[32m 683\u001B[39m \u001B[38;5;28;01mwith\u001B[39;00m set_config_context(config, run) \u001B[38;5;28;01mas\u001B[39;00m context:\n\u001B[32m--> \u001B[39m\u001B[32m684\u001B[39m \u001B[38;5;28minput\u001B[39m = \u001B[30;43mcontext\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mstep\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 685\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 686\u001B[39m \u001B[38;5;28minput\u001B[39m = step.invoke(\u001B[38;5;28minput\u001B[39m, config)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/_internal/_runnable.py:426\u001B[39m, in \u001B[36mRunnableCallable.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 424\u001B[39m run_manager.on_chain_end(ret)\n\u001B[32m 425\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m426\u001B[39m ret = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfunc\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 427\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m.recurse \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(ret, Runnable):\n\u001B[32m 428\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ret.invoke(\u001B[38;5;28minput\u001B[39m, config)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:822\u001B[39m, in \u001B[36mToolNode._func\u001B[39m\u001B[34m(self, input, config, runtime)\u001B[39m\n\u001B[32m 820\u001B[39m input_types = [input_type] * \u001B[38;5;28mlen\u001B[39m(tool_calls)\n\u001B[32m 821\u001B[39m \u001B[38;5;28;01mwith\u001B[39;00m get_executor_for_config(config) \u001B[38;5;28;01mas\u001B[39;00m executor:\n\u001B[32m--> \u001B[39m\u001B[32m822\u001B[39m outputs = \u001B[30;43mlist\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 823\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mexecutor\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mmap\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_run_one\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mtool_calls\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput_types\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mtool_runtimes\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 824\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 826\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m._combine_tool_outputs(outputs, input_type)\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:619\u001B[39m, in \u001B[36mExecutor.map..result_iterator\u001B[39m\u001B[34m()\u001B[39m\n\u001B[32m 616\u001B[39m \u001B[38;5;28;01mwhile\u001B[39;00m fs:\n\u001B[32m 617\u001B[39m \u001B[38;5;66;03m# Careful not to keep a reference to the popped future\u001B[39;00m\n\u001B[32m 618\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m timeout \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m619\u001B[39m \u001B[38;5;28;01myield\u001B[39;00m \u001B[30;43m_result_or_cancel\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfs\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mpop\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 620\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 621\u001B[39m \u001B[38;5;28;01myield\u001B[39;00m _result_or_cancel(fs.pop(), end_time - time.monotonic())\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:317\u001B[39m, in \u001B[36m_result_or_cancel\u001B[39m\u001B[34m(***failed resolving arguments***)\u001B[39m\n\u001B[32m 315\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m 316\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m317\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mfut\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mresult\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtimeout\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 318\u001B[39m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[32m 319\u001B[39m fut.cancel()\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:456\u001B[39m, in \u001B[36mFuture.result\u001B[39m\u001B[34m(self, timeout)\u001B[39m\n\u001B[32m 454\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m CancelledError()\n\u001B[32m 455\u001B[39m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._state == FINISHED:\n\u001B[32m--> \u001B[39m\u001B[32m456\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m__get_result\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 457\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 458\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mTimeoutError\u001B[39;00m()\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:401\u001B[39m, in \u001B[36mFuture.__get_result\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 399\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._exception:\n\u001B[32m 400\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m401\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;28mself\u001B[39m._exception\n\u001B[32m 402\u001B[39m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[32m 403\u001B[39m \u001B[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001B[39;00m\n\u001B[32m 404\u001B[39m \u001B[38;5;28mself\u001B[39m = \u001B[38;5;28;01mNone\u001B[39;00m\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/thread.py:58\u001B[39m, in \u001B[36m_WorkItem.run\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 55\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m\n\u001B[32m 57\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m---> \u001B[39m\u001B[32m58\u001B[39m result = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfn\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 59\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mBaseException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 60\u001B[39m \u001B[38;5;28mself\u001B[39m.future.set_exception(exc)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/runnables/config.py:610\u001B[39m, in \u001B[36mContextThreadPoolExecutor.map.._wrapped_fn\u001B[39m\u001B[34m(*args)\u001B[39m\n\u001B[32m 609\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34m_wrapped_fn\u001B[39m(*args: Any) -> T:\n\u001B[32m--> \u001B[39m\u001B[32m610\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mcontexts\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mpop\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfn\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:1046\u001B[39m, in \u001B[36mToolNode._run_one\u001B[39m\u001B[34m(self, call, input_type, tool_runtime)\u001B[39m\n\u001B[32m 1042\u001B[39m config = tool_runtime.config\n\u001B[32m 1044\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._wrap_tool_call \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[32m 1045\u001B[39m \u001B[38;5;66;03m# No wrapper - execute directly\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m1046\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_execute_tool_sync\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtool_request\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput_type\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 1048\u001B[39m \u001B[38;5;66;03m# Define execute callable that can be called multiple times\u001B[39;00m\n\u001B[32m 1049\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34mexecute\u001B[39m(req: ToolCallRequest) -> ToolMessage | Command:\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:1006\u001B[39m, in \u001B[36mToolNode._execute_tool_sync\u001B[39m\u001B[34m(self, request, input_type, config)\u001B[39m\n\u001B[32m 1003\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m\n\u001B[32m 1005\u001B[39m \u001B[38;5;66;03m# Error is handled - create error ToolMessage\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m1006\u001B[39m content = \u001B[30;43m_handle_tool_error\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43me\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mflag\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_handle_tool_errors\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 1007\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ToolMessage(\n\u001B[32m 1008\u001B[39m content=content,\n\u001B[32m 1009\u001B[39m name=call[\u001B[33m\"\u001B[39m\u001B[33mname\u001B[39m\u001B[33m\"\u001B[39m],\n\u001B[32m 1010\u001B[39m tool_call_id=call[\u001B[33m\"\u001B[39m\u001B[33mid\u001B[39m\u001B[33m\"\u001B[39m],\n\u001B[32m 1011\u001B[39m status=\u001B[33m\"\u001B[39m\u001B[33merror\u001B[39m\u001B[33m\"\u001B[39m,\n\u001B[32m 1012\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:434\u001B[39m, in \u001B[36m_handle_tool_error\u001B[39m\u001B[34m(e, flag)\u001B[39m\n\u001B[32m 432\u001B[39m content = flag\n\u001B[32m 433\u001B[39m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28mcallable\u001B[39m(flag):\n\u001B[32m--> \u001B[39m\u001B[32m434\u001B[39m content = \u001B[30;43mflag\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43me\u001B[39;49m\u001B[30;43m)\u001B[39;49m \u001B[38;5;66;03m# type: ignore [assignment, call-arg]\u001B[39;00m\n\u001B[32m 435\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 436\u001B[39m msg = (\n\u001B[32m 437\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mGot unexpected type of `handle_tool_error`. Expected bool, str \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 438\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mor callable. Received: \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mflag\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m\"\u001B[39m\n\u001B[32m 439\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:391\u001B[39m, in \u001B[36m_default_handle_tool_errors\u001B[39m\u001B[34m(e)\u001B[39m\n\u001B[32m 389\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(e, ToolInvocationError):\n\u001B[32m 390\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m e.message\n\u001B[32m--> \u001B[39m\u001B[32m391\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m e\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:958\u001B[39m, in \u001B[36mToolNode._execute_tool_sync\u001B[39m\u001B[34m(self, request, input_type, config)\u001B[39m\n\u001B[32m 956\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m 957\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m958\u001B[39m response = \u001B[30;43mtool\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mcall_args\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 959\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m ValidationError \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 960\u001B[39m \u001B[38;5;66;03m# Filter out errors for injected arguments\u001B[39;00m\n\u001B[32m 961\u001B[39m injected = \u001B[38;5;28mself\u001B[39m._injected_args.get(call[\u001B[33m\"\u001B[39m\u001B[33mname\u001B[39m\u001B[33m\"\u001B[39m])\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:642\u001B[39m, in \u001B[36mBaseTool.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 634\u001B[39m \u001B[38;5;129m@override\u001B[39m\n\u001B[32m 635\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34minvoke\u001B[39m(\n\u001B[32m 636\u001B[39m \u001B[38;5;28mself\u001B[39m,\n\u001B[32m (...)\u001B[39m\u001B[32m 639\u001B[39m **kwargs: Any,\n\u001B[32m 640\u001B[39m ) -> Any:\n\u001B[32m 641\u001B[39m tool_input, kwargs = _prep_run_args(\u001B[38;5;28minput\u001B[39m, config, **kwargs)\n\u001B[32m--> \u001B[39m\u001B[32m642\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtool_input\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:1001\u001B[39m, in \u001B[36mBaseTool.run\u001B[39m\u001B[34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[39m\n\u001B[32m 999\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m error_to_raise:\n\u001B[32m 1000\u001B[39m run_manager.on_tool_error(error_to_raise, tool_call_id=tool_call_id)\n\u001B[32m-> \u001B[39m\u001B[32m1001\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m error_to_raise\n\u001B[32m 1002\u001B[39m output = _format_output(content, artifact, tool_call_id, \u001B[38;5;28mself\u001B[39m.name, status)\n\u001B[32m 1003\u001B[39m run_manager.on_tool_end(output, color=color, name=\u001B[38;5;28mself\u001B[39m.name, **kwargs)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:967\u001B[39m, in \u001B[36mBaseTool.run\u001B[39m\u001B[34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[39m\n\u001B[32m 965\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m config_param := _get_runnable_config_param(\u001B[38;5;28mself\u001B[39m._run):\n\u001B[32m 966\u001B[39m tool_kwargs |= {config_param: config}\n\u001B[32m--> \u001B[39m\u001B[32m967\u001B[39m response = \u001B[30;43mcontext\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_run\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mtool_args\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mtool_kwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 968\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m.response_format == \u001B[33m\"\u001B[39m\u001B[33mcontent_and_artifact\u001B[39m\u001B[33m\"\u001B[39m:\n\u001B[32m 969\u001B[39m msg = (\n\u001B[32m 970\u001B[39m \u001B[33m\"\u001B[39m\u001B[33mSince response_format=\u001B[39m\u001B[33m'\u001B[39m\u001B[33mcontent_and_artifact\u001B[39m\u001B[33m'\u001B[39m\u001B[33m \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 971\u001B[39m \u001B[33m\"\u001B[39m\u001B[33ma two-tuple of the message content and raw tool output is \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 972\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mexpected. Instead, generated response is of type: \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 973\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[38;5;132;01m{\u001B[39;00m\u001B[38;5;28mtype\u001B[39m(response)\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m.\u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 974\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/simple.py:122\u001B[39m, in \u001B[36mTool._run\u001B[39m\u001B[34m(self, config, run_manager, *args, **kwargs)\u001B[39m\n\u001B[32m 120\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m config_param := _get_runnable_config_param(\u001B[38;5;28mself\u001B[39m.func):\n\u001B[32m 121\u001B[39m kwargs[config_param] = config\n\u001B[32m--> \u001B[39m\u001B[32m122\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfunc\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 123\u001B[39m msg = \u001B[33m\"\u001B[39m\u001B[33mTool does not support sync invocation.\u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 124\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mNotImplementedError\u001B[39;00m(msg)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_community/utilities/google_serper.py:74\u001B[39m, in \u001B[36mGoogleSerperAPIWrapper.run\u001B[39m\u001B[34m(self, query, **kwargs)\u001B[39m\n\u001B[32m 72\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34mrun\u001B[39m(\u001B[38;5;28mself\u001B[39m, query: \u001B[38;5;28mstr\u001B[39m, **kwargs: Any) -> \u001B[38;5;28mstr\u001B[39m:\n\u001B[32m 73\u001B[39m \u001B[38;5;250m \u001B[39m\u001B[33;03m\"\"\"Run query through GoogleSearch and parse result.\"\"\"\u001B[39;00m\n\u001B[32m---> \u001B[39m\u001B[32m74\u001B[39m results = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_google_serper_api_results\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 75\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mquery\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 76\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mgl\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mgl\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 77\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mhl\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mhl\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 78\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mnum\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mk\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 79\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mtbs\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtbs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 80\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43msearch_type\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtype\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 81\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 82\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 84\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m._parse_results(results)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_community/utilities/google_serper.py:164\u001B[39m, in \u001B[36mGoogleSerperAPIWrapper._google_serper_api_results\u001B[39m\u001B[34m(self, search_term, search_type, **kwargs)\u001B[39m\n\u001B[32m 157\u001B[39m params = {\n\u001B[32m 158\u001B[39m \u001B[33m\"\u001B[39m\u001B[33mq\u001B[39m\u001B[33m\"\u001B[39m: search_term,\n\u001B[32m 159\u001B[39m **{key: value \u001B[38;5;28;01mfor\u001B[39;00m key, value \u001B[38;5;129;01min\u001B[39;00m kwargs.items() \u001B[38;5;28;01mif\u001B[39;00m value \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m},\n\u001B[32m 160\u001B[39m }\n\u001B[32m 161\u001B[39m response = requests.post(\n\u001B[32m 162\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mhttps://google.serper.dev/\u001B[39m\u001B[38;5;132;01m{\u001B[39;00msearch_type\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m\"\u001B[39m, headers=headers, params=params\n\u001B[32m 163\u001B[39m )\n\u001B[32m--> \u001B[39m\u001B[32m164\u001B[39m \u001B[30;43mresponse\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mraise_for_status\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 165\u001B[39m search_results = response.json()\n\u001B[32m 166\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m search_results\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/requests/models.py:1167\u001B[39m, in \u001B[36mResponse.raise_for_status\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 1162\u001B[39m http_error_msg = (\n\u001B[32m 1163\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[38;5;132;01m{\u001B[39;00m\u001B[38;5;28mself\u001B[39m.status_code\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m Server Error: \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mreason\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m for url: \u001B[39m\u001B[38;5;132;01m{\u001B[39;00m\u001B[38;5;28mself\u001B[39m.url\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m\"\u001B[39m\n\u001B[32m 1164\u001B[39m )\n\u001B[32m 1166\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m http_error_msg:\n\u001B[32m-> \u001B[39m\u001B[32m1167\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m HTTPError(http_error_msg, response=\u001B[38;5;28mself\u001B[39m)\n", - "\u001B[31mHTTPError\u001B[39m: 403 Client Error: Forbidden for url: https://google.serper.dev/search?q=2026+%EB%85%84+%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD+%EB%8C%80%ED%86%B5%EB%A0%B9&gl=us&hl=en&num=10", - "During task with name 'tools' and id '109f6fcf-db56-33ef-3150-1626cfa7d289'" - ] + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='2026년 대한민국 대통령은 누구인가요?', additional_kwargs={}, response_metadata={}, id='8507b426-852d-40c5-b857-a20c4bedbbba'),\n", + " AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:56:52.939504636Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3127383291, 'load_duration': 167933855, 'prompt_eval_count': 310, 'prompt_eval_duration': 451918748, 'eval_count': 94, 'eval_duration': 2345037856, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-5213-7641-b977-51c8d6150d57-0', tool_calls=[{'name': 'google_search', 'args': {'__arg1': '2026 년 대한민국 대통령'}, 'id': 'b6fdc6fd-67ce-45a9-9119-5d86c97d11dc', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 310, 'output_tokens': 94, 'total_tokens': 404}),\n", + " ToolMessage(content='대한민국 제21대 대통령 선거는 대한민국의 제21대 대통령을 선출한 대한민국 대통령 선거이다. 개표 결과 더불어민주당 이재명 후보가 국민의힘 김문수 후보와 개혁신당 이준석 후보를 누르고 대통령에 당선되었다.', name='google_search', id='b84e3eac-b696-4c98-a4b4-a585615e6099', tool_call_id='b6fdc6fd-67ce-45a9-9119-5d86c97d11dc'),\n", + " AIMessage(content='2026 년 대한민국 대통령은 **이재명**입니다.\\n\\n대한민국 제 21 대 대통령 선거에서 더불어민주당 이재명 후보가 국민의힘 김문수 후보와 개혁신당 이준석 후보를 누르고 대통령에 당선되었습니다.', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:00.039783832Z', 'done': True, 'done_reason': 'stop', 'total_duration': 4984911973, 'load_duration': 260127787, 'prompt_eval_count': 417, 'prompt_eval_duration': 567720741, 'eval_count': 159, 'eval_duration': 4025909249, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-668e-70e0-a32f-c87eec27516c-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 417, 'output_tokens': 159, 'total_tokens': 576})]}" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 109 + "execution_count": 61 }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:00.108540051Z", + "start_time": "2026-06-05T01:57:00.082003448Z" + } + }, "cell_type": "code", "source": [ "# !pip install wikipedia\n", @@ -381,13 +383,13 @@ ], "id": "5062b8e1e253697", "outputs": [], - "execution_count": null + "execution_count": 62 }, { "metadata": { "ExecuteTime": { - "end_time": "2026-06-04T08:42:12.269352703Z", - "start_time": "2026-06-04T08:42:08.808621715Z" + "end_time": "2026-06-05T01:57:09.696858566Z", + "start_time": "2026-06-05T01:57:00.109616819Z" } }, "cell_type": "code", @@ -415,59 +417,173 @@ "id": "636e7ea8a2c1c614", "outputs": [ { - "ename": "JSONDecodeError", - "evalue": "Expecting value: line 1 column 1 (char 0)", - "output_type": "error", - "traceback": [ - "\u001B[31m---------------------------------------------------------------------------\u001B[39m", - "\u001B[31mJSONDecodeError\u001B[39m Traceback (most recent call last)", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/requests/models.py:1116\u001B[39m, in \u001B[36mResponse.json\u001B[39m\u001B[34m(self, **kwargs)\u001B[39m\n\u001B[32m 1115\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m-> \u001B[39m\u001B[32m1116\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mcomplexjson\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mloads\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtext\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 1117\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m JSONDecodeError \u001B[38;5;28;01mas\u001B[39;00m e:\n\u001B[32m 1118\u001B[39m \u001B[38;5;66;03m# Catch JSON-related errors and raise as requests.JSONDecodeError\u001B[39;00m\n\u001B[32m 1119\u001B[39m \u001B[38;5;66;03m# This aliases json.JSONDecodeError and simplejson.JSONDecodeError\u001B[39;00m\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/json/__init__.py:346\u001B[39m, in \u001B[36mloads\u001B[39m\u001B[34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)\u001B[39m\n\u001B[32m 343\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m (\u001B[38;5;28mcls\u001B[39m \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m object_hook \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m\n\u001B[32m 344\u001B[39m parse_int \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m parse_float \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m\n\u001B[32m 345\u001B[39m parse_constant \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m object_pairs_hook \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m kw):\n\u001B[32m--> \u001B[39m\u001B[32m346\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43m_default_decoder\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mdecode\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43ms\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 347\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mcls\u001B[39m \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/json/decoder.py:337\u001B[39m, in \u001B[36mJSONDecoder.decode\u001B[39m\u001B[34m(self, s, _w)\u001B[39m\n\u001B[32m 333\u001B[39m \u001B[38;5;250m\u001B[39m\u001B[33;03m\"\"\"Return the Python representation of ``s`` (a ``str`` instance\u001B[39;00m\n\u001B[32m 334\u001B[39m \u001B[33;03mcontaining a JSON document).\u001B[39;00m\n\u001B[32m 335\u001B[39m \n\u001B[32m 336\u001B[39m \u001B[33;03m\"\"\"\u001B[39;00m\n\u001B[32m--> \u001B[39m\u001B[32m337\u001B[39m obj, end = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mraw_decode\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43ms\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43midx\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43m_w\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43ms\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m0\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mend\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 338\u001B[39m end = _w(s, end).end()\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/json/decoder.py:355\u001B[39m, in \u001B[36mJSONDecoder.raw_decode\u001B[39m\u001B[34m(self, s, idx)\u001B[39m\n\u001B[32m 354\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mStopIteration\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m err:\n\u001B[32m--> \u001B[39m\u001B[32m355\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m JSONDecodeError(\u001B[33m\"\u001B[39m\u001B[33mExpecting value\u001B[39m\u001B[33m\"\u001B[39m, s, err.value) \u001B[38;5;28;01mfrom\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[32m 356\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m obj, end\n", - "\u001B[31mJSONDecodeError\u001B[39m: Expecting value: line 1 column 1 (char 0)", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001B[31mJSONDecodeError\u001B[39m Traceback (most recent call last)", - "\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[128]\u001B[39m\u001B[32m, line 8\u001B[39m\n\u001B[32m 4\u001B[39m tools = WikipediaQueryRun(api_wrapper = WikipediaAPIWrapper())\n\u001B[32m 5\u001B[39m \n\u001B[32m 6\u001B[39m agent = create_agent(model = qwen_llm, tools=[tools], system_prompt=\u001B[33m\"You are a helpful assistant\"\u001B[39m)\n\u001B[32m 7\u001B[39m \n\u001B[32m----> \u001B[39m\u001B[32m8\u001B[39m result = agent.invoke(\n\u001B[32m 9\u001B[39m {\n\u001B[32m 10\u001B[39m \"messages\" : [\n\u001B[32m 11\u001B[39m {\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/main.py:3880\u001B[39m, in \u001B[36mPregel.invoke\u001B[39m\u001B[34m(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, control, version, **kwargs)\u001B[39m\n\u001B[32m 3877\u001B[39m chunks.append(chunk)\n\u001B[32m 3878\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 3879\u001B[39m \u001B[38;5;66;03m# v1: collect interrupts from updates stream\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m3880\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mchunk\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mstream\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 3881\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3882\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3883\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mcontext\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mcontext\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3884\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 3885\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m[\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mupdates\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m]\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01melse\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\n\u001B[32m 3886\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3887\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3888\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43moutput_keys\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43moutput_keys\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3889\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minterrupt_before\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43minterrupt_before\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3890\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43minterrupt_after\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43minterrupt_after\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3891\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mdurability\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mdurability\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3892\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mcontrol\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mcontrol\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3893\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 3894\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 3895\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m\"\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 3896\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mlen\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mchunk\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m==\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m2\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/main.py:2936\u001B[39m, in \u001B[36mPregel.stream\u001B[39m\u001B[34m(self, input, config, context, stream_mode, print_mode, output_keys, interrupt_before, interrupt_after, durability, control, subgraphs, debug, version, **kwargs)\u001B[39m\n\u001B[32m 2934\u001B[39m \u001B[38;5;28;01mfor\u001B[39;00m task \u001B[38;5;129;01min\u001B[39;00m loop.match_cached_writes():\n\u001B[32m 2935\u001B[39m loop.output_writes(task.id, task.writes, cached=\u001B[38;5;28;01mTrue\u001B[39;00m)\n\u001B[32m-> \u001B[39m\u001B[32m2936\u001B[39m \u001B[30;43m\u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43m_\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mrunner\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtick\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 2937\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m[\u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mfor\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01min\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mloop\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtasks\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mvalues\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mif\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43;01mnot\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mwrites\u001B[39;49m\u001B[30;43m]\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2938\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mtimeout\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mstep_timeout\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2939\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mget_waiter\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mget_waiter\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2940\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mloop\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43maccept_push\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2941\u001B[39m \u001B[30;43m\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m:\u001B[39;49m\n\u001B[32m 2942\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;03m# emit output\u001B[39;49;00m\n\u001B[32m 2943\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43;01myield from\u001B[39;49;00m\u001B[30;43m \u001B[39;49m\u001B[30;43m_output\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 2944\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mstream_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2945\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mprint_mode\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m (...)\u001B[39m\u001B[32m 2951\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m_state_mapper\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 2952\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 2953\u001B[39m loop.after_tick()\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/_runner.py:207\u001B[39m, in \u001B[36mPregelRunner.tick\u001B[39m\u001B[34m(self, tasks, reraise, timeout, retry_policy, get_waiter, schedule_task)\u001B[39m\n\u001B[32m 205\u001B[39m scheduled_error_handler = \u001B[38;5;28;01mFalse\u001B[39;00m\n\u001B[32m 206\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m207\u001B[39m \u001B[30;43mrun_with_retry\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 208\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 209\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 210\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mconfigurable\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43m{\u001B[39;49m\n\u001B[32m 211\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mCONFIG_KEY_CALL\u001B[39;49m\u001B[30;43m:\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mpartial\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 212\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m_call\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 213\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mweakref\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mref\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mt\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 214\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mretry_policy\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 215\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mfutures\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mweakref\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mref\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfutures\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 216\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mschedule_task\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 217\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43msubmit\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43msubmit\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 218\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 219\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m}\u001B[39;49m\u001B[30;43m,\u001B[39;49m\n\u001B[32m 220\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 221\u001B[39m \u001B[38;5;28mself\u001B[39m.commit(t, \u001B[38;5;28;01mNone\u001B[39;00m)\n\u001B[32m 222\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m exc:\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/pregel/_retry.py:585\u001B[39m, in \u001B[36mrun_with_retry\u001B[39m\u001B[34m(task, retry_policy, configurable)\u001B[39m\n\u001B[32m 583\u001B[39m task.writes.clear()\n\u001B[32m 584\u001B[39m \u001B[38;5;66;03m# run the task\u001B[39;00m\n\u001B[32m--> \u001B[39m\u001B[32m585\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mtask\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mproc\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtask\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 586\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m ParentCommand \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 587\u001B[39m ns: \u001B[38;5;28mstr\u001B[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/_internal/_runnable.py:684\u001B[39m, in \u001B[36mRunnableSeq.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 682\u001B[39m \u001B[38;5;66;03m# run in context\u001B[39;00m\n\u001B[32m 683\u001B[39m \u001B[38;5;28;01mwith\u001B[39;00m set_config_context(config, run) \u001B[38;5;28;01mas\u001B[39;00m context:\n\u001B[32m--> \u001B[39m\u001B[32m684\u001B[39m \u001B[38;5;28minput\u001B[39m = \u001B[30;43mcontext\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mstep\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 685\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 686\u001B[39m \u001B[38;5;28minput\u001B[39m = step.invoke(\u001B[38;5;28minput\u001B[39m, config)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/_internal/_runnable.py:426\u001B[39m, in \u001B[36mRunnableCallable.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 424\u001B[39m run_manager.on_chain_end(ret)\n\u001B[32m 425\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m426\u001B[39m ret = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfunc\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 427\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m.recurse \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(ret, Runnable):\n\u001B[32m 428\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ret.invoke(\u001B[38;5;28minput\u001B[39m, config)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:822\u001B[39m, in \u001B[36mToolNode._func\u001B[39m\u001B[34m(self, input, config, runtime)\u001B[39m\n\u001B[32m 820\u001B[39m input_types = [input_type] * \u001B[38;5;28mlen\u001B[39m(tool_calls)\n\u001B[32m 821\u001B[39m \u001B[38;5;28;01mwith\u001B[39;00m get_executor_for_config(config) \u001B[38;5;28;01mas\u001B[39;00m executor:\n\u001B[32m--> \u001B[39m\u001B[32m822\u001B[39m outputs = \u001B[30;43mlist\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 823\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mexecutor\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mmap\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_run_one\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mtool_calls\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput_types\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mtool_runtimes\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 824\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 826\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m._combine_tool_outputs(outputs, input_type)\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:619\u001B[39m, in \u001B[36mExecutor.map..result_iterator\u001B[39m\u001B[34m()\u001B[39m\n\u001B[32m 616\u001B[39m \u001B[38;5;28;01mwhile\u001B[39;00m fs:\n\u001B[32m 617\u001B[39m \u001B[38;5;66;03m# Careful not to keep a reference to the popped future\u001B[39;00m\n\u001B[32m 618\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m timeout \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m619\u001B[39m \u001B[38;5;28;01myield\u001B[39;00m \u001B[30;43m_result_or_cancel\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfs\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mpop\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 620\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 621\u001B[39m \u001B[38;5;28;01myield\u001B[39;00m _result_or_cancel(fs.pop(), end_time - time.monotonic())\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:317\u001B[39m, in \u001B[36m_result_or_cancel\u001B[39m\u001B[34m(***failed resolving arguments***)\u001B[39m\n\u001B[32m 315\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m 316\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m317\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mfut\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mresult\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtimeout\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 318\u001B[39m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[32m 319\u001B[39m fut.cancel()\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:456\u001B[39m, in \u001B[36mFuture.result\u001B[39m\u001B[34m(self, timeout)\u001B[39m\n\u001B[32m 454\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m CancelledError()\n\u001B[32m 455\u001B[39m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._state == FINISHED:\n\u001B[32m--> \u001B[39m\u001B[32m456\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m__get_result\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 457\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 458\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mTimeoutError\u001B[39;00m()\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/_base.py:401\u001B[39m, in \u001B[36mFuture.__get_result\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 399\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._exception:\n\u001B[32m 400\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m401\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;28mself\u001B[39m._exception\n\u001B[32m 402\u001B[39m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[32m 403\u001B[39m \u001B[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001B[39;00m\n\u001B[32m 404\u001B[39m \u001B[38;5;28mself\u001B[39m = \u001B[38;5;28;01mNone\u001B[39;00m\n", - "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.12/concurrent/futures/thread.py:58\u001B[39m, in \u001B[36m_WorkItem.run\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 55\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m\n\u001B[32m 57\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m---> \u001B[39m\u001B[32m58\u001B[39m result = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfn\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 59\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mBaseException\u001B[39;00m \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 60\u001B[39m \u001B[38;5;28mself\u001B[39m.future.set_exception(exc)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/runnables/config.py:610\u001B[39m, in \u001B[36mContextThreadPoolExecutor.map.._wrapped_fn\u001B[39m\u001B[34m(*args)\u001B[39m\n\u001B[32m 609\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34m_wrapped_fn\u001B[39m(*args: Any) -> T:\n\u001B[32m--> \u001B[39m\u001B[32m610\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mcontexts\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mpop\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mfn\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:1046\u001B[39m, in \u001B[36mToolNode._run_one\u001B[39m\u001B[34m(self, call, input_type, tool_runtime)\u001B[39m\n\u001B[32m 1042\u001B[39m config = tool_runtime.config\n\u001B[32m 1044\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._wrap_tool_call \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[32m 1045\u001B[39m \u001B[38;5;66;03m# No wrapper - execute directly\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m1046\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_execute_tool_sync\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtool_request\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43minput_type\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 1048\u001B[39m \u001B[38;5;66;03m# Define execute callable that can be called multiple times\u001B[39;00m\n\u001B[32m 1049\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34mexecute\u001B[39m(req: ToolCallRequest) -> ToolMessage | Command:\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:1006\u001B[39m, in \u001B[36mToolNode._execute_tool_sync\u001B[39m\u001B[34m(self, request, input_type, config)\u001B[39m\n\u001B[32m 1003\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m\n\u001B[32m 1005\u001B[39m \u001B[38;5;66;03m# Error is handled - create error ToolMessage\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m1006\u001B[39m content = \u001B[30;43m_handle_tool_error\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43me\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mflag\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_handle_tool_errors\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 1007\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ToolMessage(\n\u001B[32m 1008\u001B[39m content=content,\n\u001B[32m 1009\u001B[39m name=call[\u001B[33m\"\u001B[39m\u001B[33mname\u001B[39m\u001B[33m\"\u001B[39m],\n\u001B[32m 1010\u001B[39m tool_call_id=call[\u001B[33m\"\u001B[39m\u001B[33mid\u001B[39m\u001B[33m\"\u001B[39m],\n\u001B[32m 1011\u001B[39m status=\u001B[33m\"\u001B[39m\u001B[33merror\u001B[39m\u001B[33m\"\u001B[39m,\n\u001B[32m 1012\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:434\u001B[39m, in \u001B[36m_handle_tool_error\u001B[39m\u001B[34m(e, flag)\u001B[39m\n\u001B[32m 432\u001B[39m content = flag\n\u001B[32m 433\u001B[39m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28mcallable\u001B[39m(flag):\n\u001B[32m--> \u001B[39m\u001B[32m434\u001B[39m content = \u001B[30;43mflag\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43me\u001B[39;49m\u001B[30;43m)\u001B[39;49m \u001B[38;5;66;03m# type: ignore [assignment, call-arg]\u001B[39;00m\n\u001B[32m 435\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 436\u001B[39m msg = (\n\u001B[32m 437\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mGot unexpected type of `handle_tool_error`. Expected bool, str \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 438\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mor callable. Received: \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mflag\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m\"\u001B[39m\n\u001B[32m 439\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:391\u001B[39m, in \u001B[36m_default_handle_tool_errors\u001B[39m\u001B[34m(e)\u001B[39m\n\u001B[32m 389\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(e, ToolInvocationError):\n\u001B[32m 390\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m e.message\n\u001B[32m--> \u001B[39m\u001B[32m391\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m e\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langgraph/prebuilt/tool_node.py:958\u001B[39m, in \u001B[36mToolNode._execute_tool_sync\u001B[39m\u001B[34m(self, request, input_type, config)\u001B[39m\n\u001B[32m 956\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m 957\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m958\u001B[39m response = \u001B[30;43mtool\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43minvoke\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mcall_args\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mconfig\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 959\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m ValidationError \u001B[38;5;28;01mas\u001B[39;00m exc:\n\u001B[32m 960\u001B[39m \u001B[38;5;66;03m# Filter out errors for injected arguments\u001B[39;00m\n\u001B[32m 961\u001B[39m injected = \u001B[38;5;28mself\u001B[39m._injected_args.get(call[\u001B[33m\"\u001B[39m\u001B[33mname\u001B[39m\u001B[33m\"\u001B[39m])\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:642\u001B[39m, in \u001B[36mBaseTool.invoke\u001B[39m\u001B[34m(self, input, config, **kwargs)\u001B[39m\n\u001B[32m 634\u001B[39m \u001B[38;5;129m@override\u001B[39m\n\u001B[32m 635\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34minvoke\u001B[39m(\n\u001B[32m 636\u001B[39m \u001B[38;5;28mself\u001B[39m,\n\u001B[32m (...)\u001B[39m\u001B[32m 639\u001B[39m **kwargs: Any,\n\u001B[32m 640\u001B[39m ) -> Any:\n\u001B[32m 641\u001B[39m tool_input, kwargs = _prep_run_args(\u001B[38;5;28minput\u001B[39m, config, **kwargs)\n\u001B[32m--> \u001B[39m\u001B[32m642\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mtool_input\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:1001\u001B[39m, in \u001B[36mBaseTool.run\u001B[39m\u001B[34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[39m\n\u001B[32m 999\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m error_to_raise:\n\u001B[32m 1000\u001B[39m run_manager.on_tool_error(error_to_raise, tool_call_id=tool_call_id)\n\u001B[32m-> \u001B[39m\u001B[32m1001\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m error_to_raise\n\u001B[32m 1002\u001B[39m output = _format_output(content, artifact, tool_call_id, \u001B[38;5;28mself\u001B[39m.name, status)\n\u001B[32m 1003\u001B[39m run_manager.on_tool_end(output, color=color, name=\u001B[38;5;28mself\u001B[39m.name, **kwargs)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_core/tools/base.py:967\u001B[39m, in \u001B[36mBaseTool.run\u001B[39m\u001B[34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001B[39m\n\u001B[32m 965\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m config_param := _get_runnable_config_param(\u001B[38;5;28mself\u001B[39m._run):\n\u001B[32m 966\u001B[39m tool_kwargs |= {config_param: config}\n\u001B[32m--> \u001B[39m\u001B[32m967\u001B[39m response = \u001B[30;43mcontext\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_run\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mtool_args\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mtool_kwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 968\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m.response_format == \u001B[33m\"\u001B[39m\u001B[33mcontent_and_artifact\u001B[39m\u001B[33m\"\u001B[39m:\n\u001B[32m 969\u001B[39m msg = (\n\u001B[32m 970\u001B[39m \u001B[33m\"\u001B[39m\u001B[33mSince response_format=\u001B[39m\u001B[33m'\u001B[39m\u001B[33mcontent_and_artifact\u001B[39m\u001B[33m'\u001B[39m\u001B[33m \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 971\u001B[39m \u001B[33m\"\u001B[39m\u001B[33ma two-tuple of the message content and raw tool output is \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 972\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mexpected. Instead, generated response is of type: \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 973\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[38;5;132;01m{\u001B[39;00m\u001B[38;5;28mtype\u001B[39m(response)\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m.\u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 974\u001B[39m )\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_community/tools/wikipedia/tool.py:38\u001B[39m, in \u001B[36mWikipediaQueryRun._run\u001B[39m\u001B[34m(self, query, run_manager)\u001B[39m\n\u001B[32m 32\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34m_run\u001B[39m(\n\u001B[32m 33\u001B[39m \u001B[38;5;28mself\u001B[39m,\n\u001B[32m 34\u001B[39m query: \u001B[38;5;28mstr\u001B[39m,\n\u001B[32m 35\u001B[39m run_manager: Optional[CallbackManagerForToolRun] = \u001B[38;5;28;01mNone\u001B[39;00m,\n\u001B[32m 36\u001B[39m ) -> \u001B[38;5;28mstr\u001B[39m:\n\u001B[32m 37\u001B[39m \u001B[38;5;250m \u001B[39m\u001B[33;03m\"\"\"Use the Wikipedia tool.\"\"\"\u001B[39;00m\n\u001B[32m---> \u001B[39m\u001B[32m38\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mapi_wrapper\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mrun\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mquery\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/langchain_community/utilities/wikipedia.py:53\u001B[39m, in \u001B[36mWikipediaAPIWrapper.run\u001B[39m\u001B[34m(self, query)\u001B[39m\n\u001B[32m 51\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34mrun\u001B[39m(\u001B[38;5;28mself\u001B[39m, query: \u001B[38;5;28mstr\u001B[39m) -> \u001B[38;5;28mstr\u001B[39m:\n\u001B[32m 52\u001B[39m \u001B[38;5;250m \u001B[39m\u001B[33;03m\"\"\"Run Wikipedia search and get page summaries.\"\"\"\u001B[39;00m\n\u001B[32m---> \u001B[39m\u001B[32m53\u001B[39m page_titles = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mwiki_client\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43msearch\u001B[39;49m\u001B[30;43m(\u001B[39;49m\n\u001B[32m 54\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43mquery\u001B[39;49m\u001B[30;43m[\u001B[39;49m\u001B[30;43m:\u001B[39;49m\u001B[30;43mWIKIPEDIA_MAX_QUERY_LENGTH\u001B[39;49m\u001B[30;43m]\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mresults\u001B[39;49m\u001B[30;43m=\u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mtop_k_results\u001B[39;49m\n\u001B[32m 55\u001B[39m \u001B[30;43m \u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 56\u001B[39m summaries = []\n\u001B[32m 57\u001B[39m \u001B[38;5;28;01mfor\u001B[39;00m page_title \u001B[38;5;129;01min\u001B[39;00m page_titles[: \u001B[38;5;28mself\u001B[39m.top_k_results]:\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/wikipedia/util.py:28\u001B[39m, in \u001B[36mcache.__call__\u001B[39m\u001B[34m(self, *args, **kwargs)\u001B[39m\n\u001B[32m 26\u001B[39m ret = \u001B[38;5;28mself\u001B[39m._cache[key]\n\u001B[32m 27\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m---> \u001B[39m\u001B[32m28\u001B[39m ret = \u001B[38;5;28mself\u001B[39m._cache[key] = \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mfn\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43margs\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43m*\u001B[39;49m\u001B[30;43mkwargs\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 30\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ret\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/wikipedia/wikipedia.py:103\u001B[39m, in \u001B[36msearch\u001B[39m\u001B[34m(query, results, suggestion)\u001B[39m\n\u001B[32m 100\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m suggestion:\n\u001B[32m 101\u001B[39m search_params[\u001B[33m'\u001B[39m\u001B[33msrinfo\u001B[39m\u001B[33m'\u001B[39m] = \u001B[33m'\u001B[39m\u001B[33msuggestion\u001B[39m\u001B[33m'\u001B[39m\n\u001B[32m--> \u001B[39m\u001B[32m103\u001B[39m raw_results = \u001B[30;43m_wiki_request\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43msearch_params\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m 105\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[33m'\u001B[39m\u001B[33merror\u001B[39m\u001B[33m'\u001B[39m \u001B[38;5;129;01min\u001B[39;00m raw_results:\n\u001B[32m 106\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m raw_results[\u001B[33m'\u001B[39m\u001B[33merror\u001B[39m\u001B[33m'\u001B[39m][\u001B[33m'\u001B[39m\u001B[33minfo\u001B[39m\u001B[33m'\u001B[39m] \u001B[38;5;129;01min\u001B[39;00m (\u001B[33m'\u001B[39m\u001B[33mHTTP request timed out.\u001B[39m\u001B[33m'\u001B[39m, \u001B[33m'\u001B[39m\u001B[33mPool queue is full\u001B[39m\u001B[33m'\u001B[39m):\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/wikipedia/wikipedia.py:742\u001B[39m, in \u001B[36m_wiki_request\u001B[39m\u001B[34m(params)\u001B[39m\n\u001B[32m 739\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m RATE_LIMIT:\n\u001B[32m 740\u001B[39m RATE_LIMIT_LAST_CALL = datetime.now()\n\u001B[32m--> \u001B[39m\u001B[32m742\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[30;43mr\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mjson\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Source/.venv/lib/python3.12/site-packages/requests/models.py:1120\u001B[39m, in \u001B[36mResponse.json\u001B[39m\u001B[34m(self, **kwargs)\u001B[39m\n\u001B[32m 1116\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m complexjson.loads(\u001B[38;5;28mself\u001B[39m.text, **kwargs)\n\u001B[32m 1117\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m JSONDecodeError \u001B[38;5;28;01mas\u001B[39;00m e:\n\u001B[32m 1118\u001B[39m \u001B[38;5;66;03m# Catch JSON-related errors and raise as requests.JSONDecodeError\u001B[39;00m\n\u001B[32m 1119\u001B[39m \u001B[38;5;66;03m# This aliases json.JSONDecodeError and simplejson.JSONDecodeError\u001B[39;00m\n\u001B[32m-> \u001B[39m\u001B[32m1120\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m RequestsJSONDecodeError(e.msg, e.doc, e.pos)\n", - "\u001B[31mJSONDecodeError\u001B[39m: Expecting value: line 1 column 1 (char 0)", - "During task with name 'tools' and id '66df14bd-4912-9076-b765-1b23f959852b'" + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "포켓몬스터 언제 출시?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'wikipedia', 'args': {'query': '포켓몬스터 출시일'}, 'id': 'bc0010c4-1bad-4ace-a136-7973b7ad7252', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "No good Wikipedia Search Result was found\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "포켓몬스터는 1996 년 12 월 1 일 일본에서 처음 출시되었습니다.\n", + "\n", + "초기 버전은 게임보이 (Game Boy) 를 기반으로 한 '포켓몬스터 레드/블랙'이었습니다. 이후 1998 년에 닌텐도 64 를 기반으로 한 3D 버전인 '포켓몬스터 골드/실버'가 출시되었으며, 2002 년에는 포켓몬스터의 세계적 인기를 끌면서 다양한 플랫폼과 버전으로 출시되었습니다.\n", + "\n", + "한국에서는 2002 년 10 월 1 일 '포켓몬스터 레드/블랙'이 처음 출시되었습니다.\n", + "[]\n" ] } ], - "execution_count": 128 + "execution_count": 63 }, { "metadata": { "ExecuteTime": { - "end_time": "2026-06-04T08:58:11.748765296Z", - "start_time": "2026-06-04T08:58:08.860776093Z" + "end_time": "2026-06-05T01:57:41.131433152Z", + "start_time": "2026-06-05T01:57:09.722885275Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_community.utilities import WikipediaAPIWrapper\n", + "from langchain_community.tools import WikipediaQueryRun\n", + "from langchain_core.tools import Tool\n", + "\n", + "tools = WikipediaQueryRun(api_wrapper = WikipediaAPIWrapper())\n", + "\n", + "print(tools.name)\n", + "print(tools.description)\n", + "\n", + "agent = create_agent(\n", + " model = qwen_llm,\n", + " tools = [tools],\n", + " system_prompt=\"\"\"\n", + "당신은 도구를 사용할 수 있습니다.\n", + "\n", + "사용자의 질문에서 검색어를 추출할 때\n", + "원문을 절대 수정하지 마세요.\n", + "\n", + "예시:\n", + "\n", + "질문:\n", + "앨런 튜링은 누구인가요?\n", + "\n", + "Tool:\n", + "wikipedia(query=\"앨런 튜링\")\n", + "\"\"\",\n", + " debug=True\n", + ")\n", + "\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\":\"user\",\n", + " \"content\": \"앨런 튜링은 누구인가요?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "\n", + "print_result(result)\n", + "\n", + "# print_result(result)" + ], + "id": "a512f1819e7f22c2", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "wikipedia\n", + "A wrapper around Wikipedia. Useful for when you need to answer general questions about people, places, companies, facts, historical events, or other subjects. Input should be a search query.\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f')]}\n", + "\u001B[1m[updates]\u001B[0m {'model': {'messages': [AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492})]}}\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492})]}\n", + "\u001B[1m[updates]\u001B[0m {'tools': {'messages': [ToolMessage(content='No good Wikipedia Search Result was found', name='wikipedia', id='ca29abe3-a97d-4223-9ba3-a75c19b127d3', tool_call_id='a44dec9b-b241-4abc-93fd-ef2622cfbf13')]}}\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492}), ToolMessage(content='No good Wikipedia Search Result was found', name='wikipedia', id='ca29abe3-a97d-4223-9ba3-a75c19b127d3', tool_call_id='a44dec9b-b241-4abc-93fd-ef2622cfbf13')]}\n", + "\u001B[1m[updates]\u001B[0m {'model': {'messages': [AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:15.942750181Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1825170203, 'load_duration': 178324593, 'prompt_eval_count': 430, 'prompt_eval_duration': 567584861, 'eval_count': 42, 'eval_duration': 1051467600, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-b104-7c71-b64b-3acdff67d776-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': 'Alan Turing'}, 'id': 'f01ca164-2a41-463e-9b42-9d0d26e814c9', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 430, 'output_tokens': 42, 'total_tokens': 472})]}}\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492}), ToolMessage(content='No good Wikipedia Search Result was found', name='wikipedia', id='ca29abe3-a97d-4223-9ba3-a75c19b127d3', tool_call_id='a44dec9b-b241-4abc-93fd-ef2622cfbf13'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:15.942750181Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1825170203, 'load_duration': 178324593, 'prompt_eval_count': 430, 'prompt_eval_duration': 567584861, 'eval_count': 42, 'eval_duration': 1051467600, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-b104-7c71-b64b-3acdff67d776-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': 'Alan Turing'}, 'id': 'f01ca164-2a41-463e-9b42-9d0d26e814c9', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 430, 'output_tokens': 42, 'total_tokens': 472})]}\n", + "\u001B[1m[updates]\u001B[0m {'tools': {'messages': [ToolMessage(content='Page: Alan Turing\\nSummary: Alan Mathison Turing (; 23 June 1912 – 7 June 1954) was an English mathematician, computer scientist, logician, cryptanalyst, philosopher and theoretical biologist. He was highly influential in the development of theoretical computer science, providing a formalisation of the concepts of algorithm and computation with the Turing machine, which can be considered a model of a general-purpose computer. Turing is widely considered to be the father of theoretical computer science.\\nBorn in London, Turing was raised in southern England. He graduated from King\\'s College, Cambridge, and in 1938, earned a doctorate degree from Princeton University. During World War II, Turing worked for the Government Code and Cypher School at Bletchley Park, Britain\\'s codebreaking centre that produced Ultra intelligence. He led Hut 8, the section responsible for German naval cryptanalysis. Turing devised techniques for speeding the breaking of German ciphers, including improvements to the pre-war Polish bomba method, an electromechanical machine that could find settings for the Enigma machine. He played a crucial role in cracking intercepted messages that enabled the Allies to defeat the Axis powers in the Battle of the Atlantic and other engagements.\\nAfter the war, Turing worked at the National Physical Laboratory, where he designed the Automatic Computing Engine, one of the first designs for a stored-program computer. In 1948, Turing joined Max Newman\\'s Computing Machine Laboratory at the University of Manchester, where he contributed to the development of early Manchester computers and became interested in mathematical biology. Turing wrote on the chemical basis of morphogenesis and predicted oscillating chemical reactions such as the Belousov–Zhabotinsky reaction, first observed in the 1960s. Despite these accomplishments, he was never fully recognised during his lifetime because much of his work was covered by the Official Secrets Act.\\nIn 1952, Turing was prosecuted for homosexual acts. He accepted hormone treatment, a procedure commonly referred to as chemical castration, as an alternative to prison. Turing died on 7 June 1954, aged 41, from cyanide poisoning. An inquest determined his death as suicide, but the evidence is also consistent with accidental poisoning. Following a campaign in 2009, British prime minister Gordon Brown made an official public apology for \"the appalling way [Turing] was treated\". Queen Elizabeth II granted a pardon in 2013. The term \"Alan Turing law\" is used informally to refer to a 2017 law in the UK that retroactively pardoned men cautioned or convicted under historical legislation that outlawed homosexual acts.\\nTuring left an extensive legacy in mathematics and computing which has become widely recognised with statues and many things named after him, including an annual award for computing innovation. His portrait appears on the Bank of England £50 note, first released on 23 June 2021 to coincide with his birthday. The audience vote in a 2019 BBC series named Turing the greatest scientist of the 20th century.\\nThe cognitive scientist Douglas Hofstadter writes:\\n\\nAtheist, homosexual, eccentric, marathon-running mathematician, A. M. Turing was in large part responsible not only for the concept of computers, incisive theorems about their powers, and a clear vision of the possibility of computer minds, but also for the cracking of German ciphers during the Second World War. It is fair to say we owe much to Alan Turing for the fact that we are not under Nazi rule today.\"\\n\\n\\n\\nPage: Turing Award\\nSummary: The ACM A. M. Turing Award is an annual prize given by the Association for Computing Machinery (ACM) for contributions of lasting and major technical importance to computer science. It is generally recognized as the highest distinction in the field of computer science and is often referred to as the \"Nobel Prize of Computing\". As of 2026, 81 people have been awarded the prize, with the most recent rec', name='wikipedia', id='85516040-5432-4ef7-8447-a8df65444ded', tool_call_id='f01ca164-2a41-463e-9b42-9d0d26e814c9')]}}\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492}), ToolMessage(content='No good Wikipedia Search Result was found', name='wikipedia', id='ca29abe3-a97d-4223-9ba3-a75c19b127d3', tool_call_id='a44dec9b-b241-4abc-93fd-ef2622cfbf13'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:15.942750181Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1825170203, 'load_duration': 178324593, 'prompt_eval_count': 430, 'prompt_eval_duration': 567584861, 'eval_count': 42, 'eval_duration': 1051467600, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-b104-7c71-b64b-3acdff67d776-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': 'Alan Turing'}, 'id': 'f01ca164-2a41-463e-9b42-9d0d26e814c9', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 430, 'output_tokens': 42, 'total_tokens': 472}), ToolMessage(content='Page: Alan Turing\\nSummary: Alan Mathison Turing (; 23 June 1912 – 7 June 1954) was an English mathematician, computer scientist, logician, cryptanalyst, philosopher and theoretical biologist. He was highly influential in the development of theoretical computer science, providing a formalisation of the concepts of algorithm and computation with the Turing machine, which can be considered a model of a general-purpose computer. Turing is widely considered to be the father of theoretical computer science.\\nBorn in London, Turing was raised in southern England. He graduated from King\\'s College, Cambridge, and in 1938, earned a doctorate degree from Princeton University. During World War II, Turing worked for the Government Code and Cypher School at Bletchley Park, Britain\\'s codebreaking centre that produced Ultra intelligence. He led Hut 8, the section responsible for German naval cryptanalysis. Turing devised techniques for speeding the breaking of German ciphers, including improvements to the pre-war Polish bomba method, an electromechanical machine that could find settings for the Enigma machine. He played a crucial role in cracking intercepted messages that enabled the Allies to defeat the Axis powers in the Battle of the Atlantic and other engagements.\\nAfter the war, Turing worked at the National Physical Laboratory, where he designed the Automatic Computing Engine, one of the first designs for a stored-program computer. In 1948, Turing joined Max Newman\\'s Computing Machine Laboratory at the University of Manchester, where he contributed to the development of early Manchester computers and became interested in mathematical biology. Turing wrote on the chemical basis of morphogenesis and predicted oscillating chemical reactions such as the Belousov–Zhabotinsky reaction, first observed in the 1960s. Despite these accomplishments, he was never fully recognised during his lifetime because much of his work was covered by the Official Secrets Act.\\nIn 1952, Turing was prosecuted for homosexual acts. He accepted hormone treatment, a procedure commonly referred to as chemical castration, as an alternative to prison. Turing died on 7 June 1954, aged 41, from cyanide poisoning. An inquest determined his death as suicide, but the evidence is also consistent with accidental poisoning. Following a campaign in 2009, British prime minister Gordon Brown made an official public apology for \"the appalling way [Turing] was treated\". Queen Elizabeth II granted a pardon in 2013. The term \"Alan Turing law\" is used informally to refer to a 2017 law in the UK that retroactively pardoned men cautioned or convicted under historical legislation that outlawed homosexual acts.\\nTuring left an extensive legacy in mathematics and computing which has become widely recognised with statues and many things named after him, including an annual award for computing innovation. His portrait appears on the Bank of England £50 note, first released on 23 June 2021 to coincide with his birthday. The audience vote in a 2019 BBC series named Turing the greatest scientist of the 20th century.\\nThe cognitive scientist Douglas Hofstadter writes:\\n\\nAtheist, homosexual, eccentric, marathon-running mathematician, A. M. Turing was in large part responsible not only for the concept of computers, incisive theorems about their powers, and a clear vision of the possibility of computer minds, but also for the cracking of German ciphers during the Second World War. It is fair to say we owe much to Alan Turing for the fact that we are not under Nazi rule today.\"\\n\\n\\n\\nPage: Turing Award\\nSummary: The ACM A. M. Turing Award is an annual prize given by the Association for Computing Machinery (ACM) for contributions of lasting and major technical importance to computer science. It is generally recognized as the highest distinction in the field of computer science and is often referred to as the \"Nobel Prize of Computing\". As of 2026, 81 people have been awarded the prize, with the most recent rec', name='wikipedia', id='85516040-5432-4ef7-8447-a8df65444ded', tool_call_id='f01ca164-2a41-463e-9b42-9d0d26e814c9')]}\n", + "\u001B[1m[updates]\u001B[0m {'model': {'messages': [AIMessage(content='앨런 튜링 (Alan Turing, 1912-1954) 은 영국의 수학자, 컴퓨터 과학자, 논리학자, 암호학자, 철학자이자 이론 생물학자입니다. 그는 이론 컴퓨터 과학의 아버지로 널리 여겨집니다.\\n\\n주요 업적과 생애:\\n\\n1. **출생과 학력**: 1912 년 6 월 23 일 런던에서 태어났으며, 킹스 칼리지 캠브리지에서 졸업하고 1938 년 프린스턴 대학교에서 박사 학위를 취득했습니다.\\n\\n2. **제 2 차 세계대전**: 영국 암호 해독 센터인 블레츠키 파크에서 근무하며 독일 해군 암호 해독에 주도적인 역할을 했습니다. 튜링은 Enigma 기기의 해독을 위한 기술 개발에 기여했으며, 연합군의 승리에 결정적인 역할을 했습니다.\\n\\n3. **컴퓨터 과학**: 전쟁 후 자동 계산 엔진 (Automatic Computing Engine) 을 설계했으며, 맨체스터 대학교에서 초기 컴퓨터 개발에 기여했습니다.\\n\\n4. **개인적 문제**: 1952 년 동성애 혐의로 고소당했으며, 화학적 중성화 치료를 받았습니다. 1954 년 6 월 7 일 시안화소 독으로 자살로 사망했습니다.\\n\\n5. **후세**: 2019 년 영국 총리 Gordon Brown 가 공식 사과를 발표했으며, 2013 년 엘리자베스 2 세 여왕이 사면했습니다. 그의 초상이 영국 은행 50 파운드 지폐에 등장했습니다.\\n\\n튜링은 컴퓨터 과학 분야에서 가장 높은 영예인 튜링 상을 수여받는 사람이며, 그의 업적은 수학, 컴퓨터 과학, 암호학 등 다양한 분야에서 널리 인정받고 있습니다.', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:41.059279459Z', 'done': True, 'done_reason': 'stop', 'total_duration': 20698831970, 'load_duration': 202482449, 'prompt_eval_count': 1309, 'prompt_eval_duration': 1782329646, 'eval_count': 703, 'eval_duration': 18216076422, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-c966-79e2-8b35-f0312770134b-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 1309, 'output_tokens': 703, 'total_tokens': 2012})]}}\n", + "\u001B[1m[values]\u001B[0m {'messages': [HumanMessage(content='앨런 튜링은 누구인가요?', additional_kwargs={}, response_metadata={}, id='512c1e60-bd64-4f37-b7fb-16438af2287f'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:13.468490953Z', 'done': True, 'done_reason': 'stop', 'total_duration': 3708649157, 'load_duration': 234271103, 'prompt_eval_count': 377, 'prompt_eval_duration': 497933805, 'eval_count': 115, 'eval_duration': 2907256566, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-9fff-7853-aa59-0c77c8bb6d7b-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 377, 'output_tokens': 115, 'total_tokens': 492}), ToolMessage(content='No good Wikipedia Search Result was found', name='wikipedia', id='ca29abe3-a97d-4223-9ba3-a75c19b127d3', tool_call_id='a44dec9b-b241-4abc-93fd-ef2622cfbf13'), AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:15.942750181Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1825170203, 'load_duration': 178324593, 'prompt_eval_count': 430, 'prompt_eval_duration': 567584861, 'eval_count': 42, 'eval_duration': 1051467600, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-b104-7c71-b64b-3acdff67d776-0', tool_calls=[{'name': 'wikipedia', 'args': {'query': 'Alan Turing'}, 'id': 'f01ca164-2a41-463e-9b42-9d0d26e814c9', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 430, 'output_tokens': 42, 'total_tokens': 472}), ToolMessage(content='Page: Alan Turing\\nSummary: Alan Mathison Turing (; 23 June 1912 – 7 June 1954) was an English mathematician, computer scientist, logician, cryptanalyst, philosopher and theoretical biologist. He was highly influential in the development of theoretical computer science, providing a formalisation of the concepts of algorithm and computation with the Turing machine, which can be considered a model of a general-purpose computer. Turing is widely considered to be the father of theoretical computer science.\\nBorn in London, Turing was raised in southern England. He graduated from King\\'s College, Cambridge, and in 1938, earned a doctorate degree from Princeton University. During World War II, Turing worked for the Government Code and Cypher School at Bletchley Park, Britain\\'s codebreaking centre that produced Ultra intelligence. He led Hut 8, the section responsible for German naval cryptanalysis. Turing devised techniques for speeding the breaking of German ciphers, including improvements to the pre-war Polish bomba method, an electromechanical machine that could find settings for the Enigma machine. He played a crucial role in cracking intercepted messages that enabled the Allies to defeat the Axis powers in the Battle of the Atlantic and other engagements.\\nAfter the war, Turing worked at the National Physical Laboratory, where he designed the Automatic Computing Engine, one of the first designs for a stored-program computer. In 1948, Turing joined Max Newman\\'s Computing Machine Laboratory at the University of Manchester, where he contributed to the development of early Manchester computers and became interested in mathematical biology. Turing wrote on the chemical basis of morphogenesis and predicted oscillating chemical reactions such as the Belousov–Zhabotinsky reaction, first observed in the 1960s. Despite these accomplishments, he was never fully recognised during his lifetime because much of his work was covered by the Official Secrets Act.\\nIn 1952, Turing was prosecuted for homosexual acts. He accepted hormone treatment, a procedure commonly referred to as chemical castration, as an alternative to prison. Turing died on 7 June 1954, aged 41, from cyanide poisoning. An inquest determined his death as suicide, but the evidence is also consistent with accidental poisoning. Following a campaign in 2009, British prime minister Gordon Brown made an official public apology for \"the appalling way [Turing] was treated\". Queen Elizabeth II granted a pardon in 2013. The term \"Alan Turing law\" is used informally to refer to a 2017 law in the UK that retroactively pardoned men cautioned or convicted under historical legislation that outlawed homosexual acts.\\nTuring left an extensive legacy in mathematics and computing which has become widely recognised with statues and many things named after him, including an annual award for computing innovation. His portrait appears on the Bank of England £50 note, first released on 23 June 2021 to coincide with his birthday. The audience vote in a 2019 BBC series named Turing the greatest scientist of the 20th century.\\nThe cognitive scientist Douglas Hofstadter writes:\\n\\nAtheist, homosexual, eccentric, marathon-running mathematician, A. M. Turing was in large part responsible not only for the concept of computers, incisive theorems about their powers, and a clear vision of the possibility of computer minds, but also for the cracking of German ciphers during the Second World War. It is fair to say we owe much to Alan Turing for the fact that we are not under Nazi rule today.\"\\n\\n\\n\\nPage: Turing Award\\nSummary: The ACM A. M. Turing Award is an annual prize given by the Association for Computing Machinery (ACM) for contributions of lasting and major technical importance to computer science. It is generally recognized as the highest distinction in the field of computer science and is often referred to as the \"Nobel Prize of Computing\". As of 2026, 81 people have been awarded the prize, with the most recent rec', name='wikipedia', id='85516040-5432-4ef7-8447-a8df65444ded', tool_call_id='f01ca164-2a41-463e-9b42-9d0d26e814c9'), AIMessage(content='앨런 튜링 (Alan Turing, 1912-1954) 은 영국의 수학자, 컴퓨터 과학자, 논리학자, 암호학자, 철학자이자 이론 생물학자입니다. 그는 이론 컴퓨터 과학의 아버지로 널리 여겨집니다.\\n\\n주요 업적과 생애:\\n\\n1. **출생과 학력**: 1912 년 6 월 23 일 런던에서 태어났으며, 킹스 칼리지 캠브리지에서 졸업하고 1938 년 프린스턴 대학교에서 박사 학위를 취득했습니다.\\n\\n2. **제 2 차 세계대전**: 영국 암호 해독 센터인 블레츠키 파크에서 근무하며 독일 해군 암호 해독에 주도적인 역할을 했습니다. 튜링은 Enigma 기기의 해독을 위한 기술 개발에 기여했으며, 연합군의 승리에 결정적인 역할을 했습니다.\\n\\n3. **컴퓨터 과학**: 전쟁 후 자동 계산 엔진 (Automatic Computing Engine) 을 설계했으며, 맨체스터 대학교에서 초기 컴퓨터 개발에 기여했습니다.\\n\\n4. **개인적 문제**: 1952 년 동성애 혐의로 고소당했으며, 화학적 중성화 치료를 받았습니다. 1954 년 6 월 7 일 시안화소 독으로 자살로 사망했습니다.\\n\\n5. **후세**: 2019 년 영국 총리 Gordon Brown 가 공식 사과를 발표했으며, 2013 년 엘리자베스 2 세 여왕이 사면했습니다. 그의 초상이 영국 은행 50 파운드 지폐에 등장했습니다.\\n\\n튜링은 컴퓨터 과학 분야에서 가장 높은 영예인 튜링 상을 수여받는 사람이며, 그의 업적은 수학, 컴퓨터 과학, 암호학 등 다양한 분야에서 널리 인정받고 있습니다.', additional_kwargs={}, response_metadata={'model': 'qwen3.5:4b', 'created_at': '2026-06-05T01:57:41.059279459Z', 'done': True, 'done_reason': 'stop', 'total_duration': 20698831970, 'load_duration': 202482449, 'prompt_eval_count': 1309, 'prompt_eval_duration': 1782329646, 'eval_count': 703, 'eval_duration': 18216076422, 'logprobs': None, 'model_name': 'qwen3.5:4b', 'model_provider': 'ollama'}, id='lc_run--019e957f-c966-79e2-8b35-f0312770134b-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 1309, 'output_tokens': 703, 'total_tokens': 2012})]}\n", + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "앨런 튜링은 누구인가요?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'wikipedia', 'args': {'query': '앨런 튜링'}, 'id': 'a44dec9b-b241-4abc-93fd-ef2622cfbf13', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "No good Wikipedia Search Result was found\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'wikipedia', 'args': {'query': 'Alan Turing'}, 'id': 'f01ca164-2a41-463e-9b42-9d0d26e814c9', 'type': 'tool_call'}]\n", + "\n", + "===== 4 =====\n", + "ToolMessage\n", + "Page: Alan Turing\n", + "Summary: Alan Mathison Turing (; 23 June 1912 – 7 June 1954) was an English mathematician, computer scientist, logician, cryptanalyst, philosopher and theoretical biologist. He was highly influential in the development of theoretical computer science, providing a formalisation of the concepts of algorithm and computation with the Turing machine, which can be considered a model of a general-purpose computer. Turing is widely considered to be the father of theoretical computer science.\n", + "Born in London, Turing was raised in southern England. He graduated from King's College, Cambridge, and in 1938, earned a doctorate degree from Princeton University. During World War II, Turing worked for the Government Code and Cypher School at Bletchley Park, Britain's codebreaking centre that produced Ultra intelligence. He led Hut 8, the section responsible for German naval cryptanalysis. Turing devised techniques for speeding the breaking of German ciphers, including improvements to the pre-war Polish bomba method, an electromechanical machine that could find settings for the Enigma machine. He played a crucial role in cracking intercepted messages that enabled the Allies to defeat the Axis powers in the Battle of the Atlantic and other engagements.\n", + "After the war, Turing worked at the National Physical Laboratory, where he designed the Automatic Computing Engine, one of the first designs for a stored-program computer. In 1948, Turing joined Max Newman's Computing Machine Laboratory at the University of Manchester, where he contributed to the development of early Manchester computers and became interested in mathematical biology. Turing wrote on the chemical basis of morphogenesis and predicted oscillating chemical reactions such as the Belousov–Zhabotinsky reaction, first observed in the 1960s. Despite these accomplishments, he was never fully recognised during his lifetime because much of his work was covered by the Official Secrets Act.\n", + "In 1952, Turing was prosecuted for homosexual acts. He accepted hormone treatment, a procedure commonly referred to as chemical castration, as an alternative to prison. Turing died on 7 June 1954, aged 41, from cyanide poisoning. An inquest determined his death as suicide, but the evidence is also consistent with accidental poisoning. Following a campaign in 2009, British prime minister Gordon Brown made an official public apology for \"the appalling way [Turing] was treated\". Queen Elizabeth II granted a pardon in 2013. The term \"Alan Turing law\" is used informally to refer to a 2017 law in the UK that retroactively pardoned men cautioned or convicted under historical legislation that outlawed homosexual acts.\n", + "Turing left an extensive legacy in mathematics and computing which has become widely recognised with statues and many things named after him, including an annual award for computing innovation. His portrait appears on the Bank of England £50 note, first released on 23 June 2021 to coincide with his birthday. The audience vote in a 2019 BBC series named Turing the greatest scientist of the 20th century.\n", + "The cognitive scientist Douglas Hofstadter writes:\n", + "\n", + "Atheist, homosexual, eccentric, marathon-running mathematician, A. M. Turing was in large part responsible not only for the concept of computers, incisive theorems about their powers, and a clear vision of the possibility of computer minds, but also for the cracking of German ciphers during the Second World War. It is fair to say we owe much to Alan Turing for the fact that we are not under Nazi rule today.\"\n", + "\n", + "\n", + "\n", + "Page: Turing Award\n", + "Summary: The ACM A. M. Turing Award is an annual prize given by the Association for Computing Machinery (ACM) for contributions of lasting and major technical importance to computer science. It is generally recognized as the highest distinction in the field of computer science and is often referred to as the \"Nobel Prize of Computing\". As of 2026, 81 people have been awarded the prize, with the most recent rec\n", + "\n", + "===== 5 =====\n", + "AIMessage\n", + "앨런 튜링 (Alan Turing, 1912-1954) 은 영국의 수학자, 컴퓨터 과학자, 논리학자, 암호학자, 철학자이자 이론 생물학자입니다. 그는 이론 컴퓨터 과학의 아버지로 널리 여겨집니다.\n", + "\n", + "주요 업적과 생애:\n", + "\n", + "1. **출생과 학력**: 1912 년 6 월 23 일 런던에서 태어났으며, 킹스 칼리지 캠브리지에서 졸업하고 1938 년 프린스턴 대학교에서 박사 학위를 취득했습니다.\n", + "\n", + "2. **제 2 차 세계대전**: 영국 암호 해독 센터인 블레츠키 파크에서 근무하며 독일 해군 암호 해독에 주도적인 역할을 했습니다. 튜링은 Enigma 기기의 해독을 위한 기술 개발에 기여했으며, 연합군의 승리에 결정적인 역할을 했습니다.\n", + "\n", + "3. **컴퓨터 과학**: 전쟁 후 자동 계산 엔진 (Automatic Computing Engine) 을 설계했으며, 맨체스터 대학교에서 초기 컴퓨터 개발에 기여했습니다.\n", + "\n", + "4. **개인적 문제**: 1952 년 동성애 혐의로 고소당했으며, 화학적 중성화 치료를 받았습니다. 1954 년 6 월 7 일 시안화소 독으로 자살로 사망했습니다.\n", + "\n", + "5. **후세**: 2019 년 영국 총리 Gordon Brown 가 공식 사과를 발표했으며, 2013 년 엘리자베스 2 세 여왕이 사면했습니다. 그의 초상이 영국 은행 50 파운드 지폐에 등장했습니다.\n", + "\n", + "튜링은 컴퓨터 과학 분야에서 가장 높은 영예인 튜링 상을 수여받는 사람이며, 그의 업적은 수학, 컴퓨터 과학, 암호학 등 다양한 분야에서 널리 인정받고 있습니다.\n", + "[]\n" + ] + } + ], + "execution_count": 64 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:42.985491848Z", + "start_time": "2026-06-05T01:57:41.208231669Z" } }, "cell_type": "code", @@ -512,7 +628,7 @@ "===== 1 =====\n", "AIMessage\n", "\n", - "[{'name': 'list_directory', 'args': {'dir_path': '.'}, 'id': 'chatcmpl-tool-b4be9eb175ae27d1', 'type': 'tool_call'}]\n", + "[{'name': 'list_directory', 'args': {'dir_path': '.'}, 'id': 'chatcmpl-tool-81c40f60c0bdfc9d', 'type': 'tool_call'}]\n", "\n", "===== 2 =====\n", "ToolMessage\n", @@ -649,7 +765,7 @@ "===== 3 =====\n", "AIMessage\n", "\n", - "[{'name': 'write_file', 'args': {'file_path': 'files.txt', 'text': ''}, 'id': 'chatcmpl-tool-bf8918499b945358', 'type': 'tool_call'}]\n", + "[{'name': 'write_file', 'args': {'file_path': 'files.txt', 'text': ''}, 'id': 'chatcmpl-tool-84d46d21627defde', 'type': 'tool_call'}]\n", "\n", "===== 4 =====\n", "ToolMessage\n", @@ -662,15 +778,1099 @@ ] } ], - "execution_count": 138 + "execution_count": 65 }, { "metadata": {}, + "cell_type": "markdown", + "source": "- 데이터베이스 agent", + "id": "ca2056ef7cb1e845" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:43.055653095Z", + "start_time": "2026-06-05T01:57:43.015904981Z" + } + }, "cell_type": "code", + "source": [ + "# db 생성\n", + "import sqlite3\n", + "\n", + "conn = sqlite3.connect(\"./db/company.db\")\n", + "\n", + "cursor = conn.cursor()\n", + "\n", + "cursor.execute(\"drop table if exists employee\")\n", + "\n", + "cursor.execute(\"\"\"\n", + "CREATE TABLE employee(id integer primary key autoincrement, name text, salary integer)\n", + "\"\"\")\n", + "\n", + "cursor.execute(\"\"\"\n", + "INSERT INTO employee(name, salary)\n", + "VALUES\n", + "('kim', 9000),\n", + "('Lee', 5000),\n", + "('Par', 6000)\n", + "\"\"\")\n", + "\n", + "conn.commit()\n", + "conn.close()\n" + ], + "id": "42797b08330fbc67", "outputs": [], - "execution_count": null, + "execution_count": 66 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:43.076446322Z", + "start_time": "2026-06-05T01:57:43.060016588Z" + } + }, + "cell_type": "code", + "source": "# !pip install sqlalchemy", + "id": "611ccd077165d153", + "outputs": [], + "execution_count": 67 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:43.158488446Z", + "start_time": "2026-06-05T01:57:43.078595288Z" + } + }, + "cell_type": "code", + "source": [ + "# sqlalchemy : python 에서 데이터베이스를편하게 다루기 위한 라이브러리\n", + "\n", + "from langchain_community.utilities import SQLDatabase\n", + "\n", + "db = SQLDatabase.from_uri(\"sqlite:///./db/company.db\")\n", + "print(db.run(\"select * from employee\"))" + ], + "id": "84f82924e3cd44c6", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(1, 'kim', 9000), (2, 'Lee', 5000), (3, 'Par', 6000)]\n" + ] + } + ], + "execution_count": 68 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:59:47.885709043Z", + "start_time": "2026-06-05T01:59:45.157879593Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_community.agent_toolkits import SQLDatabaseToolkit\n", + "\n", + "toolkit = SQLDatabaseToolkit(db = db, llm = watson_llm)\n", + "tools = toolkit.get_tools()\n", + "\n", + "agent = create_agent(\n", + " model = watson_llm,\n", + " tools = tools,\n", + " system_prompt = \"You are a database assistant\"\n", + ")\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"직원들의 avg 급여는 얼마인가요?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "\n", + "print_result(result)" + ], + "id": "8a5ad3b492caa5f7", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "직원들의 avg 급여는 얼마인가요?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'sql_db_list_tables', 'args': {'tool_input': ''}, 'id': 'chatcmpl-tool-b7ebcc53c745744c', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "employee\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'sql_db_schema', 'args': {'table_names': 'employee'}, 'id': 'chatcmpl-tool-b9030a6904edeb58', 'type': 'tool_call'}]\n", + "\n", + "===== 4 =====\n", + "ToolMessage\n", + "\n", + "CREATE TABLE employee (\n", + "\tid INTEGER, \n", + "\tname TEXT, \n", + "\tsalary INTEGER, \n", + "\tPRIMARY KEY (id)\n", + ")\n", + "\n", + "/*\n", + "3 rows from employee table:\n", + "id\tname\tsalary\n", + "1\tkim\t9000\n", + "2\tLee\t5000\n", + "3\tPar\t6000\n", + "*/\n", + "\n", + "===== 5 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'sql_db_query', 'args': {'query': 'SELECT AVG(salary) AS avg_salary FROM employee;'}, 'id': 'chatcmpl-tool-8bf3d181e564fde4', 'type': 'tool_call'}]\n", + "\n", + "===== 6 =====\n", + "ToolMessage\n", + "[(6666.666666666667,)]\n", + "\n", + "===== 7 =====\n", + "AIMessage\n", + "직원들의 평균 급여는 **약 6,667** 입니다.\n", + "[]\n" + ] + } + ], + "execution_count": 77 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "- calculater\n", + " - 계산" + ], + "id": "f7f42288a958a5d7" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:47.482565043Z", + "start_time": "2026-06-05T01:57:47.468011412Z" + } + }, + "cell_type": "code", + "source": "# !pip install numexpr", + "id": "22824d2cd5ab7557", + "outputs": [], + "execution_count": 70 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:57:48.767595994Z", + "start_time": "2026-06-05T01:57:47.483917644Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_classic.chains import LLMMathChain\n", + "\n", + "llm_math = LLMMathChain.from_llm(llm = watson_llm, verbose = True)\n", + "calc_tool = Tool(name = \"calculator\", func = llm_math.run, description = \"수학 계산이 필요할 떄 사용합니다. 시장 규모 계산, 단위 변환 등에 사용하세요.\")\n", + "\n", + "calc_tool.invoke(\"1200억 달러를 원화로 환산 ( 1달러 = 1543원)\")" + ], + "id": "ff2a1bdf413b4378", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001B[1m> Entering new LLMMathChain chain...\u001B[0m\n", + "1200억 달러를 원화로 환산 ( 1달러 = 1543원)\u001B[32;1m\u001B[1;3mQuestion: 1200억 달러를 원화로 환산 (1달러 = 1543원)\n", + "```text\n", + "120000000000 * 1543\n", + "```\n", + "...numexpr.evaluate(\"120000000000 * 1543\")...\n", + "```output\n", + "185160000000000\n", + "```\n", + "Answer: 185160000000000 원\u001B[0m\n", + "\u001B[1m> Finished chain.\u001B[0m\n" + ] + }, + { + "data": { + "text/plain": [ + "'Answer: 185160000000000 원'" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 71 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "#### 커스텀 Tool\n", + "- description 작성\n", + " - 기능설명 언제 사용해야 하는지, 입력값 형식, 출력값 형식, 필수 or 선택ㅕㅁㅇ시\n", + "\n", + "- 작성 방법\n", + " - @tool 데코레이터\n", + " - StructedTool\n", + " - BaseTool" + ], + "id": "75160d583e49687d" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T04:03:44.504488963Z", + "start_time": "2026-06-05T04:03:44.488501186Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_core.tools import tool\n", + "\n", + "# 함수구현\n", + "@tool\n", + "def calculator(expression):\n", + " \"\"\"\n", + " 수식을 계산합니다.\n", + " 사칙연산에 사용하세요.\n", + " \"\"\"\n", + "\n", + " return str(eval(expression))\n", + "\n", + "print(calculator.invoke(\"123 * 567\"))\n", + "print(calculator.name)\n", + "print(calculator.description)" + ], + "id": "ac1f60552ae86215", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "69741\n", + "calculator\n", + "수식을 계산합니다.\n", + "사칙연산에 사용하세요.\n" + ] + } + ], + "execution_count": 15 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T01:59:18.958143431Z", + "start_time": "2026-06-05T01:59:17.858105644Z" + } + }, + "cell_type": "code", + "source": [ + "agent = create_agent(model=watson_llm, tools=[calculator])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"123 곱하기 789는?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "print_result(result)\n" + ], + "id": "f231cb76a140791f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "123 곱하기 789는?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'calculator', 'args': {'expression': '123 * 789'}, 'id': 'chatcmpl-tool-acfebbfe1a2210f5', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "97047\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "123 곱하기 789의 결과는 **97,047**입니다.\n", + "[]\n" + ] + } + ], + "execution_count": 76 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:05:09.095476027Z", + "start_time": "2026-06-05T02:05:09.081452602Z" + } + }, + "cell_type": "code", "source": "", - "id": "ee0493482b4f1fb8" + "id": "e196f8729d699fc8", + "outputs": [], + "execution_count": 83 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:08:45.850762787Z", + "start_time": "2026-06-05T02:08:45.838920290Z" + } + }, + "cell_type": "code", + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "class ExchangeRateInput(BaseModel):\n", + " from_currency:str = Field(description=\"변환할 통화 코드 (예 : USD, EUR, JPY, KRW\")\n", + " to_currency:str = Field(description=\"변환할 통화 코드 (예 : KRW, USD\")\n", + " amount:float = Field(default=1.0, description=\"변환할 금액 (기본갑 : 1)\")\n", + "\n", + "@tool\n", + "def get_exchange_rate(from_currency, to_currency, amount):\n", + " \"\"\"'\n", + " 실시간 호나율을 조회하고 금액을 변환합니다.\n", + " USD, EUR, JPY, KRW 통화 간 환율 계싼에 사용하세요.\n", + " 해외결제, 환전, 가격비교 질문에 사용합니다.\n", + " 통화 환율을 조회합니다.\n", + " \"\"\"\n", + " mock_rates = {\n", + " ('USD', 'KRW'): 1543.0,\n", + " ('EUR', 'KRW'): 1440.0,\n", + " ('JPY', 'KRW'): 9.2,\n", + " ('KRW', 'USD'): 0.00064,\n", + " }\n", + "\n", + " rate = mock_rates.get((from_currency, to_currency))\n", + "\n", + " if not rate :\n", + " return f\"P{from_currency} -> {to_currency()} 환율 정보가 없습니다.\"\n", + " converted = amount * rate\n", + " return f\"{amount} {from_currency} = {converted: .2f} {to_currency} (환율: {rate})\"" + ], + "id": "5e6dd401f0e8f038", + "outputs": [], + "execution_count": 88 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:08:47.673572963Z", + "start_time": "2026-06-05T02:08:46.253222543Z" + } + }, + "cell_type": "code", + "source": [ + "agent = create_agent(model=watson_llm, tools=[get_exchange_rate])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"1달러 한화로 얼마?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "print_result(result)" + ], + "id": "fc198b7c7201cf1f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "1달러 한화로 얼마?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'get_exchange_rate', 'args': {'amount': 1, 'from_currency': 'USD', 'to_currency': 'KRW'}, 'id': 'chatcmpl-tool-b22b10c41d04f78c', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "1 USD = 1543.00 KRW (환율: 1543.0)\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "1 달러는 현재 **약 1,543 원**입니다. (환율: 1 USD ≈ 1,543 KRW)\n", + "[]\n" + ] + } + ], + "execution_count": 89 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:21:48.127616013Z", + "start_time": "2026-06-05T02:21:45.827788267Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_core.tools import StructuredTool\n", + "\n", + "def multiply(a:int, b:int) -> int:\n", + " \"\"\"두 숫자를 곱합니다.\"\"\"\n", + " return a * b\n", + "\n", + "# 이미 존재하는 python 함수 Tool 로 변환\n", + "# a = 12, b = 34 로 전달한 후 연산\n", + "multi_tool = StructuredTool.from_function(func = multiply)\n", + "\n", + "agent = create_agent(model=watson_llm, tools=[multi_tool])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"12 곱하기 24는\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "print_result(result)\n" + ], + "id": "bbb8c5d208d82949", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "12 곱하기 24는\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'multiply', 'args': {'a': 12, 'b': 24}, 'id': 'chatcmpl-tool-9af021e3f1d0de22', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "288\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "12 곱하기 24는 **288**입니다.\n", + "[]\n" + ] + } + ], + "execution_count": 94 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:51:10.938737186Z", + "start_time": "2026-06-05T02:51:10.870746405Z" + } + }, + "cell_type": "code", + "source": [ + "def calculate_bmi(height:float, weight:float)->str:\n", + " \"\"\"\n", + " height(cm)과 weight(kg)를 입력받아 BMI 계산\n", + " \"\"\"\n", + "\n", + " bmi = weight / ((height / 100) ** 2)\n", + " return f\"BMI = {bmi:.2f}\"" + ], + "id": "df5a04cadf6d5068", + "outputs": [], + "execution_count": 95 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T02:55:06.838151142Z", + "start_time": "2026-06-05T02:55:05.180003093Z" + } + }, + "cell_type": "code", + "source": [ + "bmi_tool = StructuredTool.from_function(calculate_bmi)\n", + "\n", + "agent = create_agent(model=watson_llm, tools=[bmi_tool])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"키 173에 몸무게 90이면?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "print_result(result)" + ], + "id": "7718ae3381cf07d0", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "키 173에 몸무게 90이면?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'calculate_bmi', 'args': {'height': 173, 'weight': 90}, 'id': 'chatcmpl-tool-bce2a55da2b3f53a', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "BMI = 30.07 kg/m\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "키 173 cm, 몸무게 90 kg인 경우 BMI는 **30.07 kg/m²**입니다. \n", + "\n", + "이 값은 **비만(Obesity, Class I)**에 해당합니다. 건강을 위해 적절한 식습관과 운동을 고려해 보세요.\n", + "[]\n" + ] + } + ], + "execution_count": 98 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T03:01:35.258995622Z", + "start_time": "2026-06-05T03:01:35.247054446Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_core.tools import BaseTool\n", + "\n", + "class WeatherInput(BaseModel):\n", + " city:str = Field(description=\"날씨를 조회할 도시명\")\n", + "\n", + "class WeatherTool(BaseTool):\n", + " name:str = \"weather\"\n", + " description:str = \"특정 도시의 날씨를 조회합니다.\"\n", + "\n", + " args_schema:type[BaseModel] = WeatherInput\n", + "\n", + "\n", + "\n", + " def _run(self, city:str):\n", + "\n", + " # 실제 : 날씨 api\n", + " weather_data = {\n", + " \"서울\" : \"맑음, 30도\",\n", + " \"경기\" : \"맑음, 25도\",\n", + " \"부산\" : \"맑음, 32도\",\n", + " \"제주\" : \"비, 18도\",\n", + " }\n", + "\n", + " return weather_data.get(city, f\"{city} 날씨 정보 없음\")" + ], + "id": "b9252bcbb2a5a4f1", + "outputs": [], + "execution_count": 103 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T03:03:53.799701119Z", + "start_time": "2026-06-05T03:03:51.622297367Z" + } + }, + "cell_type": "code", + "source": [ + "weather_tool = WeatherTool()\n", + "\n", + "agent = create_agent(model=watson_llm, tools=[weather_tool])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"오늘 대구 날씨는?\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "print_result(result)\n" + ], + "id": "3146f15857bc31cf", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "오늘 대구 날씨는?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'weather', 'args': {'city': '대구'}, 'id': 'chatcmpl-tool-a8e2591e731edc71', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "대구 날씨 정보 없음\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "죄송합니다. 현재 제공된 도구로 대구의 날씨 정보를 확인할 수 없습니다. 다른 도시의 날씨를 알고 싶으시면 알려주세요!\n", + "[]\n" + ] + } + ], + "execution_count": 105 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T03:42:45.268595809Z", + "start_time": "2026-06-05T03:42:44.397003923Z" + } + }, + "cell_type": "code", + "source": [ + "# Retriever Tool\n", + "\n", + "from langchain_core.documents import Document\n", + "from langchain_chroma import Chroma\n", + "from langchain_classic.tools.retriever import create_retriever_tool\n", + "\n", + "docs = [\n", + " Document(page_content=\"연차는 입사 1년 미만은 월 1개 발생합니다.\"),\n", + " Document(page_content=\"병가는 연 30일까지 사용 가능합니다.\"),\n", + " Document(page_content=\"퇴직금은 1년 이상 근무 시 지급됩니다.\"),\n", + "]\n", + "\n", + "vectorstore = Chroma.from_documents(docs, embedding = watson_embedding)\n", + "\n", + "retriever = vectorstore.as_retriever()\n", + "\n", + "# retriever 를 tool로 등록\n", + "retriever_tool = create_retriever_tool(retriever, name=\"company_policy\", description=\"회사 인사 규정을 검색합니다.\")" + ], + "id": "5abda3c920dfb862", + "outputs": [], + "execution_count": 6 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T03:42:54.995039149Z", + "start_time": "2026-06-05T03:42:49.437571174Z" + } + }, + "cell_type": "code", + "source": [ + "agent = create_agent(model=watson_llm, tools=[retriever_tool])\n", + "\n", + "result = agent.invoke(\n", + " {\n", + " \"messages\":[\n", + " {\n", + " \"role\" : \"user\",\n", + " \"content\" : \"연차 발생 방법\"\n", + " }\n", + " ]\n", + " }\n", + ")\n", + "# print_result(result)" + ], + "id": "a8776b557f5fcedf", + "outputs": [], + "execution_count": 7 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T03:39:21.758238360Z", + "start_time": "2026-06-05T03:39:20.733735806Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain.agents import create_agent\n", + "load_dotenv()\n", + "\n", + "def get_weather(city: str) -> str:\n", + " \"\"\"Get weather for a given city.\"\"\"\n", + " return f\"It's always sunny in {city}!\"\n", + "\n", + "\n", + "agent = create_agent(\n", + " model=watson_llm,\n", + " tools=[get_weather],\n", + " system_prompt=\"You are a helpful assistant\",\n", + ")\n", + "\n", + "# Run the agent\n", + "result = agent.invoke(\n", + " {\"messages\": [{\"role\": \"user\", \"content\": \"What is the weather in San Francisco?\"}]}\n", + ")\n", + "\n", + "print_result(result)" + ], + "id": "27c015254ed2b91e", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===== 0 =====\n", + "HumanMessage\n", + "What is the weather in San Francisco?\n", + "\n", + "===== 1 =====\n", + "AIMessage\n", + "\n", + "[{'name': 'get_weather', 'args': {'city': 'San Francisco'}, 'id': 'chatcmpl-tool-a4bb66070cb0329d', 'type': 'tool_call'}]\n", + "\n", + "===== 2 =====\n", + "ToolMessage\n", + "It's always sunny in San Francisco!\n", + "\n", + "===== 3 =====\n", + "AIMessage\n", + "It's always sunny in San Francisco!\n", + "[]\n" + ] + } + ], + "execution_count": 5 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "#### 리서치 자동화 Agent 구현\n", + "- 입력 : 리서치 주제\n", + "- tool\n", + " - 1. 리서치 주제에 대해 웹 검색합니다.\n", + " - 2. 최신 동향 요약\n", + " - 3. 수치 데이터가 있으면 계산기로 검증/변환\n", + " - 4. 마크다운 형식의 문서로 저장\n" + ], + "id": "36271cfba6a30682" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T04:02:23.201125797Z", + "start_time": "2026-06-05T04:02:21.063085017Z" + } + }, + "cell_type": "code", + "source": [ + "# 1. 웹검색(google) tool\n", + "\n", + "from langchain_community.utilities import GoogleSerperAPIWrapper\n", + "from langchain_core.tools import Tool\n", + "\n", + "search = GoogleSerperAPIWrapper()\n", + "\n", + "search_tool =Tool(\n", + " name=\"google_search\",\n", + " func=search.run,\n", + " description=\"\"\"\n", + " Google 검색으로 최신 정보를 찾습니다.\n", + " 뉴스, 트랜드, 최신 기술 동향 검색에 사용하세요.\n", + " \"\"\"\n", + ")\n", + "\n", + "result = search_tool.run(\"AI Seniconductor Market 2025\")" + ], + "id": "6b111356e533eb3d", + "outputs": [], + "execution_count": 12 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T04:05:05.726662927Z", + "start_time": "2026-06-05T04:04:58.013299970Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_core.tools import Tool\n", + "\n", + "@tool\n", + "def trend_analysis_tool(text):\n", + " \"\"\"\n", + " 검색 겨로가에 핵심 트렌드를 분석합니다.\n", + " \"\"\"\n", + "\n", + " prompt = f\"\"\"\n", + " 아래 검색 결과를 분석하여\n", + "\n", + " 1. 핵심 트렌드\n", + " 2. 주요 기업\n", + " 3. 성장 요인\n", + " 4. 위험 요소\n", + " 를 정리하세요.\n", + "\n", + " 검색결과 :\n", + " {text}\n", + " \"\"\"\n", + "\n", + " return watson_llm.invoke(prompt).content\n", + "\n", + "trend_analysis_tool.invoke(result)" + ], + "id": "a9dee0b584f3996f", + "outputs": [ + { + "data": { + "text/plain": [ + "'Based on the provided search result, here is an analysis of the global artificial intelligence (AI) in the semiconductor market:\\n\\n### 1. **Key Trends**\\n- **Rapid Growth**: The market is expected to grow significantly from USD 65.01 billion in 2025 to USD 232.85 billion by 2034, indicating a robust expansion.\\n- **High Compound Annual Growth Rate (CAGR)**: The market is projected to grow at a CAGR of 15.23%, highlighting strong and consistent growth over the forecast period.\\n\\n### 2. **Major Companies**\\nWhile the search result does not specify the major companies, the AI in the semiconductor market typically includes key players such as:\\n- **NVIDIA Corporation**\\n- **Intel Corporation**\\n- **Advanced Micro Devices (AMD)**\\n- **Qualcomm**\\n- **Samsung Electronics**\\n- **TSMC (Taiwan Semiconductor Manufacturing Company)**\\n- **IBM**\\n- **Google (Alphabet Inc.)**\\n- **Microsoft**\\n- **Xilinx (now part of AMD)**\\n\\nThese companies are at the forefront of developing AI technologies and integrating them into semiconductor solutions.\\n\\n### 3. **Growth Factors**\\n- **Increasing Demand for AI Applications**: The proliferation of AI applications across various sectors, including healthcare, automotive, finance, and consumer electronics, is driving the demand for advanced semiconductor solutions.\\n- **Technological Advancements**: Continuous innovations in semiconductor technology, such as the development of more efficient and powerful chips, are facilitating the integration of AI capabilities.\\n- **Investment in AI Research and Development**: Significant investments by both public and private sectors in AI research and development are fostering innovation and market growth.\\n- **Edge Computing**: The rise of edge computing, which requires powerful and efficient semiconductor solutions to process data locally, is another growth driver.\\n- **5G Technology**: The rollout of 5G networks is expected to boost the demand for AI-enabled semiconductors, as these networks require advanced processing capabilities.\\n\\n### 4. **Risk Factors**\\n- **High Development Costs**: The development and production of advanced semiconductor technologies can be capital-intensive, posing financial risks for companies.\\n- **Rapid Technological Changes**: The fast pace of technological advancements can render existing products obsolete quickly, necessitating continuous innovation and investment.\\n- **Supply Chain Disruptions**: Global supply chain issues, such as those experienced during the COVID-19 pandemic, can impact the availability and cost of semiconductor components.\\n- **Regulatory and Compliance Challenges**: Compliance with international regulations and standards can be complex and costly, particularly for companies operating in multiple regions.\\n- **Market Competition**: Intense competition among key players can lead to pricing pressures and reduced profit margins.\\n\\nThis analysis provides a comprehensive overview of the current state and future prospects of the AI in the semiconductor market, highlighting key trends, major companies, growth factors, and potential risks.'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 19 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T04:08:26.541767680Z", + "start_time": "2026-06-05T04:08:26.517928Z" + } + }, + "cell_type": "code", + "source": [ + "import numexpr\n", + "@tool\n", + "def calculator_tool(expression):\n", + " \"\"\"\n", + " 수학 계산용 Tool\n", + "\n", + " 사용 예:\n", + " 999 * 1530\n", + " (250 / 120) ** ( 1/6) - 1\n", + " 100000000 / 1350\n", + "\n", + " CAGR 계산\n", + " 환율 계산\n", + " 시장 규모 계싼\n", + "\n", + " \"\"\"\n", + " result = numexpr.evaluate(expression)\n", + " return str(result.item())\n", + "\n", + "calculator_tool.invoke(\"2 + 3 * (4-1)\")" + ], + "id": "cec7288d9d7c0598", + "outputs": [ + { + "data": { + "text/plain": [ + "'11'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 20 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T04:13:35.155571176Z", + "start_time": "2026-06-05T04:13:35.136375634Z" + } + }, + "cell_type": "code", + "source": [ + "from datetime import datetime\n", + "\n", + "@tool\n", + "\n", + "def save_report_tool(content):\n", + " \"\"\"\n", + " 리서치 결과를 Markdown 파일로 저장합니다.\n", + " 최종 리포트 작성이 완료되었을 떄 사용하세요.\n", + " 입력 : Markdown 형식의 리포트 전체 내용\n", + " \"\"\"\n", + "\n", + " timestemp = datetime.now().strftime('%Y%m%d_%H%M%S')\n", + " filename = f\"research_report_{timestemp}.md\"\n", + " with open(filename, \"w\", encoding=\"utf-8\") as f :\n", + " f.write(content)\n", + "\n", + " return f\"리포트 저장 완료 : {filename}\"\n", + "\n", + "content=\"\"\"\n", + "리서치 결과를 Markdown 파일로 저장합니다.\n", + "최종 리포트 작성이 완료되었을 떄 사용하세요.\n", + "입력 : Markdown 형식의 리포트 전체 내용\n", + "\"\"\"\n", + "save_report_tool.invoke(content)" + ], + "id": "64e43fd1cb5d1613", + "outputs": [ + { + "data": { + "text/plain": [ + "'리포트 저장 완료 : research_report_20260605_131335.md'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 21 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T05:46:40.223152783Z", + "start_time": "2026-06-05T05:46:40.208218534Z" + } + }, + "cell_type": "code", + "source": [ + "def save_report(content):\n", + " \"\"\"\n", + " 리서치 결과를 Markdown 파일로 저장합니다.\n", + " 최종 리포트 작성이 완료되었을 떄 사용하세요.\n", + " 입력 : Markdown 형식의 리포트 전체 내용\n", + " \"\"\"\n", + "\n", + " timestemp = datetime.now().strftime('%Y%m%d_%H%M%S')\n", + " filename = f\"research_report_{timestemp}.md\"\n", + " with open(filename, \"w\", encoding=\"utf-8\") as f :\n", + " f.write(content)\n", + "\n", + " return filename" + ], + "id": "ada5d35665317bbd", + "outputs": [], + "execution_count": 28 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T05:48:03.901801885Z", + "start_time": "2026-06-05T05:47:33.551871152Z" + } + }, + "cell_type": "code", + "source": [ + "tools = [search_tool, trend_analysis_tool, calculator_tool]\n", + "\n", + "system_prompt = \"\"\"\n", + "당신은 전문 리서치 어시스턴트입니다.\n", + "작업 순서 :\n", + "1. search_tool 사용\n", + "2. trend_analysis_tool 사용\n", + "3. 수치 데이터는 calculator_tool 사용\n", + "4. Markdown 리포트 작성\n", + "\n", + "\"\"\"\n", + "\n", + "agent = create_agent(\n", + " model=watson_llm,\n", + " tools=tools,\n", + " system_prompt=system_prompt,\n", + ")\n", + "\n", + "result = agent.invoke(\n", + " {\"messages\": [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"\"\"\n", + " AI 반도체 시장 2026년 전망에 대한 리서치 리포트를 작성해주세요ㅗ.\n", + "\n", + " 주제 :\n", + " AI 반도체 시장 2026년 전망\n", + "\n", + " 포함 내용:\n", + " - 주요 기업\n", + " - 시장 규모\n", + " - 성장률\n", + " - 핵심 트렌드\n", + " \"\"\"\n", + " }]}\n", + ")\n", + "\n", + "# print(result['messages'])\n", + "\n", + "report = result['messages'][-1].content\n", + "filename = save_report(report)\n", + "print(f\"저장완료 : {filename}\")" + ], + "id": "204af26bcbf9721f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "저장완료 : research_report_20260605_144803.md\n" + ] + } + ], + "execution_count": 30 } ], "metadata": { diff --git a/ollama/langchain-agent2.ipynb b/ollama/langchain-agent2.ipynb new file mode 100644 index 0000000..a0e19dc --- /dev/null +++ b/ollama/langchain-agent2.ipynb @@ -0,0 +1,700 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2026-06-05T06:07:18.527729458Z", + "start_time": "2026-06-05T06:07:18.518264605Z" + } + }, + "source": [ + "import os\n", + "\n", + "from google.protobuf.timestamp import from_current_time\n", + "from langchain_classic.chains.sequential import SequentialChain\n", + "from langchain_openai import ChatOpenAI\n", + "from langchain_ibm import WatsonxEmbeddings\n", + "from langchain_ibm import ChatWatsonx\n", + "from langchain.agents import create_agent\n", + "from langchain_ollama import OllamaEmbeddings\n", + "from langchain_ollama import ChatOllama\n", + "from langchain_ollama import OllamaEmbeddings, ChatOllama\n", + "from dotenv import load_dotenv\n", + "\n", + "from langchain_community.utilities import GoogleSerperAPIWrapper\n", + "from langchain_core.tools import Tool\n", + "from langchain_core.tools import tool\n", + "\n", + "from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser\n", + "from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda\n", + "from sklearn import pipeline" + ], + "outputs": [], + "execution_count": 6 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T06:07:18.540816647Z", + "start_time": "2026-06-05T06:07:18.529482429Z" + } + }, + "cell_type": "code", + "source": [ + "# .env 내용 가져오기\n", + "load_dotenv()\n", + "\n", + "apikey = os.getenv(\"WATSONX_API_KEY\")\n", + "project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n", + "watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n", + "hf_token = os.getenv(\"HF_TOKEN\")\n", + "COHERE_API_KEY = os.getenv(\"COHERE_API_KEY\")\n", + "SERPER_API_KEY = os.getenv(\"SERPER_API_KEY\")\n", + "GEMINI_API_KEY = os.getenv(\"GEMINI_API_KEY\")\n", + "LANGSMITH_API_KEY = os.getenv(\"LANGSMITH_API_KEY\")" + ], + "id": "c2c64c7967231118", + "outputs": [], + "execution_count": 7 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T06:07:22.476243454Z", + "start_time": "2026-06-05T06:07:18.541903550Z" + } + }, + "cell_type": "code", + "source": [ + "watson_llm = ChatWatsonx(\n", + " model_id=\"ibm/granite-4-h-small\",\n", + " url=f\"{watsonx_ai_url}\",\n", + " api_key=f\"{apikey}\",\n", + " project_id=f\"{project_id}\",\n", + " max_tokens=2000,\n", + " params={\n", + " \"temperature\": 0\n", + " }\n", + ")\n", + "\n", + "watson_embedding = WatsonxEmbeddings(\n", + " model_id=\"ibm/granite-embedding-278m-multilingual\",\n", + " url=f\"{watsonx_ai_url}\",\n", + " api_key=f\"{apikey}\",\n", + " project_id=f\"{project_id}\"\n", + ")\n", + "\n", + "hugging_llm = ChatOpenAI(\n", + " model=\"Qwen/Qwen2.5-7B-Instruct:together\",\n", + " api_key=hf_token,\n", + " base_url=\"https://router.huggingface.co/v1\",\n", + " temperature=0,\n", + ")\n", + "\n", + "ollama_embedding = OllamaEmbeddings(model=\"nomic-embed-text-v2-moe\")\n", + "\n", + "# 로컬 LLM\n", + "qwen_llm = ChatOllama(model=\"qwen3.5:4b\", temperature=0)\n", + "exaone_llm = ChatOllama(model=\"exaone3.5:2.4b\", temperature=0)\n", + "gemma_llm = ChatOllama(model=\"gemma4:e2b\")" + ], + "id": "560df17d1a798650", + "outputs": [], + "execution_count": 8 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T06:07:22.498652432Z", + "start_time": "2026-06-05T06:07:22.487631249Z" + } + }, + "cell_type": "code", + "source": [ + "def print_result(result):\n", + " for i, msg in enumerate(result['messages']):\n", + " print(f\"\\n===== {i} =====\")\n", + " print(type(msg).__name__)\n", + "\n", + " if hasattr(msg, 'content'):\n", + " print(msg.content)\n", + "\n", + " if hasattr(msg, \"tool_calls\"):\n", + " print(msg.tool_calls)" + ], + "id": "abfd826c1b3e2703", + "outputs": [], + "execution_count": 9 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### RouterChain(LCEL Router : 조건부 분기 패턴\n", + "- Router 패턴 : 입력에 따라 적절한 체인 or Runnable 로 분기\n", + "- 분류기(LLM기반 or 규칙기반)가 입력을 분석 -> 적절한 체인을 라우팅\n", + "- |\n", + "- ex) 질문 유형(코딩 / 수학 / 일반 ....)에 따른 전문 프롬프트를 적용\n", + "- 동작흐름\n", + " (1) 입력 => (2) 분류기 => (3) 라우팅 => (4) 실행 => (5) 반환" + ], + "id": "fb42bec3088a485e" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T06:46:44.553396205Z", + "start_time": "2026-06-05T06:46:44.544122014Z" + } + }, + "cell_type": "code", + "source": [ + "parser = StrOutputParser()\n", + "\n", + "# 수학체인\n", + "math_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"당신은 수학 전문가입니다. 풀이 과정을 단계별로 설명하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + "]) | watson_llm | parser\n", + "\n", + "# 코드 체인\n", + "code_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"당신은 시니어 개발자입니다. 코드와 주석을 함께 제공하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + "]) | watson_llm | parser\n", + "\n", + "# 일반 체인\n", + "general_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"당신은 친절한 AI 어시스턴트입니다. 한국어로 답변하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + "]) | watson_llm | parser\n", + "\n", + "# 분류기 : 질문을 읽고 유형을 반환\n", + "classify_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"질문 유형을 math/code/general 중 하나로만 답하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + "]) | watson_llm | parser\n", + "def route(inputs:dict):\n", + " category = classify_chain.invoke(inputs).strip().lower()\n", + "\n", + " print(f\" => 븐류결과 {category}\")\n", + "\n", + " if \"math\" in category: return math_chain\n", + " elif \"code\" in category: return code_chain\n", + " else: return general_chain\n", + "\n", + "# router_chain = RunnableLambda(route) | RunnableLambda(lamda chain: chain)\n", + "router_chain = RunnableLambda(lambda x:route(x).invoke(x))" + ], + "id": "1e18698dde2e75b5", + "outputs": [], + "execution_count": 13 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T06:53:39.704503264Z", + "start_time": "2026-06-05T06:52:54.753636702Z" + } + }, + "cell_type": "code", + "source": [ + "questions = [\n", + " { 'question': '피타고라스 정리를 증명해줘'},\n", + " { 'question': '오늘 저녁 메뉴 추천해줘'},\n", + " { 'question': '파이썬으로 버블 정렬 구현해줘'},\n", + " { 'question': '피타고라스 정리를 코드로 짜줘'},\n", + "]\n", + "\n", + "for q in questions:\n", + " print(f\"Q: {q['question']}\")\n", + " print(f\"A: {router_chain.invoke(q)[:100]}...\\n\")\n" + ], + "id": "c9fa6cb3d73bc4e7", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Q: 피타고라스 정리를 증명해줘\n", + " => 븐류결과 네, 다음은 피타고라스의 정리 증명입니다:\n", + "\n", + "a, b, c의 변을 가진 직각삼각형이 있다고 가정합니다. 삼각형 안에 변의 길이가 a, b, c인 정사각형을 그립니다. 다음은 그 증명입니다:\n", + "\n", + "1. 삼각형 안에 변의 길이가 a, b, c인 정사각형의 넓이는 a^2 + b^2입니다.\n", + "\n", + "2. 삼각형을 두 개의 작은 직각삼각형으로 나누기 위해 빗변(c)에 수직선을 그립니다.\n", + "\n", + "3. 수직선이 전에 있었던 빗변의 길이가 c인 두 개의 작은 직각삼각형의 넓이는 각각 (a^2)와 (b^2)입니다.\n", + "\n", + "4. 전체 삼각형의 넓이는 작은 직각삼각형의 넓이의 합이어야 합니다. 따라서, 삼각형의 넓이는 (a^2) + (b^2)입니다.\n", + "\n", + "5. 삼각형이 직각삼각형이므로, 빗변의 길이는 c입니다. 따라서, 삼각형의 넓이는 (1/2)c^2입니다.\n", + "\n", + "6. 넓이가 같다는 것을 결론지을 수 있으므로, (a^2) + (b^2) = (1/2)c^2입니다.\n", + "\n", + "7. 양변에 2를 곱하면 a^2 + b^2 = c^2가 됩니다.\n", + "\n", + "8. 이것이 피타고라스의 정리입니다.\n", + "\n", + "따라서, 피타고라스의 정리는 직각삼각형의 빗변의 길이의 제곱이 다른 두 변의 길이의 제곱의 합과 같다는 것을 보여줍니다.\n", + "A: 피타고라스 정리는 직각삼각형에서 빗변의 길이의 제곱이 나머지 두 변의 길이의 제곱의 합과 같다는 정리입니다. 이 정리를 증명해 드리겠습니다.\n", + "\n", + "가정: 직각삼각형 ABC에서 각 A가...\n", + "\n", + "Q: 오늘 저녁 메뉴 추천해줘\n", + " => 븐류결과 질문 유형: general\n", + "\n", + "오늘 저녁 메뉴로는 간단하면서도 맛있는 \"치킨과 샐러드\"를 추천해드릴게요. 치킨은 구운 닭고기를 사용하고, 샐러드는 신선한 채소와 드레싱을 곁들여서 영양가 있고 가벼운 식사를 즐길 수 있습니다. 또한, 필요에 따라 사이드 디시로 감자 샐러드나 빵을 추가할 수도 있습니다. 이 메뉴는 준비하기도 쉽고, 가족이나 친구들과 함께 즐기기에 좋은 선택이 될 것 같아요.\n", + "A: 저녁 메뉴로는 다양한 선택지가 있습니다. 건강하고 맛있는 한국 요리를 추천해 드리겠습니다.\n", + "\n", + "1. 비빔밥: 밥에 다양한 채소와 고기를 넣고 고추장 소스를 섞어 먹는 한국의 대표적인...\n", + "\n", + "Q: 파이썬으로 버블 정렬 구현해줘\n", + " => 븐류결과 다음은 파이썬으로 구현한 버블 정렬 코드입니다.\n", + "\n", + "```python\n", + "def bubble_sort(arr):\n", + " n = len(arr)\n", + " \n", + " for i in range(n - 1):\n", + " for j in range(n - i - 1):\n", + " if arr[j] > arr[j + 1]:\n", + " arr[j], arr[j + 1] = arr[j + 1], arr[j]\n", + " \n", + " return arr\n", + "```\n", + "\n", + "이 코드는 다음과 같이 동작합니다.\n", + "\n", + "1. `bubble_sort` 함수는 정렬할 배열 `arr`을 입력으로 받습니다.\n", + "\n", + "2. 배열의 길이 `n`을 변수에 저장합니다.\n", + "\n", + "3. 외부 루프 `for i in range(n - 1)`는 배열을 `n - 1`번 반복하며, 각 반복에서 가장 큰 원소를 배열의 끝쪽으로 이동시킵니다.\n", + "\n", + "4. 내부 루프 `for j in range(n - i - 1)`는 현재 루프에서 비교해야 할 인접한 원소 쌍을 탐색합니다. `n - i - 1`은 이미 정렬된 원소를 제외한 나머지 원소들을 비교하기 위해 사용됩니다.\n", + "\n", + "5. `if arr[j] > arr[j + 1]` 조건문은 현재 원소와 다음 원소를 비교하여, 현재 원소가 다음 원소보다 크다면 두 원소를 교환합니다. 이 과정을 통해 큰 원소가 뒤로 이동하게 됩니다.\n", + "\n", + "6. 모든 루프가 완료되면 정렬된 배열을 반환합니다.\n", + "\n", + "이 함수를 사용하는 예제는 다음과 같습니다.\n", + "\n", + "```python\n", + "# 예제 사용\n", + "arr = [64, 34, 25, 12, 22, 11, 90]\n", + "print(\"정렬 전:\", arr)\n", + "\n", + "sorted_arr = bubble_sort(arr)\n", + "print(\"정렬 후:\", sorted_arr)\n", + "```\n", + "\n", + "출력 결과는 다음과 같습니다.\n", + "\n", + "```\n", + "정렬 전: [64, 34, 25, 12, 22, 11, 90]\n", + "정렬 후: [11, 12, 22, 25, 34, 64, 90]\n", + "```\n", + "\n", + "버블 정렬은 간단하고 직관적인 정렬 알고리즘이지만, 시간 복잡도가 o(n^2)로 비효율적인 편입니다. 대규모 데이터에 대해서는 다른 정렬 알고리즘을 고려하는 것이 좋습니다.\n", + "A: 물론입니다! 파이썬으로 버블 정렬을 구현해드리겠습니다. 버블 정렬은 인접한 두 원소를 비교하며 정렬하는 간단한 정렬 알고리즘입니다. 다음은 파이썬 코드입니다:\n", + "```python\n", + "d...\n", + "\n", + "Q: 피타고라스 정리를 코드로 짜줘\n", + " => 븐류결과 피타고라스 정리를 코드로 구현하려면, 먼저 피타고라스 정리가 무엇인지 이해해야 합니다. 피타고라스 정리는 직각삼각형에서 빗변의 길이의 제곱이 나머지 두 변의 길이의 제곱의 합과 같다는 정리입니다. 수식으로 표현하면 다음과 같습니다.\n", + "\n", + "```\n", + "c² = a² + b²\n", + "```\n", + "\n", + "여기서 c는 빗변의 길이이고, a와 b는 나머지 두 변의 길이입니다.\n", + "\n", + "이제 이 정리를 코드로 구현해 보겠습니다. 여기서는 python을 사용하여 구현하겠습니다.\n", + "\n", + "```python\n", + "def pythagorean_theorem(a, b):\n", + " c_squared = a**2 + b**2\n", + " c = c_squared ** 0.5\n", + " return c\n", + "\n", + "# 예시 사용\n", + "a = 3\n", + "b = 4\n", + "c = pythagorean_theorem(a, b)\n", + "print(f\"빗변의 길이는: {c}\")\n", + "```\n", + "\n", + "이 코드에서는 `pythagorean_theorem`이라는 함수를 정의하고, 두 개의 인자 `a`와 `b`를 받습니다. 이 함수는 피타고라스 정리를 사용하여 빗변의 길이 `c`를 계산하고 반환합니다. 예시 사용에서는 `a`와 `b`에 각각 3과 4를 할당하고, `pythagorean_theorem` 함수를 호출하여 빗변의 길이를 계산한 후 출력합니다.\n", + "A: 피타고라스 정리를 코드로 작성하려면, 파이썬을 사용하여 다음과 같이 작성할 수 있습니다.\n", + "```python\n", + "def pythagorean_theorem(a, b):\n", + " c = (...\n", + "\n" + ] + } + ], + "execution_count": 19 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "#### RunnableBranch(선언형 분기)", + "id": "6d1f541fb236bfd9" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T07:11:06.032073974Z", + "start_time": "2026-06-05T07:10:36.542912312Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_core.runnables import RunnableBranch\n", + "\n", + "# RunnableBranch((조건1, 체인1), (조건2, 체인2), (조건3, 체인3))\n", + "\n", + "branch = RunnableBranch(\n", + " (lambda x:\"math\" in x.get(\"topic\", ''), math_chain),\n", + " (lambda x:\"code\" in x.get(\"topic\", ''), code_chain),\n", + " (lambda x:\"cooking\" in x.get(\"topic\", ''), ChatPromptTemplate.from_messages([\n", + " (\"system\", \"당신은 요리전문가 AI 어시스턴트입니다. 한국어로 답변하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + " ]) | watson_llm | parser),\n", + " general_chain\n", + ")\n", + "\n", + "print(branch.invoke({'topic':'math', 'question':'미분이란?'}))\n", + "print(branch.invoke({'topic':'code', 'question':'미적분 코드 구현'}))\n", + "print(branch.invoke({'topic':'cooking', 'question':'김치찌개 레시피?'}))\n", + "print(branch.invoke({'topic':'other', 'question':'안녕하세요?'}))\n" + ], + "id": "7e5d342621ece0b0", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "미분은 수학에서 함수의 변화율을 나타내는 개념입니다. 미분은 함수의 그래프에서 접선의 기울기를 구하는 과정으로, 함수의 변화율을 정확하게 파악할 수 있게 해줍니다. 미분은 미적분학의 핵심 개념 중 하나이며, 미분을 통해 함수의 최대값, 최소값, 변곡점 등을 찾을 수 있습니다.\n", + "\n", + "미분의 과정을 단계별로 설명하겠습니다.\n", + "\n", + "1. 함수 f(x)를 주어진다고 가정합니다.\n", + "2. 미분을 구하기 위해 독립변수 x에 대한 미소변화량을 나타내는 기호 'dx'를 사용합니다. 이때, 함수 값의 미소변화량을 'dy'로 나타냅니다.\n", + "3. 함수 f(x)의 미분은 다음과 같이 정의됩니다: dy/dx = f'(x) = lim(h→0) (f(x+h) - f(x))/h\n", + "4. 이 식에서 lim(h→0)은 h가 0에 가까워질 때의 극한을 의미합니다. 이 극한을 구하면 함수 f(x)의 미분, 즉 도함수 f'(x)를 얻을 수 있습니다.\n", + "5. 도함수 f'(x)는 함수 f(x)의 그래프에서 접선의 기울기를 나타냅니다. 이를 통해 함수의 변화율을 파악할 수 있습니다.\n", + "\n", + "예를 들어, 함수 f(x) = x^2를 미분해보겠습니다.\n", + "\n", + "1. f(x) = x^2\n", + "2. dy/dx = f'(x) = lim(h→0) (f(x+h) - f(x))/h\n", + "3. f(x+h) = (x+h)^2 = x^2 + 2xh + h^2\n", + "4. f'(x) = lim(h→0) ((x^2 + 2xh + h^2) - x^2)/h = lim(h→0) (2xh + h^2)/h\n", + "5. h를 제외한 항으로 나누면 f'(x) = lim(h→0) (2x + h) = 2x\n", + "\n", + "따라서, 함수 f(x) = x^2의 미분은 f'(x) = 2x입니다. 이 도함수는 함수의 그래프에서 접선의 기울기를 나타내며, 함수의 변화율을 파악하는 데 사용됩니다.\n", + "미적분 코드를 구현하려면, 파이썬의 `sympy` 라이브러리를 사용할 수 있습니다. 이 라이브러리는 기호 수학을 다루는 데 사용되며, 미적분 계산에도 적합합니다. 아래는 파이썬 코드와 주석을 함께 제공한 미적분 코드 구현 예시입니다.\n", + "```python\n", + "# 필요한 라이브러리를 임포트합니다.\n", + "import sympy as sp\n", + "\n", + "# 기호 변수를 정의합니다.\n", + "x = sp.symbols('x')\n", + "\n", + "# 함수를 정의합니다.\n", + "f = x**3 - 3*x**2 + 2*x\n", + "\n", + "# 함수의 도함수를 계산합니다.\n", + "f_prime = sp.diff(f, x)\n", + "\n", + "# 함수의 적분을 계산합니다.\n", + "f_integral = sp.integrate(f, x)\n", + "\n", + "# 결과를 출력합니다.\n", + "print(\"함수:\", f)\n", + "print(\"도함수:\", f_prime)\n", + "print(\"적분:\", f_integral)\n", + "```\n", + "위 코드에서는 다음과 같은 작업을 수행합니다.\n", + "\n", + "1. `sympy` 라이브러리를 임포트합니다.\n", + "2. 기호 변수 `x`를 정의합니다.\n", + "3. 함수 `f`를 정의합니다. 이 예시에서는 `f(x) = x^3 - 3x^2 + 2x`입니다.\n", + "4. `sp.diff()` 함수를 사용하여 함수 `f`의 도함수 `f_prime`를 계산합니다.\n", + "5. `sp.integrate()` 함수를 사용하여 함수 `f`의 적분 `f_integral`을 계산합니다.\n", + "6. 결과를 출력합니다.\n", + "\n", + "이 코드를 실행하면 다음과 같은 결과가 나옵니다.\n", + "```yaml\n", + "함수: x**3 - 3*x**2 + 2*x\n", + "도함수: 3*x**2 - 6*x + 2\n", + "적분: x**4/4 - x**3 + x**2\n", + "```\n", + "이 예시는 파이썬과 `sympy` 라이브러리를 사용하여 미적분 계산을 수행하는 기본적인 방법을 보여줍니다. 더 복잡한 미적분 문제를 해결하려면, `sympy` 라이브러리의 다양한 기능을 활용할 수 있습니다.\n", + "김치찌개 레시피를 알려드리겠습니다. 기본적인 재료와 조리법을 소개하겠습니다.\n", + "\n", + "재료:\n", + "\n", + "* 김치 2컵\n", + "* 돼지고기 목살 200g (또는 소고기 대신 사용 가능)\n", + "* 두부 1/2모\n", + "* 양파 1개\n", + "* 대파 1대\n", + "* 마늘 3쪽\n", + "* 고추장 1큰술\n", + "* 고춧가루 1큰술 (선택 사항)\n", + "* 된장 1작은술\n", + "* 물 2컵\n", + "* 참기름 약간\n", + "* 소금 약간\n", + "* 찹쌀떡 (선택 사항)\n", + "\n", + "조리법:\n", + "\n", + "1. 돼지고기를 적당한 크기로 썰어 물에 헹궈 물기를 제거한 후, 소금 약간을 뿌려 두세요.\n", + "2. 양파와 대파를 적당한 크기로 썰어 두세요. 마늘은 다진 후 준비합니다.\n", + "3. 냄비에 물을 넣고 끓인 후, 고기를 넣어 익을 때까지 끓입니다.\n", + "4. 고기가 익으면 김치를 넣고 함께 끓입니다. 김치가 익으면 두부를 넣고 끓입니다.\n", + "5. 고추장, 고춧가루, 된장을 넣어 간을 맞춥니다. 고추가루를 넣지 않고 맵지 않은 김치찌개를 원한다면 생략할 수 있습니다.\n", + "6. 양파, 대파, 마늘을 넣고 끓여줍니다. 소금으로 간을 맞춥니다.\n", + "7. 끓는 김치찌개에 참기름을 넣어 향을 더해줍니다.\n", + "8. 찹쌀떡을 넣고 싶다면 이 단계에서 넣어 끓여주세요.\n", + "9. 김치찌개가 끓으면 불을 끄고 대파를 다시 잘게 썰어 뿌려 준비가 완료됩니다.\n", + "\n", + "이제 맛있는 김치찌개가 완성되었습니다. 밥과 함께 드시면 더욱 맛있습니다. 맛있게 드세요!\n", + "안녕하세요! 어떻게 도와드릴까요?\n" + ] + } + ], + "execution_count": 20 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### SequentialChain(단계별 순차 파이프라인)\n", + "\n", + "- 앞 단계 출력이 뒷단계의 입력으로 흘러 들어감\n", + "- | => SequentialChain\n", + "- .assign() : 중간 결과를 보존하여 여러 단계를 누적할 수 있음\n", + "- 각 단계의 출력 타입과 입력 타입이 일치해야 함" + ], + "id": "ec0ad7ad57ee2a01" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T07:53:53.154055958Z", + "start_time": "2026-06-05T07:53:48.801194749Z" + } + }, + "cell_type": "code", + "source": [ + "# 체인 정의\n", + "translate_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음 텍스트를 한국어로 번역하세요. 번역문만 출력:\\n{text}.\"),\n", + "]) | watson_llm | parser\n", + "\n", + "summarize_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음 텍스트를 3문장으로 번역하세요.:\\n{text}.\"),\n", + "]) | watson_llm | parser\n", + "\n", + "sentiment_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음 텍스트를 의 감정을 긍정/부정/중립 중 하나로만 답하세요.:\\n{summary}.\"),\n", + "]) | watson_llm | parser\n", + "\n", + "report_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"아래 분석 결과를 바탕으로 한 줄 최종 보고서를 작성하세요.:\\n\"\n", + " \"원문 : {text}\\n번역 : {translated}\\n요약 : {summary}\\n감정 : {sentiment}\"),\n", + "]) | watson_llm | parser\n", + "\n", + "# 원문 => 번역 => 요약 => 감정 => 보고서\n", + "# assign() : 단계별 결과 누적\n", + "# translate_chain | sentiment_chain | report_chain\n", + "pipeline = (RunnablePassthrough\n", + " .assign(translated = translate_chain)\n", + " .assign(summary = summarize_chain)\n", + " .assign(sentiment = sentiment_chain)\n", + " .assign(report = report_chain)\n", + " )\n", + "\n", + "result = pipeline.invoke({\"text\":\"Python is a versatile language loved by developers worldwide.\"})\n", + "\n", + "print(\"번역 :\", result['translated'])\n", + "print(\"요약 :\", result['summary'])\n", + "print(\"감정 :\", result['sentiment'])\n", + "print(\"보고서 :\", result['report'])\n" + ], + "id": "f487a481ff7683bd", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "번역 : 파이썬은 개발자들에게 사랑받는 다용도의 언어입니다.\n", + "요약 : 파이썬은 개발자들에게 사랑받는 다용도의 언어입니다.\n", + "감정 : 긍정\n", + "보고서 : 파이썬은 개발자들에게 사랑받는 다용도의 언어입니다.\n" + ] + } + ], + "execution_count": 25 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T08:09:12.487615584Z", + "start_time": "2026-06-05T08:09:07.590760841Z" + } + }, + "cell_type": "code", + "source": [ + "# 조건무 단계 삽입\n", + "\n", + "detect_lang_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음 텍스트 언어 korea/english/other 중 하나로만 답하세요: \\n{text}\"),\n", + "]) | watson_llm | parser\n", + "\n", + "def maybe_translate(inputs):\n", + " lang = detect_lang_chain.invoke(inputs).strip().lower()\n", + "\n", + " if 'english' in lang or 'other' in lang:\n", + " translated = translate_chain.invoke(inputs)\n", + " return {**inputs, 'text':translated, 'was_translated' : True}\n", + " return {**inputs, 'was_translated' : False}\n", + "\n", + "# smart_pipeline = RunnableLambda(maybe_translate) | RunnablePassthrough.assign(summary = summarize_chain) | RunnablePassthrough.assign(sentiment = sentiment_chain) | RunnablePassthrough(report = report_chain)\n", + "smart_pipeline = RunnableLambda(maybe_translate) | RunnablePassthrough.assign(summary = summarize_chain) | RunnablePassthrough.assign(sentiment = sentiment_chain)\n", + "\n", + "r1 = smart_pipeline.invoke({'text' : 'Python is grate!'})\n", + "r2 = smart_pipeline.invoke({'text' : '파이썬은 최고야'})\n", + "\n", + "print(r1['was_translated'], r1['summary'][:50])\n", + "print(r2['was_translated'], r2['summary'][:50])" + ], + "id": "9270dcb00e6daa5f", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True Here is the translation in 3 sentences:\n", + "\n", + "파이썬은 정말 대\n", + "False Here is the translation in 3 sentences:\n", + "\n", + "파이썬은 최고야.\n" + ] + } + ], + "execution_count": 35 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### MapReduceChain(대용량 문서 처리)\n", + "- Map : 문서를 청크로 나눠 각각 처리(요약, 추출..) => 병렬 실행 가능\n", + "- Reduce : Map 결과들을 하나로 합쳐 최종 답변 생성" + ], + "id": "2d31dbaf7136c98c" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T08:41:41.441802415Z", + "start_time": "2026-06-05T08:41:21.259025964Z" + } + }, + "cell_type": "code", + "source": [ + "from langchain_community.document_loaders import PyPDFLoader\n", + "from langchain_text_splitters import RecursiveCharacterTextSplitter\n", + "\n", + "map_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음 텍스트를 2문장으로 요약하세요.\"),\n", + " (\"user\", \"{chunk}\")\n", + "]) | watson_llm | parser\n", + "\n", + "reduce_chain = ChatPromptTemplate.from_messages([\n", + " (\"system\", \"다음은 긴 문서의 섹션별 요약입니다. 전체를 5문장으로 통합 요약하세요.\"),\n", + " (\"user\", \"{summaries}\")\n", + "]) | watson_llm | parser\n", + "\n", + "def map_reduce_summarize(file_path):\n", + " loader = PyPDFLoader(file_path)\n", + " splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)\n", + " chunks = splitter.split_documents(loader.load())\n", + " print(f\"총 청크 수 {len(chunks)}\")\n", + "\n", + " #---- Map : 청크별 요약\n", + " chunk_inputs = [{'chunk':c.page_content} for c in chunks]\n", + " summaries = map_chain.batch(chunk_inputs, config={'max_concurrency': 5})\n", + " print(f\"총 정크 수 {len(summaries)} 개 요약 생성\")\n", + "\n", + " #---- Reduce : 요약 통합\n", + " # 문서 결합\n", + " combined = \"\\n\\n\".join(f\"[섹션 {i+1}] {s}\" for i, s in enumerate(summaries))\n", + " final = reduce_chain.invoke({'summaries' : combined})\n", + " return final\n", + "\n", + "result = map_reduce_summarize('./data/직무기술서/2026 상 삼성E&A 직무기술서.pdf')\n", + "print(result)" + ], + "id": "239a2e466b376541", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "총 청크 수 41\n", + "총 정크 수 41 개 요약 생성\n", + "삼성E&A는 2026년 대학생 인턴 모집에서 기술직(설계/조달)과 기술직(사업관리/시공관리/품질) 두 가지 직군을 운영하며, 주요 전공 분야로는 화학/화공, 기계, 재료/금속, 전기전자(HW), 토목, 건축을 포함한다. 기술직(설계/조달)은 프로젝트의 최적화 설계와 기자재 구매, 공정 관리 등을 담당하고, 기술직(사업관리/시공관리/품질)은 프로젝트 종료 단계의 수행 관리와 품질 보증, 안전 관리를 수행한다. 삼성E&A는 석유화학, 산업·환경, 바이오 의약품, 재생에너지와 수전해 기술 등 다양한 사업 분야에서 환경 기술과 지속 가능한 솔루션을 제공하며, 신입사원들은 각 분야에서 전문성을 쌓아 사업 관리, 기술 전문가, 프로젝트 관리자 등으로 성장할 수 있는 기회를 제공한다.\n" + ] + } + ], + "execution_count": 44 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2026-06-05T08:21:18.906741845Z", + "start_time": "2026-06-05T08:21:18.888749584Z" + } + }, + "cell_type": "code", + "source": "", + "id": "a82774f4d34a1306", + "outputs": [], + "execution_count": 41 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ollama/rag_company.py b/ollama/rag_company.py index 21a4c8d..9b9b3e2 100644 --- a/ollama/rag_company.py +++ b/ollama/rag_company.py @@ -21,8 +21,6 @@ import os import shutil import pickle -# TODO : 일단 되게 함 공유되면 코드 비교해서 다른 부분 체크 - # 모델(LLM, Embedding) load_dotenv() @@ -105,7 +103,7 @@ META_FILEDS = [ ] # 대화 메모리 -memory = ConversationBufferWindowMemory(k=5, memory_key="chat_history", return_messages=True, output_key="answer", input_key="question") # TODO : input_key="question" 이것도 추가했음 +memory = ConversationBufferWindowMemory(k=5, memory_key="chat_history", return_messages=True, output_key="answer", input_key="question") SYSTEM_PROMPT = """\ 당신은 회사 내부 문서를 기반으로 직원들의 질문에 답하는 AI 어시스턴트입니다. @@ -124,7 +122,7 @@ QA_PROMPT = ChatPromptTemplate.from_messages([ {context} [질문] {question}""" - ), # TODO : 이것도 원래 query 였는데 question으로 변경했음 + ), ]) # ======================= @@ -237,7 +235,7 @@ def upload_files(files): SELFQUERY_RETRIEVER = None FINAL_RETRIEVER = None CHUNKS = [] - VECTORSTORE = None # TODO : 원래 []였는데 None로 바꿈 + VECTORSTORE = [] # 💡 [Check] 파일이 업로드되지 않고 빈 상태로 버튼을 누른 경우 처리 if files is None: @@ -377,7 +375,7 @@ def create_chain(): retriever = FINAL_RETRIEVER, memory = memory, combine_docs_chain_kwargs = {"prompt": QA_PROMPT}, - get_chat_history=lambda h: h, # TODO : 이거 맞음? + get_chat_history=lambda h: h, return_source_documents = True, ) diff --git a/ollama/research_report_20260605_131335.md b/ollama/research_report_20260605_131335.md new file mode 100644 index 0000000..35886c1 --- /dev/null +++ b/ollama/research_report_20260605_131335.md @@ -0,0 +1,4 @@ + +리서치 결과를 Markdown 파일로 저장합니다. +최종 리포트 작성이 완료되었을 떄 사용하세요. +입력 : Markdown 형식의 리포트 전체 내용 diff --git a/ollama/research_report_20260605_144723.md b/ollama/research_report_20260605_144723.md new file mode 100644 index 0000000..b666010 --- /dev/null +++ b/ollama/research_report_20260605_144723.md @@ -0,0 +1,77 @@ +# AI 반도체 시장 2026년 전망 리서치 리포트 + +## 1. 시장 규모 및 성장률 + +| 항목 | 2024년 | 2026년 (예측) | CAGR | +|------|--------|---------------|------| +| 시장 규모 | $627 B | **$834.5 B** | **≈ 10 %** | +| 성장 동력 | AI·머신러닝 워크로드 급증, 전기차·스마트팩토리 등 | AI 가속기·HBM·에지 AI 칩 수요 확대 | + +*계산 과정* +- 2024년 기준 $627 B → 2026년 2년간 CAGR 10 % 가정 +- $627 B × (1 + 0.10)² = **$758.67 B** (2년 성장) +- 추가 10 % 성장 (2025‑2026 연평균) → **$834.5 B** + +> **※** 위 계산은 2024‑2026 기간 동안 AI 반도체 수요가 연평균 10 % 성장한다는 보수적 가정에 기반합니다. + +## 2. 주요 기업 + +| 순위 | 기업 | 2026년 시가총액* | 주요 AI 반도체 제품·기술 | 시장 점유율(예상) | +|------|------|----------------|--------------------------|-------------------| +| 1 | **NVIDIA** | $4.32 T | H100, H200, Grace CPU, AI‑전용 GPU | 30 % | +| 2 | **TSMC** | $1.76 T | 3nm·5nm AI 패키징, HBM·CoWoS | 20 % | +| 3 | **AMD** | $1.12 T | Instinct MI300X, EPYC CPU‑GPU 통합 | 12 % | +| 4 | **Intel** | $0.95 T | Gaudi3, Xeon CPU‑AI 가속기 | 10 % | +| 5 | **Qualcomm** | $0.68 T | Snapdragon 8 Gen 3, AI Edge SoC | 8 % | +| 6 | **Samsung** | $0.62 T | 3nm·2nm AI 메모리·시스템 LSI | 7 % | +| 7 | **Google (TPU)** | $0.55 T | TPU v5, Edge TPU | 3 % | +| 8 | **Other (Broadcom, Marvell, etc.)** | — | — | 10 % | + +\* 2026년 3월 기준 시가총액(공개 자료). + +### 2.1 NVIDIA +- **시장 지배력**: AI 가속기 시장 점유율 70 % 이상. +- **성장 엔진**: Hopper 아키텍처(H100/H200)와 Grace CPU 통합으로 데이터센터 AI‑트레이닝·추론 수요를 선도. +- **전략**: AI‑전용 메모리(HBM3e)·에지 AI 칩(Jetson Orin) 확대. + +### 2.2 TSMC +- **제조 리더십**: 3nm·2nm 공정 수요 급증, AI 칩 패키징·시스템 LSI 공급량 40 % 이상. +- **전략**: 고밀도 패키징(HBM·CoWoS)·에너지 효율 공정 개발. + +### 2.3 AMD +- **다양화 전략**: CPU‑GPU 통합 칩셋(HBM2e·Infinity Fabric)로 데이터센터·클라우드 AI 시장 공략. + +### 2.4 Intel +- **재도약**: Gaudi3 AI 가속기와 Xeon CPU‑AI 가속기 통합으로 데이터센터 AI‑트레이닝·추론 시장 재진출. + +### 2.5 Qualcomm & Samsung +- **에지 AI 강화**: 모바일·IoT·자동차용 AI 가속기·HBM‑통합 SoC 개발. + +## 3. 핵심 트렌드 + +| 트렌드 | 설명 | 시장 영향 | +|--------|------|-----------| +| **에지 AI** | 디바이스 현장에서 실시간 추론·학습 | 에지 AI 칩·저전력 AI 가속기 수요 급증 | +| **Generative AI** | GPT‑4/5, Stable Diffusion 등 콘텐츠 생성 AI | 대용량 메모리·HBM·고성능 GPU·AI 가속기 수요 증가 | +| **AI 가속기** | 전용 ASIC·FPGA·AI‑전용 GPU | 데이터센터·클라우드 AI‑트레이닝·추론 성능 향상 | +| **파워 효율** | 에너지 절감·지속가능성 요구 | 저전력 AI ASIC·HBM·3nm·2nm 공정 채택 | +| **HBM (High Bandwidth Memory)** | AI 칩과 직접 통합, 대역폭 1 TB/s 이상 | 데이터센터 AI‑트레이닝·추론 성능 극대화 | +| **데이터센터 AI 트레이닝·추론** | 대규모 모델 학습·실시간 추론 | H100/H200, Gaudi3, Instinct 등 고성능 AI 칩 수요 | +| **AI‑전용 메모리·패키징** | HBM·CoWoS·3D‑스택 | 칩 면적·전력 절감, 성능 향상 | + +## 4. 시장 전망 요약 + +- **2026년 시장 규모**: **$834.5 B** (CAGR ≈ 10 %). +- **성장 주도 요인**: + 1. **AI 워크로드 급증** – 데이터센터·클라우드·에지 AI 모두에서 AI 연산량 2배 이상 증가. + 2. **에지 AI 확산** – 자율주행차, 스마트팩토리, IoT 디바이스에서 실시간 AI 처리 필요. + 3. **HBM·3nm·2nm 공정 도입** – 고성능·저전력 AI 칩 수요 증가. + 4. **Generative AI·대형 언어 모델** – GPU·AI 가속기·HBM 수요 급증. +- **위험·불확실성** + - **공급망·공정 위험** – TSMC·Samsung 공정 용량 한계, 원자재 가격 변동. + - **규제·데이터 프라이버시** – AI 모델·데이터 사용 규제 강화. + - **기술 변화 속도** – AI 알고리즘·하드웨어 수명 단축 위험. + +## 5. 전망 요약 + +AI 반도체 \ No newline at end of file diff --git a/ollama/research_report_20260605_144803.md b/ollama/research_report_20260605_144803.md new file mode 100644 index 0000000..f8baa40 --- /dev/null +++ b/ollama/research_report_20260605_144803.md @@ -0,0 +1,73 @@ +## AI 반도체 시장 2026년 전망 리서치 리포트 + +--- + +### 1. 시장 규모 및 성장률 + +| 항목 | 2024년 | 2025년 (예측) | 2026년 (예측) | CAGR* | +|------|--------|---------------|---------------|-------| +| 시장 규모 (USD) | 627 억 달러 | 758.7 억 달러 | **834.5 억 달러** | **≈ 10 %** | +| 성장 동력 | AI·데이터센터 확장, 전기차·IoT 확산 | AI 가속기·에지 AI 수요 증가 | AIoT·생성형 AI 칩 수요 급증 | | + +\* **CAGR** = Compound Annual Growth Rate (복합 연평균 성장률) +계산: +- 2024 → 2025: 627 × 1.10 = 758.7 억 달러 +- 2025 → 2026: 758.7 × 1.10 = 834.5 억 달러 + +> **※** 2024년 실적 기준 10 %의 연평균 성장률을 가정하면 2026년 시장 규모는 약 **835 억 달러**에 달한다. + +--- + +### 2. 주요 기업 + +| 순위 | 기업 | 2026년 예상 매출 (AI 칩 부문) | 주요 제품·기술 | +|------|------|------------------------------|----------------| +| 1 | **NVIDIA** | 46 억 달러 | H100, H200 GPU, AI 가속기 | +| 2 | **Intel** | 49 억 달러 | Gaudi, Habana AI 가속기 | +| 3 | **AMD** | 30 억 달러 | Instinct GPU, MI300X | +| 4 | **Qualcomm** | 32 억 달러 | Snapdragon AI 엔진, 엣지 AI | +| 5 | **Samsung Electronics** | 66 억 달러 | HBM·AI 가속기, 파운드리 (7nm/5nm) | +| 6 | **TSMC** | 42 억 달러 | 3nm·5nm AI 칩 제조 (파운드리) | +| 7 | **SK Hynix** | 43 억 달러 | HBM2e/3, AI 메모리 | +| 8 | **Micron** | 20 억 달러 | DRAM·NAND, AI 스토리지 | +| 9 | **Broadcom** | 15 억 달러 | 네트워킹·AI 가속기 | +| 10| **Marvell** | 10 억 달러 | AIoT 칩, 엣지 프로세서 | + +> **주요 특징** +> - **NVIDIA**는 AI 가속기 시장을 주도하며, 2026년까지 H100·H200 시리즈를 중심으로 매출을 확대할 것으로 전망. +> - **Intel**과 **AMD**는 자체 AI 가속기(​Gaudi, Instinct)를 통해 데이터센터·클라우드 시장을 공략. +> - **Samsung·TSMC**는 고성능 AI 칩을 위한 **3nm·5nm** 파운드리 용량 확장에 집중해 공급 안정성을 제공. +> - **SK Hynix**와 **Micron**은 **HBM**·메모리 수요 급증에 발맞춰 AI 메모리 시장을 선도. + +--- + +### 3. 핵심 트렌드 + +| 트렌드 | 설명 | 시장 영향 | +|--------|------|-----------| +| **에지 AI (Edge AI)** | 디바이스 현장에서 실시간 추론·학습을 수행해 대역폭 절감·지연 최소화 | AI 가속기·저전력 칩 수요 급증 | +| **생성형 AI (Generative AI)** | 텍스트·이미지·영상·코드 자동 생성 기술 확산 | 고성능 GPU·TPU·AI 가속기 수요 증가 | +| **AI 가속기·AI 칩** | 전용 AI 처리 장치(​TPU, ASIC, FPGA) 개발·상용화 | 데이터센터·클라우드·엣지에서 필수 | +| **AIoT (AI + IoT)** | IoT 디바이스에 AI 기능 내장 (예: 스마트 카메라, 센서) | 저전력·고효율 AIoT 칩 필요 | +| **AIoT·AI 보안** | AI 시스템의 데이터·모델 보안 강화 | 보안 칩·암호화 기술 수요 증가 | +| **데이터센터 AI** | AI 추론·학습을 위한 고성능 칩·HBM 메모리 | 데이터센터·클라우드 인프라 확장 | +| **5G·6G·연결성** | 고속 네트워크와 결합해 실시간 AI 서비스 제공 | 네트워크·AI 칩 통합 솔루션 필요 | + +--- + +### 4. 전망 요약 + +1. **시장 규모**는 2024년 627 억 달러에서 2026년 **834 억 달러**로 **≈ 10 % CAGR** 성장. +2. **AI 가속기·에지 AI**가 가장 큰 성장 동력이며, **생성형 AI**와 **AIoT** 칩 수요가 급증. +3. **NVIDIA·Intel·AMD** 등 기존 CPU/GPU 선두사와 **Samsung·TSMC**·**SK Hynix** 등 파운드리·메모리 기업이 시장을 주도. +4. **공급·기술 변화**에 따른 **투자·M&A** 활발 예상. +5. **위험 요소** – 기술 변동성, 공급망 불안, 규제·윤리적 이슈 등. + +--- + +### 5. 결론 + +AI 반도체 시장은 **데이터센터·클라우드·엣지** 영역에서 지속적인 수요 성장을 이끌며, **2026년까지 835 억 달러** 규모로 확대될 전망이다. **NVIDIA·Intel·AMD**와 같은 AI 가속기 선두사와 **파운드리·메모리** 기업들이 핵심 플레이어로 자리매김할 것이며, **에지 AI·생성형 AI·AIoT**와 같은 새로운 트렌드가 시장 구조를 재편할 것으로 예상된다. + +> **연구·투자 결정 시** +> - **성장률**과 **시장 규모**를 기 \ No newline at end of file