Skip to content

Commit b644e55

Browse files
arturoliduenakibanamachineneptunian
authored andcommitted
[Obs AI Assistant] aware of new .integration_knowledge* system index (elastic#237085)
Closes elastic/obs-ai-team#357 ## Summary This PR adds the awareness of `.integration_knowledge*` index as another index for recalling. The Obs AI Assistant will retrieve integration knowledge from the index Value added to the Obs AI Assistant (elastic/obs-ai-team#357 (comment)): > The assistant will become aware of LLM-facing documentation for any installed integrations in the user's cluster. For example, the Logstash integration might ship documentation that explains how to understand the health reporting metrics collected by the integration and the assistant could answer prompts like "why was my logstash server down yesterday?" using the user's real data. Manual Testing: 1 -> Follow the instructions from elastic#230107 (comment): > 1. If elastic/elasticsearch#132506 has been merged, run `yarn es snapshot` in Kibana, otherwise, checkout that branch in your local ES and then in kibana run `yarn es source` in order to use that version of ES which contains the index management, mappings, etc. >2. Install a package with any number of knowledge base docs in the `docs/knowledge_base` folder. You can use [this sample package](https://github.com/user-attachments/files/21867395/masonstestpackage-0.0.1.zip), or create your own following the guide below: > > - Using `elastic-package`, create a new package using `elastic-package create integration` - Once created add `knowledge_base` as a folder inside of the generated `docs` folder of the integration - Add an arbitrary amount of `.md` files to the knowledge_base folder - Run `elastic-package build` to build the package - There are a lot of different options for installing the package in a local kibana instance. I prefer to just take the generated .zip folder from `/build` in `elastic-package` and upload it to kibana using the custom integrations feature. You can also expose the package registry, or whatever you see fit. >3. Watch the Kibana logs for errors/debug messages etc >4. Use the new endpoint or just directly check the index using `GET /.integration_knowledge/_search` to verify that the documents are ingested into the system index of `.integration_knowledge` >5. Update the package and verify that the KB documents are updated by checking the response again, they should have the updated pkgVersion on the associated docs. >6. Remove the package and then verify (using the endpoint) that the docs are removed from the index 2 - Ask the AI Assistant about information contained in the integration documents 3- check that the documents are listed on the response of executed the function context inside learnings ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [ ] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Sandra G <[email protected]>
1 parent 8f793a7 commit b644e55

File tree

2 files changed

+95
-26
lines changed
  • x-pack/platform/plugins/shared/observability_ai_assistant

2 files changed

+95
-26
lines changed

x-pack/platform/plugins/shared/observability_ai_assistant/common/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,12 @@ export enum ConversationAccess {
205205
SHARED = 'shared',
206206
PRIVATE = 'private',
207207
}
208+
209+
export interface IntegrationKnowledgeBaseEntry {
210+
content: string;
211+
package_name: string;
212+
filename: string;
213+
version: string;
214+
path: string;
215+
installed_at: string;
216+
}

x-pack/platform/plugins/shared/observability_ai_assistant/server/service/knowledge_base_service/index.ts

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import { encode } from 'gpt-tokenizer';
1313
import { isLockAcquisitionError } from '@kbn/lock-manager';
1414
import type { DocumentationManagerAPI } from '@kbn/product-doc-base-plugin/server/services/doc_manager';
1515
import { resourceNames } from '..';
16-
import type { Instruction, KnowledgeBaseEntry } from '../../../common/types';
16+
import type {
17+
Instruction,
18+
IntegrationKnowledgeBaseEntry,
19+
KnowledgeBaseEntry,
20+
} from '../../../common/types';
1721
import { KnowledgeBaseEntryRole, KnowledgeBaseType } from '../../../common/types';
1822
import { getAccessQuery, getUserAccessFilters } from '../util/get_access_query';
1923
import { getCategoryQuery } from '../util/get_category_query';
@@ -36,6 +40,8 @@ import { getInferenceIdFromWriteIndex } from './get_inference_id_from_write_inde
3640
import { createOrUpdateKnowledgeBaseIndexAssets } from '../index_assets/create_or_update_knowledge_base_index_assets';
3741
import { LEGACY_CUSTOM_INFERENCE_ID } from '../../../common/preconfigured_inference_ids';
3842

43+
const INTEGRATION_KNOWLEDGE_INDEX = '.integration_knowledge';
44+
3945
interface Dependencies {
4046
core: CoreSetup<ObservabilityAIAssistantPluginStartDependencies>;
4147
esClient: {
@@ -112,6 +118,45 @@ export class KnowledgeBaseService {
112118
}));
113119
}
114120

121+
private async recallFromIntegrationsKnowledge({
122+
queries,
123+
esClient,
124+
}: {
125+
queries: Array<{ text: string; boost?: number }>;
126+
esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient };
127+
}): Promise<RecalledEntry[]> {
128+
try {
129+
// Search the .integration_knowledge index using semantic search on the content field
130+
const response = await esClient.asInternalUser.search<IntegrationKnowledgeBaseEntry>({
131+
index: INTEGRATION_KNOWLEDGE_INDEX,
132+
query: {
133+
bool: {
134+
should: queries.map(({ text, boost = 1 }) => ({
135+
semantic: {
136+
field: 'content',
137+
query: text,
138+
boost,
139+
},
140+
})),
141+
},
142+
},
143+
size: 10,
144+
_source: ['package_name', 'filename', 'content', 'version'],
145+
});
146+
return response.hits.hits.map((hit) => ({
147+
text: hit._source?.content!,
148+
labels: { filename: hit._source?.filename ?? '', version: hit._source?.version ?? '' },
149+
title: hit._source?.package_name,
150+
esScore: hit._score!,
151+
id: hit._id!,
152+
}));
153+
} catch (error) {
154+
this.dependencies.logger.error(`Error recalling from ${INTEGRATION_KNOWLEDGE_INDEX} index`);
155+
this.dependencies.logger.debug(error);
156+
return [];
157+
}
158+
}
159+
115160
recall = async ({
116161
user,
117162
queries,
@@ -137,40 +182,55 @@ export class KnowledgeBaseService {
137182
() => `Recalling entries from KB for queries: "${JSON.stringify(queries)}"`
138183
);
139184

140-
const [documentsFromKb, documentsFromConnectors] = await Promise.all([
141-
this.recallFromKnowledgeBase({
142-
user,
143-
queries,
144-
categories,
145-
namespace,
146-
}).catch((error) => {
147-
if (isInferenceEndpointMissingOrUnavailable(error)) {
148-
throwKnowledgeBaseNotReady(error);
149-
}
150-
throw error;
151-
}),
152-
recallFromSearchConnectors({
153-
esClient,
154-
uiSettingsClient,
155-
queries,
156-
core: this.dependencies.core,
157-
logger: this.dependencies.logger,
158-
}).catch((error) => {
159-
this.dependencies.logger.error('Error getting data from search indices');
160-
this.dependencies.logger.debug(error);
161-
return [];
162-
}),
163-
]);
185+
const [documentsFromKb, documentsFromConnectors, documentsFromIntegrations] = await Promise.all(
186+
[
187+
this.recallFromKnowledgeBase({
188+
user,
189+
queries,
190+
categories,
191+
namespace,
192+
}).catch((error) => {
193+
if (isInferenceEndpointMissingOrUnavailable(error)) {
194+
throwKnowledgeBaseNotReady(error);
195+
}
196+
throw error;
197+
}),
198+
recallFromSearchConnectors({
199+
esClient,
200+
uiSettingsClient,
201+
queries,
202+
core: this.dependencies.core,
203+
logger: this.dependencies.logger,
204+
}).catch((error) => {
205+
this.dependencies.logger.error('Error getting data from search indices');
206+
this.dependencies.logger.debug(error);
207+
return [];
208+
}),
209+
this.recallFromIntegrationsKnowledge({
210+
esClient,
211+
queries,
212+
}).catch((error) => {
213+
this.dependencies.logger.error(
214+
`Error getting data from ${INTEGRATION_KNOWLEDGE_INDEX} index`
215+
);
216+
this.dependencies.logger.debug(error);
217+
return [];
218+
}),
219+
]
220+
);
164221

165222
this.dependencies.logger.debug(
166223
`documentsFromKb: ${JSON.stringify(documentsFromKb.slice(0, 5), null, 2)}`
167224
);
168225
this.dependencies.logger.debug(
169226
`documentsFromConnectors: ${JSON.stringify(documentsFromConnectors.slice(0, 5), null, 2)}`
170227
);
228+
this.dependencies.logger.debug(
229+
`documentsFromIntegrations: ${JSON.stringify(documentsFromIntegrations.slice(0, 5), null, 2)}`
230+
);
171231

172232
const sortedEntries = orderBy(
173-
documentsFromKb.concat(documentsFromConnectors),
233+
[...documentsFromKb, ...documentsFromConnectors, ...documentsFromIntegrations],
174234
'esScore',
175235
'desc'
176236
).slice(0, limit.size ?? 20);

0 commit comments

Comments
 (0)