Building AI-React-Agent

You want an AI agent that actually reasons, queries databases, and uses tools intelligently, not a chatbot that rephrases your question back with a different tone. The ReAct pattern with LangGraph and Azure OpenAI is how you build that.

TL;DR: Build a production-ready AI agent using LangGraph’s ReAct pattern with Elasticsearch (vector search), Neo4j (graph recommendations), and Streamlit (UI), deployed on Azure OpenAI.
Stack: Python, LangGraph, LangChain, Azure OpenAI, Elasticsearch, Neo4j, Streamlit, Poetry
Level: Advanced
Reading time: ~20 min

I built this kind of system for a real media platform to power intelligent search and recommendations over content catalogs. The architecture here follows the same pattern, adapted as a self-contained tutorial. It’s not trivial, but follow each step and you’ll have a real agent, not a demo toy.

Configuring Poetry

Because manually managing dependencies is suffering we no longer accept.

poetry init

pyproject.toml

[tool.poetry]
name = "ai-react-agent-azure"
version = "0.1.0"

[tool.poetry.dependencies]
python = "^3.10"
langchain = "^0.3.14"
langchain-openai = "^0.3.0"
langgraph = "^0.2.62"
streamlit = "^1.41.1"
elasticsearch = "^8.17.1"
neo4j = "^5.28.1"
python-dotenv = "^1.0.1"
poetry shell
poetry install

State (state.py)

LangGraph is state-machine based. This file defines the shared state object that flows between every node in the agent graph.

from typing import Annotated, TypedDict, Union
from langchain_core.agents import AgentAction, AgentFinish
import operator

class AgentState(TypedDict):
    input: str
    agent_outcome: Union[AgentAction, AgentFinish, None]
    intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add]

Tools (tools.py)

The toolbox. Because a ReAct agent without tools is just a very opinionated chatbot. The agent has four tools: vector search via Elasticsearch, social graph recommendations via Neo4j, promotion queries, and a general chat fallback.

ReAct agent (react.py)

This is where the magic happens, or where you spend 3 hours debugging why the agent keeps calling the wrong tool.

from langchain import hub
from langchain.agents import create_react_agent
from langchain_openai import ChatOpenAI

react_prompt = hub.pull("hwchase17/react")
llm = ChatOpenAI(model="gpt-3.5-turbo-1106")
react_agent_runnable = create_react_agent(llm, tools, react_prompt)

Nodes (nodes.py)

from langgraph.prebuilt.tool_executor import ToolExecutor

def run_agent_reasoning_engine(state: AgentState):
    agent_outcome = react_agent_runnable.invoke(state)
    return {"agent_outcome": agent_outcome}

tool_executor = ToolExecutor(tools)

def execute_tools(state: AgentState):
    agent_action = state["agent_outcome"]
    output = tool_executor.invoke(agent_action)
    return {"intermediate_steps": [(agent_action, str(output))]}

Graph (run.py)

from langgraph.graph import END, StateGraph

def should_continue(state: AgentState) -> str:
    if isinstance(state["agent_outcome"], AgentFinish):
        return END
    return "act"

flow = StateGraph(AgentState)
flow.add_node("agent_reason", run_agent_reasoning_engine)
flow.add_node("act", execute_tools)
flow.set_entry_point("agent_reason")
flow.add_conditional_edges("agent_reason", should_continue)
flow.add_edge("act", "agent_reason")
app = flow.compile()

Configuring Elasticsearch locally

sudo apt-get install elasticsearch -y
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i

Configuring Neo4j locally

sudo apt install neo4j -y
sudo neo4j-admin set-initial-password yourpassword
sudo systemctl enable neo4j
sudo systemctl start neo4j
# Access: http://localhost:7474/browser/

What you’ve built

A complete, production-pattern AI agent: a LangGraph ReAct loop that orchestrates Azure OpenAI reasoning, Elasticsearch vector search, and Neo4j graph-based recommendations, wrapped in a Streamlit interface. The architecture handles tool selection, verification, and graceful fallback. More importantly, it runs the same pattern whether your databases are local or cloud services.

Next steps

  • Add LangSmith tracing to observe every reasoning step and tool call. Invaluable when the agent starts doing something unexpected.
  • Extend the tools layer with a database specific to your domain and watch the agent specialize without changing the graph structure.
  • Deploy the Streamlit app as a containerized service on Azure Container Apps with managed identity, so API keys never sit in environment files on a server.

Questions or feedback? Find me on LinkedIn or GitHub.

Leave a Comment