Skip to content

Commit 4697133

Browse files
committed
feat(app): maximize RAG context and improve chat UX
Dynamic context budget system that scales with model context window: - Compute available context budget from window size minus history/system/reserve - Widen distance thresholds proportionally when budget allows more content - Include AI-generated summaries as overview/fallback in formatted context - Subtract page content from budget in extension chat to prevent overflow Chat improvements: - Rewrite system prompt for grounding and natural source attribution - Fix missing list markers in chat markdown (add list-style-type) - Remove hover blur from assistant message bubbles
1 parent 790a76d commit 4697133

8 files changed

Lines changed: 239 additions & 38 deletions

File tree

.changeset/rag-context-budget.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"think-app": minor
3+
---
4+
5+
Maximize RAG context utilization with dynamic budget system and improve chat UX
6+
7+
- Add dynamic context budget that scales with model context window size
8+
- Widen distance thresholds proportionally when budget allows more content
9+
- Include AI-generated summaries as context overview/fallback for each memory
10+
- Subtract page content from budget in browser extension chat to prevent overflow
11+
- Rewrite system prompt for better grounding and natural source attribution
12+
- Fix missing bullet point markers in chat message markdown rendering
13+
- Remove hover blur effect from assistant chat message bubbles

app/src/components/ChatMessage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function ChatMessage({ message }: ChatMessageProps) {
4242
"p-4 rounded-2xl",
4343
isUser
4444
? "bg-primary text-primary-foreground"
45-
: cn(glass.base, glass.hover)
45+
: glass.base
4646
)}
4747
>
4848
{isUser ? (

app/src/index.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,15 @@
153153
.chat-prose p:last-child {
154154
margin-bottom: 0;
155155
}
156-
.chat-prose ul, .chat-prose ol {
156+
.chat-prose ul {
157157
margin: 0.5em 0;
158158
padding-left: 1.5em;
159+
list-style-type: disc;
160+
}
161+
.chat-prose ol {
162+
margin: 0.5em 0;
163+
padding-left: 1.5em;
164+
list-style-type: decimal;
159165
}
160166
.chat-prose li {
161167
margin: 0.25em 0;

backend/app/native_messaging.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
from .services.embeddings import (
1515
get_embedding, get_current_embedding_model,
1616
filter_memories_dynamically, format_memories_as_context,
17+
compute_context_budget,
1718
)
1819
from .services.ai import (
19-
chat, process_memory_async, process_conversation_title_async,
20+
chat, get_model, process_memory_async, process_conversation_title_async,
2021
maybe_rewrite_query, generate_followup_suggestions,
2122
)
23+
from .models_info import get_context_window
2224
from .services.query import preprocess_query, extract_keywords, is_special_prompt, execute_special_handler
2325
from .events import event_manager, MemoryEvent, EventType
2426

@@ -283,6 +285,14 @@ async def _chat_message(self, params: dict) -> dict:
283285
sources = []
284286
return_page_summary = None
285287

288+
# Compute context budget based on model and conversation history
289+
model = get_model()
290+
context_window = get_context_window(model)
291+
# Account for page content that will also consume context space
292+
page_content_chars = min(len(page_content), 8000) if page_content else 0
293+
context_budget = compute_context_budget(context_window, history)
294+
context_budget = max(2000, context_budget - page_content_chars)
295+
286296
# Generate page summary for memory search (only on first message)
287297
if page_content and not page_summary:
288298
page_summary = await self._generate_page_summary(page_content, page_title)
@@ -333,7 +343,7 @@ async def _chat_message(self, params: dict) -> dict:
333343
# Use model-specific thresholds for filtering
334344
embedding_model = get_current_embedding_model()
335345
filtered_memories = filter_memories_dynamically(
336-
memories, embedding_model=embedding_model
346+
memories, embedding_model=embedding_model, context_budget_chars=context_budget
337347
)
338348

339349
# Build sources list for the response
@@ -349,7 +359,7 @@ async def _chat_message(self, params: dict) -> dict:
349359
]
350360

351361
# Format memories as context
352-
memories_context = format_memories_as_context(filtered_memories)
362+
memories_context = format_memories_as_context(filtered_memories, max_chars=context_budget)
353363
if memories_context:
354364
context_parts.append(memories_context)
355365

backend/app/routes/chat.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ..services.ai.query_rewriting import maybe_rewrite_query
1515
from ..services.ai.suggestions import get_quick_prompts, generate_followup_suggestions
1616
from ..services.query.special_handlers import is_special_prompt, execute_special_handler
17-
from ..services.embeddings.filtering import filter_memories_dynamically, format_memories_as_context
17+
from ..services.embeddings.filtering import filter_memories_dynamically, format_memories_as_context, compute_context_budget
1818
from ..db.search import search_similar_memories
1919
from ..schemas import ChatRequest
2020
from .. import config
@@ -66,6 +66,11 @@ async def _retrieve_context(
6666
sources = []
6767
attached_context = ""
6868

69+
# Compute context budget based on model and conversation history
70+
model = get_model()
71+
context_window = get_context_window(model)
72+
total_budget = compute_context_budget(context_window, history)
73+
6974
# Handle explicitly attached memories first
7075
if attached_memory_ids:
7176
attached_memories = []
@@ -80,7 +85,9 @@ async def _retrieve_context(
8085
})
8186

8287
if attached_memories:
83-
attached_context = "## User's Selected Memory:\n" + format_memories_as_context(attached_memories)
88+
# Give attached memories up to half the budget; RAG gets the other half
89+
attached_budget = total_budget // 2
90+
attached_context = "## User's Selected Memory:\n" + format_memories_as_context(attached_memories, max_chars=attached_budget)
8491
logger.info(f"Using {len(attached_memories)} attached memories as context")
8592

8693
# Skip RAG for very short messages (< 10 chars) or when explicitly disabled
@@ -118,9 +125,13 @@ async def _retrieve_context(
118125
)
119126

120127
if similar_memories:
128+
# RAG budget: half if attached memories exist, full otherwise
129+
rag_budget = total_budget // 2 if attached_context else total_budget
121130
# Filter using dynamic threshold with model-specific thresholds
122-
filtered_memories = filter_memories_dynamically(similar_memories, embedding_model=embedding_model)
123-
context = format_memories_as_context(filtered_memories)
131+
filtered_memories = filter_memories_dynamically(
132+
similar_memories, embedding_model=embedding_model, context_budget_chars=rag_budget
133+
)
134+
context = format_memories_as_context(filtered_memories, max_chars=rag_budget)
124135
# Build sources list from filtered memories, avoiding duplicates with attached
125136
attached_ids = {s["id"] for s in sources}
126137
for m in filtered_memories:

backend/app/services/ai/client.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55

66

77
# Custom system prompt for Think
8-
SYSTEM_PROMPT = """You are Think, a friendly personal assistant with access to the user's saved memories and notes. You help them recall information, answer questions, and have natural conversations.
9-
10-
When context from their memories is provided, use it naturally to inform your responses without explicitly mentioning "your saved article" or "your memories" - just incorporate the knowledge seamlessly.
11-
12-
Keep responses conversational and concise. Be helpful and warm, like a knowledgeable friend."""
8+
SYSTEM_PROMPT = """You are Think, a helpful personal assistant. You help users recall and explore information from their saved content.
9+
The content can be owned by the user (like notes and voice memos) or from third-party sources (like web pages, videos, and audio).
10+
You use this information to answer questions, provide summaries, and assist with tasks.
11+
12+
Guidelines:
13+
- Ground your answers in the provided context. If it doesn't contain enough information to answer, say so rather than guessing.
14+
- Be conversational and concise.
15+
- You can naturally reference sources (e.g. "from a saved article", "in one of your videos"), but never output the raw type tags like [web] or [video] in your responses.
16+
"""
1317

1418

1519
async def get_client() -> AsyncOpenAI:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Vector Search & Similarity services."""
22
from .client import get_embedding, cosine_similarity, get_current_embedding_model
3-
from .filtering import filter_memories_dynamically, format_memories_as_context
3+
from .filtering import filter_memories_dynamically, format_memories_as_context, compute_context_budget
44
from .jobs import job_manager, reembed_worker, JobStatus

0 commit comments

Comments
 (0)