Skip to content

RAG tool for agent (PoC for Nestbot) #1780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 29, 2025
Merged

RAG tool for agent (PoC for Nestbot) #1780

merged 13 commits into from
Jul 29, 2025

Conversation

Dishant1804
Copy link
Collaborator

Proposed change

Resolves #1736

  • RAG tool handles the queries for which it has the relevant context

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@Dishant1804 Dishant1804 requested a review from arkid15r as a code owner July 22, 2025 17:20
Copy link
Contributor

coderabbitai bot commented Jul 22, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a command-line tool to query an AI-powered Retrieval-Augmented Generation (RAG) system about OWASP topics.
    • Added advanced retrieval capabilities to fetch and filter relevant information based on user queries, content types, and similarity thresholds.
    • Implemented a generation system that produces answers strictly from OWASP-related content, with special handling for location-based and personalized queries.
  • Chores

    • Added new configuration options and command-line arguments for customizing queries and AI model selection.

Walkthrough

This change introduces a Retrieval-Augmented Generation (RAG) tool to the AI backend. It adds new classes for retrieval and generation, integrates them into a RagTool orchestrator, and exposes a Django management command for local testing. Supporting constants and a Makefile target are also added for configuration and ease of use.

Changes

Cohort / File(s) Change Summary
Makefile and Constants
backend/apps/ai/Makefile, backend/apps/ai/common/constants.py
Added Makefile target ai-run-rag-tool to run the RAG tool command; added retrieval limit and similarity threshold constants.
Management Command
backend/apps/ai/management/commands/ai_run_rag_tool.py
Added Django management command ai_run_rag_tool to run and test the RAG tool with configurable CLI arguments.
RAG Generator
backend/apps/ai/agent/tools/rag/generator.py
Added Generator class that formats context and generates answers using OpenAI chat completions with strict OWASP-related instructions.
RAG Orchestrator
backend/apps/ai/agent/tools/rag/rag_tool.py
Added RagTool class that integrates retrieval and generation components to process queries and produce answers.
RAG Retriever
backend/apps/ai/agent/tools/rag/retriever.py
Added Retriever class that generates query embeddings, retrieves relevant chunks filtered by similarity and content type, and extracts rich context metadata.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

  • Complexity: The review involves new core logic for retrieval, generation, and orchestration, alongside integration with Django management commands and Makefile. The retriever module, in particular, introduces intricate logic for embedding, filtering, and metadata extraction.
  • Scope: Multiple new modules, configuration changes, and a command-line interface are introduced.
  • Estimated Time: ~40 minutes for a thorough review of logic, security (API key usage), and integration points.

Assessment against linked issues

Objective Addressed Explanation
Implement a PoC agent that works locally (#1736)
Implement a node to cover #1684 functionality (#1736) The PR introduces a RAG tool and CLI but does not explicitly mention or show implementation of the node for #1684 functionality.

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes identified. All code additions relate directly to implementing and supporting the RAG tool as a PoC agent for local operation per the linked issue objectives.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b94aed and cef4d80.

📒 Files selected for processing (2)
  • backend/apps/ai/agent/tools/rag/rag_tool.py (1 hunks)
  • backend/apps/ai/management/commands/ai_run_rag_tool.py (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • backend/apps/ai/agent/tools/rag/rag_tool.py
  • backend/apps/ai/management/commands/ai_run_rag_tool.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (javascript-typescript)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
backend/apps/ai/agent/tools/RAG/generator.py (1)

16-32: Fix grammatical issues in system prompt.

The system prompt contains several grammatical errors and awkward phrasing that should be corrected for clarity.

Apply these corrections:

     SYSTEM_PROMPT = """
 You are a helpful and professional AI assistant for the OWASP Foundation.
 Your task is to answer user queries based ONLY on the provided context.
 Follow these rules strictly:
 1. Base your entire answer on the information given in the "CONTEXT" section. Do not use any
-external knowledge unless and until it is about OWASP.
+external knowledge unless it is about OWASP.
 2. Do not mention or refer to the word "context", "based on context", "provided information",
 "Information given to me" or similar phrases in your responses.
-3. you will answer questions only related to OWASP and within the scope of OWASP.
+3. You will answer questions only related to OWASP and within the scope of OWASP.
 4. Be concise and directly answer the user's query.
 5. Provide the necessary link if the context contains a URL.
 6. If there is any query based on location, you need to look for latitude and longitude in the
 context and provide the nearest OWASP chapter based on that.
 7. You can ask for more information if the query is very personalized or user-centric.
-8. after trying all of the above, If the context does not contain the information or you think that
-it is out of scope for OWASP, you MUST state: "please ask question related to OWASP."
+8. After trying all of the above, if the context does not contain the information or you think that
+it is out of scope for OWASP, you MUST state: "Please ask questions related to OWASP."
 """
backend/apps/ai/agent/tools/RAG/retriever.py (1)

243-266: Consider more context-aware content type detection

The current implementation might match content type names that appear in unrelated contexts (e.g., "message" in "error message"). Consider implementing more sophisticated detection that looks for content-type-specific context clues.

For example, you could look for patterns like "find {content_type}", "search for {content_type}", "get {content_type}", etc., or use a more sophisticated NLP approach to identify when these words are used as entity types rather than common words.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c9e933 and bff36bd.

📒 Files selected for processing (6)
  • backend/apps/ai/Makefile (1 hunks)
  • backend/apps/ai/agent/tools/RAG/generator.py (1 hunks)
  • backend/apps/ai/agent/tools/RAG/rag_tool.py (1 hunks)
  • backend/apps/ai/agent/tools/RAG/retriever.py (1 hunks)
  • backend/apps/ai/common/constants.py (1 hunks)
  • backend/apps/ai/management/commands/ai_run_rag_tool.py (1 hunks)
🔇 Additional comments (6)
backend/apps/ai/common/constants.py (1)

4-5: LGTM! Well-defined constants for RAG defaults.

The constants follow the existing naming convention and provide sensible defaults for the RAG tool's retrieval functionality.

backend/apps/ai/Makefile (1)

21-23: LGTM! Makefile target follows established patterns.

The new target is well-integrated with the existing Makefile structure and provides a convenient way to run the RAG tool.

backend/apps/ai/agent/tools/RAG/rag_tool.py (1)

46-46: Confirmed Python 3.10+ Compatibility

The project’s backend/pyproject.toml specifies

python = "^3.13"

which implies a minimum of Python 3.13. Since Python 3.13 > 3.10, the list[str] | None union type syntax is fully supported. No changes required.

backend/apps/ai/agent/tools/RAG/retriever.py (3)

64-71: Well-designed source name extraction

Good approach to handle various content types by checking multiple common identifier attributes with a sensible fallback.


171-242: Well-implemented vector similarity search

Excellent implementation with proper use of pgvector, Django ORM optimizations (select_related/prefetch_related), and comprehensive error handling for missing content objects.


57-59: Update OpenAIError exception catch
The openai.error.OpenAIError class doesn’t exist in the installed OpenAI Python SDK (v1.97.1). You should catch openai.OpenAIError instead to avoid runtime errors.

Please update in backend/apps/ai/agent/tools/RAG/retriever.py (around lines 57–59):

-        except openai.error.OpenAIError:
+        except openai.OpenAIError:
             logger.exception("OpenAI API error")
             raise

This aligns with how errors are handled elsewhere (e.g. catching openai.APIConnectionError directly).

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
backend/apps/ai/agent/tools/RAG/generator.py (2)

89-103: Resolve conflicting instructions between system and user prompts.

The system prompt restricts answers to "ONLY the provided context" (line 18), but the user prompt allows answering "based on your knowledge" for OWASP questions (line 92). This contradiction could lead to inconsistent behavior.

Either align both prompts to be context-only or allow knowledge-based answers. Here's a suggested fix for consistency:

         user_prompt = f"""
 - You are an assistant for question-answering tasks related to OWASP.
 - Use the following pieces of retrieved context to answer the question.
-- If the question is related to OWASP then you can try to answer based on your knowledge, if you
-don't know the answer, just say that you don't know.
-- Try to give answer and keep the answer concise, but you really think that the response will be
-longer and better you will provide more information.
+- Base your answer ONLY on the provided context below.
+- Keep the answer concise, but provide more detail if necessary for clarity.
 - Ask for the current location if the query is related to location.
 - Ask for the information you need if the query is very personalized or user-centric.
 - Do not mention or refer to the word "context", "based on context", "provided information",
 "Information given to me" or similar phrases in your responses.
 Question: {query}
 Context: {formatted_context}
 Answer:
 """

105-120: Enhance error handling to catch all exceptions.

While the current implementation catches OpenAIError, it should also handle other potential exceptions (e.g., network issues, JSON parsing errors).

         try:
             response = self.openai_client.chat.completions.create(
                 model=self.chat_model,
                 messages=[
                     {"role": "system", "content": self.SYSTEM_PROMPT},
                     {"role": "user", "content": user_prompt},
                 ],
                 temperature=self.TEMPERATURE,
                 max_tokens=self.MAX_TOKENS,
             )
             final_answer = response.choices[0].message.content.strip()
         except openai.OpenAIError:
             logger.exception("OpenAI API error")
             return {
                 "answer": "I'm sorry, I'm currently unable to process your request.",
             }
+        except Exception:
+            logger.exception("Unexpected error during generation")
+            return {
+                "answer": "An unexpected error occurred. Please try again later.",
+            }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bff36bd and f254af8.

📒 Files selected for processing (4)
  • backend/apps/ai/agent/tools/RAG/generator.py (1 hunks)
  • backend/apps/ai/agent/tools/RAG/rag_tool.py (1 hunks)
  • backend/apps/ai/agent/tools/RAG/retriever.py (1 hunks)
  • backend/apps/ai/management/commands/ai_run_rag_tool.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/apps/ai/agent/tools/RAG/rag_tool.py
  • backend/apps/ai/management/commands/ai_run_rag_tool.py
  • backend/apps/ai/agent/tools/RAG/retriever.py
🔇 Additional comments (3)
backend/apps/ai/agent/tools/RAG/generator.py (3)

1-14: LGTM!

Imports are appropriate and the class structure is well-defined with a clear docstring.


35-52: LGTM!

Well-implemented initialization with proper error handling for missing API key and appropriate logging.


53-75: LGTM!

Clean implementation of context formatting with proper handling of edge cases and clear separation between context chunks.

@arkid15r
Copy link
Collaborator

@Dishant1804 it seems your PRs not longer trigger CI/CD required steps. No idea why.
I updated the branch and it worked. Just FYI.

@Dishant1804
Copy link
Collaborator Author

Dishant1804 commented Jul 22, 2025

@Dishant1804 it seems your PRs not longer trigger CI/CD required steps. No idea why.
I updated the branch and it worked. Just FYI.

It is strange for me as well, I don't have any idea why CI/CD is not triggered when I push, it requires workflow approval everytime

"""Generates answers to user queries based on retrieved context."""

MAX_TOKENS = 2000
SYSTEM_PROMPT = """
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a core.Prompt model for this. It's in core but will be migrated ot ai later.

@Dishant1804 Dishant1804 requested a review from arkid15r July 25, 2025 15:23
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
backend/apps/ai/agent/tools/rag/rag_tool.py (1)

28-30: Clarify the Raises section in docstring

The ValueError is not directly raised in this method but propagated from Retriever or Generator. Consider updating the docstring to be more accurate.

         Raises:
-            ValueError: If the OpenAI API key is not set.
+            ValueError: If the OpenAI API key is not set (raised by Retriever or Generator).
+            Exception: Any other exception during initialization.
backend/apps/ai/agent/tools/rag/generator.py (2)

17-32: Fix grammar and clarity issues in SYSTEM_PROMPT

Several grammatical improvements needed for clarity:

 2. Do not mention or refer to the word "context", "based on context", "provided information",
 "Information given to me" or similar phrases in your responses.
-3. you will answer questions only related to OWASP and within the scope of OWASP.
+3. You will answer questions only related to OWASP and within the scope of OWASP.
 4. Be concise and directly answer the user's query.
 5. Provide the necessary link if the context contains a URL.
 6. If there is any query based on location, you need to look for latitude and longitude in the
 context and provide the nearest OWASP chapter based on that.
 7. You can ask for more information if the query is very personalized or user-centric.
-8. after trying all of the above, If the context does not contain the information or you think that
-it is out of scope for OWASP, you MUST state: "please ask question related to OWASP."
+8. After trying all of the above, if the context does not contain the information or you think that
+it is out of scope for OWASP, you MUST state: "Please ask questions related to OWASP."

89-103: Fix grammar issues in user prompt

Similar grammar improvements needed as in the system prompt:

 - You are an assistant for question-answering tasks related to OWASP.
 - Use the following pieces of retrieved context to answer the question.
-- If the question is related to OWASP then you can try to answer based on your knowledge, if you
-don't know the answer, just say that you don't know.
-- Try to give answer and keep the answer concise, but you really think that the response will be
-longer and better you will provide more information.
+- If the question is related to OWASP, then you can try to answer based on your knowledge. If you
+don't know the answer, just say that you don't know.
+- Try to give an answer and keep it concise, but if you think that the response will be
+better with more information, you can provide it.
backend/apps/ai/agent/tools/rag/retriever.py (2)

75-183: Consider refactoring into smaller methods for better maintainability

While the implementation is correct, this method is quite long (108 lines). Consider extracting each content type's context building into separate methods for better readability and maintainability.

Example refactor approach:

def get_additional_context(self, content_object, content_type: str) -> dict[str, Any]:
    """Get additional context information based on content type."""
    clean_content_type = content_type.split(".")[-1] if "." in content_type else content_type
    
    context_methods = {
        "chapter": self._get_chapter_context,
        "project": self._get_project_context,
        "event": self._get_event_context,
        "committee": self._get_committee_context,
        "message": self._get_message_context,
    }
    
    if method := context_methods.get(clean_content_type):
        context = method(content_object)
    else:
        context = {}
    
    return {k: v for k, v in context.items() if v is not None}

def _get_chapter_context(self, content_object) -> dict[str, Any]:
    """Extract context for chapter objects."""
    return {
        "location": getattr(content_object, "suggested_location", None),
        "region": getattr(content_object, "region", None),
        # ... rest of chapter attributes
    }

# Similar methods for other content types...

268-272: Consider improving readability

While the list comprehension works, it might be more readable as a regular loop given the multiple conditions:

-        detected_types = [
-            content_type
-            for content_type in self.SUPPORTED_CONTENT_TYPES
-            if content_type in query_words or f"{content_type}s" in query_words
-        ]
+        detected_types = []
+        for content_type in self.SUPPORTED_CONTENT_TYPES:
+            if content_type in query_words or f"{content_type}s" in query_words:
+                detected_types.append(content_type)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f254af8 and 4b38f5a.

📒 Files selected for processing (5)
  • backend/apps/ai/agent/tools/rag/generator.py (1 hunks)
  • backend/apps/ai/agent/tools/rag/rag_tool.py (1 hunks)
  • backend/apps/ai/agent/tools/rag/retriever.py (1 hunks)
  • backend/apps/ai/common/constants.py (1 hunks)
  • backend/apps/ai/management/commands/ai_run_rag_tool.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/apps/ai/common/constants.py
  • backend/apps/ai/management/commands/ai_run_rag_tool.py
🔇 Additional comments (4)
backend/apps/ai/agent/tools/rag/generator.py (2)

35-52: LGTM!

Good implementation with proper environment variable validation and error handling.


53-75: LGTM!

Well-structured method with proper handling of empty context and clear formatting.

backend/apps/ai/agent/tools/rag/retriever.py (2)

67-74: LGTM!

Nice implementation that gracefully handles different object types by trying multiple common name attributes.


184-254: LGTM!

Excellent implementation with efficient database queries, proper content type filtering, and good null safety checks.

Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the tests for your code -- please don't create the tech debt.

I'm going to merge this as is for now but I expect the comments to be addressed ASAP

"""Generates answers to user queries based on retrieved context."""

MAX_TOKENS = 2000
SYSTEM_PROMPT = """
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be moved into DB

try:
self.retriever = Retriever(embedding_model=embedding_model)
self.generator = Generator(chat_model=chat_model)
except Exception:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good practice.

Copy link

@arkid15r arkid15r enabled auto-merge July 29, 2025 03:09
@arkid15r arkid15r added this pull request to the merge queue Jul 29, 2025
Merged via the queue into OWASP:main with commit f3fee08 Jul 29, 2025
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement PoC agent that works locally
2 participants