Skip to content

Added markdown links to ai insgihts and obs agent#250030

Merged
yuliia-fryshko merged 20 commits intoelastic:mainfrom
yuliia-fryshko:markdown-links-to-select-entities
Jan 29, 2026
Merged

Added markdown links to ai insgihts and obs agent#250030
yuliia-fryshko merged 20 commits intoelastic:mainfrom
yuliia-fryshko:markdown-links-to-select-entities

Conversation

@yuliia-fryshko
Copy link
Copy Markdown
Contributor

@yuliia-fryshko yuliia-fryshko commented Jan 22, 2026

#Closes 431

The agent now automatically formats known Observability entities as Markdown links in its responses. This enables users to click directly on entity references to navigate to the relevant APM views, improving workflow efficiency.

To make these links space-aware for the agent as well, we introduced a change that allows BuiltInAgentDefinition.configuration to be defined as either: a static configuration object, or a function that receives runtime context and returns the configuration dynamically.

How it works

  1. At setup (registration) time, the agent stores the configuration function.
  2. When handling a request, createAgentHandler checks whether the configuration is a function.
  3. If it is, the function is called with { spaceId, request } to resolve the actual configuration.
  4. The resolved instructions then include the correct space-aware links.

Changes

  • Added entity-linking instructions to the Observability Agent’s system prompt.
  • Added entity-linking support to Errors, Logs and Alerts AI Insights.
  • All links are space-aware.

Testing with Cursor:

Test Prompt:
test_prompt.md

Results:
hereisresults.md

Test scenario:

  • Start es, kibana and otel-demo
  • Enable productCatalogFailure feature flag
  • Start a conversation with the Observability Agent
  • Ask about a specific service (e.g., "What's the status of checkout service?")
  • The agent's response should format service names as clickable Markdown links

Traces for Error AI insight and Alert AI Insight

@yuliia-fryshko yuliia-fryshko requested a review from a team as a code owner January 22, 2026 12:02
@yuliia-fryshko yuliia-fryshko self-assigned this Jan 22, 2026
@yuliia-fryshko yuliia-fryshko added backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes labels Jan 22, 2026
Comment on lines +118 to +128
| Service | \`[<serviceName>](/app/apm/services/<serviceName>)\` | "The [payments](/app/apm/services/payments) service is experiencing high latency." |
| Transaction | \`[<transactionName>](/app/apm/services/<serviceName>/transactions)\` | "The transaction [POST /checkout](/app/apm/services/payments/transactions) took 500ms." |
| Trace | \`[<traceId>](/app/apm/link-to/trace/<traceId>)\` | "See trace [8bc26008603e16819bd6fcfb80fceff5](/app/apm/link-to/trace/8bc26008603e16819bd6fcfb80fceff5)" |
| Error | \`[<errorKey>](/app/apm/services/<serviceName>/errors/<errorKey>)\` | "Error [upstream-5xx](/app/apm/services/catalog-api/errors/upstream-5xx) suggests a dependency failure." |
| Service Errors | \`[errors](/app/apm/services/<serviceName>/errors)\` | "Review all [errors](/app/apm/services/frontend/errors) for the [frontend](/app/apm/services/frontend) service." |
| Service Logs | \`[logs](/app/apm/services/<serviceName>/logs)\` | "Check [logs](/app/apm/services/frontend/logs) for the [frontend](/app/apm/services/frontend) service." |
| Host | \`[<hostName>](/app/metrics/detail/host/<hostName>)\` | "Host [web-01](/app/metrics/detail/host/web-01) is experiencing high CPU usage." |
| Service Map | \`[Service Map](/app/apm/services/<serviceName>/service-map)\` | "Check the [Service Map](/app/apm/services/payments/service-map) to see dependencies." |
| Dependencies | \`[Dependencies](/app/apm/services/<serviceName>/dependencies)\` | "View [Dependencies](/app/apm/services/catalog-api/dependencies) to identify upstream issues." |
| Alert | \`[<alertId>](/app/observability/alerts/<alertId>)\` | "Alert [alert-uuid-123](/app/observability/alerts/alert-uuid-123) was triggered." |
| Logs Explorer | \`[Logs](/app/logs)\` | "View [Logs](/app/logs) to investigate the issue further." |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you will have to consider the kibana base path and spaces for the links (and maybe the time range?).

When I have a base path: The URLs result in Not Found

Screen.Recording.2026-01-22.at.7.59.40.PM.mov

When I am in a space: Same result as above

Screen.Recording.2026-01-22.at.8.01.24.PM.mov

Copy link
Copy Markdown
Contributor

@sorenlouv sorenlouv Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point @viduni94 .
@yuliia-fryshko You should be able to get the the base url like coreStart.http.basePath.publicBaseUrl

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @viduni94 ! I updated the PR , now links are space aware for AI Insights and the Observability Agent

Screen.Recording.2026-01-27.at.18.29.21.mov

- <TraceServices>: Service aggregates for the trace (serviceName, count, errorCount)
- <TraceLogCategories>: Categorized log patterns tied to the trace (errorCategory, docCount, sampleMessage)
`);
${getEntityLinkingInstructions(spaceId)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see you are importing the instruction here. This got me thinking: what about the other instructions:

${getInvestigationInstructions()}
${getReasoningInstructions()}
${getFieldDiscoveryInstructions()}
${getKqlInstructions()}

Should they be included as well?

How are you handling this for alerts and log AI insights? @viduni94 @neptunian?

Copy link
Copy Markdown
Contributor

@sorenlouv sorenlouv Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, nevermind. The other instructions are only relevant when doing tool calling, which the AI insight is not doing atm. So probably only the instructions related to formatting (like markdown links) are needed.

${getReasoningInstructions()}
${getFieldDiscoveryInstructions()}
${getKqlInstructions()}
${getEntityLinkingInstructions()}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's hear AB team if we can render this dynamically so we can access context

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a change, so now it allows BuiltInAgentDefinition.configuration to be either: a static configuration object or a function that receives context and returns configuration dynamically.

How It will work:

  1. At the setup (registration) time agent stores a configuration function
  2. Then createAgentHandler checks if configuration is a function
  3. If function, calls it with { spaceId, request } to get the actual config
  4. Instructions include correct space

cc: @pgayvallet

@yuliia-fryshko yuliia-fryshko requested a review from a team as a code owner January 27, 2026 16:21
@pgayvallet pgayvallet self-requested a review January 27, 2026 19:40

| Entity | Link Format | Example |
|--------|-------------|---------|
| Service | [<serviceName>](${prefix}/app/apm/services/<serviceName>) | "The [payments](${prefix}/app/apm/services/payments) service is experiencing high latency." |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the space path changes.

How about the base path?
The URLs will result in 404 if there is a base path configured.

Copy link
Copy Markdown
Contributor

@viduni94 viduni94 Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, you could try to use this shared util by agent builder which handles the space and base path:

export function getKibanaUrl(
coreSetup: CoreSetup,
cloudSetup?: CloudSetup,
request?: KibanaRequest,
spaces?: SpacesPluginStart
) {
const baseUrl =
coreSetup.http.basePath.publicBaseUrl ??
cloudSetup?.kibanaUrl ??
getFallbackKibanaUrl(coreSetup);
const pathname = new URL(baseUrl).pathname;
const serverBasePath = coreSetup.http.basePath.serverBasePath;
const { pathHasExplicitSpaceIdentifier } = getSpaceIdFromPath(pathname, serverBasePath);
if (!pathHasExplicitSpaceIdentifier && request && spaces) {
const spaceId = spaces.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID;
return addSpaceIdToPath(baseUrl, spaceId);
}
return baseUrl;
}

Comment on lines +39 to +40
configuration: ({ spaceId }) => {
const basePath = core.http.basePath.serverBasePath;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untested, but can you try this?

Suggested change
configuration: ({ spaceId }) => {
const basePath = core.http.basePath.serverBasePath;
configuration: ({ request }) => {
const prefix = coreStart.http.basePath.get(request);

The advantage is that you don't to manually stitch basePath and spaceId together.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, @sorenlouv ! I changed it and tested, it works (here I run kibana with basePath: /kibana and in the space: Agent )

Screen.Recording.2026-01-28.at.22.15.04.mov

@elasticmachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #38 / Options list control Interactions between options list and dashboard Test exists query "before all" hook for "creating exists query has expected results"

Metrics [docs]

✅ unchanged

History

cc @yuliia-fryshko

@yuliia-fryshko yuliia-fryshko merged commit b2f0c4b into elastic:main Jan 29, 2026
18 checks passed
hannahbrooks pushed a commit to hannahbrooks/kibana that referenced this pull request Jan 30, 2026
#Closes [431](elastic/obs-ai-team#431)

The agent now automatically formats known Observability entities as
Markdown links in its responses. This enables users to click directly on
entity references to navigate to the relevant APM views, improving
workflow efficiency.

To make these links space-aware for the agent as well, we introduced a
change that allows `BuiltInAgentDefinition.configuration` to be defined
as either: a static configuration object, or a function that receives
runtime context and returns the configuration dynamically.

**How it works**
1. At setup (registration) time, the agent stores the configuration
function.
2. When handling a request, createAgentHandler checks whether the
configuration is a function.
3. If it is, the function is called with { spaceId, request } to resolve
the actual configuration.
4. The resolved instructions then include the correct space-aware links.

### Changes

- Added entity-linking instructions to the Observability Agent’s system
prompt.
- Added entity-linking support to Errors, Logs and Alerts AI Insights.
- All links are space-aware. 

**Testing with Cursor:**

Test Prompt:

[test_prompt.md](https://github.com/user-attachments/files/24771581/test_prompt.md)

Results:

[hereisresults.md](https://github.com/user-attachments/files/24771728/hereisresults.md)

**Test scenario:**

- Start es, kibana and otel-demo
- Enable productCatalogFailure feature flag
- Start a conversation with the Observability Agent
- Ask about a specific service (e.g., "What's the status of checkout
service?")
- The agent's response should format service names as clickable Markdown
links

Traces for [Error AI
insight](https://oblt-apps.elastic.dev/phoenix-ai/projects/UHJvamVjdDoxMTIy/traces/5fd67e44e280ab0514f17d53e23cd828?selected)
and [Alert AI
Insight](https://oblt-apps.elastic.dev/phoenix-ai/projects/UHJvamVjdDoxMTIy/traces/9dff61da9be3240830e83e6d99d17265?selected)

---------

Co-authored-by: Viduni Wickramarachchi <viduni.ushanka@gmail.com>
Co-authored-by: Søren Louv-Jansen <sorenlouv@gmail.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants