Files
wehub-resource-sync e64161ec32
CI / ci (3.11) (push) Has been cancelled
CI / ci (3.10) (push) Has been cancelled
CI / dependabot (push) Has been cancelled
Release / release_and_publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:15 +08:00

312 lines
12 KiB
Python

import json
from pathlib import Path
from typing import List, Union
import pandas as pd
from rdagent.components.knowledge_management.vector_base import Document, PDVectorBase
from rdagent.log import rdagent_logger as logger
from rdagent.oai.llm_utils import APIBackend
from rdagent.scenarios.kaggle.knowledge_management.extract_knowledge import (
extract_knowledge_from_feedback,
)
from rdagent.utils.agent.tpl import T
class KGKnowledgeDocument(Document):
"""
Class for handling Kaggle competition specific metadata
"""
def __init__(
self,
content: str = "",
label: str = None,
embedding=None,
identity=None,
competition_name=None,
task_category=None,
field=None,
ranking=None,
score=None,
entities=None,
relations=None,
):
"""
Initialize KGKnowledgeMetaData for Kaggle competition posts
Parameters:
----------
competition_name: str, optional
The name of the Kaggle competition.
task_category: str, required
The type of task (e.g., classification, regression).
field: str, optional
The specific field of knowledge (e.g., feature engineering, modeling).
ranking: str or int, optional
The ranking achieved in the competition.
score: float, optional
The score or metric achieved in the competition.
entities: list, optional
Entities related to the content (for knowledge graph integration).
relations: list, optional
Relations between entities (for knowledge graph integration).
"""
super().__init__(content, label, embedding, identity)
self.competition_name = competition_name
self.task_category = task_category # Task type is required
self.field = field # Knowledge field, optional (model/data/others/overall)
self.ranking = ranking # Ranking
# TODO ranking and score might be unified
self.score = score # Competition score
# TODO Perhaps this shouldn't be here?
self.entities = entities or [] # Entities in the knowledge graph
self.relations = relations or [] # Relations in the knowledge graph
def split_into_trunk(self, size: int = 1000, overlap: int = 0):
"""
Split content into trunks and create embeddings by trunk
#TODO let GPT do the split based on the field of knowledge(data/model/others)
"""
def split_string_into_chunks(string: str, chunk_size: int):
chunks = []
for i in range(0, len(string), chunk_size):
chunk = string[i : i + chunk_size]
chunks.append(chunk)
return chunks
self.trunks = split_string_into_chunks(self.content, chunk_size=size)
self.trunks_embedding = APIBackend().create_embedding(input_content=self.trunks)
def from_dict(self, data: dict):
"""
Load Kaggle post data from a dictionary
"""
super().from_dict(data)
self.competition_name = data.get("competition_name", None)
self.task_category = data.get("task_category", None)
self.field = data.get("field", None)
self.ranking = data.get("ranking", None)
self.score = data.get("score", None)
self.entities = data.get("entities", [])
self.relations = data.get("relations", [])
return self
def __repr__(self):
return (
f"KGKnowledgeMetaData(id={self.id}, label={self.label}, competition={self.competition_name}, "
f"task_category={self.task_category}, field={self.field}, ranking={self.ranking}, score={self.score})"
)
KGDocument = KGKnowledgeDocument
class KaggleExperienceBase(PDVectorBase):
"""
Class for handling Kaggle competition experience posts and organizing them for reference
"""
def __init__(self, vector_df_path: Union[str, Path] = None, kaggle_experience_path: Union[str, Path] = None):
"""
Initialize the KaggleExperienceBase class
Parameters:
----------
vector_df_path: str or Path, optional
Path to the vector DataFrame for embedding management.
kaggle_experience_path: str or Path, optional
Path to the Kaggle experience post data.
"""
super().__init__(vector_df_path)
self.kaggle_experience_path = kaggle_experience_path
self.kaggle_experience_data = []
if kaggle_experience_path:
self.load_kaggle_experience(kaggle_experience_path)
def add(self, document: Union[KGDocument, List[KGDocument]]):
document.split_into_trunk()
docs = [
{
"id": document.id,
"label": document.label,
"content": document.content,
"competition_name": document.competition_name,
"task_category": document.task_category,
"field": document.field,
"ranking": document.ranking,
"score": document.score,
"embedding": document.embedding,
}
]
if len(document.trunks) > 1:
docs.extend(
[
{
"id": document.id,
"label": document.label,
"content": document.content,
"competition_name": document.competition_name,
"task_category": document.task_category,
"field": document.field,
"ranking": document.ranking,
"score": document.score,
"embedding": trunk_embedding,
}
for trunk, trunk_embedding in zip(document.trunks, document.trunks_embedding)
]
)
self.vector_df = pd.concat([self.vector_df, pd.DataFrame(docs)], ignore_index=True)
def load_kaggle_experience(self, kaggle_experience_path: Union[str, Path]):
"""
Load Kaggle experience posts from a JSON or text file
Parameters:
----------
kaggle_experience_path: str or Path
Path to the Kaggle experience post data.
"""
try:
with open(kaggle_experience_path, "r", encoding="utf-8") as file:
self.kaggle_experience_data = json.load(file)
logger.info(f"Kaggle experience data loaded from {kaggle_experience_path}")
except FileNotFoundError:
logger.error(f"Kaggle experience data not found at {kaggle_experience_path}")
self.kaggle_experience_data = []
def add_experience_to_vector_base(self, experiment_feedback=None):
"""
Process Kaggle experience data or experiment feedback and add relevant information to the vector base.
Args:
experiment_feedback (dict, optional): A dictionary containing experiment feedback.
If provided, this feedback will be processed and added to the vector base.
"""
# If experiment feedback is provided, extract relevant knowledge and add it to the vector base
if experiment_feedback:
extracted_knowledge = extract_knowledge_from_feedback(experiment_feedback)
document = KGKnowledgeDocument(
content=experiment_feedback.get("hypothesis_text", ""),
label="Experiment Feedback",
competition_name="Experiment Result",
task_category=experiment_feedback.get("tasks_factors", "General Task"),
field="Research Feedback",
ranking=None,
score=experiment_feedback.get("current_result", None),
)
document.create_embedding()
self.add(document)
return
# Process Kaggle experience data
logger.info(f"Processing {len(self.kaggle_experience_data)} Kaggle experience posts")
for experience in self.kaggle_experience_data:
logger.info(f"Processing experience index: {self.kaggle_experience_data.index(experience)}")
content = experience.get("content", "")
label = experience.get("title", "Kaggle Experience")
competition_name = experience.get("competition_name", "Unknown Competition")
task_category = experience.get("task_category", "General Task")
field = experience.get("field", None)
ranking = experience.get("ranking", None)
score = experience.get("score", None)
document = KGKnowledgeDocument(
content=content,
label=label,
competition_name=competition_name,
task_category=task_category,
field=field,
ranking=ranking,
score=score,
)
document.create_embedding()
self.add(document)
def search_experience(self, target: str, query: str, topk_k: int = 5, similarity_threshold: float = 0.1):
"""
Search for Kaggle experience posts related to the query, initially filtered by the target.
Parameters:
----------
target: str
The target context to refine the search query.
query: str
The search query to find relevant experience posts.
topk_k: int, optional
Number of top similar results to return (default is 5).
similarity_threshold: float, optional
The similarity threshold for filtering results (default is 0.1).
Returns:
-------
List[KGKnowledgeMetaData], List[float]:
A list of the most relevant documents and their similarities.
"""
# Modify the query to include the target
modified_query = f"The target is {target}. And I need you to query {query} based on the {target}."
# First, search based on the modified query
search_results, similarities = super().search(
modified_query, topk_k=topk_k, similarity_threshold=similarity_threshold
)
# If the results do not match the target well, refine the search using LLM or further adjustment
kaggle_docs = []
for result in search_results:
kg_doc = KGKnowledgeDocument().from_dict(result.__dict__)
gpt_feedback = self.refine_with_LLM(target, kg_doc)
if gpt_feedback:
kg_doc.content = gpt_feedback
kaggle_docs.append(kg_doc)
return kaggle_docs, similarities
def refine_with_LLM(self, target: str, text: str) -> str:
sys_prompt = T(".prompts:refine_with_LLM.system").r()
user_prompt = T(".prompts:refine_with_LLM.user").r(target=target, text=text)
response = APIBackend().build_messages_and_create_chat_completion(
user_prompt=user_prompt,
system_prompt=sys_prompt,
json_mode=False,
)
return response
def save(self, vector_df_path: Union[str, Path]):
"""
Save the vector DataFrame to a file
Parameters:
----------
vector_df_path: str or Path
Path to save the vector DataFrame.
"""
self.vector_df.to_pickle(vector_df_path)
logger.info(f"Vector DataFrame saved to {vector_df_path}")
if __name__ == "__main__":
kaggle_base = KaggleExperienceBase(
kaggle_experience_path="git_ignore_folder/data_minicase/kaggle_experience_results.json"
)
kaggle_base.add_experience_to_vector_base()
kaggle_base.save("git_ignore_folder/vector_base/kaggle_vector_base.pkl")
print(f"There are {kaggle_base.shape()[0]} records in the vector base.")
search_results, similarities = kaggle_base.search_experience(query="image classification", topk_k=3)
for result, similarity in zip(search_results, similarities):
print(
f"Competition name: {result.competition_name}, task_category: {result.task_category}, score: {result.score}, similarity: {similarity}"
)