DJAYADEV commited on
Commit
374834e
·
verified ·
1 Parent(s): 3358fd3

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -43,7 +43,7 @@ RagOptimizerEnv simulates the critical and computationally intensive role of an
43
 
44
  <br>
45
  <p align="center">
46
- <img src="assets/Architecture_diagram.png" width="800" alt="System Architecture Diagram" />
47
  </p>
48
  <br>
49
 
@@ -109,7 +109,7 @@ Agents manipulate the embedding search space via a strictly defined remote schem
109
  Pydantic-typed JSON states returned immediately upon trajectory execution:
110
  1. `message`: Terminal I/O logs, error stack-traces, or `read_document` raw buffers.
111
  2. `current_docs`: A hierarchical mapping of the existing document index payload ensuring contextual grounding.
112
- 3. `reward`: The live theoretical model convergence rate formulated between `0.0` and `1.0`.
113
 
114
  ---
115
 
@@ -125,6 +125,33 @@ Evaluation is robust, empirical, and mathematically bounded:
125
 
126
  **Reward Yield:** `(Successful Vectors / Total Vector Payload)` providing high-density intermediate signals mapping continuously toward the `±1.0` upper bound.
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  ---
129
 
130
  ## 4. Curriculum Topologies
@@ -135,9 +162,9 @@ The environment tests agents across three progressively demanding task distribut
135
  **The Vector Issue:** The base contains heavily overlapping parameters (competing versions of legacy and modern timeline protocols).
136
  **System Goal:** Autonomously survey the semantic differences, deduce the temporal conflict, and execute `delete_document` sweeps to purge vector hallucination triggers.
137
 
138
- ### Level II: Ontological Tagging
139
- **The Vector Issue:** Textual segments representing technical telemetry are too dense and sparse on specific categorical keywords, resulting in low coordinate density for deterministic algorithms.
140
- **System Goal:** Execute inferential reading, deduce categorical bounds organically, and route exact programmatic `metadata` tags onto corresponding payloads to anchor the search vectors.
141
 
142
  ### Level III: Syntactic Splintering (The Monolith)
143
  **The Vector Issue:** Extreme embedding decay caused by disparate conceptual structures compacted under a single referential document. This represents the well-known "PDF chunk wash-out" phenomenon.
@@ -156,7 +183,7 @@ Telemetry formats strictly adhere to high-velocity logging required for large-sc
156
  [STEP] step=1 action=read('doc_monolithic_onboarding') reward=0.33 done=false error=null
157
  [STEP] step=2 action=update('doc_vpn_policy') reward=0.50 done=false error=null
158
  ...
159
- [END] success=true steps=12 score=1.00 rewards=0.33,0.50,...
160
  ```
161
 
162
  ### Execution
 
43
 
44
  <br>
45
  <p align="center">
46
+ <img src="assets/Architecture_diagram.jpeg" width="800" alt="System Architecture Diagram" />
47
  </p>
48
  <br>
49
 
 
109
  Pydantic-typed JSON states returned immediately upon trajectory execution:
110
  1. `message`: Terminal I/O logs, error stack-traces, or `read_document` raw buffers.
111
  2. `current_docs`: A hierarchical mapping of the existing document index payload ensuring contextual grounding.
112
+ 3. `reward`: The live theoretical model convergence rate formulated between `0.01` and `0.99`.
113
 
114
  ---
115
 
 
125
 
126
  **Reward Yield:** `(Successful Vectors / Total Vector Payload)` providing high-density intermediate signals mapping continuously toward the `±1.0` upper bound.
127
 
128
+ ### Practical RAG FAQ (Important Clarifications)
129
+
130
+ These are fantastic questions, and they cut right to the core of how real-world RAG systems operate.
131
+
132
+ #### 1. If we just send the KB summary, is it just metadata optimization?
133
+ Not exactly. While the agent sees the summary by default, it also has the `read_document` action.
134
+
135
+ - When the agent uses `read_document("doc_monolithic")`, the environment returns the entire raw text of that document in the `message` field of the next observation.
136
+ - The agent can read the text, identify that it is noisy or overloaded across multiple topics, and then use `update_document` to split or rewrite content into cleaner chunks.
137
+ - This means the benchmark supports true content optimization, not just metadata operations.
138
+
139
+ #### 2. Doesn't sending the entire KB to the embedding model blow up its context window?
140
+ No. The grader does not send the entire KB as one giant prompt.
141
+
142
+ - Embedding models operate per document, not as one monolithic concatenated input.
143
+ - The environment encodes each document individually into vectors, then stores those vectors in an index-like structure for retrieval scoring.
144
+ - Query-time evaluation compares a query vector against document vectors. It does not require processing the full KB in one context window.
145
+
146
+ #### 3. Is the result just fetching the right document, not the exact answer?
147
+ Exactly. This environment evaluates retrieval quality, the "R" in RAG.
148
+
149
+ - If retrieval is wrong, generation quality collapses and hallucination risk rises.
150
+ - The grader checks whether documents containing the target concept are ranked near the top for each control query.
151
+ - Optimizing document quality, structure, and noise levels pushes the clean source document toward rank 1, enabling downstream generators to answer correctly.
152
+
153
+ In practice, this environment trains an agent to behave like a retrieval-focused KB operator: maintain high signal quality so the retriever does not fail under noisy enterprise conditions.
154
+
155
  ---
156
 
157
  ## 4. Curriculum Topologies
 
162
  **The Vector Issue:** The base contains heavily overlapping parameters (competing versions of legacy and modern timeline protocols).
163
  **System Goal:** Autonomously survey the semantic differences, deduce the temporal conflict, and execute `delete_document` sweeps to purge vector hallucination triggers.
164
 
165
+ ### Level II: Signal Separation
166
+ **The Vector Issue:** The KB contains overlapping incident narratives mixed with distractor engineering notes, causing retrieval ambiguity.
167
+ **System Goal:** Deduplicate noisy/partial incident content so retrieval consistently surfaces the clean resolution document at rank 1.
168
 
169
  ### Level III: Syntactic Splintering (The Monolith)
170
  **The Vector Issue:** Extreme embedding decay caused by disparate conceptual structures compacted under a single referential document. This represents the well-known "PDF chunk wash-out" phenomenon.
 
183
  [STEP] step=1 action=read('doc_monolithic_onboarding') reward=0.33 done=false error=null
184
  [STEP] step=2 action=update('doc_vpn_policy') reward=0.50 done=false error=null
185
  ...
186
+ [END] success=true steps=12 score=0.99 rewards=0.33,0.50,...
187
  ```
188
 
189
  ### Execution
assets/Architecture_diagram.jpeg ADDED
inference.py CHANGED
@@ -41,7 +41,7 @@ SYSTEM_PROMPT = """You are an automated Data Engineer managing an AI Knowledge B
41
  Your goal is to optimize the messy chunks of text in the database so that a TF-IDF Search Algorithm can find answers easily.
42
  You must resolve contradictions, categorize documents, and delete unnecessary documents.
43
 
44
- After each action you will receive a "current_reward" score (0.0 to 1.0) indicating how well the KB currently performs. Use this to guide your strategy.
45
 
46
  You have the following actions:
47
  - {"action_type": "read_document", "doc_id": "..."}
@@ -65,6 +65,34 @@ def format_action_str(action: RagOptimizerAction) -> str:
65
  return "submit()"
66
  return f"{action.action_type}()"
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  def _safe_reset(env: RagOptimizerEnvClient, task_id: str):
70
  """Reset env for a specific task with compatibility fallbacks."""
@@ -111,7 +139,7 @@ def run_task_episode(
111
  print(f"[END] success=false steps=0 score=0.01 rewards=")
112
  return
113
 
114
- history = [{"role": "system", "content": SYSTEM_PROMPT}]
115
 
116
  init_obs = {
117
  "server_feedback": observation.message,
@@ -194,6 +222,23 @@ def run_task_episode(
194
  done_str = "true" if success else "false"
195
  print(f"[END] success={done_str} steps={step} score={score:.2f} rewards={rewards_str}")
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  def main():
198
  # Setup OpenAI Client
199
  client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
 
41
  Your goal is to optimize the messy chunks of text in the database so that a TF-IDF Search Algorithm can find answers easily.
42
  You must resolve contradictions, categorize documents, and delete unnecessary documents.
43
 
44
+ After each action you will receive a "current_reward" score (0.01 to 0.99) indicating how well the KB currently performs. Use this to guide your strategy.
45
 
46
  You have the following actions:
47
  - {"action_type": "read_document", "doc_id": "..."}
 
65
  return "submit()"
66
  return f"{action.action_type}()"
67
 
68
+ # --- Reflexion (Long Term Memory) ---
69
+ LESSONS_FILE = os.path.join(os.path.dirname(__file__), "memory", "lessons_learned.json")
70
+
71
+ def load_lessons():
72
+ if os.path.exists(LESSONS_FILE):
73
+ try:
74
+ with open(LESSONS_FILE, "r") as f:
75
+ return json.load(f)
76
+ except:
77
+ pass
78
+ return []
79
+
80
+ def save_lesson(lesson_text, task_id):
81
+ os.makedirs(os.path.dirname(LESSONS_FILE), exist_ok=True)
82
+ lessons = load_lessons()
83
+ lessons.append({"task": task_id, "lesson": lesson_text})
84
+ with open(LESSONS_FILE, "w") as f:
85
+ json.dump(lessons, f, indent=2)
86
+
87
+ def get_system_prompt():
88
+ prompt = SYSTEM_PROMPT
89
+ lessons = load_lessons()
90
+ if lessons:
91
+ prompt += "\n\nPAST LESSONS LEARNED (DO NOT REPEAT MISTAKES):\n"
92
+ for l in lessons[-5:]: # Show only top 5 recent
93
+ prompt += f"- {l['lesson']}\n"
94
+ return prompt
95
+
96
 
97
  def _safe_reset(env: RagOptimizerEnvClient, task_id: str):
98
  """Reset env for a specific task with compatibility fallbacks."""
 
139
  print(f"[END] success=false steps=0 score=0.01 rewards=")
140
  return
141
 
142
+ history = [{"role": "system", "content": get_system_prompt()}]
143
 
144
  init_obs = {
145
  "server_feedback": observation.message,
 
222
  done_str = "true" if success else "false"
223
  print(f"[END] success={done_str} steps={step} score={score:.2f} rewards={rewards_str}")
224
 
225
+ # Memory Reflexion Trigger
226
+ if not success and score < 0.6:
227
+ # Agent failed, try to reflect
228
+ hist_str = json.dumps([m["content"] for m in history[-6:]]) # get last few actions/obs
229
+ ref_prompt = f"The agent failed task '{task_id}' with final reward {score}. Last context: {hist_str}. Write a 1-sentence tactical lesson stating explicitly what data engineering action the agent should have done instead."
230
+ try:
231
+ resp = llm_client.chat.completions.create(
232
+ model=MODEL_NAME,
233
+ messages=[{"role": "user", "content": ref_prompt}],
234
+ max_tokens=60
235
+ )
236
+ lesson = resp.choices[0].message.content.strip()
237
+ save_lesson(lesson, task_id)
238
+ print(f"[MEMORY] Learned lesson: {lesson}")
239
+ except:
240
+ pass
241
+
242
  def main():
243
  # Setup OpenAI Client
244
  client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
models.py CHANGED
@@ -58,5 +58,5 @@ class RagOptimizerObservation(BaseModel):
58
 
59
  # Required OpenEnv standard fields
60
  done: bool = Field(False, description="Whether the episode has finished.")
61
- reward: float = Field(0.0, description="The reward obtained from the last step.")
62
  metadata: Dict = Field(default_factory=dict, description="Additional optional information.")
 
58
 
59
  # Required OpenEnv standard fields
60
  done: bool = Field(False, description="Whether the episode has finished.")
61
+ reward: float = Field(0.01, description="The reward obtained from the last step.")
62
  metadata: Dict = Field(default_factory=dict, description="Additional optional information.")
openenv.yaml CHANGED
@@ -13,17 +13,23 @@ tasks:
13
  - id: easy
14
  name: "Conflict Resolution"
15
  description: "Resolve overlapping pricing parameters by deleting the legacy pricing format."
16
- reward_range: [0.1, 0.99]
17
  grader: "rag_optimizer.server.grader:grade_easy"
18
  - id: medium
19
- name: "Ontological Tagging"
20
- description: "Route exact programmatic metadata tags to support tickets."
21
- reward_range: [0.1, 0.99]
 
 
 
 
 
 
22
  grader: "rag_optimizer.server.grader:grade_medium"
23
  - id: hard
24
  name: "Syntactic Splintering"
25
  description: "Break down the monolithic onboarding blob into multiple granular chunks."
26
- reward_range: [0.1, 0.99]
27
  grader: "rag_optimizer.server.grader:grade_hard"
28
  agent:
29
  inference_entrypoint: "inference.py"
 
13
  - id: easy
14
  name: "Conflict Resolution"
15
  description: "Resolve overlapping pricing parameters by deleting the legacy pricing format."
16
+ reward_range: [0.01, 0.99]
17
  grader: "rag_optimizer.server.grader:grade_easy"
18
  - id: medium
19
+ name: "Signal Separation"
20
+ description: >
21
+ Multi-doc deduplication. KB contains 3 overlapping incident reports
22
+ for the same outage - one raw, one partial, one with the correct
23
+ resolution - plus 20 engineering distractors. Agent must delete the
24
+ noisy duplicates so retrieval surfaces the clean resolution doc at
25
+ rank 1. Reward improves continuously as noise is removed; no binary
26
+ metadata gate.
27
+ reward_range: [0.01, 0.99]
28
  grader: "rag_optimizer.server.grader:grade_medium"
29
  - id: hard
30
  name: "Syntactic Splintering"
31
  description: "Break down the monolithic onboarding blob into multiple granular chunks."
32
+ reward_range: [0.01, 0.99]
33
  grader: "rag_optimizer.server.grader:grade_hard"
34
  agent:
35
  inference_entrypoint: "inference.py"
rag_optimizer_environment.py CHANGED
@@ -119,7 +119,7 @@ class RagOptimizerEnvironment(Environment):
119
  def _evaluate_kb(self) -> float:
120
  """The Grader: Evaluates the agent's current KB using TF-IDF."""
121
  if not self.kb:
122
- return 0.0
123
 
124
  doc_texts = [doc["text"] for doc in self.kb.values()]
125
 
@@ -127,7 +127,7 @@ class RagOptimizerEnvironment(Environment):
127
  try:
128
  doc_vectors = vectorizer.fit_transform(doc_texts)
129
  except ValueError:
130
- return 0.0
131
 
132
  score = 0.0
133
 
@@ -147,14 +147,14 @@ class RagOptimizerEnvironment(Environment):
147
  if found:
148
  score += 1.0
149
 
150
- return float(score / len(self.test_suite))
151
 
152
  def step(self, action: RagOptimizerAction) -> RagOptimizerObservation: # type: ignore[override]
153
  self._state.step_count += 1
154
 
155
  msg = ""
156
  done = False
157
- reward = 0.0
158
 
159
  try:
160
  if action.action_type == "read_document":
 
119
  def _evaluate_kb(self) -> float:
120
  """The Grader: Evaluates the agent's current KB using TF-IDF."""
121
  if not self.kb:
122
+ return 0.01
123
 
124
  doc_texts = [doc["text"] for doc in self.kb.values()]
125
 
 
127
  try:
128
  doc_vectors = vectorizer.fit_transform(doc_texts)
129
  except ValueError:
130
+ return 0.01
131
 
132
  score = 0.0
133
 
 
147
  if found:
148
  score += 1.0
149
 
150
+ return max(0.01, min(0.99, float(score / len(self.test_suite))))
151
 
152
  def step(self, action: RagOptimizerAction) -> RagOptimizerObservation: # type: ignore[override]
153
  self._state.step_count += 1
154
 
155
  msg = ""
156
  done = False
157
+ reward = 0.01
158
 
159
  try:
160
  if action.action_type == "read_document":
server/app.py CHANGED
@@ -53,7 +53,7 @@ app = create_app(
53
  )
54
 
55
 
56
- def main(host: str = "0.0.0.0", port: int = 8002):
57
  """
58
  Entry point for direct execution via uv run or python -m.
59
 
 
53
  )
54
 
55
 
56
+ def main(host: str = "0.0.0.0", port: int = 8000):
57
  """
58
  Entry point for direct execution via uv run or python -m.
59
 
server/kb_seed.json ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "easy": {
3
+ "fastapi_1": {
4
+ "text": "FastAPI is a modern Python web framework designed for building APIs with high performance. It leverages Python type hints and integrates tightly with validation libraries. One of its key strengths is that it defaults to Pydantic v2, enabling efficient data parsing and validation with minimal boilerplate. Developers can define request and response models clearly, ensuring correctness. FastAPI also supports asynchronous programming using async and await, making it ideal for I/O-bound services. Its automatic OpenAPI schema generation provides interactive API documentation out of the box, improving developer productivity.",
5
+ "metadata": {
6
+ "topic": "fastapi",
7
+ "type": "useful"
8
+ }
9
+ },
10
+ "fastapi_2": {
11
+ "text": "FastAPI includes a powerful dependency injection system that allows developers to manage shared logic such as authentication, database sessions, and configuration in a clean and reusable way. Dependencies can be declared at different levels, including routes and entire applications. This feature reduces duplication and improves maintainability. The framework also supports async database calls, ensuring efficient resource usage. By combining dependency injection with type hints, FastAPI ensures that applications remain scalable and structured, making it a preferred choice for production-grade microservices.",
12
+ "metadata": {
13
+ "topic": "fastapi"
14
+ }
15
+ },
16
+ "fastapi_3": {
17
+ "text": "FastAPI supports WebSocket communication, enabling real-time features such as chat applications, live dashboards, and streaming data pipelines. Unlike traditional HTTP request-response cycles, WebSockets maintain persistent connections. FastAPI provides simple decorators to define WebSocket endpoints and manage connections efficiently. Combined with async support, this allows handling thousands of concurrent connections. Developers can integrate WebSockets alongside REST endpoints in the same application, making FastAPI highly versatile for both synchronous and real-time applications.",
18
+ "metadata": {
19
+ "topic": "fastapi"
20
+ }
21
+ },
22
+ "fastapi_4": {
23
+ "text": "Running FastAPI applications in production typically involves using ASGI servers such as Uvicorn. A common command used is uvicorn main:app --workers 4, which starts multiple worker processes for handling concurrent requests. This setup improves performance and scalability. FastAPI is designed around ASGI, allowing it to fully utilize asynchronous capabilities. With proper configuration, it can handle high throughput while maintaining low latency, making it suitable for APIs serving large-scale applications.",
24
+ "metadata": {
25
+ "topic": "fastapi"
26
+ }
27
+ },
28
+ "fastapi_5": {
29
+ "text": "FastAPI automatically generates interactive documentation using Swagger UI and ReDoc. This feature is powered by OpenAPI standards and provides developers with a convenient way to test endpoints directly from the browser. The documentation reflects the actual codebase, ensuring accuracy. By leveraging type annotations, FastAPI produces clear schemas for requests and responses. This reduces manual documentation effort and improves collaboration between frontend and backend teams.",
30
+ "metadata": {
31
+ "topic": "fastapi"
32
+ }
33
+ },
34
+ "fastapi_6": {
35
+ "text": "FastAPI emphasizes performance comparable to Node.js and Go by utilizing asynchronous programming and efficient serialization. It uses Starlette under the hood, which provides high-speed routing and middleware support. Developers can create middleware for logging, authentication, and caching. FastAPI also integrates seamlessly with ORMs and external services. Its performance benefits make it suitable for modern cloud-native applications.",
36
+ "metadata": {
37
+ "topic": "fastapi"
38
+ }
39
+ },
40
+ "fastapi_7": {
41
+ "text": "FastAPI simplifies request validation using Python type hints. When defining endpoints, developers specify expected input types, and FastAPI automatically validates incoming requests. Errors are returned in a structured format, helping developers debug quickly. This reduces the need for manual validation logic. Combined with automatic serialization, FastAPI ensures consistency and reliability across APIs.",
42
+ "metadata": {
43
+ "topic": "fastapi"
44
+ }
45
+ },
46
+ "fastapi_8": {
47
+ "text": "FastAPI integrates well with modern Python ecosystems, including async frameworks, ORMs, and testing tools. It supports dependency overrides, which are useful during testing. Developers can simulate database connections or authentication systems easily. This makes writing unit and integration tests straightforward. The framework encourages clean architecture and modular design.",
48
+ "metadata": {
49
+ "topic": "fastapi"
50
+ }
51
+ },
52
+ "fastapi_9": {
53
+ "text": "FastAPI allows developers to define background tasks that run after returning a response. This is useful for sending emails, logging events, or processing data asynchronously. The background task system is lightweight and easy to use. It helps improve response times while handling additional processing efficiently.",
54
+ "metadata": {
55
+ "topic": "fastapi"
56
+ }
57
+ },
58
+ "fastapi_10": {
59
+ "text": "FastAPI supports security features such as OAuth2, API keys, and JWT authentication. It provides built-in utilities for implementing authentication flows. These features integrate seamlessly with dependency injection, allowing secure endpoints to be defined easily. This makes FastAPI suitable for building secure APIs.",
60
+ "metadata": {
61
+ "topic": "fastapi"
62
+ }
63
+ },
64
+ "distractor_1": {
65
+ "text": "Django is a high-level Python web framework that follows the Model-View-Template pattern. It provides an ORM for database interactions and includes built-in features such as authentication, admin panels, and form handling. Django is known for its batteries-included philosophy, allowing developers to build full-stack applications quickly. It is widely used for monolithic applications and supports scalability through modular apps.",
66
+ "metadata": {
67
+ "topic": "distractor"
68
+ }
69
+ },
70
+ "distractor_2": {
71
+ "text": "Flask is a lightweight Python web framework designed for simplicity and flexibility. Unlike Django, it does not include many built-in components, giving developers more control. Flask uses extensions to add functionality such as database integration and authentication. It is ideal for small projects and microservices.",
72
+ "metadata": {
73
+ "topic": "distractor"
74
+ }
75
+ },
76
+ "distractor_3": {
77
+ "text": "Express.js is a popular web framework for Node.js that simplifies building server-side applications. It provides routing, middleware support, and integration with databases. Express is widely used for building REST APIs and full-stack applications using JavaScript. Its minimal design allows developers to structure applications as needed.",
78
+ "metadata": {
79
+ "topic": "distractor"
80
+ }
81
+ },
82
+ "distractor_4": {
83
+ "text": "React is a JavaScript library for building user interfaces. It uses a component-based architecture and virtual DOM for efficient rendering. React is commonly used for frontend development and can be integrated with backend APIs. It supports state management and hooks for managing component lifecycle.",
84
+ "metadata": {
85
+ "topic": "distractor"
86
+ }
87
+ },
88
+ "distractor_5": {
89
+ "text": "Ruby on Rails is a web application framework that emphasizes convention over configuration. It provides a full-stack solution with built-in tools for database management, routing, and views. Rails is known for rapid development and clean code structure.",
90
+ "metadata": {
91
+ "topic": "distractor"
92
+ }
93
+ },
94
+ "distractor_6": {
95
+ "text": "Angular is a frontend framework developed by Google. It uses TypeScript and provides tools for building large-scale applications. Angular includes dependency injection, routing, and state management.",
96
+ "metadata": {
97
+ "topic": "distractor"
98
+ }
99
+ },
100
+ "distractor_7": {
101
+ "text": "Laravel is a PHP framework that simplifies web development with features like routing, authentication, and database migrations. It is widely used for building scalable web applications.",
102
+ "metadata": {
103
+ "topic": "distractor"
104
+ }
105
+ },
106
+ "distractor_8": {
107
+ "text": "Spring Boot is a Java-based framework for building enterprise applications. It simplifies configuration and provides tools for building REST APIs and microservices.",
108
+ "metadata": {
109
+ "topic": "distractor"
110
+ }
111
+ },
112
+ "distractor_9": {
113
+ "text": "Vue.js is a progressive JavaScript framework for building user interfaces. It focuses on simplicity and flexibility, making it easy to integrate into projects.",
114
+ "metadata": {
115
+ "topic": "distractor"
116
+ }
117
+ },
118
+ "distractor_10": {
119
+ "text": "ASP.NET Core is a cross-platform framework for building web applications using C#. It supports MVC architecture and integrates with Microsoft technologies.",
120
+ "metadata": {
121
+ "topic": "distractor"
122
+ }
123
+ }
124
+ },
125
+ "medium": {
126
+ "falcon9": {
127
+ "text": "Falcon 9 is a reusable rocket developed by SpaceX for transporting payloads to orbit. It uses SpaceX Merlin engines, which are designed for efficiency and reusability. The first stage can land vertically after launch, significantly reducing costs. Falcon 9 has been used for satellite deployments, cargo missions, and crewed flights to the International Space Station. Its reliability and cost-effectiveness have made it a cornerstone of modern space exploration.",
128
+ "metadata": {}
129
+ },
130
+ "dragon": {
131
+ "text": "The Dragon spacecraft developed by SpaceX is used for cargo and crew transport. The original Dragon capsule was replaced by Dragon 2, which includes advanced life-support systems and autonomous docking capabilities. Dragon has been used extensively for resupply missions to the International Space Station and has enabled private spaceflight.",
132
+ "metadata": {}
133
+ },
134
+ "starship": {
135
+ "text": "Starship is a fully reusable spacecraft designed by SpaceX for deep space missions. It aims to transport humans to Mars and beyond. The system includes a booster and spacecraft, both reusable. Starship is powered by Raptor engines and is designed for rapid turnaround.",
136
+ "metadata": {}
137
+ },
138
+ "distractor_1": {
139
+ "text": "Deep sea exploration involves studying ocean depths using submersibles and remote-operated vehicles. Scientists investigate marine ecosystems and geological formations.",
140
+ "metadata": {}
141
+ }
142
+ },
143
+ "hard": {
144
+ "crisis_monolith": {
145
+ "text": "The 2008 financial crisis was one of the most severe economic downturns since the Great Depression. It was triggered by the collapse of the housing market and widespread use of Mortgage-backed securities. Financial institutions had heavily invested in subprime mortgages, which were loans given to low-income homebuyers with poor credit histories. As housing prices declined, these borrowers began defaulting, leading to massive losses. The bankruptcy of Lehman Brothers marked a critical moment, causing panic across global markets. Complex financial instruments such as CDOs amplified risk, spreading it throughout the system. Governments intervened with bailouts, including the TARP program, to stabilize banks. Central banks implemented quantitative easing to inject liquidity into the economy. The crisis exposed weaknesses in financial regulation and led to reforms such as the Dodd-Frank Act. It also highlighted the dangers of excessive leverage and lack of transparency in financial markets.",
146
+ "metadata": {}
147
+ },
148
+ "tarp": {
149
+ "text": "The Troubled Asset Relief Program was introduced to stabilize financial institutions during the crisis. It allowed the government to purchase toxic assets and inject capital into banks.",
150
+ "metadata": {}
151
+ },
152
+ "qe": {
153
+ "text": "Quantitative easing was used by central banks to increase liquidity by purchasing government securities and lowering interest rates.",
154
+ "metadata": {}
155
+ },
156
+ "noise_1": {
157
+ "text": "During the 1920s, financial markets experienced rapid growth followed by a crash. Speculative investments and excessive leverage contributed to economic instability.",
158
+ "metadata": {}
159
+ }
160
+ }
161
+ }
server/rag_optimizer_environment.py CHANGED
@@ -16,11 +16,15 @@ from typing import Dict, Any, List
16
  from openenv.core.env_server.interfaces import Environment
17
  from openenv.core.env_server.types import State
18
 
19
- # Import scikit-learn for our Grader
20
  from sklearn.feature_extraction.text import TfidfVectorizer
21
  from sklearn.metrics.pairwise import cosine_similarity
22
  import numpy as np
23
 
 
 
 
 
24
  try:
25
  from models import RagOptimizerAction, RagOptimizerObservation
26
  except ImportError:
@@ -37,62 +41,81 @@ class RagOptimizerEnvironment(Environment):
37
 
38
  def __init__(self):
39
  self._state = State(episode_id=str(uuid4()), step_count=0)
 
40
  self.kb = {}
41
  self.test_suite = []
 
 
 
 
 
 
 
 
 
42
  self._setup_task("easy")
43
 
44
  def _setup_task(self, task_id: str):
 
 
 
 
 
 
45
  if task_id == "easy":
46
- self.kb = {
47
- "doc_pricing_legacy": {
48
- "text": "Pricing for 2021: Enterprise tier is $1000/mo. Standard is $500/mo. All plans include 10 users.",
49
- "metadata": {"type": "pricing"}
50
- },
51
- "doc_pricing_current_v2": {
52
- "text": "Current Pricing 2024: Enterprise is $1500/mo. Standard is $750/mo. Refunds are not permitted on the enterprise tier.",
53
- "metadata": {}
54
- },
55
- **{f"doc_distractor_random_{i}": {"text": f"Weekly team update notes. Nothing important here, just discussed the weather and the upcoming launch {i}.", "metadata":{}} for i in range(10)}
56
- }
57
  self.test_suite = [
58
- {"query": "What is the current 2024 price for standard?", "target_concept": "750/mo"},
59
- {"query": "What is the refund policy for enterprise?", "target_concept": "Refunds are not permitted"}
60
  ]
 
61
  elif task_id == "medium":
62
  self.kb = {
63
- "doc_messy_support_ticket_1": {
64
- "text": "User complained the button disappeared on the frontend. Another user said the database latency was high. The frontend team fixed the button by updating CSS.",
 
 
 
 
65
  "metadata": {}
66
  },
67
- "doc_messy_support_ticket_2": {
68
- "text": "Email integration is failing with error 401 Unauthorized. The API key was rotated on Tuesday.",
69
  "metadata": {}
70
  },
71
- **{f"doc_distractor_eng_{i}": {"text": f"Engineering architecture decision record {i}. We decided to use {['React', 'Postgres', 'Redis', 'Kafka'][i%4]} because of scaling concerns.", "metadata":{}} for i in range(10)}
72
  }
 
 
 
 
 
73
  self.test_suite = [
74
- {"query": "UI issues frontend CSS missing button", "target_concept": "frontend team fixed the button"},
75
- {"query": "Email integration 401", "target_concept": "API key was rotated"}
 
 
76
  ]
 
77
  elif task_id == "hard":
78
- self.kb = {
79
- "doc_shipping_policy": {
80
- "text": "All internal shipments to remote branch offices take 5-7 business days. Overnight shipping is only available for C-suite.",
81
- "metadata": {"department": "logistics"}
82
- },
83
- "doc_monolithic_onboarding": {
84
- "text": "Welcome to the company! Here are some rules. 1) VPN access requires DUO. 2) The cafetaria opens at 8 AM. 3) For HR issues, email hr@company.com. 4) The 2024 holiday schedule includes Dec 25, Jan 1, and July 4. 5) Parking passes must be renewed annually in March.",
85
- "metadata": {}
86
- },
87
- **{f"doc_distractor_hr_{i}": {"text": f"This is an old HR policy document regarding {['pto', 'sick leave', 'travel', 'expenses'][i%4]} from 201{i%10}.", "metadata":{}} for i in range(10)}
88
- }
89
  self.test_suite = [
90
- {"query": "How long does shipping take to branch offices?", "target_concept": "5-7 business days"},
91
- {"query": "What months do parking passes need to be renewed?", "target_concept": "March"},
92
- {"query": "What holidays are we off in 2024?", "target_concept": "July 4"}
93
  ]
94
- else:
95
- self._setup_task("easy")
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  def _get_kb_summary(self) -> Dict[str, Dict]:
98
  """Returns a summary of the KB for the observation."""
@@ -114,44 +137,78 @@ class RagOptimizerEnvironment(Environment):
114
  )
115
 
116
  def _evaluate_kb(self) -> float:
117
- """The Grader: Evaluates the agent's current KB using TF-IDF."""
118
- if not self.kb:
119
- return 0.0
120
 
121
- doc_texts = [doc["text"] for doc in self.kb.values()]
 
122
 
123
- vectorizer = TfidfVectorizer(stop_words='english')
124
- try:
125
- doc_vectors = vectorizer.fit_transform(doc_texts)
126
- except ValueError:
127
- return 0.0
128
-
129
- score = 0.0
 
130
 
131
  for case in self.test_suite:
132
- query_vec = vectorizer.transform([case["query"]])
133
- similarities = cosine_similarity(query_vec, doc_vectors)[0]
 
 
134
 
135
- # Get top 3
136
- top_k_indices = similarities.argsort()[-3:][::-1]
 
 
137
 
138
- found = False
139
- for idx in top_k_indices:
140
- if similarities[idx] > 0.01:
141
- if case["target_concept"].lower() in doc_texts[idx].lower():
142
- found = True
143
- break
144
- if found:
145
- score += 1.0
146
 
147
- return float(score / len(self.test_suite))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  def step(self, action: RagOptimizerAction) -> RagOptimizerObservation: # type: ignore[override]
150
  self._state.step_count += 1
151
 
152
  msg = ""
153
  done = False
154
- reward = 0.0
155
 
156
  try:
157
  if action.action_type == "read_document":
@@ -163,6 +220,7 @@ class RagOptimizerEnvironment(Environment):
163
  elif action.action_type == "delete_document":
164
  if action.doc_id in self.kb:
165
  del self.kb[action.doc_id]
 
166
  msg = f"Deleted {action.doc_id}."
167
  else:
168
  msg = f"Error: doc_id {action.doc_id} not found."
@@ -174,6 +232,7 @@ class RagOptimizerEnvironment(Environment):
174
  if action.doc_id not in self.kb:
175
  self.kb[action.doc_id] = {"text": "", "metadata": {}}
176
  self.kb[action.doc_id]["text"] = action.text
 
177
  msg = f"Updated text for {action.doc_id}."
178
 
179
  elif action.action_type == "add_metadata":
@@ -184,6 +243,7 @@ class RagOptimizerEnvironment(Environment):
184
  msg = f"Error: doc_id {action.doc_id} not found."
185
  else:
186
  self.kb[action.doc_id]["metadata"][action.metadata_key] = action.metadata_value
 
187
  msg = f"Added metadata to {action.doc_id}."
188
 
189
  elif action.action_type == "submit":
 
16
  from openenv.core.env_server.interfaces import Environment
17
  from openenv.core.env_server.types import State
18
 
19
+ # Import scikit-learn for our Grader (fallback/legacy)
20
  from sklearn.feature_extraction.text import TfidfVectorizer
21
  from sklearn.metrics.pairwise import cosine_similarity
22
  import numpy as np
23
 
24
+ # Hybrid Search imports
25
+ from sentence_transformers import SentenceTransformer
26
+ from rank_bm25 import BM25Okapi
27
+
28
  try:
29
  from models import RagOptimizerAction, RagOptimizerObservation
30
  except ImportError:
 
41
 
42
  def __init__(self):
43
  self._state = State(episode_id=str(uuid4()), step_count=0)
44
+ self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
45
  self.kb = {}
46
  self.test_suite = []
47
+ self.dense_vectors = {}
48
+
49
+ # Load the Real World Datasets
50
+ import json
51
+ import os
52
+ seed_path = os.path.join(os.path.dirname(__file__), "kb_seed.json")
53
+ with open(seed_path, "r") as f:
54
+ self.seed_data = json.load(f)
55
+
56
  self._setup_task("easy")
57
 
58
  def _setup_task(self, task_id: str):
59
+ if task_id not in ["easy", "medium", "hard"]:
60
+ task_id = "easy"
61
+
62
+ # Clone the fresh dataset from seed so the agent can destroy it
63
+ self.kb = deepcopy(self.seed_data[task_id])
64
+
65
  if task_id == "easy":
 
 
 
 
 
 
 
 
 
 
 
66
  self.test_suite = [
67
+ {"query": "How do I run the server in production with concurrency?", "target_concept": "uvicorn main:app --workers 4"},
68
+ {"query": "Which version of Pydantic does FastAPI use by default?", "target_concept": "defaults to Pydantic v2"}
69
  ]
70
+
71
  elif task_id == "medium":
72
  self.kb = {
73
+ "doc_incident_raw": {
74
+ "text": "User reported frontend button missing. Database latency also flagged. No fix yet.",
75
+ "metadata": {}
76
+ },
77
+ "doc_incident_partial": {
78
+ "text": "Button issue and DB latency investigated. CSS fix attempted. Email 401 error also reported.",
79
  "metadata": {}
80
  },
81
+ "doc_incident_resolved": {
82
+ "text": "Frontend button restored by updating CSS stylesheet. Email 401 resolved after API key rotation on Tuesday.",
83
  "metadata": {}
84
  },
 
85
  }
86
+ for i in range(20):
87
+ self.kb[f"doc_distractor_eng_{i}"] = {
88
+ "text": f"Architecture decision {i}: chose Postgres for horizontal scaling.",
89
+ "metadata": {}
90
+ }
91
  self.test_suite = [
92
+ {"query": "How was the missing UI element fixed?",
93
+ "target_concept": "updating CSS stylesheet"},
94
+ {"query": "What caused the authentication failure?",
95
+ "target_concept": "API key rotation on Tuesday"},
96
  ]
97
+
98
  elif task_id == "hard":
 
 
 
 
 
 
 
 
 
 
 
99
  self.test_suite = [
100
+ {"query": "Which entity bankruptcy was the climax of the disaster?", "target_concept": "bankruptcy of Lehman Brothers"},
101
+ {"query": "What types of collateralized assets lost their worth?", "target_concept": "Mortgage-backed securities"},
102
+ {"query": "Who did predatory lenders primarily go after?", "target_concept": "low-income homebuyers"}
103
  ]
104
+
105
+ self._rebuild_cache()
106
+
107
+ def _rebuild_cache(self):
108
+ """Called whenever KB documents are added, removed, or updated."""
109
+ if not self.kb:
110
+ self.dense_vectors = {}
111
+ return
112
+
113
+ doc_ids = list(self.kb.keys())
114
+ # Append metadata to text for embedding
115
+ doc_texts = [(self.kb[d]["text"] + " " + " ".join(self.kb[d]["metadata"].values())).strip() for d in doc_ids]
116
+
117
+ vectors = self.embedding_model.encode(doc_texts, convert_to_tensor=False)
118
+ self.dense_vectors = {doc_id: vectors[i] for i, doc_id in enumerate(doc_ids)}
119
 
120
  def _get_kb_summary(self) -> Dict[str, Dict]:
121
  """Returns a summary of the KB for the observation."""
 
137
  )
138
 
139
  def _evaluate_kb(self) -> float:
140
+ """The Grader: Evaluates the current KB using Hybrid RRF (BM25 + Semantic MRR)."""
141
+ if not self.kb or not self.test_suite:
142
+ return 0.01
143
 
144
+ doc_ids = list(self.kb.keys())
145
+ doc_texts = [(self.kb[d]["text"] + " " + " ".join(self.kb[d]["metadata"].values())).strip() for d in doc_ids]
146
 
147
+ # 1. BM25 Corpus Preparation
148
+ tokenized_corpus = [doc.lower().split() for doc in doc_texts]
149
+ bm25 = BM25Okapi(tokenized_corpus)
150
+
151
+ # 2. Dense Matrix
152
+ doc_vectors = np.array([self.dense_vectors[d] for d in doc_ids])
153
+
154
+ mrr_sum = 0.0
155
 
156
  for case in self.test_suite:
157
+ # BM25 Search
158
+ tokenized_query = case["query"].lower().split()
159
+ bm25_scores = bm25.get_scores(tokenized_query)
160
+ bm25_ranks = bm25_scores.argsort()[::-1]
161
 
162
+ # Dense Search
163
+ query_vec = self.embedding_model.encode(case["query"])
164
+ dense_scores = cosine_similarity([query_vec], doc_vectors)[0]
165
+ dense_ranks = dense_scores.argsort()[::-1]
166
 
167
+ # Reciprocal Rank Fusion (RRF)
168
+ rrf_scores = {d: 0.0 for d in doc_ids}
169
+ k = 60
170
+ for rank, idx in enumerate(bm25_ranks):
171
+ rrf_scores[doc_ids[idx]] += 1.0 / (k + rank + 1)
172
+ for rank, idx in enumerate(dense_ranks):
173
+ rrf_scores[doc_ids[idx]] += 1.0 / (k + rank + 1)
 
174
 
175
+ # Grade Top-K fused list using MRR
176
+ ranked_doc_ids = sorted(rrf_scores.keys(), key=lambda x: rrf_scores[x], reverse=True)
177
+
178
+ req_meta = case.get("required_metadata_key")
179
+ req_meta_val = case.get("required_metadata_value")
180
+
181
+ case_mrr = 0.0
182
+ for i, doc_id in enumerate(ranked_doc_ids):
183
+ # Is this the true doc?
184
+ doc_text = self.kb[doc_id]["text"].lower()
185
+ if case["target_concept"].lower() in doc_text:
186
+ valid = True
187
+
188
+ # Medium Task Semantic Check
189
+ if req_meta and req_meta_val:
190
+ if self.kb[doc_id]["metadata"].get(req_meta) != req_meta_val:
191
+ valid = False
192
+
193
+ if valid:
194
+ case_mrr = 1.0 / (i + 1) # MRR formula starts at rank 1
195
+ break
196
+
197
+ mrr_sum += case_mrr
198
+
199
+ base_reward = float(mrr_sum / len(self.test_suite))
200
+
201
+ # Step Cost Penalty calculation (-0.01 per step)
202
+ cost_penalty = self._state.step_count * 0.01
203
+
204
+ return max(0.01, min(0.99, base_reward - cost_penalty))
205
 
206
  def step(self, action: RagOptimizerAction) -> RagOptimizerObservation: # type: ignore[override]
207
  self._state.step_count += 1
208
 
209
  msg = ""
210
  done = False
211
+ reward = 0.01
212
 
213
  try:
214
  if action.action_type == "read_document":
 
220
  elif action.action_type == "delete_document":
221
  if action.doc_id in self.kb:
222
  del self.kb[action.doc_id]
223
+ self._rebuild_cache()
224
  msg = f"Deleted {action.doc_id}."
225
  else:
226
  msg = f"Error: doc_id {action.doc_id} not found."
 
232
  if action.doc_id not in self.kb:
233
  self.kb[action.doc_id] = {"text": "", "metadata": {}}
234
  self.kb[action.doc_id]["text"] = action.text
235
+ self._rebuild_cache()
236
  msg = f"Updated text for {action.doc_id}."
237
 
238
  elif action.action_type == "add_metadata":
 
243
  msg = f"Error: doc_id {action.doc_id} not found."
244
  else:
245
  self.kb[action.doc_id]["metadata"][action.metadata_key] = action.metadata_value
246
+ self._rebuild_cache()
247
  msg = f"Added metadata to {action.doc_id}."
248
 
249
  elif action.action_type == "submit":
server/requirements.txt CHANGED
@@ -2,3 +2,5 @@ openenv[core]>=0.2.0
2
  fastapi>=0.115.0
3
  uvicorn>=0.24.0
4
  scikit-learn>=1.3.0
 
 
 
2
  fastapi>=0.115.0
3
  uvicorn>=0.24.0
4
  scikit-learn>=1.3.0
5
+ sentence-transformers>=3.0.0
6
+ rank_bm25>=0.2.2