Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ data class BotRAGConfigurationDTO(
val documentsRequired: Boolean = true,
val debugEnabled: Boolean,
val maxDocumentsRetrieved: Int,
val maxDocumentsInContext: Int,
val maxMessagesFromHistory: Int,
) {
constructor(configuration: BotRAGConfiguration) : this(
Expand All @@ -67,6 +68,7 @@ data class BotRAGConfigurationDTO(
documentsRequired = configuration.documentsRequired,
debugEnabled = configuration.debugEnabled,
maxDocumentsRetrieved = configuration.maxDocumentsRetrieved,
maxDocumentsInContext = configuration.maxDocumentsInContext,
maxMessagesFromHistory = configuration.maxMessagesFromHistory,
)

Expand Down Expand Up @@ -102,6 +104,7 @@ data class BotRAGConfigurationDTO(
documentsRequired = documentsRequired,
debugEnabled = debugEnabled,
maxDocumentsRetrieved = maxDocumentsRetrieved,
maxDocumentsInContext = maxDocumentsInContext,
maxMessagesFromHistory = maxMessagesFromHistory,
)
}
Expand Down
3 changes: 2 additions & 1 deletion bot/admin/server/src/test/kotlin/service/RAGServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class RAGServiceTest : AbstractTest() {
const val INDEX_SESSION_ID = "1010101"

private val DEFAULT_RAG_CONFIG = BotRAGConfigurationDTO(
id = "ragId",
id = "ragId",
namespace = NAMESPACE,
botId = BOT_ID,
enabled = false,
Expand Down Expand Up @@ -95,6 +95,7 @@ class RAGServiceTest : AbstractTest() {
documentsRequired = true,
debugEnabled = false,
maxDocumentsRetrieved = 2,
maxDocumentsInContext = 3,
maxMessagesFromHistory = 2,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class RAGValidationServiceTest {
documentsRequired = true,
debugEnabled = false,
maxDocumentsRetrieved = 2,
maxDocumentsInContext = 3,
maxMessagesFromHistory = 2,
)

Expand Down
130 changes: 105 additions & 25 deletions bot/admin/web/src/app/rag/rag-settings/models/engines-configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,115 @@ import {
PromptDefinitionFormatter
} from '../../../shared/model/ai-settings';

export const QuestionCondensingDefaultPrompt: string = `Given a chat history and the latest user question which might reference context in the chat history, formulate a standalone question which can be understood without the chat history. Do NOT answer the question, just reformulate it if needed and otherwise return it as is.`;

export const QuestionAnsweringDefaultPrompt: string = `# TOCK (The Open Conversation Kit) chatbot

## General context

You are a chatbot designed to provide short conversational messages in response to user queries.

## Guidelines

Incorporate any relevant details from the provided context into your answers, ensuring they are directly related to the user's query.

## Style and format

Your tone is empathetic, informative and polite.

## Additional instructions

Use the following pieces of retrieved context to answer the question.
If you dont know the answer, answer (exactly) with "{{no_answer}}".
Answer in {{locale}}.
export const QuestionCondensingDefaultPrompt: string = `# Question Reformulation Assistant

## Context
You are a helpful assistant that reformulates questions.

You are given:
- The conversation history between the user and the assistant
- The most recent user question

## Task
1. Reformulate the user’s latest question into a clear, standalone query.
2. Incorporate relevant context from the conversation history.
3. Enrich the reformulation with the business/domain lexicon whenever relevant.
4. Expand any acronym into its full meaning, and also keep the acronym in parentheses.
- Example: "PTZ" → "Prêt à Taux Zéro (PTZ)"
5. If the user provides the full term without acronym, add the acronym in parentheses if it is commonly used in the business domain.
- Example: "Prêt à Taux Zéro" → "Prêt à Taux Zéro (PTZ)"
6. Do NOT answer the question.

## Business/domain lexicon
PTZ : Prêt à Taux Zéro
Éco-PTZ : Éco-Prêt à Taux Zéro

## Output
Return only the reformulated question.
`;

{{context}}

## Question
export const QuestionAnsweringDefaultPrompt: string = `# TOCK (The Open Conversation Kit) chatbot

{{question}}
## Instructions:
You must answer STRICTLY in valid JSON format (no extra text, no explanations).
Use only the following context and the rules below to answer the question.

### Rules for JSON output:

- If the answer is found in the context:
- "status": "found_in_context"

- If the answer is NOT found in the context:
- "status": "not_found_in_context"
- "answer":
- The "answer" must not be a generic refusal. Instead, generate a helpful and intelligent response:
- If a similar or related element exists in the context (e.g., another product, service, or regulation with a close name, date, or wording), suggest it naturally in the answer.
- If no similar element exists, politely acknowledge the lack of information while encouraging clarification or rephrasing.
- Always ensure the response is phrased in a natural and user-friendly way, rather than a dry "not found in context".

- If the question matches a special case defined below:
- "status": "<the corresponding case code>"

And for all cases (MANDATORY):
- "answer": "<the best possible answer in {{ locale }}>"
- "topic": "<exactly ONE topic chosen STRICTLY from the predefined list below. If no exact match is possible, set 'unknown'>"
- "suggested_topics": ["<zero or more free-form suggestions if topic is unknown>"]

Exception: If the question is small talk (only to conversational rituals such as greetings (e.g., “hello”, “hi”) and farewells or leave-takings (e.g., “goodbye”, “see you”) ), you may ignore the context and generate a natural small-talk response in the "answer". In this case:
- "status": "small_talk"
- "topic": "<e.g., greetings>"
- "suggested_topics": []
- "context": []

### Context tracing requirements (MANDATORY):
- You MUST include **every** chunk from the input context in the "context" array, in the same order they appear. **No chunk may be omitted**.
- If explicit chunk identifiers are present in the context, use them; otherwise assign sequential numbers starting at 1.
- For each chunk object:
- "chunk": "<chunk_identifier_or_sequential_number>"
- "sentences": ["<verbatim sentence(s) from this chunk used to answer the question>"] — leave empty \`[]\` if none.
- "reason": null if the chunk contributed; otherwise a concise explanation of why this chunk is not relevant to the question (e.g., "general background only", "different product", "no data for the asked period", etc.).
- If there are zero chunks in the context, return \`"context": []\`.

### Predefined list of topics (use EXACT spelling, no variations):

## Context:
{{ context }}

## Conversation history
{{ chat_history }}

## User question
{{ question }}

## Output format (JSON only):
Return your response in the following format:

{
"status": "found_on_context" | "not_in_context" | "small_talk",
"answer": "TEXTUAL_ANSWER",
"topic": "EXACT_TOPIC_FROM_LIST_OR_UNKNOWN",
"suggested_topics": [
"SUGGESTED_TOPIC_1",
"SUGGESTED_TOPIC_2"
],
"context": [
{
"chunk": "1",
"sentences": ["SENTENCE_1", "SENTENCE_2"],
"reason": null
},
{
"chunk": "2",
"sentences": [],
"reason": "General description; no details related to the question."
},
{
"chunk": "3",
"sentences": ["SENTENCE_X"],
"reason": null
}
]
}
`;

export const QuestionCondensing_prompt: ProvidersConfigurationParam[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface RagSettings {
indexName: string;

maxDocumentsRetrieved: number;
maxDocumentsInContext: number;

documentsRequired: boolean;
}
21 changes: 21 additions & 0 deletions bot/admin/web/src/app/rag/rag-settings/rag-settings.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,27 @@ <h1 class="flex-grow-1">Rag settings</h1>
/>
</tock-form-control>
</div>
<div class="col-6 px-3">
<tock-form-control
label="Max documents in LLM context"
name="maxDocumentsInContext"
[controls]="maxDocumentsInContext"
[required]="false"
[boldLabel]="false"
information="Maximum number of documents to be provided as context to the LLM."
>
<input
type="number"
step="1"
min="0"
max="50"
nbInput
fieldSize="small"
fullWidth
formControlName="maxDocumentsInContext"
/>
</tock-form-control>
</div>

<div class="col-6 px-3">
<tock-form-control
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ interface RagSettingsForm {
indexName: FormControl<string>;

maxDocumentsRetrieved: FormControl<number>;
maxDocumentsInContext: FormControl<number>;

documentsRequired: FormControl<boolean>;

Expand Down Expand Up @@ -217,6 +218,7 @@ export class RagSettingsComponent implements OnInit, OnDestroy {

documentsRequired: new FormControl(undefined),
maxDocumentsRetrieved: new FormControl(undefined),
maxDocumentsInContext: new FormControl(undefined),

questionCondensingLlmProvider: new FormControl(undefined, [Validators.required]),
questionCondensingLlmSetting: new FormGroup({}),
Expand Down Expand Up @@ -275,6 +277,10 @@ export class RagSettingsComponent implements OnInit, OnDestroy {
return this.form.get('maxDocumentsRetrieved') as FormControl;
}

get maxDocumentsInContext(): FormControl {
return this.form.get('maxDocumentsInContext') as FormControl;
}

get indexName(): FormControl {
return this.form.get('indexName') as FormControl;
}
Expand Down Expand Up @@ -396,7 +402,8 @@ export class RagSettingsComponent implements OnInit, OnDestroy {
documentsRequired: true,
debugEnabled: false,
maxMessagesFromHistory: 5,
maxDocumentsRetrieved: 4
maxDocumentsRetrieved: 4,
maxDocumentsInContext: 4,
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@
</span>

<nb-icon
*ngIf="footNote.score"
*ngIf="footNote.score != null || footNote.rrfScore != null"
icon="trophy"
class="font-size-xsmall align-center ml-2"
nbTooltip="Compressor score : {{ footNote.score.toFixed(2) }}"
[nbTooltip]="(footNote.score != null ? 'Compressor score : ' + footNote.score.toFixed(3) : '') +
(footNote.score != null && footNote.rrfScore != null ? ' | ' : '') +
(footNote.rrfScore != null ? 'RRF score : ' + footNote.rrfScore.toFixed(3) : '')"
></nb-icon>
</span>

Expand Down
1 change: 1 addition & 0 deletions bot/admin/web/src/app/shared/model/dialog-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export interface Footnote {
url: string;
content?: string;
score?: number;
rrfScore?: number;
_showFullContent?: boolean;
identifier: string;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class GoogleChatFootnoteFormatterTest {
"keeps footnotes with same URL but different titles",
"Check this out",
listOf(
Footnote("id1", "Title A", "https://example.com", null, null),
Footnote("id2", "Title B", "https://example.com", null, null)
Footnote("id1", "Title A", "https://example.com", null, null,null),
Footnote("id2", "Title B", "https://example.com", null, null,null)
),
"""
Check this out
Expand All @@ -78,8 +78,8 @@ class GoogleChatFootnoteFormatterTest {
"formats mix of footnotes with and without URL",
"Here's some info",
listOf(
Footnote("id1", "Google", "https://google.com", null, null),
Footnote("id2", "Just text", null, null, null)
Footnote("id1", "Google", "https://google.com", null,null, null),
Footnote("id2", "Just text", null, null, null,null)
),
"""
Here's some info
Expand All @@ -93,8 +93,8 @@ class GoogleChatFootnoteFormatterTest {
"does not deduplicate footnotes with same title and different URLs",
"Interesting links",
listOf(
Footnote("id1", "Duplicate", "https://a.com", null, null),
Footnote("id2", "Duplicate", "https://b.com", null, null)
Footnote("id1", "Duplicate", "https://a.com", null, null,null),
Footnote("id2", "Duplicate", "https://b.com", null, null,null)
),
"""
Interesting links
Expand All @@ -108,8 +108,8 @@ class GoogleChatFootnoteFormatterTest {
"deduplicates footnotes based only on url and title ignoring other fields",
"References",
listOf(
Footnote("id1", "Doc", "https://doc.com", "Content A", 0.9f),
Footnote("id2", "Doc", "https://doc.com", "Content B", 0.2f)
Footnote("id1", "Doc", "https://doc.com", "Content A", 0.9f,null),
Footnote("id2", "Doc", "https://doc.com", "Content B", 0.2f,null)
),
"""
References
Expand All @@ -126,8 +126,8 @@ class GoogleChatFootnoteFormatterTest {
"generates numbered links with and without URLs",
"Sources below",
listOf(
Footnote("id1", "Tock", "https://tock.ai", null, null),
Footnote("id2", "Offline doc", null, null, null)
Footnote("id1", "Tock", "https://tock.ai", null, null,null),
Footnote("id2", "Offline doc", null, null, null,null)
),
"""
Sources below
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ internal object MattermostMessageConverter {
footnote.title,
footnote.url,
footnote.content,
footnote.score
footnote.score,
footnote.rrfScore
)
})
}
Expand Down
6 changes: 5 additions & 1 deletion bot/connector-mattermost/src/main/kotlin/model/Footnote.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ data class Footnote(
*/
val content: String?,
/**
* A footnote score
* A footnote score (Compressor)
*/
val score: Float?,
/**
* A footnote score (RFF)
*/
val rrfScore: Float?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ data class ClientFootnote(
val title: String,
val url: String?,
val content: String?,
val score: Float?
val score: Float?,
val rrfScore: Float?
)
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ data class Footnote(
*/
val content: String?,
/**
* A footnote score
* A footnote score (Compressor)
*/
val score: Float?,
/**
* A footnote score (RFF)
*/
val rrfScore: Float?,
)
3 changes: 2 additions & 1 deletion bot/connector-web/src/main/kotlin/WebMessageProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ internal class WebMessageProcessor(private val processMarkdown: Boolean) {
footnote.title,
footnote.url,
footnote.content?.let { postProcess(it) },
footnote.score
footnote.score,
footnote.rrfScore
)
})
}
Expand Down
Loading