diff --git a/book/1-crystalstructure-agent-anthropic.ipynb b/book/1-crystalstructure-agent-anthropic.ipynb new file mode 100644 index 0000000..9673838 --- /dev/null +++ b/book/1-crystalstructure-agent-anthropic.ipynb @@ -0,0 +1,440 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "abebe249-91bd-44ad-bcec-79bf4de2e1a6", + "metadata": {}, + "source": [ + "# Crystalstructure Agent\n", + "As a first demonstration the langchain team decided to develop a LLM agent, which can predict the crystal structure of a chemical element, by accessing the reference database of the [Atomic Simulation Environment](https://wiki.fysik.dtu.dk/ase/). While the corresponding python function is simple and only requires a few lines of code, this example already limits the hallucinations of the LLM, by using the [langchain](https://www.langchain.com) framework to interface the LLM with the python function. \n", + "\n", + "In particular, we follow the [Custom Agent](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) tutorial from the Langchain documentation. " + ] + }, + { + "cell_type": "markdown", + "id": "42b74827-5fe4-4200-8623-a650525cf5e7", + "metadata": {}, + "source": [ + "## Python Function\n", + "For this first example, we use [Anthropic](https://www.anthropic.com) as LLM provider but the example can also be adjusted to work with other LLM providers, for more details check the [langchain documentation](https://python.langchain.com/v0.2/docs/integrations/platforms/). We store the Anthropic API key in the `ANTHROPIC_API_KEY` variable: " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6906a90b-4a3d-4aa5-b649-33e8caf829f3", + "metadata": {}, + "outputs": [], + "source": [ + "from getpass import getpass" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "34ee60c4-924e-4af5-b736-d474c7a1086c", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your Anthropic Token: ········\n" + ] + } + ], + "source": [ + "ANTHROPIC_API_KEY = getpass(prompt='Enter your Anthropic Token:')" + ] + }, + { + "cell_type": "markdown", + "id": "136e7571-ac3b-4587-82b5-3351d6a6d3f2", + "metadata": {}, + "source": [ + "As a next step, we import the corresponding functionality from `ASE` and the `tool` decorator from `langchain`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "bf956016-ef67-4ffa-93ae-06cb4e242145", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.data import reference_states, atomic_numbers\n", + "from langchain.agents import tool" + ] + }, + { + "cell_type": "markdown", + "id": "3aae31dd-fb84-45f9-81b9-3f1f18786f4f", + "metadata": {}, + "source": [ + "For the python function, it is important to include `type` hints and documentation based on a Docstring for the LLM to understand the functionality of the function. Finally, all data types used as input or output of the function need to have a `JSON` representation so they can be communicated to the LLM. For example, numpy arrays have to be converted to standard python lists. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a985d5ac-7b52-4f7c-bb16-24400ebaba1c", + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def get_crystal_structure(chemical_symbol: str) -> str:\n", + " \"\"\"Returns the atomic crystal structure of a chemcial symbol\"\"\"\n", + " ref_state = reference_states[atomic_numbers[chemical_symbol]]\n", + " if ref_state is None:\n", + " return \"No crystal structure known.\"\n", + " else:\n", + " return ref_state[\"symmetry\"]" + ] + }, + { + "cell_type": "markdown", + "id": "93b51c33-91c8-42d0-9e89-e0012879f122", + "metadata": {}, + "source": [ + "After applying the `decorator`, the functions can be called using `invoke()`. We validate the functionality for two elements iron (Fe) and gold (Au). " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d939068-506e-4fbe-aac9-97ca482d2b6e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'bcc'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_crystal_structure.invoke(\"Fe\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "07aa9ba5-6575-4022-b707-4c5433f86166", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'fcc'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_crystal_structure.invoke(\"Au\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c888942-335e-43eb-b088-47c155eef0b3", + "metadata": {}, + "source": [ + "## Define Agent\n", + "After the definition of the Python function, the next step is the definition of the agent which the LLM uses to interact with the Python function. In this example the `ChatAnthropic` interface of the `langchain_anthropic` package is used. Depending on your configuration, it might be necessary to install the `langchain_anthropic` package using the following command: \n", + "```\n", + "conda install -c conda-forge langchain-anthropic\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6529e329-0947-443a-8325-42ef3510803d", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_anthropic import ChatAnthropic\n", + "\n", + "llm = ChatAnthropic(\n", + " model=\"claude-3-5-sonnet-20240620\",\n", + " temperature=0,\n", + " anthropic_api_key=ANTHROPIC_API_KEY,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "752d18e2-4645-4af9-a55c-1adafd08a0af", + "metadata": {}, + "source": [ + "Following the definition of the LLM, the next step is the definition of the prompt. Here we start with a very basis prompt. In the following section on prompt engineering the optimization of the prompt is discussed in more detail." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c2edad58-849b-424f-8460-92507e9545ac", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are very powerful assistant, but don't know current events.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "367384b0-1fe6-43cf-a2b5-b3689e65b9fa", + "metadata": {}, + "source": [ + "Finally, the different parts are plugged together, by creating an agent which combines the prompt with the python function referenced here as one tool in a list of potentially many tools and an executor to communicate with the agent. The technical details are discussed in the corresponding [langchain Custom Agent tutorial](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c1e01a3b-8b21-4538-b67d-6d5c40c66f94", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents import AgentExecutor\n", + "from langchain.agents.format_scratchpad.openai_tools import (\n", + " format_to_openai_tool_messages,\n", + ")\n", + "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n", + "\n", + "tools = [get_crystal_structure]\n", + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b0d455bd-28bf-44c9-97e9-91f9cb3552cc", + "metadata": {}, + "source": [ + "## Conversation\n", + "Once the `AgentExecutor` is defined we can communicate with the LLM using the `stream()` interface. We repeat the test from above and ask for the crystal structure of gold." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ac9edf53-8963-4b58-bcb6-742106424757", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_crystal_structure` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': 'To answer your question about the crystal structure of gold, I can use the available tool to retrieve this information. Let me do that for you.', 'type': 'text', 'index': 0}, {'id': 'toolu_01CjhjuywsNK48xWcJ7YK2ND', 'input': {}, 'name': 'get_crystal_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3mfcc\u001b[0m\u001b[32;1m\u001b[1;3m[{'text': \"\\n\\nBased on the result from the tool, I can tell you that the crystal structure of gold (Au) is face-centered cubic (fcc).\\n\\nFace-centered cubic (fcc) is one of the most common crystal structures found in metals. In this structure, atoms are arranged at the corners of a cubic lattice, with additional atoms at the center of each face of the cube. This arrangement gives gold its characteristic properties, such as its malleability and ductility.\\n\\nSome key features of the fcc structure in gold include:\\n\\n1. High atomic packing factor, which contributes to gold's high density.\\n2. Good ductility and malleability, allowing gold to be easily shaped into thin sheets or wires.\\n3. Excellent thermal and electrical conductivity.\\n4. Twelve nearest neighbors for each atom, contributing to its stability.\\n\\nThis crystal structure is one of the reasons why gold is so valued in jewelry making and various industrial applications, as it allows for easy manipulation while maintaining its desirable properties.\", 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"What is the crystal structure of gold\"})) # Yeah this worked !!" + ] + }, + { + "cell_type": "markdown", + "id": "43bc4d75-bae7-4fe2-8005-52e2629eec8d", + "metadata": {}, + "source": [ + "With the `verbose=True` parameter the internal steps of the LLM agent are printed in green. As a first step the agent calls the `get_crystal_structure()` already with the converted input parameter, rather than using gold as input it uses the chemical symbol `Au`. The function returns `fcc` and the LLM converts this answer in a sentence a human can understand: \n", + "```\n", + "The crystal structure of gold is face-centered cubic (fcc).\n", + "```\n", + "This example highlights how easy it is these days to make a python function accessible via a LLM for all kinds of users to interact with this python funtion." + ] + }, + { + "cell_type": "markdown", + "id": "bc28370b-6002-4685-9884-44432099b5ea", + "metadata": {}, + "source": [ + "## Prompt Engineering\n", + "Some LLMs like the ChatGPT3.5 model start to hallucinate rather quickly, for example by asking for the crystal structure of a car, it does not ask which elements a car typically consists of, but rather connects car with carbon and replies the crystal structure of a car is diamond, which is obviously wrong. Luckily, the Antropic Claude model does not make this mistake. " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7ac99ce9-f93c-406f-8348-f16793b1059e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m[{'text': 'I apologize, but I need to clarify something before I can answer your question. The term \"car\" is not a chemical symbol or element. To determine a crystal structure, we need to specify a chemical element or compound.\\n\\nThe tool available to us can provide information about the crystal structure of chemical elements based on their chemical symbols. For example, we could look up the crystal structure of iron (Fe), carbon (C), or aluminum (Al), which are common elements used in car manufacturing.\\n\\nCould you please specify which chemical element you\\'re interested in learning about? Once you provide a valid chemical symbol, I can use the tool to retrieve its crystal structure information for you.', 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"What is the crystal structure of car\"})) # I did not know cars were made of carbon" + ] + }, + { + "cell_type": "markdown", + "id": "fe544511-f499-4bdf-b24a-67fed6dd7ce4", + "metadata": {}, + "source": [ + "To restrict the hallucination of the agent we extend the system prompt with the following statement:\n", + "```\n", + "For each query vailidate that it contains a chemical element and otherwise cancel. \n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4ffbd77b-7fc4-47a4-8c7b-4f179d453c9b", + "metadata": {}, + "outputs": [], + "source": [ + "prompt_improved = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " # \"You are very powerful assistant, but don't know current events.\",\n", + " \"You are very powerful assistant, but don't know current events. For each query vailidate that it contains a chemical element and otherwise cancel.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d3b4b76d-a455-41cf-89f7-318061c95ac5", + "metadata": {}, + "outputs": [], + "source": [ + "agent_improved = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt_improved\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_improved_executor = AgentExecutor(agent=agent_improved, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "3033a731-5605-4103-9da8-63c56655a53f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m[{'text': 'I apologize, but \"car\" is not a chemical element. To provide information about crystal structures, I need a valid chemical symbol for an element. Could you please provide the chemical symbol for an element you\\'d like to know about? For example, you could ask about elements like carbon (C), iron (Fe), or gold (Au).', 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_improved_executor.stream({\"input\": \"What is the crystal structure of car\"}))" + ] + }, + { + "cell_type": "markdown", + "id": "f23ddd66-9032-41e0-90fb-a574845ce426", + "metadata": {}, + "source": [ + "With the modified system prompt, the all LLM models correctly reply that they were not able to determine the crystal structure of a car, because they fail to determine the chemical element a car consists of. " + ] + }, + { + "cell_type": "markdown", + "id": "4a7f9b5f-cfe3-4b9c-9190-33d9505beedd", + "metadata": {}, + "source": [ + "## Summary\n", + "By following the [Custom Agent](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) tutorial from the Langchain documentation, we were able to create a first simulation agent, which calls specialized python frameworks like [ASE](https://wiki.fysik.dtu.dk/ase/) to address material-science specific questions. Still, it is important to carefully engineer the prompt of the agent, otherwise even these LLMs with access to specialized agents tend to hallucinate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7124521-c58d-4700-bb51-7eb97be53588", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/book/1-crystalstructure-agent-groq.ipynb b/book/1-crystalstructure-agent-groq.ipynb new file mode 100644 index 0000000..b3138b7 --- /dev/null +++ b/book/1-crystalstructure-agent-groq.ipynb @@ -0,0 +1,448 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "abebe249-91bd-44ad-bcec-79bf4de2e1a6", + "metadata": {}, + "source": [ + "# Crystalstructure Agent\n", + "As a first demonstration the langchain team decided to develop a LLM agent, which can predict the crystal structure of a chemical element, by accessing the reference database of the [Atomic Simulation Environment](https://wiki.fysik.dtu.dk/ase/). While the corresponding python function is simple and only requires a few lines of code, this example already limits the hallucinations of the LLM, by using the [langchain](https://www.langchain.com) framework to interface the LLM with the python function. \n", + "\n", + "In particular, we follow the [Custom Agent](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) tutorial from the Langchain documentation. " + ] + }, + { + "cell_type": "markdown", + "id": "42b74827-5fe4-4200-8623-a650525cf5e7", + "metadata": {}, + "source": [ + "## Python Function\n", + "For this first example, we use [Groq](https://groq.com) as LLM provider but the example can also be adjusted to work with other LLM providers, for more details check the [langchain documentation](https://python.langchain.com/v0.2/docs/integrations/platforms/). We store the Groq API key in the `GROQ_API_KEY` variable: " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6906a90b-4a3d-4aa5-b649-33e8caf829f3", + "metadata": {}, + "outputs": [], + "source": [ + "from getpass import getpass" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "34ee60c4-924e-4af5-b736-d474c7a1086c", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your Groq Token: ········\n" + ] + } + ], + "source": [ + "GROQ_API_KEY = getpass(prompt='Enter your Groq Token:')" + ] + }, + { + "cell_type": "markdown", + "id": "136e7571-ac3b-4587-82b5-3351d6a6d3f2", + "metadata": {}, + "source": [ + "As a next step, we import the corresponding functionality from `ASE` and the `tool` decorator from `langchain`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "bf956016-ef67-4ffa-93ae-06cb4e242145", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.data import reference_states, atomic_numbers\n", + "from langchain.agents import tool" + ] + }, + { + "cell_type": "markdown", + "id": "3aae31dd-fb84-45f9-81b9-3f1f18786f4f", + "metadata": {}, + "source": [ + "For the python function, it is important to include `type` hints and documentation based on a Docstring for the LLM to understand the functionality of the function. Finally, all data types used as input or output of the function need to have a `JSON` representation so they can be communicated to the LLM. For example, numpy arrays have to be converted to standard python lists. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a985d5ac-7b52-4f7c-bb16-24400ebaba1c", + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def get_crystal_structure(chemical_symbol: str) -> str:\n", + " \"\"\"Returns the atomic crystal structure of a chemcial symbol\"\"\"\n", + " ref_state = reference_states[atomic_numbers[chemical_symbol]]\n", + " if ref_state is None:\n", + " return \"No crystal structure known.\"\n", + " else:\n", + " return ref_state[\"symmetry\"]" + ] + }, + { + "cell_type": "markdown", + "id": "93b51c33-91c8-42d0-9e89-e0012879f122", + "metadata": {}, + "source": [ + "After applying the `decorator`, the functions can be called using `invoke()`. We validate the functionality for two elements iron (Fe) and gold (Au). " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6d939068-506e-4fbe-aac9-97ca482d2b6e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'bcc'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_crystal_structure.invoke(\"Fe\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "07aa9ba5-6575-4022-b707-4c5433f86166", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'fcc'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_crystal_structure.invoke(\"Au\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c888942-335e-43eb-b088-47c155eef0b3", + "metadata": {}, + "source": [ + "## Define Agent\n", + "After the definition of the Python function, the next step is the definition of the agent which the LLM uses to interact with the Python function. In this example the `ChatGroq` interface of the `langchain_groq` package is used. Depending on your configuration, it might be necessary to install the `langchain_groq` package using the following command: \n", + "```\n", + "conda install -c conda-forge langchain-groq\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6529e329-0947-443a-8325-42ef3510803d", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_groq import ChatGroq\n", + "\n", + "llm = ChatGroq(\n", + " model=\"llama3-70b-8192\",\n", + " temperature=0,\n", + " groq_api_key=GROQ_API_KEY,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "752d18e2-4645-4af9-a55c-1adafd08a0af", + "metadata": {}, + "source": [ + "Following the definition of the LLM, the next step is the definition of the prompt. Here we start with a very basis prompt. In the following section on prompt engineering the optimization of the prompt is discussed in more detail." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c2edad58-849b-424f-8460-92507e9545ac", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are very powerful assistant, but don't know current events.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "367384b0-1fe6-43cf-a2b5-b3689e65b9fa", + "metadata": {}, + "source": [ + "Finally, the different parts are plugged together, by creating an agent which combines the prompt with the python function referenced here as one tool in a list of potentially many tools and an executor to communicate with the agent. The technical details are discussed in the corresponding [langchain Custom Agent tutorial](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c1e01a3b-8b21-4538-b67d-6d5c40c66f94", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents import AgentExecutor\n", + "from langchain.agents.format_scratchpad.openai_tools import (\n", + " format_to_openai_tool_messages,\n", + ")\n", + "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n", + "\n", + "tools = [get_crystal_structure]\n", + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b0d455bd-28bf-44c9-97e9-91f9cb3552cc", + "metadata": {}, + "source": [ + "## Conversation\n", + "Once the `AgentExecutor` is defined we can communicate with the LLM using the `stream()` interface. We repeat the test from above and ask for the crystal structure of gold." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ac9edf53-8963-4b58-bcb6-742106424757", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_crystal_structure` with `{'chemical_symbol': 'Au'}`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3mfcc\u001b[0m\u001b[32;1m\u001b[1;3mThe crystal structure of gold is Face-Centered Cubic (FCC).\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"What is the crystal structure of gold\"})) # Yeah this worked !!" + ] + }, + { + "cell_type": "markdown", + "id": "43bc4d75-bae7-4fe2-8005-52e2629eec8d", + "metadata": {}, + "source": [ + "With the `verbose=True` parameter the internal steps of the LLM agent are printed in green. As a first step the agent calls the `get_crystal_structure()` already with the converted input parameter, rather than using gold as input it uses the chemical symbol `Au`. The function returns `fcc` and the LLM converts this answer in a sentence a human can understand: \n", + "```\n", + "The crystal structure of gold is face-centered cubic (fcc).\n", + "```\n", + "This example highlights how easy it is these days to make a python function accessible via a LLM for all kinds of users to interact with this python funtion." + ] + }, + { + "cell_type": "markdown", + "id": "bc28370b-6002-4685-9884-44432099b5ea", + "metadata": {}, + "source": [ + "## Prompt Engineering\n", + "Some LLMs like the ChatGPT3.5 model start to hallucinate rather quickly, for example by asking for the crystal structure of a car, it does not ask which elements a car typically consists of, but rather connects car with carbon and replies the crystal structure of a car is diamond, which is obviously wrong. Luckily, the Antropic Claude model does not make this mistake. " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7ac99ce9-f93c-406f-8348-f16793b1059e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_crystal_structure` with `{'chemical_symbol': 'C'}`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3mdiamond\u001b[0m\u001b[32;1m\u001b[1;3mThe crystal structure of carbon (car) is diamond.\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"What is the crystal structure of car\"})) # I did not know cars were made of carbon" + ] + }, + { + "cell_type": "markdown", + "id": "fe544511-f499-4bdf-b24a-67fed6dd7ce4", + "metadata": {}, + "source": [ + "To restrict the hallucination of the agent we extend the system prompt with the following statement:\n", + "```\n", + "For each query vailidate that it contains a chemical element and otherwise cancel. \n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4ffbd77b-7fc4-47a4-8c7b-4f179d453c9b", + "metadata": {}, + "outputs": [], + "source": [ + "prompt_improved = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " # \"You are very powerful assistant, but don't know current events.\",\n", + " \"You are very powerful assistant, but don't know current events. For each query vailidate that it contains a chemical element and otherwise cancel.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d3b4b76d-a455-41cf-89f7-318061c95ac5", + "metadata": {}, + "outputs": [], + "source": [ + "agent_improved = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt_improved\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_improved_executor = AgentExecutor(agent=agent_improved, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "3033a731-5605-4103-9da8-63c56655a53f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_crystal_structure` with `{'chemical_symbol': 'C'}`\n", + "\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3mdiamond\u001b[0m\u001b[32;1m\u001b[1;3mThe crystal structure of carbon is indeed diamond.\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_improved_executor.stream({\"input\": \"What is the crystal structure of car\"}))" + ] + }, + { + "cell_type": "markdown", + "id": "f23ddd66-9032-41e0-90fb-a574845ce426", + "metadata": {}, + "source": [ + "With the modified system prompt, the most LLM models correctly reply that they were not able to determine the crystal structure of a car, because they fail to determine the chemical element a car consists of. Unfortunatley, in the example of the public LLama model this is not the case so further optimization of the prompt would be required." + ] + }, + { + "cell_type": "markdown", + "id": "4a7f9b5f-cfe3-4b9c-9190-33d9505beedd", + "metadata": {}, + "source": [ + "## Summary\n", + "By following the [Custom Agent](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) tutorial from the Langchain documentation, we were able to create a first simulation agent, which calls specialized python frameworks like [ASE](https://wiki.fysik.dtu.dk/ase/) to address material-science specific questions. Still, it is important to carefully engineer the prompt of the agent, otherwise even these LLMs with access to specialized agents tend to hallucinate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7124521-c58d-4700-bb51-7eb97be53588", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/book/2-energy-volume-curve-agent-anthropic.ipynb b/book/2-energy-volume-curve-agent-anthropic.ipynb new file mode 100644 index 0000000..28fe7ba --- /dev/null +++ b/book/2-energy-volume-curve-agent-anthropic.ipynb @@ -0,0 +1,377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3fb43db-e147-4245-8c60-cbbb8fd349a6", + "metadata": {}, + "source": [ + "# Energy Volume Curve Agent\n", + "Based on the previous tutorial, the next step is to extend the simple crystal structure agent to address the benchmark challenge of calculating an energy-versus-volume curve using the EMT simulation code. Again, this demonstration is based on the Langchain tutorial for [custom agents](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/)." + ] + }, + { + "cell_type": "markdown", + "id": "abebe249-91bd-44ad-bcec-79bf4de2e1a6", + "metadata": {}, + "source": [ + "As a first step, we import a number of python modules, these consist of the [ASE](https://wiki.fysik.dtu.dk/ase/), some standard python libraries as well as a number of tools from [langchain](https://www.langchain.com):" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "cbc2afa1-d50b-4c30-9e0a-5dbd8cac7b3b", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.atoms import Atoms\n", + "from ase.build import bulk\n", + "from ase.calculators.emt import EMT\n", + "from ase.constraints import UnitCellFilter\n", + "from ase.eos import calculate_eos, plot\n", + "from ase.optimize import LBFGS" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c2e3de94-c93d-490d-90ba-d4ba6e9e3d6d", + "metadata": {}, + "outputs": [], + "source": [ + "from getpass import getpass\n", + "from typing import List" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b9b96907-ad13-4a4b-862f-bf3f887dd52a", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents import AgentExecutor, tool\n", + "from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages\n", + "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n", + "from langchain_core.pydantic_v1 import BaseModel\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_anthropic import ChatAnthropic" + ] + }, + { + "cell_type": "markdown", + "id": "c0e2a114-486a-424c-9371-4b12258f06d5", + "metadata": {}, + "source": [ + "## Multiple Functions\n", + "To connect multiple python functions, it is essential to convert all inputs and outputs to `JSON` compatible data types as the communication to the LLM in the background happens in terms of web requests using the `JSON` format. This especially applies to python functions which return python objects. In the case of [ASE](https://wiki.fysik.dtu.dk/ase/) a typical python object is the `ase.atoms.Atoms()` object. To convert the `Atoms()` object to JSON format, we use a [pydantic](https://pydantic.dev) data class as suggested by [langchain](https://www.langchain.com):" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "25f967bd-f431-4780-ab45-21495f40bce3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'title': 'AtomsDict',\n", + " 'type': 'object',\n", + " 'properties': {'numbers': {'title': 'Numbers',\n", + " 'type': 'array',\n", + " 'items': {'type': 'integer'}},\n", + " 'positions': {'title': 'Positions',\n", + " 'type': 'array',\n", + " 'items': {'type': 'array', 'items': {'type': 'number'}}},\n", + " 'cell': {'title': 'Cell',\n", + " 'type': 'array',\n", + " 'items': {'type': 'array', 'items': {'type': 'number'}}},\n", + " 'pbc': {'title': 'Pbc', 'type': 'array', 'items': {'type': 'boolean'}}},\n", + " 'required': ['numbers', 'positions', 'cell', 'pbc']}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class AtomsDict(BaseModel):\n", + " numbers: List[int]\n", + " positions: List[List[float]]\n", + " cell: List[List[float]]\n", + " pbc: List[bool]\n", + "\n", + "AtomsDict.schema()" + ] + }, + { + "cell_type": "markdown", + "id": "30085089-4b70-40c9-9a92-62caa4c0aa22", + "metadata": {}, + "source": [ + "In terms of functions, two functions are defined: A `get_equilibirum_lattice()` function which for a given chemical element returns the optimized equilibrium structure as `AtomsDict`. The second function is a `plot_equation_of_state()` function, which takes the already optimized structure as `AtomsDict` as input and then uses the [ASE](https://wiki.fysik.dtu.dk/ase/) internal functionality to plot the energy volume curve. \n", + "\n", + "The important point here is that `get_equilibirum_lattice()` would typically just return an `ase.atoms.Atoms()` object, but for compatibility with the LLM it has to be converted to an `AtomsDict` which can be converted to JSON. Finally, in the `plot_equation_of_state()` function the `AtomsDict` is again converted to an `ase.atoms.Atoms()` object to continue the calculation. This is currently a bit tedious. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d244a7a4-d564-4611-aea4-10ff733315ef", + "metadata": {}, + "outputs": [], + "source": [ + "@tool \n", + "def get_equilibirum_lattice(chemical_symbol: str) -> AtomsDict:\n", + " \"\"\"Returns equilibrium atoms dictionary for a given chemical symbol\"\"\"\n", + " atoms = bulk(name=chemical_symbol)\n", + " atoms.calc = EMT()\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n", + " ase_optimizer_obj.run(fmax=0.000001)\n", + " return AtomsDict(**{k: v.tolist() for k, v in atoms.todict().items()})" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "33454233-8bff-4578-a0dc-99bf001a3fb3", + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def plot_equation_of_state(atom_dict: AtomsDict) -> str:\n", + " \"\"\"Returns plot of equation of state of chemical symbol for a given atoms dictionary\"\"\"\n", + " atoms = Atoms(**atom_dict.dict())\n", + " atoms.calc = EMT()\n", + " eos = calculate_eos(atoms)\n", + " plotdata = eos.getplotdata()\n", + " return plot(*plotdata)" + ] + }, + { + "cell_type": "markdown", + "id": "8ae5f04f-b9b9-40ad-9306-de17b66c11b4", + "metadata": {}, + "source": [ + "Finally, the functions converted to LLM tools using the `tool` decorator can again be tested using the `invoke()` function. It takes a python dictionary as input to address the different arguments individually. With this test, the correct implementation of the python functions is validated:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cf1a636e-3507-42db-b9e0-761053dfeff4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step Time Energy fmax\n", + "LBFGS: 0 07:48:58 -0.001502 0.161794\n", + "LBFGS: 1 07:48:58 -0.002533 0.135471\n", + "LBFGS: 2 07:48:58 -0.004879 0.005452\n", + "LBFGS: 3 07:48:58 -0.004883 0.000157\n", + "LBFGS: 4 07:48:58 -0.004883 0.000000\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/9p/rztyv06d0xv4h26cyv8nrw3m0000gq/T/ipykernel_2250/2598102435.py:6: FutureWarning: Import UnitCellFilter from ase.filters\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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myrNZ8yaTCWJjIwUL774omjcuLFQqVTCzs5ONGvWTLzwwgti//79Fm1L2xOWkpIixowZIzw9PYW9vb3o3r27OHTokOjZsyd7wspIJsSdk9dE1dzHH3+MqVOnIi0tDY6OjlLHoQoSFhaGQYMG4e+//+aDoGsBvp9EpcfTkVTt6XQ6REREYMOGDfD392cBVsscOHAAw4cP5w92LcH3k6j02BNG1d7vv/+Oxx9/HG3atMGqVasq/EHbREREUmARRkRERCQBjhNGREREJAEWYUREREQSYBFGREREJAHeHVmNmEwm3L59G05OTmV+3AURERFVLSEE0tLS4OPjA7m89P1bLMKqkdu3b8PX11fqGERERFQGN2/etOoJAizCqhEnJycA+W9iWR4pQURERFVPr9fD19fX/DteWizCqpGCU5DOzs4swoiIiGoYay8lqjEX5qekpCAkJAQajQYajQYhISFITU0tcRkhBObOnQsfHx+o1Wr06tUL58+ft2iTk5ODyZMnw8PDAw4ODggKCsKtW7cs2ixcuBBdu3aFvb09XFxcityWTCYrNK1evbo8u0xERES1WI0pwoKDgxEZGYnw8HCEh4cjMjISISEhJS6zdOlSLFu2DCtXrsSJEyeg1WrRr18/8xPsAWDq1KnYuXMnQkNDcfjwYaSnp2PQoEEwGo3mNrm5uRg6dCgmTJhQ4vbWr1+PmJgY8zRq1Kjy7TQRERHVXtI9O7z0Lly4IACIo0ePmudFREQIAOLSpUtFLmMymYRWqxVLliwxz8vOzhYajUasXr1aCCFEamqqUCqVIjQ01NwmOjpayOVyER4eXmid69evFxqNpsjtARA7d+4sw979j06nEwCETqcr13qIiIio6pT197tG9IRFRERAo9Ggc+fO5nldunSBRqPBkSNHilzm6tWriI2NRf/+/c3zVCoVevbsaV7m5MmTMBgMFm18fHzg7+9f7HpLMmnSJHh4eKBjx45YvXo1TCZTie1zcnKg1+stJiIiIqobasSF+bGxsfD09Cw039PTE7GxscUuAwBeXl4W8728vHD9+nVzG1tbW7i6uhZqU9x6i7NgwQL06dMHarUa+/fvx7Rp05CYmIhZs2YVu8zixYsxb948q7ZDREREtYOkPWFz584t8oL2u6e//voLQNF3HAgh7nsnwr2vl2aZ0rS516xZsxAQEIB27dph2rRp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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "el = \"Al\"\n", + "structure_dict = get_equilibirum_lattice.invoke({\"chemical_symbol\": el})\n", + "plot_equation_of_state.invoke({\"atom_dict\": structure_dict})" + ] + }, + { + "cell_type": "markdown", + "id": "f3881566-c046-46b0-b016-8081ec2a2e7b", + "metadata": {}, + "source": [ + "## Agents\n", + "Following the same Langchain tutorial for [custom agents](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) as before a LLM agent is constructed which combines the system prompt, the python functions `get_equilibirum_lattice()` and `plot_equation_of_state()` as tools and the `OpenAIToolsAgentOutputParser()`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "6529e329-0947-443a-8325-42ef3510803d", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your Anthropic Token: ········\n" + ] + } + ], + "source": [ + "ANTHROPIC_API_KEY = getpass(prompt='Enter your Anthropic Token:')\n", + "llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0, anthropic_api_key=ANTHROPIC_API_KEY,)\n", + "tools = [get_equilibirum_lattice, plot_equation_of_state]\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are very powerful assistant, but don't know current events. For each query vailidate that it contains a chemical element and otherwise cancel.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")\n", + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "30aff6cc-c41b-4346-9542-88f0d7833856", + "metadata": {}, + "source": [ + "## User Interactions\n", + "The user can then interact with the agent by asking it to `Plot the equation of state of gold`. In the background the agent first calls the `get_equilibirum_lattice()` with the parameter `Au` for the chemical symbol of gold and afterwards `plot_equation_of_state()` with the atomistic structure returned by `get_equilibirum_lattice()`. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c2edad58-849b-424f-8460-92507e9545ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_equilibirum_lattice` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"Certainly! I'd be happy to plot the equation of state for gold. To do this, we'll need to use two functions: first, we'll get the equilibrium lattice for gold, and then we'll use that to plot the equation of state.\\n\\nLet's start by getting the equilibrium lattice for gold:\", 'type': 'text', 'index': 0}, {'id': 'toolu_017dXVwAGh4D1LPEY24xDbZL', 'input': {}, 'name': 'get_equilibirum_lattice', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:49:04 0.002606 0.308859\n", + "LBFGS: 1 07:49:04 0.000032 0.077808\n", + "LBFGS: 2 07:49:04 -0.000135 0.003099\n", + "LBFGS: 3 07:49:04 -0.000135 0.000029\n", + "LBFGS: 4 07:49:04 -0.000135 0.000000\n", + "\u001b[36;1m\u001b[1;3mnumbers=[79] positions=[[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]] cell=[[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]] pbc=[True, True, True]\u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/9p/rztyv06d0xv4h26cyv8nrw3m0000gq/T/ipykernel_2250/2598102435.py:6: FutureWarning: Import UnitCellFilter from ase.filters\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `plot_equation_of_state` with `{'atom_dict': {'numbers': [79], 'positions': [[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]], 'cell': [[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]], 'pbc': [True, True, True]}}`\n", + "responded: [{'text': \"\\n\\nGreat! Now that we have the equilibrium lattice for gold, let's use this information to plot the equation of state:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01FAAFetx8gyYiSeE4v2siuj', 'input': {}, 'name': 'plot_equation_of_state', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"atom_dict\": {\"numbers\": [79], \"positions\": [[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]], \"cell\": [[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]], \"pbc\": [true, true, true]}}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mAxes(0.125,0.11;0.775x0.77)\u001b[0m\u001b[32;1m\u001b[1;3m[{'text': \"\\n\\nThe equation of state for gold has been successfully plotted. The result is represented by an Axes object, which indicates that a graph has been generated.\\n\\nThis plot typically shows the relationship between the volume of the gold crystal and its energy. It helps in understanding the structural and thermodynamic properties of gold under different conditions.\\n\\nThe x-axis usually represents the volume (or sometimes the lattice constant), while the y-axis represents the total energy of the system. The minimum point on this curve corresponds to the equilibrium volume and energy of the gold crystal.\\n\\nIs there anything specific about the equation of state for gold that you'd like to know or discuss further?\", 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"Plot the equation of state of gold\"}))" + ] + }, + { + "cell_type": "markdown", + "id": "555d589b-8607-410f-8a37-12aefd2fec59", + "metadata": {}, + "source": [ + "## Summary\n", + "The important point in this example is, that the order of execution of the Python functions is not defined by the user. Instead the LLM automatically determines that `plot_equation_of_state()` needs an `AtomsDict()` object as input and that `get_equilibirum_lattice()` returns such a `AtomsDict()` object as an output, so it makes sense to call `get_equilibirum_lattice()` first and `plot_equation_of_state()` second. The same principles apply to LLM agents with a larger number of python functions. \n", + "\n", + "The limiting point at the moment is that the LLMs are web services, so all Python objects have to be converted to `JSON` to be communicated to the LLMs. This restricts the choice of Python objects and requires the development of specialized data classes to construct those interfaces between different Python functions. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3bfcac8e-aa17-414e-a83d-fd766eefb460", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/book/2-energy-volume-curve-agent-groq.ipynb b/book/2-energy-volume-curve-agent-groq.ipynb new file mode 100644 index 0000000..6f4fe00 --- /dev/null +++ b/book/2-energy-volume-curve-agent-groq.ipynb @@ -0,0 +1,363 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a3fb43db-e147-4245-8c60-cbbb8fd349a6", + "metadata": {}, + "source": [ + "# Energy Volume Curve Agent\n", + "Based on the previous tutorial, the next step is to extend the simple crystal structure agent to address the benchmark challenge of calculating an energy-versus-volume curve using the EMT simulation code. Again, this demonstration is based on the Langchain tutorial for [custom agents](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/)." + ] + }, + { + "cell_type": "markdown", + "id": "abebe249-91bd-44ad-bcec-79bf4de2e1a6", + "metadata": {}, + "source": [ + "As a first step, we import a number of python modules, these consist of the [ASE](https://wiki.fysik.dtu.dk/ase/), some standard python libraries as well as a number of tools from [langchain](https://www.langchain.com):" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "cbc2afa1-d50b-4c30-9e0a-5dbd8cac7b3b", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.atoms import Atoms\n", + "from ase.build import bulk\n", + "from ase.calculators.emt import EMT\n", + "from ase.constraints import UnitCellFilter\n", + "from ase.eos import calculate_eos, plot\n", + "from ase.optimize import LBFGS" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c2e3de94-c93d-490d-90ba-d4ba6e9e3d6d", + "metadata": {}, + "outputs": [], + "source": [ + "from getpass import getpass\n", + "from typing import List" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b9b96907-ad13-4a4b-862f-bf3f887dd52a", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain.agents import AgentExecutor, tool\n", + "from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages\n", + "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n", + "from langchain_core.pydantic_v1 import BaseModel\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "from langchain_groq import ChatGroq" + ] + }, + { + "cell_type": "markdown", + "id": "c0e2a114-486a-424c-9371-4b12258f06d5", + "metadata": {}, + "source": [ + "## Multiple Functions\n", + "To connect multiple python functions, it is essential to convert all inputs and outputs to `JSON` compatible data types as the communication to the LLM in the background happens in terms of web requests using the `JSON` format. This especially applies to python functions which return python objects. In the case of [ASE](https://wiki.fysik.dtu.dk/ase/) a typical python object is the `ase.atoms.Atoms()` object. To convert the `Atoms()` object to JSON format, we use a [pydantic](https://pydantic.dev) data class as suggested by [langchain](https://www.langchain.com):" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "25f967bd-f431-4780-ab45-21495f40bce3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'title': 'AtomsDict',\n", + " 'type': 'object',\n", + " 'properties': {'numbers': {'title': 'Numbers',\n", + " 'type': 'array',\n", + " 'items': {'type': 'integer'}},\n", + " 'positions': {'title': 'Positions',\n", + " 'type': 'array',\n", + " 'items': {'type': 'array', 'items': {'type': 'number'}}},\n", + " 'cell': {'title': 'Cell',\n", + " 'type': 'array',\n", + " 'items': {'type': 'array', 'items': {'type': 'number'}}},\n", + " 'pbc': {'title': 'Pbc', 'type': 'array', 'items': {'type': 'boolean'}}},\n", + " 'required': ['numbers', 'positions', 'cell', 'pbc']}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class AtomsDict(BaseModel):\n", + " numbers: List[int]\n", + " positions: List[List[float]]\n", + " cell: List[List[float]]\n", + " pbc: List[bool]\n", + "\n", + "AtomsDict.schema()" + ] + }, + { + "cell_type": "markdown", + "id": "30085089-4b70-40c9-9a92-62caa4c0aa22", + "metadata": {}, + "source": [ + "In terms of functions, two functions are defined: A `get_equilibirum_lattice()` function which for a given chemical element returns the optimized equilibrium structure as `AtomsDict`. The second function is a `plot_equation_of_state()` function, which takes the already optimized structure as `AtomsDict` as input and then uses the [ASE](https://wiki.fysik.dtu.dk/ase/) internal functionality to plot the energy volume curve. \n", + "\n", + "The important point here is that `get_equilibirum_lattice()` would typically just return an `ase.atoms.Atoms()` object, but for compatibility with the LLM it has to be converted to an `AtomsDict` which can be converted to JSON. Finally, in the `plot_equation_of_state()` function the `AtomsDict` is again converted to an `ase.atoms.Atoms()` object to continue the calculation. This is currently a bit tedious. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d244a7a4-d564-4611-aea4-10ff733315ef", + "metadata": {}, + "outputs": [], + "source": [ + "@tool \n", + "def get_equilibirum_lattice(chemical_symbol: str) -> AtomsDict:\n", + " \"\"\"Returns equilibrium atoms dictionary for a given chemical symbol\"\"\"\n", + " atoms = bulk(name=chemical_symbol)\n", + " atoms.calc = EMT()\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n", + " ase_optimizer_obj.run(fmax=0.000001)\n", + " return AtomsDict(**{k: v.tolist() for k, v in atoms.todict().items()})" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "33454233-8bff-4578-a0dc-99bf001a3fb3", + "metadata": {}, + "outputs": [], + "source": [ + "@tool\n", + "def plot_equation_of_state(atom_dict: AtomsDict) -> str:\n", + " \"\"\"Returns plot of equation of state of chemical symbol for a given atoms dictionary\"\"\"\n", + " atoms = Atoms(**atom_dict.dict())\n", + " atoms.calc = EMT()\n", + " eos = calculate_eos(atoms)\n", + " plotdata = eos.getplotdata()\n", + " return plot(*plotdata)" + ] + }, + { + "cell_type": "markdown", + "id": "8ae5f04f-b9b9-40ad-9306-de17b66c11b4", + "metadata": {}, + "source": [ + "Finally, the functions converted to LLM tools using the `tool` decorator can again be tested using the `invoke()` function. It takes a python dictionary as input to address the different arguments individually. With this test, the correct implementation of the python functions is validated:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cf1a636e-3507-42db-b9e0-761053dfeff4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step Time Energy fmax\n", + "LBFGS: 0 07:51:18 -0.001502 0.161794\n", + "LBFGS: 1 07:51:18 -0.002533 0.135471\n", + "LBFGS: 2 07:51:18 -0.004879 0.005452\n", + "LBFGS: 3 07:51:18 -0.004883 0.000157\n", + "LBFGS: 4 07:51:18 -0.004883 0.000000\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/9p/rztyv06d0xv4h26cyv8nrw3m0000gq/T/ipykernel_2816/2598102435.py:6: FutureWarning: Import UnitCellFilter from ase.filters\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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myrNZ8yaTCWJjIwUL774omjcuLFQqVTCzs5ONGvWTLzwwgti//79Fm1L2xOWkpIixowZIzw9PYW9vb3o3r27OHTokOjZsyd7wspIJsSdk9dE1dzHH3+MqVOnIi0tDY6OjlLHoQoSFhaGQYMG4e+//+aDoGsBvp9EpcfTkVTt6XQ6REREYMOGDfD392cBVsscOHAAw4cP5w92LcH3k6j02BNG1d7vv/+Oxx9/HG3atMGqVasq/EHbREREUmARRkRERCQBjhNGREREJAEWYUREREQSYBFGREREJAHeHVmNmEwm3L59G05OTmV+3AURERFVLSEE0tLS4OPjA7m89P1bLMKqkdu3b8PX11fqGERERFQGN2/etOoJAizCqhEnJycA+W9iWR4pQURERFVPr9fD19fX/DteWizCqpGCU5DOzs4swoiIiGoYay8lqjEX5qekpCAkJAQajQYajQYhISFITU0tcRkhBObOnQsfHx+o1Wr06tUL58+ft2iTk5ODyZMnw8PDAw4ODggKCsKtW7cs2ixcuBBdu3aFvb09XFxcityWTCYrNK1evbo8u0xERES1WI0pwoKDgxEZGYnw8HCEh4cjMjISISEhJS6zdOlSLFu2DCtXrsSJEyeg1WrRr18/8xPsAWDq1KnYuXMnQkNDcfjwYaSnp2PQoEEwGo3mNrm5uRg6dCgmTJhQ4vbWr1+PmJgY8zRq1Kjy7TQRERHVXtI9O7z0Lly4IACIo0ePmudFREQIAOLSpUtFLmMymYRWqxVLliwxz8vOzhYajUasXr1aCCFEamqqUCqVIjQ01NwmOjpayOVyER4eXmid69evFxqNpsjtARA7d+4sw979j06nEwCETqcr13qIiIio6pT197tG9IRFRERAo9Ggc+fO5nldunSBRqPBkSNHilzm6tWriI2NRf/+/c3zVCoVevbsaV7m5MmTMBgMFm18fHzg7+9f7HpLMmnSJHh4eKBjx45YvXo1TCZTie1zcnKg1+stJiIiIqobasSF+bGxsfD09Cw039PTE7GxscUuAwBeXl4W8728vHD9+nVzG1tbW7i6uhZqU9x6i7NgwQL06dMHarUa+/fvx7Rp05CYmIhZs2YVu8zixYsxb948q7ZDREREtYOkPWFz584t8oL2u6e//voLQNF3HAgh7nsnwr2vl2aZ0rS516xZsxAQEIB27dph2rRp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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "el = \"Al\"\n", + "structure_dict = get_equilibirum_lattice.invoke({\"chemical_symbol\": el})\n", + "plot_equation_of_state.invoke({\"atom_dict\": structure_dict})" + ] + }, + { + "cell_type": "markdown", + "id": "f3881566-c046-46b0-b016-8081ec2a2e7b", + "metadata": {}, + "source": [ + "## Agents\n", + "Following the same Langchain tutorial for [custom agents](https://python.langchain.com/docs/modules/agents/how_to/custom_agent/) as before a LLM agent is constructed which combines the system prompt, the python functions `get_equilibirum_lattice()` and `plot_equation_of_state()` as tools and the `OpenAIToolsAgentOutputParser()`:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "6529e329-0947-443a-8325-42ef3510803d", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your Groq Token: ········\n" + ] + } + ], + "source": [ + "GROQ_API_KEY = getpass(prompt='Enter your Groq Token:')\n", + "llm = ChatGroq(model=\"llama3-70b-8192\", temperature=0, groq_api_key=GROQ_API_KEY)\n", + "tools = [get_equilibirum_lattice, plot_equation_of_state]\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are very powerful assistant, but don't know current events. For each query vailidate that it contains a chemical element and otherwise cancel.\",\n", + " ),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")\n", + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " }\n", + " | prompt\n", + " | llm.bind_tools(tools)\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "id": "30aff6cc-c41b-4346-9542-88f0d7833856", + "metadata": {}, + "source": [ + "## User Interactions\n", + "The user can then interact with the agent by asking it to `Plot the equation of state of gold`. In the background the agent first calls the `get_equilibirum_lattice()` with the parameter `Au` for the chemical symbol of gold and afterwards `plot_equation_of_state()` with the atomistic structure returned by `get_equilibirum_lattice()`. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c2edad58-849b-424f-8460-92507e9545ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_equilibirum_lattice` with `{'chemical_symbol': 'Au'}`\n", + "\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:51:22 0.002606 0.308859\n", + "LBFGS: 1 07:51:22 0.000032 0.077808\n", + "LBFGS: 2 07:51:22 -0.000135 0.003099\n", + "LBFGS: 3 07:51:22 -0.000135 0.000029\n", + "LBFGS: 4 07:51:22 -0.000135 0.000000\n", + "\u001b[36;1m\u001b[1;3mnumbers=[79] positions=[[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]] cell=[[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]] pbc=[True, True, True]\u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/9p/rztyv06d0xv4h26cyv8nrw3m0000gq/T/ipykernel_2816/2598102435.py:6: FutureWarning: Import UnitCellFilter from ase.filters\n", + " ase_optimizer_obj = LBFGS(UnitCellFilter(atoms))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32;1m\u001b[1;3m{\"tool_calls\":[{\"id\":\"pending\",\"type\":\"function\",\"function\":{\"name\":\"plot_equation_of_state\"},\"parameters\":{\"atom_dict\":{\"numbers\":[79],\"positions\":[[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]],\"cell\":[[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]],\"pbc\":[True, True, True]}}}]}]}\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + } + ], + "source": [ + "lst = list(agent_executor.stream({\"input\": \"Plot the equation of state of gold\"}))" + ] + }, + { + "cell_type": "markdown", + "id": "555d589b-8607-410f-8a37-12aefd2fec59", + "metadata": {}, + "source": [ + "## Summary\n", + "The important point in this example is, that the order of execution of the Python functions is not defined by the user. Instead the LLM automatically determines that `plot_equation_of_state()` needs an `AtomsDict()` object as input and that `get_equilibirum_lattice()` returns such a `AtomsDict()` object as an output, so it makes sense to call `get_equilibirum_lattice()` first and `plot_equation_of_state()` second. The same principles apply to LLM agents with a larger number of python functions. \n", + "\n", + "The limiting point at the moment is that the LLMs are web services, so all Python objects have to be converted to `JSON` to be communicated to the LLMs. This restricts the choice of Python objects and requires the development of specialized data classes to construct those interfaces between different Python functions. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3bfcac8e-aa17-414e-a83d-fd766eefb460", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/book/3-hackathon-demonstration-anthropic.ipynb b/book/3-hackathon-demonstration-anthropic.ipynb new file mode 100644 index 0000000..65486a4 --- /dev/null +++ b/book/3-hackathon-demonstration-anthropic.ipynb @@ -0,0 +1,587 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d7616c03-7a5b-463a-b2db-0e0501619993", + "metadata": {}, + "source": [ + "# Hackathon Demonstration\n", + "Beyond the two previous examples, which closely followed the Langchain [custom agent](https://python.langchain.com/v0.1/docs/modules/agents/how_to/custom_agent/) tutorial, the goal for the final presentation was to highlight the use of `LangSim` in production. For this purpose, the functionality was transferred from the Jupyter notebook to a Python module. As a first step the `LangSim` module is loaded using the `%reload_ext` jupyter magic command:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "db5f8988-a11f-4f4d-8a8c-74f419ddcd80", + "metadata": {}, + "outputs": [], + "source": [ + "%reload_ext langsim" + ] + }, + { + "cell_type": "markdown", + "id": "01688907-23ea-42fd-ab8c-0d4f6a918465", + "metadata": {}, + "source": [ + "Then the [Anthropic](https://www.anthropic.com) API key is set to the `LANGSIM_API_KEY` environment variable so it can be used by `LangSim` internally, in addition we set the variables `LANGSIM_PROVIDER` to select `anthropic` and `LANGSIM_MODEL` to select `claude-3-5-sonnet-20240620`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a743c1f8-63a7-49a7-b77a-4cdf85d49b84", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Enter your API key ········\n" + ] + } + ], + "source": [ + "import os, getpass\n", + "os.environ[\"LANGSIM_PROVIDER\"] = \"anthropic\" \n", + "os.environ[\"LANGSIM_API_KEY\"] = getpass.getpass(\"Enter your API key\")\n", + "os.environ[\"LANGSIM_MODEL\"] = \"claude-3-5-sonnet-20240620\"" + ] + }, + { + "cell_type": "markdown", + "id": "aa95152b-aa79-4a9b-900d-28a0b3b067fe", + "metadata": {}, + "source": [ + "## Dialog\n", + "From this point on the user can chat with the `LangSim` interface using the `chat` jupyter magic. As a first challenge the user communicates their interest to find a material with a bulk modulus in the range of 200 GPa; following with a clarification of this statement by asking the LLM to calculate the bulk modulus for the noble metals. In contrast to the previous example, the LLM is tasked with repeating the same calculation multiple times and then identify the chemical element, which most closely matches the targeted bulk modulus of 200GPa. None of the required logic is implemented explicitly, rather the LLM only has access to a `get_equilibrium_lattice()` function to calculate the equilibrium crystal structure and a `get_bulk_modulus()` function, which calculates the bulk modulus for a given equilibrium crystal structure." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2b698ffd-3b16-4937-ae97-8e9f44a95bb7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m[{'text': \"Certainly! I'd be happy to help you compute the bulk modulus for the noble metals. The noble metals typically include gold (Au), silver (Ag), platinum (Pt), palladium (Pd), rhodium (Rh), iridium (Ir), and sometimes copper (Cu) is included as well.\\n\\nTo calculate the bulk modulus, we need to use one of the available models: either EMT (Effective Medium Theory) or MACE (Machine Learning Force Field). Before we proceed, I need to know which model you'd prefer to use for these calculations. \\n\\nWould you like to use:\\n1. EMT (Effective Medium Theory)\\n2. MACE (Machine Learning Force Field)\\n\\nPlease let me know your preference, and then I can proceed with the calculations for the noble metals.\", 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Certainly! I'd be happy to help you compute the bulk modulus for the noble metals. The noble metals typically include gold (Au), silver (Ag), platinum (Pt), palladium (Pd), rhodium (Rh), iridium (Ir), and sometimes copper (Cu) is included as well.\n", + "\n", + "To calculate the bulk modulus, we need to use one of the available models: either EMT (Effective Medium Theory) or MACE (Machine Learning Force Field). Before we proceed, I need to know which model you'd prefer to use for these calculations. \n", + "\n", + "Would you like to use:\n", + "1. EMT (Effective Medium Theory)\n", + "2. MACE (Machine Learning Force Field)\n", + "\n", + "Please let me know your preference, and then I can proceed with the calculations for the noble metals." + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%chat\n", + "I am looking for a material with a bulk modulus in the range of 200 GPa.\n", + "Can you compute the bulk modulus for the noble metals for me?" + ] + }, + { + "cell_type": "markdown", + "id": "837750e4-d51d-4ea9-bb7e-be3fd7b1f63c", + "metadata": {}, + "source": [ + "The LLM is instructed by the system prompt to clarify which simulation method it should use to calculate a given material property. At the time of the demonstration, two simulation methods were implemented, [EMT](https://wiki.fysik.dtu.dk/ase/ase/calculators/emt.html) and [MACE](https://mace-docs.readthedocs.io). As the user in this demonstration is not aware of the technical details of these simulation methods, he asks for clarification. This highlights how LLMs can use the context of the previous conversation to answer a given question:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "d96cabcc-a36b-4d40-9999-808ecdc746dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m[{'text': \"I apologize for not providing more information about the methods earlier. Let me explain the two available models for calculating the bulk modulus:\\n\\n1. EMT (Effective Medium Theory):\\n EMT is a computationally efficient, analytical model that describes the macroscopic properties of composite materials. It's a simpler and faster method, but it may be less accurate for some systems, especially those with complex electronic structures.\\n\\n2. MACE (Machine Learning Force Field):\\n MACE is a more advanced machine learning-based force field for predicting many-body atomic interactions. It covers the entire periodic table and is generally more accurate than EMT, especially for a wider range of elements and structures. However, it may be computationally more intensive.\\n\\nFor noble metals, both methods can provide reasonable results, but MACE might be more accurate. However, the choice depends on your specific needs:\\n\\n- If you need quick estimates and computational efficiency is a priority, EMT might be preferable.\\n- If you need higher accuracy and don't mind slightly longer computation times, MACE would be the better choice.\\n\\nWhich method would you prefer to use for calculating the bulk modulus of noble metals? Once you decide, I can proceed with the calculations.\", 'type': 'text', 'index': 0}]\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "I apologize for not providing more information about the methods earlier. Let me explain the two available models for calculating the bulk modulus:\n", + "\n", + "1. EMT (Effective Medium Theory):\n", + " EMT is a computationally efficient, analytical model that describes the macroscopic properties of composite materials. It's a simpler and faster method, but it may be less accurate for some systems, especially those with complex electronic structures.\n", + "\n", + "2. MACE (Machine Learning Force Field):\n", + " MACE is a more advanced machine learning-based force field for predicting many-body atomic interactions. It covers the entire periodic table and is generally more accurate than EMT, especially for a wider range of elements and structures. However, it may be computationally more intensive.\n", + "\n", + "For noble metals, both methods can provide reasonable results, but MACE might be more accurate. However, the choice depends on your specific needs:\n", + "\n", + "- If you need quick estimates and computational efficiency is a priority, EMT might be preferable.\n", + "- If you need higher accuracy and don't mind slightly longer computation times, MACE would be the better choice.\n", + "\n", + "Which method would you prefer to use for calculating the bulk modulus of noble metals? Once you decide, I can proceed with the calculations." + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%chat \n", + "What are those methods?" + ] + }, + { + "cell_type": "markdown", + "id": "ba002cb9-b406-49dd-a716-c57904d48472", + "metadata": {}, + "source": [ + "Based on the explanation of the LLM, the user chooses the [EMT](https://wiki.fysik.dtu.dk/ase/ase/calculators/emt.html) method, primarily because it is computationally more efficient to calculate a large number of calculations in a short amount of time. The LLM then executes two calculations for each of the three noble metals silver (Ag), gold (Au) and platinum (Pt). These calculations are the calculation of the equilibrium structure `get_equilibrium_lattice()` which in addition to the chemical symbol now also takes the name of the chosen calculator as an input and the calculation of the bulk modulus `get_bulk_modulus()` which takes the optimized structure as `AtomsDict()` object as an input in addition to the chosen calculator as string. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3caea2fc-a3de-400c-91cb-ae53c616ece1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"Certainly! We'll use the EMT (Effective Medium Theory) method to calculate the bulk modulus for the noble metals. I'll compute this for gold (Au), silver (Ag), platinum (Pt), palladium (Pd), rhodium (Rh), and iridium (Ir). Let's start with these calculations.\\n\\nFor each metal, we need to first get the bulk structure, then equilibrate it, and finally calculate the bulk modulus. I'll do this for each metal one by one.\\n\\nLet's begin with gold (Au):\", 'type': 'text', 'index': 0}, {'id': 'toolu_01G3s6u3824duQkUYC2S48vR', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[79] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_equilibrated_structure` with `{'atom_dict': {'numbers': [79], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'text': \"\\n\\nNow that we have the bulk structure for gold, let's equilibrate it using the EMT calculator:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01BUiM5erRnzLRsAhQ6hEC1m', 'input': {}, 'name': 'get_atom_dict_equilibrated_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"atom_dict\": {\"numbers\": [79], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:56:23 0.002606 0.308859\n", + "LBFGS: 1 07:56:23 0.000032 0.077808\n", + "LBFGS: 2 07:56:23 -0.000135 0.003099\n", + "LBFGS: 3 07:56:23 -0.000135 0.000029\n", + "LBFGS: 4 07:56:23 -0.000135 0.000000\n", + "\u001b[38;5;200m\u001b[1;3mnumbers=[79] positions=[[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]] cell=[[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [79], 'positions': [[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]], 'cell': [[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'text': \"\\n\\nNow that we have the equilibrated structure for gold, let's calculate its bulk modulus:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01BuZFY3zvENzYWh9Jasus7M', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"atom_dict\": {\"numbers\": [79], \"positions\": [[4.761270571021482e-17, -3.44317321286765e-17, -2.0729599738876008e-16]], \"cell\": [[7.040904860568557e-17, 2.028082809705617, 2.0280828097056176], [2.028082809705617, 1.0384771021574885e-16, 2.0280828097056176], [2.028082809705617, 2.028082809705618, 4.963320342464553e-17]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m173.8383045764123\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Ag'}`\n", + "responded: [{'text': \"\\n\\nThe bulk modulus of gold (Au) calculated using EMT is approximately 173.84 GPa.\\n\\nNow, let's repeat this process for the other noble metals: silver (Ag), platinum (Pt), palladium (Pd), rhodium (Rh), and iridium (Ir).\", 'type': 'text', 'index': 0}, {'id': 'toolu_01RFH3aZTKbthvQDzM5U6ttV', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Ag\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[47] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_equilibrated_structure` with `{'atom_dict': {'numbers': [47], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01HsAhBjQsREjnh5x7SRFEot', 'input': {}, 'name': 'get_atom_dict_equilibrated_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [47], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:56:31 0.001584 0.198735\n", + "LBFGS: 1 07:56:31 0.000254 0.113272\n", + "LBFGS: 2 07:56:31 -0.000366 0.004325\n", + "LBFGS: 3 07:56:31 -0.000367 0.000089\n", + "LBFGS: 4 07:56:31 -0.000367 0.000000\n", + "\u001b[38;5;200m\u001b[1;3mnumbers=[47] positions=[[3.632034897593557e-17, -7.643082441891503e-17, -4.3481411845850515e-16]] cell=[[9.839176801279394e-17, 2.031776305380803, 2.0317763053808036], [2.031776305380803, -6.293048194449085e-17, 2.0317763053808036], [2.031776305380803, 2.031776305380803, -2.945414531846451e-17]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [47], 'positions': [[3.632034897593557e-17, -7.643082441891503e-17, -4.3481411845850515e-16]], 'cell': [[9.839176801279394e-17, 2.031776305380803, 2.0317763053808036], [2.031776305380803, -6.293048194449085e-17, 2.0317763053808036], [2.031776305380803, 2.031776305380803, -2.945414531846451e-17]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01SjbgmFaXuiJAr2e3pzc4w9', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [47], \"positions\": [[3.632034897593557e-17, -7.643082441891503e-17, -4.3481411845850515e-16]], \"cell\": [[9.839176801279394e-17, 2.031776305380803, 2.0317763053808036], [2.031776305380803, -6.293048194449085e-17, 2.0317763053808036], [2.031776305380803, 2.031776305380803, -2.945414531846451e-17]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m100.1580099647698\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pt'}`\n", + "responded: [{'id': 'toolu_01SAFc88U6jiHVaeKAGUo6AM', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pt\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[78] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_equilibrated_structure` with `{'atom_dict': {'numbers': [78], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_014fK8mbWdbCc6QHFxSPf6Ui', 'input': {}, 'name': 'get_atom_dict_equilibrated_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [78], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:56:38 -0.000125 0.036183\n", + "LBFGS: 1 07:56:38 -0.000149 0.004500\n", + "LBFGS: 2 07:56:38 -0.000150 0.000013\n", + "LBFGS: 3 07:56:38 -0.000150 0.000000\n", + "\u001b[38;5;200m\u001b[1;3mnumbers=[78] positions=[[1.7335088009500337e-17, 1.1611829961508788e-16, 1.1092203033984381e-16]] cell=[[4.60232654901218e-18, 1.9609007393798525, 1.960900739379854], [1.9609007393798532, -3.193849078504324e-16, 1.9609007393798543], [1.9609007393798532, 1.9609007393798528, 1.704915243852484e-16]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [78], 'positions': [[1.7335088009500337e-17, 1.1611829961508788e-16, 1.1092203033984381e-16]], 'cell': [[4.60232654901218e-18, 1.9609007393798525, 1.960900739379854], [1.9609007393798532, -3.193849078504324e-16, 1.9609007393798543], [1.9609007393798532, 1.9609007393798528, 1.704915243852484e-16]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01EkTZs8zu44MA7mAASvd2hM', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [78], \"positions\": [[1.7335088009500337e-17, 1.1611829961508788e-16, 1.1092203033984381e-16]], \"cell\": [[4.60232654901218e-18, 1.9609007393798525, 1.960900739379854], [1.9609007393798532, -3.193849078504324e-16, 1.9609007393798543], [1.9609007393798532, 1.9609007393798528, 1.704915243852484e-16]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m278.2966136639589\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pd'}`\n", + "responded: [{'id': 'toolu_01BGCFWyDttt9iN8q4NNyrcj', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pd\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[46] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_equilibrated_structure` with `{'atom_dict': {'numbers': [46], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_014tysKpH352XUFXQZfuQg8p', 'input': {}, 'name': 'get_atom_dict_equilibrated_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [46], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m Step Time Energy fmax\n", + "LBFGS: 0 07:56:45 0.000342 0.140218\n", + "LBFGS: 1 07:56:45 -0.000211 0.043682\n", + "LBFGS: 2 07:56:45 -0.000270 0.000742\n", + "LBFGS: 3 07:56:45 -0.000270 0.000004\n", + "LBFGS: 4 07:56:45 -0.000270 0.000000\n", + "\u001b[38;5;200m\u001b[1;3mnumbers=[46] positions=[[-1.6927811771902953e-17, -1.2503517079340123e-16, -7.690125956122276e-17]] cell=[[-2.451382079594262e-16, 1.9393703009539969, 1.9393703009539975], [1.939370300953997, 2.8937020560458515e-17, 1.9393703009539973], [1.9393703009539969, 1.9393703009539969, 2.7783509637994643e-16]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [46], 'positions': [[-1.6927811771902953e-17, -1.2503517079340123e-16, -7.690125956122276e-17]], 'cell': [[-2.451382079594262e-16, 1.9393703009539969, 1.9393703009539975], [1.939370300953997, 2.8937020560458515e-17, 1.9393703009539973], [1.9393703009539969, 1.9393703009539969, 2.7783509637994643e-16]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01MC9td1UE2qdbPnW9PPD5mr', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [46], \"positions\": [[-1.6927811771902953e-17, -1.2503517079340123e-16, -7.690125956122276e-17]], \"cell\": [[-2.451382079594262e-16, 1.9393703009539969, 1.9393703009539975], [1.939370300953997, 2.8937020560458515e-17, 1.9393703009539973], [1.9393703009539969, 1.9393703009539969, 2.7783509637994643e-16]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m179.20795176571716\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Rh'}`\n", + "responded: [{'id': 'toolu_01NogAQ8aHYZMn9Gr59R1a4D', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Rh\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[45] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.9, 1.9], [1.9, 0.0, 1.9], [1.9, 1.9, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_equilibrated_structure` with `{'atom_dict': {'numbers': [45], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.9, 1.9], [1.9, 0.0, 1.9], [1.9, 1.9, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_019s65ryZK38VAk2TzvPudX4', 'input': {}, 'name': 'get_atom_dict_equilibrated_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [45], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.9, 1.9], [1.9, 0.0, 1.9], [1.9, 1.9, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[38;5;200m\u001b[1;3mAn exception occurred: {}\u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Stopping agent prematurely due to triggering stop condition\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Ir'}`\n", + "responded: [{'text': \"\\n\\nI apologize for the error. It seems that the EMT calculator doesn't have parameters for rhodium (Rh). Let's skip rhodium and move on to iridium (Ir).\", 'type': 'text', 'index': 0}, {'id': 'toolu_01CjHj3icBnVDpTuqSurEy4x', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Ir\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[77] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.92, 1.92], [1.92, 0.0, 1.92], [1.92, 1.92, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Agent stopped due to max iterations." + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%chat\n", + "Let's take EMT then" + ] + }, + { + "cell_type": "markdown", + "id": "6aab7bc7-026a-412b-be3c-89391f07cfd3", + "metadata": {}, + "source": [ + "The results are summarized in a short list. This demonstration highlights that `LangSim` can be used without prior technical experience in calculating material properties. From students to teachers and professors, it is a handy tool for everybody who wants to have quick access to material properties computed with one of the standard methods like [EMT](https://wiki.fysik.dtu.dk/ase/ase/calculators/emt.html). Obviously, the [MACE](https://mace-docs.readthedocs.io) method is more accurate than the [EMT](https://wiki.fysik.dtu.dk/ase/ase/calculators/emt.html) method, but also computationally more expensive. \n", + "\n", + "Still, to further highlight the real-life application of a tool like `LangSim` the next step after the completion of these calculations is the comparison to an experimental reference. For this purpose, the `get_experimental_elastic_property_wikipedia()` function is implemented which downloads the experimental values for the elastic properties from the wikipedia site for [elastic properties](https://en.wikipedia.org/wiki/Elastic_properties_of_the_elements_(data_page))." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "bfae3bb2-6ad1-4e1f-bab4-b1c51fc7044a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"Certainly! I'll calculate the bulk modulus for the noble metals using the EMT (Effective Medium Theory) method as you've requested. Then, we'll compare these results to experimental values. Let's start with the calculations for gold (Au), silver (Ag), platinum (Pt), palladium (Pd), copper (Cu), and iridium (Ir). \\n\\nLet's begin with the calculations:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01Xa7Td1GhBnm4iFmXE4KKs9', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[79] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [79], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'text': \"\\n\\nNow that we have the bulk structure for gold, let's calculate its bulk modulus:\", 'type': 'text', 'index': 0}, {'id': 'toolu_0158ZDyvXUJ1HDrjGtrhn5YL', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"atom_dict\": {\"numbers\": [79], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m173.84249328565886\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Ag'}`\n", + "responded: [{'text': \"\\n\\nThe calculated bulk modulus for gold (Au) using EMT is approximately 173.84 GPa.\\n\\nNow, let's repeat this process for the other noble metals:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01RGKXnHKrrNPEo1utQsidX6', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Ag\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[47] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [47], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_016QSFTz8QzQC6RfjfWfseWt', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [47], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m100.1601184287953\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pt'}`\n", + "responded: [{'id': 'toolu_0117HVu9RSpUFL9aigQ7sPYS', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pt\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[78] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [78], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_011uYUfGxXMg3BnsiXFVuQ7L', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [78], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m278.296219466303\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pd'}`\n", + "responded: [{'id': 'toolu_01SB7ycqtfzN1PZUYAS75nW1', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pd\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[46] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [46], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_014anyQJeJSbzVpEkARPMyYT', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [46], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m179.24432817947203\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Cu'}`\n", + "responded: [{'id': 'toolu_01Da3sTehBt7hSKHrQBnoBkr', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Cu\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[29] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [29], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_017st1WeGeimCrWdp8SoRKmv', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [29], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m134.48011624554215\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"\\n\\nNow, let's compare these calculated values with experimental data:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01VVVf344F93n8C9cxidWiz5', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 78.0, 'poissons_ratio': 0.44, 'bulk_modulus': 180.0, 'shear_modulus': 27.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Ag'}`\n", + "responded: [{'id': 'toolu_016hTX9eUKGQ6yCCa8Jvf2n3', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Ag\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 83.0, 'poissons_ratio': 0.37, 'bulk_modulus': 100.0, 'shear_modulus': 30.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Pt'}`\n", + "responded: [{'id': 'toolu_014gvyswXvWuxLYXiDoaJZC4', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pt\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 168.0, 'poissons_ratio': 0.38, 'bulk_modulus': 230.0, 'shear_modulus': 61.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Pd'}`\n", + "responded: [{'id': 'toolu_016VnJRPHqK7nAmGnxB2dvmX', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pd\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 121.0, 'poissons_ratio': 0.39, 'bulk_modulus': 180.0, 'shear_modulus': 44.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Cu'}`\n", + "responded: [{'id': 'toolu_01MEacCFNGgw5pkp2239HSH3', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Cu\"}'}]\n", + "\n", + "\u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Stopping agent prematurely due to triggering stop condition\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33;1m\u001b[1;3m{'youngs_modulus': 130.0, 'poissons_ratio': 0.34, 'bulk_modulus': 140.0, 'shear_modulus': 48.0}\u001b[0m\u001b[32;1m\u001b[1;3m\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Agent stopped due to max iterations." + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%chat\n", + "How does this compare to experiment?" + ] + }, + { + "cell_type": "markdown", + "id": "9f9cb1aa-cf38-49be-8654-7b77cc6ef207", + "metadata": {}, + "source": [ + "Finally, as the last step the user tasks the LLM to compare the theoretical predictions and the experimental results to give an indication of the trustworthiness of these calculations. Given the optimization for computational efficiency, the result for Platinum with an error of over 20% is insufficient for any real life application. Still, this can be addressed by switching to the [MACE](https://mace-docs.readthedocs.io) model. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2a6c69a8-73bb-40af-a7f1-500d97f6279b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"Certainly! I'll calculate the bulk modulus for the noble metals using the EMT (Effective Medium Theory) method and then compare it to experimental values. After that, I'll show you the deviation in percent. Let's go through this step by step.\\n\\nFirst, let's calculate the bulk modulus for each noble metal using EMT. Then, we'll fetch the experimental values and calculate the deviation.\\n\\nLet's start with gold (Au), silver (Ag), copper (Cu), platinum (Pt), and palladium (Pd). We'll do this one by one:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01DBmt6HcKaVxRAf2cjG7LUK', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[79] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [79], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'text': \"\\n\\nNow that we have the bulk structure for gold, let's calculate its bulk modulus:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01VtFGEKMWWWBqxYNnzgKjaa', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"atom_dict\": {\"numbers\": [79], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.04, 2.04], [2.04, 0.0, 2.04], [2.04, 2.04, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m173.84249328565886\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Ag'}`\n", + "responded: [{'text': \"\\n\\nGreat, we have the calculated bulk modulus for gold using EMT. Let's do the same for the other noble metals:\", 'type': 'text', 'index': 0}, {'id': 'toolu_01Jj29VPfeaMeVERUyyR7jYx', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Ag\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[47] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [47], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01Cd7piaoBh2HPF9eBP9jGus', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [47], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 2.045, 2.045], [2.045, 0.0, 2.045], [2.045, 2.045, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m100.1601184287953\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Cu'}`\n", + "responded: [{'id': 'toolu_01Bfb7yN9J1EXHFSpMGssfWj', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Cu\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[29] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [29], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_01GjJzbKhRdC7f7vvjZMG56x', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [29], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.805, 1.805], [1.805, 0.0, 1.805], [1.805, 1.805, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m134.48011624554215\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pt'}`\n", + "responded: [{'id': 'toolu_01YKLLjGYvLgeLKH2QGPmiXK', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pt\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[78] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [78], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_014w76fEpgzKJSGWH66SLuUy', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [78], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m278.296219466303\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_atom_dict_bulk_structure` with `{'chemical_symbol': 'Pd'}`\n", + "responded: [{'id': 'toolu_01T7jyw2zwZz4TPDyaNDUgRt', 'input': {}, 'name': 'get_atom_dict_bulk_structure', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pd\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3mnumbers=[46] positions=[[0.0, 0.0, 0.0]] cell=[[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]] pbc=[True, True, True]\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_bulk_modulus` with `{'atom_dict': {'numbers': [46], 'positions': [[0.0, 0.0, 0.0]], 'cell': [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], 'pbc': [True, True, True]}, 'calculator_str': 'emt'}`\n", + "responded: [{'id': 'toolu_015KxgZAkvoScYbpNwkijzq6', 'input': {}, 'name': 'get_bulk_modulus', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"atom_dict\": {\"numbers\": [46], \"positions\": [[0.0, 0.0, 0.0]], \"cell\": [[0.0, 1.945, 1.945], [1.945, 0.0, 1.945], [1.945, 1.945, 0.0]], \"pbc\": [true, true, true]}, \"calculator_str\": \"emt\"}'}]\n", + "\n", + "\u001b[0m\u001b[36;1m\u001b[1;3m179.24432817947203\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Au'}`\n", + "responded: [{'text': \"\\n\\nNow that we have the calculated values, let's fetch the experimental values for comparison:\", 'type': 'text', 'index': 0}, {'id': 'toolu_014Vy9kzBSAESKGiLQTQ6qbp', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"chemical_symbol\": \"Au\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 78.0, 'poissons_ratio': 0.44, 'bulk_modulus': 180.0, 'shear_modulus': 27.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Ag'}`\n", + "responded: [{'id': 'toolu_01DvPXNBzLede1ufX8bFswkV', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Ag\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 83.0, 'poissons_ratio': 0.37, 'bulk_modulus': 100.0, 'shear_modulus': 30.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Cu'}`\n", + "responded: [{'id': 'toolu_013Z68xM7RrHa1963srGfoPr', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Cu\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 130.0, 'poissons_ratio': 0.34, 'bulk_modulus': 140.0, 'shear_modulus': 48.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Pt'}`\n", + "responded: [{'id': 'toolu_01UgV48Ju7BFYcJeUCgzgzCE', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pt\"}'}]\n", + "\n", + "\u001b[0m\u001b[33;1m\u001b[1;3m{'youngs_modulus': 168.0, 'poissons_ratio': 0.38, 'bulk_modulus': 230.0, 'shear_modulus': 61.0}\u001b[0m\u001b[32;1m\u001b[1;3m\n", + "Invoking: `get_experimental_elastic_property_wikipedia` with `{'chemical_symbol': 'Pd'}`\n", + "responded: [{'id': 'toolu_011Tr2F4H2EewvwooKHGAkty', 'input': {}, 'name': 'get_experimental_elastic_property_wikipedia', 'type': 'tool_use', 'index': 0, 'partial_json': '{\"chemical_symbol\": \"Pd\"}'}]\n", + "\n", + "\u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Stopping agent prematurely due to triggering stop condition\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33;1m\u001b[1;3m{'youngs_modulus': 121.0, 'poissons_ratio': 0.39, 'bulk_modulus': 180.0, 'shear_modulus': 44.0}\u001b[0m\u001b[32;1m\u001b[1;3m\u001b[0m\n", + "\n", + "\u001b[1m> Finished chain.\u001b[0m\n" + ] + }, + { + "data": { + "text/markdown": [ + "Agent stopped due to max iterations." + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%%chat\n", + "Can you show me the deviation in percent?" + ] + }, + { + "cell_type": "markdown", + "id": "a48a2446-186b-4b7b-b76d-6994301df696", + "metadata": {}, + "source": [ + "## Summary\n", + "The jury was convinced that this demonstration of the capabilities of `LangSim` highlights a future direction in the application of LLMs for materials and chemistry. So the `LangSim` team won the [first prize](https://medium.com/@blaiszik/llms-to-accelerate-discovery-in-materials-science-and-chemistry-refections-on-a-hackathon-b8364ca32242) sponsored by [RadicalAI](https://www.radical-ai.com). \n", + "\n", + "In comparison to the previous examples, the highlight of this presentation, besides hiding the technical complexity by developing a python module, was the capability of the LLM to repeat the workflow consisting of two calculations, the `get_equilibrium_lattice()` function and the `get_bulk_modulus()` function, over three elements without the need to define any additional functionality. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dabd4ab1-f3df-4081-b95a-a3c091103fcc", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}