Developing a new agent based on Mistral¶
In this tutorial, you will learn how to develop a new agent for the Mistral API and integrate it into an existing Assistant. The end game code we will be developing can be found in llm/agents/mistral_agent.py and the config file.
The following diagram illustrates what is built:

Pre-requisites¶
You need to get a MISTRAL_API_KEY from mistral.ai. Be sure to get a little familiar with their API using their getting started guide.
Update the .env file in the backend with the MISTRAL_API_KEY environment variable.
You need to set up your development environment according to this note
Defining The Agent¶
An agent defines at a minimum the LLM API client code, a model reference and parameters, and how to manage a conversation. A new agent has a contract with the OwlAgent Framework via the OwlAgent class. Therefore the first step is to create an agent Python file named mistral_agent.py and define a class that supports the contract of the constructor as shown below:
from athena.llm.agents.agent_mgr import OwlAgentInterface,
class MistralAgent(OwlAgentInterface):
    def __init__(self,agentEntity: OwlAgentEntity, prompt: BasePromptTemplate, tool_instances: Optional[list[Any]]):
        self.prompt = prompt
        self.model=self._instantiate_model(agentEntity.modelName, agentEntity.modelClassName, agentEntity.temperature)
Since we are good test-driven developers, let's start by writing unit tests for this class: Under the tests/ut folder add a Python file called test_mistral_agent.py by copying the unit test template:
Rename the class and add a first test:
from athena.llm.agents.agent_mgr import get_agent_manager, OwlAgentEntity
class TestMistral(unittest.TestCase):
    def test_define_agent_entity_create_instance(self):
        """
        From the OwlAgentEntity verify the factory can create the agent executor
        """
        print("\n\n >>> test_define_agent_entity_create_instance\n")
        agent_entity = OwlAgentEntity(agent_id="mistral_large",
                                      modelName="mistral-large-latest",
                                      modelClassName="langchain_mistralai.chat_models.ChatMistralAI",
                                      temperature=0,
                                      )
        assert agent_entity
        mgr = get_agent_manager()
        agent_executor = mgr.build_agent_from_entity(agent_entity)
Running this test with pytest -s tests/ut/test_mistral_agent.py may fail as the langchain-mistralai is missing in the src/requirements.txt. You can fix that by adding it explicitly and doing pip install langchain-mistral. Once done the test should be able to create an agent instance.
Adding a system prompt¶
A system prompt is a text instruction to the LLM that says what is required so it can better answer the user's query. Taking the example from Mistral site, you will define a RAG system prompt which takes into account existing context.
- Add a yaml definition within the 
tests/ut/config/prompts.yamlfile: 
mistral_rag_prompt:
    name: Mistral Bank Query Classification
    prompt_id: mistral_rag_prompt
    locales:
    - locale: en
    text: |
        "Answer the following question based only on the provided context:
        <context>
        {context}
        </context>
        Question: {input}"
For unit testing, the configuration of the Owl framework is defined in ./tests/ut/config/config.yaml and set up in each test class using the environment variable CONFIG_FILE.
Add a unit test to validate that the prompt is correctly loaded:
def test_valide_prompt_is_loaded(self):
    prompt_mgr = get_prompt_manager()
    mistral_prompt = prompt_mgr.get_prompt("mistral_rag_prompt")
    assert mistral_prompt    # this is a string
    print(pe)
Add the prompt reference to the OwlAgentEntity
agent_entity = OwlAgentEntity(agent_id="mistral_large",
                                name="Mistral based agent",
                                class_name="athena.llm.agents.mistral_agent.MistralAgent",
                                modelName="mistral-large-latest",
                                prompt_ref="mistral_rag_prompt",   
)
For the snippet above: The reference needs to match the prompt_id.
Rerunning the unit test should succeed, but we are not yet calling the LLM. The next step is to create a LangChain chain and then do a simple invocation.
Adding A Chain¶
To add a chain to the agent, add the following code:
    def __init__(self,agentEntity: OwlAgentEntity, prompt: BasePromptTemplate, tool_instances: Optional[list[Any]]):
        self.prompt = prompt
        self.model=self._instantiate_model(agentEntity.modelName, agentEntity.modelClassName, agentEntity.temperature)
        self.llm = self.prompt | self.model | StrOutputParser()   # (1)
    def get_runnable(self):
        return  self.llm  # (1)
The lines marked (1) are newly added.
The prompt has defined a new variable called context. The OwlFramework defines the following prompt template from the system prompt text:
 ChatPromptTemplate.from_messages([
            ("system", text),
            MessagesPlaceholder(variable_name="chat_history", optional=True),
            ("human", "{input}"),
            MessagesPlaceholder(variable_name="agent_scratchpad", optional=True),
        ])
The chat_history and agent_scratchpad variables are optional, but not the input. So you need to add some parameters to the test:
    assert agent_executor
    rep = agent_executor.invoke({"input" : "What is langgraph?", "context": ""})
    print(rep)
The invocation to Mistral should work but respond something like the following answer: "I'm sorry for the confusion, but the context provided is empty, and I don't have any information about "langgraph" from the given context."
At this stage we have a running (albeit not particularly useful) agent, but one that can be a component for building a useful assistant that can call Mistral.
Define a New Assistant¶
The OwlAgent Framework has some predefined agents. In the new assistant you will use the LangGraph flow for two nodes, the call to LLM and the tool node.
The first operation is to add the new agent in src/athena/config/agents.yaml
mistral_large:
  agent_id: mistral_large
  class_name: athena.llm.agents.mistral_agent.MistralAgent
  description: A Mistral large agent for RAG question and answer
  modelClassName: langchain_mistralai.chat_models.ChatMistralAI
  modelName: mistral-large-latest
  name: Mistral based agent
  prompt_ref: mistral_rag_prompt
  temperature: 0
  tools: []
  top_k: 1
Then create an assistant that uses this agent:
mistral_tool_assistant:
  assistant_id: mistral_tool_assistant
  class_name: athena.llm.assistants.BaseToolGraphAssistant.BaseToolGraphAssistant
  description: A default assistant that uses Mistral
  name: Mistral large with tool assitant
  agents: 
    - mistral_large
Now let's add a test for the assistant. For that, add a new test function:
from athena.routers.dto_models import ConversationControl
from athena.main import app
from athena.app_settings import  get_config
from fastapi.testclient import TestClient
def test_mistral_assistant_with_mistral_agent(self):
        print("\n\n >>> test_mistral_assistant_with_mistral_agent at the API level\n")
        client = TestClient(app)
        ctl = ConversationControl()
        ctl.assistant_id="mistral_tool_assistant"
        ctl.user_id="test_user"
        ctl.thread_id="1"
        ctl.query="What is Athena Owl Agent?"
        response=client.post(get_config().api_route + "/c/generic_chat", json= ctl.model_dump())
        print(f"\n---> {response.content}")
The test should fail because we have missing tools.
Adding tools to retrieve documents from a corpus¶
JB to continue...