Skip to content

Commit 022243b

Browse files
committed
Enhance web spider functionality by increasing page count limit and ensuring HTTPS protocol for start URLs
1 parent 3d9cb89 commit 022243b

2 files changed

Lines changed: 112 additions & 37 deletions

File tree

crawlProcess.py

Lines changed: 107 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# from pydispatch import dispatcher
88
from dotenv import load_dotenv
99
import os
10+
from urllib.parse import urlparse , urlunparse
1011

1112
from fastapi import HTTPException
1213

@@ -86,34 +87,72 @@ def makeDecisionFromKG(query: str) -> str:
8687

8788
async def ReasoningAgent():
8889
SYSTEM_PROMPT = """
89-
You are an intelligent AI reasoning agent connected to a Neo4j Knowledge Graph.
90-
You can:
91-
- Generate Cypher queries to retrieve relevant graph data (using fuzzy search where appropriate)
92-
- Analyze the graph results and make logical or data-driven decisions based on them.
93-
94-
You have access to these tools:
95-
1. queryNeo4J(query: str) — Execute Cypher queries on Neo4j and return results.
96-
2. makeDecisionFromKG(data: dict) — Analyze Neo4j query results and make a decision or summary.
97-
98-
Rules:
99-
- Always first use `queryNeo4J` to gather information before making any decision.
100-
- When forming Cypher queries:
101-
- Use fuzzy or partial matching (`CONTAINS`, `toLower()`, or regex `=~ '(?i).*<term>.*'`)
102-
- Match node and relationship names exactly as defined in the KG schema.
103-
- Never hallucinate labels or relationships that are not in the schema.
104-
- After retrieving data, use `makeDecisionFromKG` to interpret the results, summarize insights, or provide reasoning.
105-
- If the question cannot be answered from the graph, say so clearly.
106-
107-
Example reasoning flow:
108-
User: "What packages does SLT Mobitel offer?"
109-
→ Step 1: Use `queryNeo4J` with:
110-
MATCH (c:Company)-[:HAS]->(p:Package)
111-
WHERE toLower(c.name) CONTAINS toLower("slt mobitel")
112-
RETURN p.name, p.price
113-
→ Step 2: Use `makeDecisionFromKG` to analyze results and summarize.
114-
115-
Be clear, structured, and logical in your thought process.
116-
"""
90+
You are an intelligent AI reasoning agent connected to a Neo4j Knowledge Graph.
91+
Your capabilities:
92+
- Discover schema elements (labels, relationship types, property keys) when the user doesn't know exact KG keywords.
93+
- Generate Cypher queries that use fuzzy/partial matching to find relevant nodes and relationships.
94+
- Analyze query results and make decisions or summaries using the tool `makeDecisionFromKG`.
95+
96+
Tools available:
97+
1. queryNeo4J(query: str) — Execute Cypher queries on Neo4j and return results.
98+
2. makeDecisionFromKG(data: dict) — Analyze Neo4j query results and make a decision or summary.
99+
100+
High-level rules:
101+
- Always start by discovering schema candidates relevant to the user's query (labels, relationship types, property keys) before issuing content queries.
102+
- NEVER hallucinate labels, relationship types, or properties that are not discoverable in the graph. Use actual results from Neo4j to decide.
103+
- Prefer safe, read-only Cypher (MATCH, RETURN, CALL db.*) unless explicitly asked to write.
104+
- Use fuzzy matching (`CONTAINS`, `toLower()`, or case-insensitive regex) when matching user terms to schema elements or data values.
105+
- If no matches are found, report that clearly and provide suggested alternative search terms, synonyms, or explain how the user could rephrase.
106+
107+
Schema-discovery queries (Neo4j-native):
108+
- List all relationship types:
109+
CALL db.relationshipTypes() YIELD relationshipType RETURN relationshipType;
110+
- List all labels:
111+
CALL db.labels() YIELD label RETURN label;
112+
- List all property keys:
113+
CALL db.propertyKeys() YIELD propertyKey RETURN propertyKey;
114+
115+
Fuzzy-search templates (replace <term>):
116+
- Find relationship types matching a user term:
117+
MATCH ()-[r]-()
118+
WHERE toLower(type(r)) CONTAINS toLower('<term>')
119+
RETURN DISTINCT type(r) AS relType, count(r) AS occurrences
120+
ORDER BY occurrences DESC;
121+
- Find labels that match a user term:
122+
CALL db.labels() YIELD label
123+
WHERE toLower(label) CONTAINS toLower('<term>')
124+
RETURN label;
125+
- Find nodes whose properties match a user term:
126+
MATCH (n)
127+
WHERE any(k IN keys(n) WHERE toString(n[k]) =~ '(?i).*<term>.*')
128+
RETURN labels(n) AS labels, n AS node, size(keys(n)) AS propertyCount;
129+
130+
Once a candidate relationship or label is found, fetch content nodes:
131+
MATCH (a)-[r:`<relationship>`]->(b)
132+
RETURN labels(a) AS fromLabels, a.name AS fromName,
133+
type(r) AS rel,
134+
labels(b) AS toLabels, b.name AS toName;
135+
136+
When you find candidate relationship types or labels:
137+
- Return a short ranked list of best matches (relType or label, count of occurrences).
138+
- Automatically run a follow-up content query on the top candidates and summarize results using `makeDecisionFromKG`.
139+
140+
Fallback behavior:
141+
- If no schema or data matches are found for the user term:
142+
- Return: "No matching labels or relationship types found for '<term>' in the knowledge graph."
143+
- Provide 2–4 suggested synonyms or alternate search terms the user might try.
144+
- Suggest an explicit schema-discovery run (CALL db.* queries) if permitted by the user.
145+
146+
Safety and precision:
147+
- Always put the user term into safe, parameterized Cypher or escape user input properly to avoid syntax issues.
148+
- Prefer `toLower(... ) CONTAINS toLower(...)` for robust partial matching. Use regex `=~ '(?i).*term.*'` only when needed.
149+
150+
Response style:
151+
- Be clear, structured, and logical.
152+
- For schema discovery steps, show the query used and the succinct ranked results (up to 5 candidates).
153+
- For content queries, summarize findings and pass the raw results to `makeDecisionFromKG` for final interpretation.
154+
"""
155+
117156

118157

119158
tools = [queryNeo4J, makeDecisionFromKG]
@@ -466,6 +505,26 @@ async def getKeywordById(id):
466505
return None
467506
return result
468507

508+
# Get details with keyword name
509+
async def getKeywordByDomain(url):
510+
try:
511+
if not url.startswith(("http://", "https://")):
512+
url = "http://" + url
513+
514+
parsed_url = urlparse(url)
515+
domain = parsed_url.netloc.replace("www.", "")
516+
517+
result = await keyword_collection.find_one(
518+
{"keyword": {"$regex": domain, "$options": "i"}}
519+
)
520+
521+
return result
522+
523+
except Exception as e:
524+
print(e)
525+
return None
526+
return result
527+
469528

470529
# Add urls to keyword document
471530
async def storeRelevantUrls(keywordId):
@@ -604,7 +663,7 @@ async def summarizeUsingAgent(keywordId):
604663
return None
605664

606665

607-
async def exec(keyword , domain):
666+
async def exec(keyword):
608667
"""
609668
Complete workflow:
610669
1. Store keyword
@@ -613,19 +672,32 @@ async def exec(keyword , domain):
613672
4. Summarize content (only if crawl succeeds)
614673
"""
615674

616-
# Step 1: Store keyword
675+
# Step 1: Store keyword or add it to existing keyword
617676
print("\n" + "=" * 80)
618-
print("STEP 1: Storing keyword")
677+
print("STEP 1.1: Check keyword")
619678
print("=" * 80)
620-
domain = "com"
621-
storedKeyword = await storeKeyword(keyword, domain)
622-
print(f"Keyword stored with ID: {storedKeyword.inserted_id}")
679+
680+
result = await getKeywordByDomain(keyword)
681+
682+
if not result :
683+
print("\n" + "=" * 80)
684+
print("STEP 1.2: Storing keyword")
685+
print("=" * 80)
686+
domain = "com"
687+
storedKeyword = await storeKeyword(keyword, domain)
688+
storedKeywordId = storedKeyword.inserted_id
689+
print(f"Keyword stored with ID: {storedKeywordId}")
690+
else :
691+
print("Id is founded!")
692+
print(result["_id"])
693+
storedKeywordId = result["_id"]
694+
print("Keyword Already founded! Skip creating new keyword id...")
623695

624696
# Step 2: Get keyword details
625697
print("\n" + "=" * 80)
626698
print("STEP 2: Fetching keyword details")
627699
print("=" * 80)
628-
resultMongo = await getKeywordById(storedKeyword.inserted_id)
700+
resultMongo = await getKeywordById(storedKeywordId)
629701
keywordId = resultMongo["_id"]
630702

631703
# Step 3: Fetch Google URLs

webscrapy/webscrapy/spiders/web_spider_new.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,18 @@ class WebCrawSpider(scrapy.Spider):
2525
'RETRY_TIMES': 2,
2626
'LOG_LEVEL': 'INFO',
2727
'CLOSESPIDER_TIMEOUT': 0,
28-
'CLOSESPIDER_PAGECOUNT': 1, # Stop after 100 pages
28+
'CLOSESPIDER_PAGECOUNT': 5, # Stop after 100 pages
2929
}
3030

3131
def __init__(self, start_urls=None, keywordId=None, *args, **kwargs):
3232
super(WebCrawSpider, self).__init__(*args, **kwargs)
33+
34+
if not start_urls[0].startswith("https://"):
35+
start_urls[0] = "https://" + start_urls[0]
36+
3337
self.start_urls = [start_urls[0]] or []
3438
self.keywordId = keywordId or ""
3539
self.client = pymongo.MongoClient(CONNECTION_STRING)
36-
3740
self.db = self.client['webcrawl']
3841
self.collection = self.db['sitesData']
3942

0 commit comments

Comments
 (0)