From 75008b05e48e1957bfb483327f3a44f430c17c58 Mon Sep 17 00:00:00 2001 From: LJ5O <75009579+LJ5O@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:22:32 +0100 Subject: [PATCH] =?UTF-8?q?Premi=C3=A8re=20version=20agent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dérivé TP3 --- .gitignore | 2 + AgentReact/__pycache__/agent.cpython-312.pyc | Bin 0 -> 1399 bytes AgentReact/agent.py | 32 +++++++++++++ AgentReact/start.py | 10 +++++ .../utils/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 1778 bytes .../utils/__pycache__/state.cpython-312.pyc | Bin 0 -> 730 bytes .../utils/__pycache__/tools.cpython-312.pyc | Bin 0 -> 1377 bytes AgentReact/utils/nodes.py | 42 ++++++++++++++++++ AgentReact/utils/state.py | 13 ++++++ AgentReact/utils/tools.py | 37 +++++++++++++++ agent.png | Bin 0 -> 12627 bytes requirements.txt | 18 ++++++++ 12 files changed, 154 insertions(+) create mode 100644 .gitignore create mode 100644 AgentReact/__pycache__/agent.cpython-312.pyc create mode 100644 AgentReact/agent.py create mode 100644 AgentReact/start.py create mode 100644 AgentReact/utils/__pycache__/nodes.cpython-312.pyc create mode 100644 AgentReact/utils/__pycache__/state.cpython-312.pyc create mode 100644 AgentReact/utils/__pycache__/tools.cpython-312.pyc create mode 100644 AgentReact/utils/nodes.py create mode 100644 AgentReact/utils/state.py create mode 100644 AgentReact/utils/tools.py create mode 100644 agent.png create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71b98ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv/ +.env \ No newline at end of file diff --git a/AgentReact/__pycache__/agent.cpython-312.pyc b/AgentReact/__pycache__/agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffe08d029d5436642e80cb8c245964fc9e671cfa GIT binary patch literal 1399 zcmZuwOK22H7_RQFp6ThHm|@qEtTEDLAv%PekwpbX7Q~1TRu74LnLQNERAtghzqY#C zkf12)LC}LYugl^k#-j)CbM!K^&Jt->L_92e8;B=QR#i{iz6!ekXZ^45ujdmJCg5jD zz2%w^fVV=(k2Yp@|KiL;fB{xK;45v#SKF#0WYyDr*oKnVJl!v~OMbas_Kmh7boI}8AwQvtEI$4OSF^Rxy1Xqzc?IfsoWm2D-H6tH1#m<{WH zAvV-hU%GO7@k*+ly}Zy+((lfMe&l)tFENLabJU4e8d_STBnpF=pg$54GdB#<3JXII z1tBKsWV{k49!8xoU~Z5Q9>wSqCIh4;N?1aJPF{(i2LHskcBgnr4*?q&20-4zb#M)= z<^vTs#)t8tXQyc9+n!iqrH{U9#wj9dCD#@dtDi-)3QNvmh5LLOWg@o}6sv4cE@6%5 zujNv~x$5?~D_P6;xko|s2`qsIOxC&HwE0==ZFQ2Uw?zrHJz{qSD#Y$cqq_5FEX-klcaMG8Sd2T93nCb%QxM5N;y3}wmmat2s zbmI#^7$O0q8z>6hfMreucS%|o?tC$@E4Ie*kkH0dYycHtrsb>)xisq`U0e3T^;j%~ z{r-4sCG<(lTRnE9^;_UxZC(1g<#c%-5M+SI+xo99O*pik_;`T*94`{su5&`Vg�P7SP7L8sV}}C+c%VPi8p0!= zIP`x!HiXBeVs-#$x%l+POJnkZ^{-*wKQq#`3rgu7(Dd@i1l7Ge*7U%deqqh@;mj_t zf=>`3-*E$kHY>85W)yV!lc|LXixP&G`Qt;8!&WXgCeumJ3Az&QruvUznP) zc(7C#DXfIKUfdL~GcItFQ_pRgw}4bmNmLa$K0zq0@VAR(wzP(+vyOa1dBGU*I=29= ziz{Mt-y5g=5GNk_h1y&cn8m;H-$+vw)BsMsfctmg`~c2B`@Ik6|A7}qI)M9Swhz6@On(3X literal 0 HcmV?d00001 diff --git a/AgentReact/agent.py b/AgentReact/agent.py new file mode 100644 index 0000000..39464fd --- /dev/null +++ b/AgentReact/agent.py @@ -0,0 +1,32 @@ +from langgraph.graph import START, END +from langgraph.graph.state import CompiledStateGraph + +from utils.nodes import reponse_question, tool_node, should_continue +from utils.state import getState + +def getGraph()->CompiledStateGraph: + """ + Récupérer le graphe compilé et prêt à invoquer + + Returns: + CompiledStateGraph: Graphe compilé + """ + workflow = getState() # State prêt à utiliser + + # Définition des sommets du graphe + workflow.add_node(reponse_question) + workflow.add_node("tool_node", tool_node) # N'est pas une fonction, mais une classe instanciée, je dois précisier le nom du node + + # Arrêtes + workflow.set_entry_point("reponse_question") + workflow.add_edge("tool_node", "reponse_question") + workflow.add_conditional_edges("reponse_question", should_continue, { + "tools":"tool_node", + END:END + }) + + return workflow.compile() + +if __name__ == "__main__": + # Affichage du graphe + getGraph().get_graph().draw_mermaid_png(output_file_path="agent.png") \ No newline at end of file diff --git a/AgentReact/start.py b/AgentReact/start.py new file mode 100644 index 0000000..66d1807 --- /dev/null +++ b/AgentReact/start.py @@ -0,0 +1,10 @@ +from dotenv import load_dotenv +load_dotenv() + +from langchain.messages import HumanMessage, SystemMessage, AIMessage, ToolMessage + +from agent import getGraph + +out_state = getGraph().invoke({'messages':[HumanMessage("What's the price for bitcoin ?")]}) +for message in out_state['messages']: + message.pretty_print() \ No newline at end of file diff --git a/AgentReact/utils/__pycache__/nodes.cpython-312.pyc b/AgentReact/utils/__pycache__/nodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fec14c7b3935517009b96c582c50aad411a10e70 GIT binary patch literal 1778 zcmZ8h&2Jk;6rbJo=Q_6261PcH&>}=zqsAtYDoO}Q6-^OEF;bEu#X_<+*@-jm?5>#^ zhc-&3sBmab&{JJnM31eK0}_8iFO|5sBofhxhy&%2TdMTb3%r?l6Uu1!&6_vxy?OI~ z@6E5-tc_s&V%{W+afJR-i)g@FkG&sYx`RCA85H?O)i4xqQq#ApmgX%Q^W)XH=3_MB zC#y-#$3Z7mP5J3+8rBIfNo_w<%^1i;*zi)|CB1I1S^LLSd%O&`=1s5X+gLRV8gckD zYx|pMJ-cRf^>SUk9O&h*=Zn2NYNKe#?8ghPm>^s*mtOc(2_`V-ZXNR};R+0b)N~lq zO6Vy*PXZ!pb6`^QK|+hBj89Epn4Ff@c;(`bI!#&Bu~0^Qsn6f{JVaeq$3O+#y@LuEsr;(D?=^tjK?3OhXmm3Yt<@|d zG*Fb0C?7Wr&sa7Z$^UBa^UHwP0I-o}-5|ykDw?fH5LQCm@|>?)#9@ygHN(K;&f^CT zbwLF8B4KdW!GXh1yTo$?;c(*2Vjhyf@i2G7mLOCcc!W1YPLzR6`Q3CEW74A3m-Z|P zyqZwyW2(+%f&`1J2v-?&oR?$eh0w=k+Bo-a`AR?*%M&AIfG!X<;?|lh zY+zBopw^SvofBmpKsf;5c&T|^_A(52;J6k=&yIoHBg+2>kQMYIfwH~dRaPta&)*x} z=sC4Az6+A$?ZQtd@1A^sA6|ZX>b;GD^PT?pJDJf=Y;+H-6#j7?{VTY|U`OM&2@K$N zfax+4>N0w)bvc%ZP`F_PXO>ap0LMrZK`+%&q<_u0VS|2O)r)|GaXle&kqT5ua_Jk}fpLXn2M`wv*qMV0 z(I~sLhP^s=MCdRm1I;LfsIwe0uMMcnh0`qv?QFs2U=&hk&ZU%>oX^3EeN8w%`>G=x zcqMJH^A8NBFkC8E!pIkQ=EGLtsklhcY(ZtJ#!}%Z=Q_4gN7{O!PhtuQ)(@nZk~ZNa z0E=J_%NT`*mKHR)w7+m^3y(7vGMR)xT_G5hFb>$DGEOODOV_NlT~g~_OUK4lDL}4R z0cM%!3t@|THAt5rK?}3DKzb5>?gN1`v{CBl_ix|t+f2T(RXEWexjnOL19NBQ_Dp-} z;mAgz)Jd0a^#j=|yxyL@{pG6tEZ5%|y!_r9@|WwS)cm( z#P7qGo(^By82aSNo4*Zxwv|8jM<%y&nH`4{E5%;<8g+v@ymKU|b)PVoFcr1TtDQP? zn+qjPnJR60^ibMlc(z5TV5(|m;lNSp@Y&=n0eegpsm_zmwC;9xRLN8y%Cv?iuty+AckPaaf+9gvmBy`atM0EOaWl6m|IoMEWz<)qD zXUv-B&&b%N&<;gQhfduB_7o`e-66WFQ(Hwr-J+Zm=H(kM1Es95d>16#aT~rHD z>pnqIbQrn5_nm-8>T`5CF3K@iCBnE{7w*Fd-AdeiQSQy9tE}p0!>Um zE5-XbFVCDc2sv`k7@GV*+xs79vOFqu&27v2KWUrPPt)`gt_(qJp{a)=pP~8$tEbT4 z{;@s$zRAAX%$zv#I?Yxd7D=L?e`s_|w(&@d*A(&ij=SD|`;f22bByZoNEg+uMF{x? ZyDOWJCl?=AfXi)ngY0~ER)CAS^B=_-v8ez6 literal 0 HcmV?d00001 diff --git a/AgentReact/utils/__pycache__/tools.cpython-312.pyc b/AgentReact/utils/__pycache__/tools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2040898cbe036b6c92b58567ba165e6cdba82ab GIT binary patch literal 1377 zcmZuw&1(}u6rb6z=F@^yTeaZyWOFg1RHT@Sgo*|A5L<*?;<7tQr)+lP?5q;$L8J%8 zi-MpB55be6{ueoVi6|(G1;LZIU_5#9&193d;=s&%GjHCT-+R9|AM*JOf^h{sW|of7 z54jl+Gf{>sXpAzQR1KzX+3w#nr9QJ~8pNfpSe3duCWu-}1g_10Wf)|#_I^|7DcoMDGO@hk= z4!F3?5zFwV2q3mgiDyq-sPc#nI}!m@!fXaE^$Hr78OK&gZ2(!o+=6W=vnGEo7dL|@ zEqYr^=ZYIXyI;I{zGyd~kTqu4T0GdIqIgyAE7W#GF%rxRi?Z#UXlR@fAJiKsiW{ zi+2;M@Bz}c(f(;$16tmKo2o-+FJSg z+G?d#ijDyIYa?i*CuB#PEe?eS;-QSOF~7lO^Tj%pKQ`n)*?(WX#E*e_AIG26 zW%wbHM_0J)SJ_#&{2rJ#=~x&3?z`KuhC`uP+P`h|bI}DTX@IEOkDZQ(6ws fzt#r`L`VN6MREhQ!MuSB&yDAIUgUoxP!)o|=<-W& literal 0 HcmV?d00001 diff --git a/AgentReact/utils/nodes.py b/AgentReact/utils/nodes.py new file mode 100644 index 0000000..00e7ef4 --- /dev/null +++ b/AgentReact/utils/nodes.py @@ -0,0 +1,42 @@ +from langchain_mistralai import ChatMistralAI +from langgraph.graph import MessagesState +from langgraph.prebuilt import ToolNode +from langchain.chat_models import init_chat_model +from langgraph.graph import START, END + +from .tools import getTools + +# LLM principal +llm = ChatMistralAI( # LLM sans outils + model="mistral-large-latest", + temperature=0, + max_retries=2, +) + +# NODES +def reponse_question(state: MessagesState): + """Noeud qui réponds à la question, en s'aidant si besoin des outils à disposition""" + # Initialisation du LLM + model = llm.bind_tools(getTools()) + + # Appel du LLM + return {"messages": [model.invoke(state["messages"])]} + +tool_node = ToolNode(tools=getTools()) # Node gérant les outils + +# fonction de routage : Après reponse_question, si le LLM veut appeler un outil, on va au tool_node, sinon on termine +def should_continue(state: MessagesState): + """ + Use in the conditional_edge to route to the ToolNode if the last message + has tool calls. Otherwise, route to the end. + """ + if isinstance(state, list): + ai_message = state[-1] + elif messages := state.get("messages", []): + ai_message = messages[-1] + else: + raise ValueError(f"No messages found in input state to tool_edge: {state}") + + if hasattr(ai_message, "tool_calls") and len(ai_message.tool_calls) > 0: + return "tools" + return END \ No newline at end of file diff --git a/AgentReact/utils/state.py b/AgentReact/utils/state.py new file mode 100644 index 0000000..da1fa34 --- /dev/null +++ b/AgentReact/utils/state.py @@ -0,0 +1,13 @@ +from langgraph.graph import StateGraph, MessagesState + +class hjgzefvuiyguhzfvihuozdef(MessagesState): # J'ai du mal à nommer mes classes ._. + pass + +def getState()->StateGraph: + """ + Retourne un StateGraph prêt à utiliser pour préparer un workflow + + Returns: + StateGraph: prêt à utiliser + """ + return StateGraph(hjgzefvuiyguhzfvihuozdef) \ No newline at end of file diff --git a/AgentReact/utils/tools.py b/AgentReact/utils/tools.py new file mode 100644 index 0000000..503531f --- /dev/null +++ b/AgentReact/utils/tools.py @@ -0,0 +1,37 @@ +from langchain.tools import tool +from tavily import TavilyClient +from typing import List + +@tool +def internet_search(query: str)->dict: + """ + Rechercher une information sur internet + + Args: + query (str): Terme recherché + Returns: + dict: Retour de la recherche + """ + return TavilyClient().search(query, model='auto') + + +@tool +def write_file(content: str) -> str: + """ + Écrire les données dans un fichier + + Args: + content (str): Contenu du fichier à écrire + + Returns: + str: Résultat de l'écriture + """ + print("==ECRITURE FICHIER==") + print(content) + return "Fichier écrit" + +def getTools()->List['Tools']: + """ + Récupérer la liste des tools + """ + return [internet_search, write_file] \ No newline at end of file diff --git a/agent.png b/agent.png new file mode 100644 index 0000000000000000000000000000000000000000..89e34a06a2d22bd527a9dbe10773e9d550b0ff8c GIT binary patch literal 12627 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 CDSF2M literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..78de078 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,18 @@ + beautifulsoup4>=4.14.2 + bs4>=0.0.2 + einops>=0.8.1 + jupyterlab>=4.5.0 + jupyterlab-slideshow>=0.3.4 + langchain>=1.1.0 + langchain-chroma>=1.0.0 + langchain-community>=0.4.1 + langchain-huggingface>=1.1.0 + langchain-mcp-adapters>=0.2.1 + langchain-mistralai>=1.1.0 + langchain-text-splitters>=1.0.0 + sentence-transformers>=5.1.2 + tavily-python>=0.7.14 + unstructured[all-docs]>=0.18.21 + ipywidgets + peft + python-dotenv \ No newline at end of file