Design Patterns for Maritime ReAct Agents

Jordan Taylor
Shipping Intel
Published in
7 min readJul 24, 2024

Within artificial intelligence, ReAct (“reasoning” and “acting”) refers to a process where a foundation model responds to user input by performing specific actions, such as fetching data from application program interfaces, and making decisions based on the results.

Source: J. Taylor.

Introduction

Zero shot prompting using large language models involves sending one input to a model and receiving one output. When engaging in ReAct methodology (Yao et al., 2023), the model reasons out a problem iteratively. One or many prompts may be sent to one or many application program interfaces (APIs) in order to formulate one output.

ReAct is a style of prompt engineering that reflects human behavior. For example, if an oceangoing vessel loses power underway a ReAct agent might evaluate the steps necessary to resolve the loss of power before producing an output. In other words, the agent might look up an explicit loss of power procedure in a safety management system (or whatever other technical documentation is available) before making a decision. The alternative is making a decision without reasoning (zero shot) and hoping for the best.

Source: Shipping Intel.

Within foundational models, using ReAct methodology on OpenAI’s now depreciated GPT 3.5 results in similar performance to GPT 4 (Ng, 2024). Applications of ReAct agents to GPT 4 are believed to replicate the yet-to-be-released GPT 5. As such, use of ReAct is considered a hard requirement (Berman, 2024) when constructing copilots.

ReAct Agents within the Maritime Industry

Within the maritime industry, agents are necessary for three reasons.

The first is — due to the small amount of maritime technical corpora found within foundation models—the guardrails that agents offer is necessary to obtain an accurate domain-specific response.

The second reason is that much of the data in maritime is spatial-temporal and will require specialized geospatial information system (GIS) tools in order to produce an output.

The third is a need to access and model real-time maritime-related data, notably automated information systems (AIS), weather, and other types of sensor-related data.

ReAct, in short, will reduce operational risk when applying artificial intelligence solutions within the maritime domain.

Method

The following code assumes an intermediate level of proficiency in Python. Second, ReAct is an abstract concept, and not easy to understand—give yourself a break if you don’t get it first time around. I didn’t. Last, unfortunately for some, it does take some degree of maritime subject-matter expertise to build domain-specific agents.

I will use LangChain as a constructor. To learn more about Langchain, Eden Marco on Udemy does an excellent deep dive into LLMs and LangChain.

Task

As a mariner, I wish to obtain weather information. To do this, I want to use an agent to look up the latitude and longitude of a port, find the nearest ODAS buoy, and extract real-time sensor information.

Dependencies

To start, load the following dependencies:

pip install langchain-openai langchain-community langchain-core requests pandas langchainhub 

Due that agents use foundational models, you will also need an OpenAI key and commit the key to your user configuration or .env file.

llm = ChatOpenAI(
temperature=0,
model_name="gpt-3.5-turbo",
)

Step 1: Reasoning

To start, the agent will need to decide on a course of action. The logic behind the action can be found at smith.langchain.com or uploaded using langchain hub. For this example I will use the most common ReAct template constructed by Harrison Chase:

from langchain import hub
react_prompt = hub.pull('hwchase17/react')

react_prompt now contains the following text:

Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer

Thought: you should always think about what to do

Action: the action to take, should be one of [{tool_names}]

Action Input: the input to the action

Observation: the result of the action

... (this Thought/Action/Action Input/Observation can repeat N times)

Thought: I now know the final answer

Final Answer: the final answer to the original input question

Begin!

Question: {input}

Thought:{agent_scratchpad}

Step 2: Template

A template defines the structure or pattern of the prompt that will be sent to the language model. Below specifies a placeholder {port} that is replaced with data when the prompt is formatted. Per GPT, “this mechanism enables the generation of tailored queries or commands based on the input, allowing for more versatile interactions with the language model.”

template = """Find the coordinates of {port} and return the current weather."""

prompt_template = PromptTemplate(
template=template, input_variables=['port']
)

Step 3: Tool Selection

The magic of ReAct is the ability of the agent to use hard coded tools, or functions. Tools are usually API calls, but they can be simple functions as well.

For many applications, agent tools exist and are publicly available. Travily is an example of a tool provider for ReAct agents. However in the maritime industry, there do not appear to be many domain-specific tools available. For example, geospatial and spatial-temporal tools. For anyone who wants to carve out an in-demand niche within maritime, tool construction is a viable path, in my opinion.

Tool #1

Looks up a port in a CSV database and return a latitude and longitude.

# Load the CSV file
csv_path = '/portlist.csv'
df = pd.read_csv(csv_path, header=None, names=['Port', 'Country', 'Lat', 'Lon'])

def port_lat_long(port):
query = port.strip().lower()
port_country_list = df['Port'] + ',' + df['Country']
choices = port_country_list.str.lower().tolist()
best_match, score = process.extractOne(query, choices)
if score > 80:
match_index = choices.index(best_match)
return [df.iloc[match_index]['Lat'], df.iloc[match_index]['Lon']]
return None

Tool #2

Obtain real-time ODAS buoy data using an API call from Shipping Intel.

Of minor note, the ast class is used to convert type string to list, as agents appear to prefer string types.

def fetch_ODAS_data(coord):
coord_as_list = ast.literal_eval(coord)
print({'lat': coord_as_list[0], 'long': coord_as_list[1]})
url = 'https://www.shippingintel.com/api/noaa_buoy'
payload = {'lat': coord_as_list[0], 'long': coord_as_list[1]}
response = requests.post(url, data=payload)
return response.json()

Tools are instantiated using:

    tools_for_agent = [
Tool(
name="port_lat_long",
func=port_lat_long,
description="Returns port coordinates as a list.",
),
Tool(
name="fetch_ODAS_data",
func=fetch_ODAS_data,
description="Accepts type list with two floats: [lat, lon]."
)
]

Step 4: Chain of Thought

The next is to initiate an executor for chain-of-thought. This will allow the agent to iteratively process the question being asked using the reasoning logic found in step 1. This executor manages the execution of the agent, using tools, and maintains logs of the execution.

The executor can be instantiated as follows:

 agent = create_react_agent(llm=llm, tools=tools_for_agent, prompt=react_prompt)

agent_executor = AgentExecutor(agent=agent, tools=tools_for_agent, verbose=True)

Verbose should always be set to true as the output will be more descriptive in terms of the reasoning process.

When user enters “San Francisco”, the following back-end reasoning is produced:

Within a copilot, the user will only see the statement after “Finished chain.” However within the chain-of-thought process, the foundational model, using tools, is conversing with itself how to come up with the solution. For example, it extracts the latitude and longitude using a CSV file, obtains real-time sensor data using an API, condenses the data into information, and produces a final answer:

The current weather data for San Francisco is as follows: Wind Direction: MM, Wind Speed: MM, Air Temperature: 21.0°C, Visibility: 5.9 nm.

Full Code Example

from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
from langchain.agents import (create_react_agent, AgentExecutor)
from langchain import hub, PromptTemplate
import requests, ast
import pandas as pd
from fuzzywuzzy import process

# Load the CSV file
csv_path = '/portlist.csv'
df = pd.read_csv(csv_path, header=None, names=['Port', 'Country', 'Lat', 'Lon'])

def port_lat_long(port):
query = port.strip().lower()
port_country_list = df['Port'] + ',' + df['Country']
choices = port_country_list.str.lower().tolist()
best_match, score = process.extractOne(query, choices)
if score > 80:
match_index = choices.index(best_match)
return [df.iloc[match_index]['Lat'], df.iloc[match_index]['Lon']]
return None

def fetch_ODAS_data(coord):
coord_as_list = ast.literal_eval(coord)
print({'lat': coord_as_list[0], 'long': coord_as_list[1]})
url = 'https://www.shippingintel.com/api/noaa_buoy'
payload = {'lat': coord_as_list[0], 'long': coord_as_list[1]}
response = requests.post(url, data=payload)
return response.json()

def lookup(name: str) -> str:
llm = ChatOpenAI(
temperature=0,
model_name="gpt-3.5-turbo",
)
template = """Find the coordinates of {port} as a list and return the current weather."""
prompt_template = PromptTemplate(
template=template, input_variables=['port']
)
tools_for_agent = [
Tool(
name="port_lat_long",
func=port_lat_long,
description="Returns port coordinates as a list.",
),
Tool(
name="fetch_ODAS_data",
func=fetch_ODAS_data,
description="Fetches ODAS buoy data."
)
]
react_prompt = hub.pull('hwchase17/react')
agent = create_react_agent(llm=llm, tools=tools_for_agent, prompt=react_prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools_for_agent, verbose=True)
result = agent_executor.invoke(
input={"input": prompt_template.format_prompt(port=name)}
)
output = result["output"]
return output

if __name__ == "__main__":
linkedin_url = lookup(name="san francisco")
print(output)

Conclusion

The final step is to create multi-agents, and have agents converse among themselves. Current literature suggests a 10–15% improvement in output with this approach.

For example, within maritime, one agent could be purposed as an operational agent, and another a technical agent. The agents could then converse with each other to formulate a nuanced outcome. One could imagine, in a loss of power scenario, how the multi-agent concept would have efficacy, just like the real world.

We’re not there yet.

Shipping Intel

To learn more about how Shipping Intel could help in your maritime tech journey contact info@shippingintel or visit shippingintel.com. Alternatively, you are welcome to contact me directly through LinkedIn.

References

ReAct: Synergizing Reasoning and Acting in Language Models. (2022, October 6). arXiv. Retrieved July 23, 2024, from https://arxiv.org/abs/2210.03629

langchain.agents.react.base.ReActChain — LangChain 0.2.10. (2023, July 3). LangChain. Retrieved July 23, 2024, from https://api.python.langchain.com/en/latest/agents/langchain.agents.react.base.ReActChain.html

LangSmith. (2023, November 14). LangSmith. Retrieved July 23, 2024, from https://smith.langchain.com/hub/hwchase17/react

Shipping Intel. (2024). Shipping Intelligence. Retrieved from https://www.shippingintel.com/

--

--

Jordan Taylor
Shipping Intel

Merchant marine officer with a B.S. in Marine Transportation and a M.S. in Transportation Management.