mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 11:26:31 +00:00
Support agent goal query param for markdownAsk (#4324)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Support an optional `goal` query parameter on the markdown ask interface (`?ask=…&goal=…`), letting agents describe the broader end goal they are working towards so the answer can be steered towards it.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { type RouteLayoutParams, getStaticSiteContext } from '@/app/utils';
|
||||
import { serveAskMarkdown } from '@/routes/markdownAsk';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
/**
|
||||
* Serve an AI answer as markdown for a page, steered by the end goal ("goal")
|
||||
* the calling agent provided via the `?goal=` search parameter.
|
||||
*
|
||||
* The goal is encoded as a path segment (rather than read from the query) because
|
||||
* this route is statically rendered, mirroring how the question itself is handled.
|
||||
*/
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<RouteLayoutParams & { question: string; goal: string }> }
|
||||
) {
|
||||
const { question: encodedQuestion, goal: encodedGoal } = await params;
|
||||
const { context } = await getStaticSiteContext(await params);
|
||||
const question = decodeURIComponent(encodedQuestion);
|
||||
const goal = decodeURIComponent(encodedGoal);
|
||||
|
||||
return serveAskMarkdown(context, question, { goal });
|
||||
}
|
||||
@@ -816,11 +816,18 @@ function encodePathInSiteContent(
|
||||
acceptsMarkdown(request);
|
||||
if (pathname.match(MARKDOWN_PATH_REGEX) || shouldServeMarkdown) {
|
||||
const pagePathWithoutMD = pathname.replace(MARKDOWN_PATH_REGEX, '');
|
||||
const ask = new URL(request.url).searchParams.get('ask');
|
||||
const searchParams = new URL(request.url).searchParams;
|
||||
const ask = searchParams.get('ask');
|
||||
// Optional end goal the calling agent is trying to accomplish, used to steer the answer.
|
||||
// It is encoded as a second path segment (the route is statically rendered, so it can't
|
||||
// read query params at runtime — the question is path-encoded for the same reason).
|
||||
const goal = searchParams.get('goal');
|
||||
return {
|
||||
pathname:
|
||||
typeof ask === 'string'
|
||||
? `~gitbook/markdown-ask/${encodeURIComponent(ask)}`
|
||||
? `~gitbook/markdown-ask/${encodeURIComponent(ask)}${
|
||||
typeof goal === 'string' ? `/${encodeURIComponent(goal)}` : ''
|
||||
}`
|
||||
: `~gitbook/markdown/${encodePagePath(pagePathWithoutMD)}`,
|
||||
routeType: 'static',
|
||||
// TODO: track pageId / spaceId when possible
|
||||
|
||||
@@ -8,12 +8,28 @@ import { filterOutNullable } from '@/lib/typescript';
|
||||
import { serveMarkdown } from '@/routes/markdownPage';
|
||||
import type { SearchAIAnswer, SearchAIAnswerSource } from '@gitbook/api';
|
||||
|
||||
/**
|
||||
* Options to steer the AI answer served as markdown.
|
||||
*/
|
||||
export interface ServeAskMarkdownOptions {
|
||||
/**
|
||||
* The end goal the calling agent is trying to accomplish on behalf of the user,
|
||||
* passed via the `?goal=` search parameter. Used by the backend to steer the answer.
|
||||
*/
|
||||
goal?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve an AI answer as markdown for a page.
|
||||
*/
|
||||
export async function serveAskMarkdown(context: GitBookSiteContext, rawQuestion: string) {
|
||||
export async function serveAskMarkdown(
|
||||
context: GitBookSiteContext,
|
||||
rawQuestion: string,
|
||||
options: ServeAskMarkdownOptions = {}
|
||||
) {
|
||||
return serveMarkdown(async () => {
|
||||
const question = rawQuestion.trim();
|
||||
const goal = options.goal?.trim() || undefined;
|
||||
|
||||
if (
|
||||
!question ||
|
||||
@@ -32,6 +48,7 @@ export async function serveAskMarkdown(context: GitBookSiteContext, rawQuestion:
|
||||
question,
|
||||
context: {
|
||||
siteSpaceId: context.siteSpace.id,
|
||||
goal,
|
||||
},
|
||||
scope: {
|
||||
mode: 'default',
|
||||
@@ -80,10 +97,12 @@ export async function serveAskMarkdown(context: GitBookSiteContext, rawQuestion:
|
||||
result +=
|
||||
'If you need more information, consider asking one of these follow-up questions by performing an HTTP GET request on the URL:\n\n';
|
||||
result += followupQuestions
|
||||
.map(
|
||||
(q) =>
|
||||
`- [${q}](${context.linker.toAbsoluteURL(context.linker.toPathInSite(''))}?ask=${encodeURIComponent(q)})`
|
||||
)
|
||||
.map((q) => {
|
||||
const base = `${context.linker.toAbsoluteURL(context.linker.toPathInSite(''))}?ask=${encodeURIComponent(q)}`;
|
||||
// Carry the goal forward so a multi-step agent keeps its end goal across asks.
|
||||
const url = goal ? `${base}&goal=${encodeURIComponent(goal)}` : base;
|
||||
return `- [${q}](${url})`;
|
||||
})
|
||||
.join('\n');
|
||||
result += '\n\n';
|
||||
}
|
||||
|
||||
@@ -57,17 +57,19 @@ If the exact page cannot be found, you can still retrieve the information using
|
||||
|
||||
### Option 1 — Ask a question (recommended)
|
||||
|
||||
Perform an HTTP GET request on the documentation index with the \`ask\` parameter:
|
||||
Perform an HTTP GET request on the documentation index with the \`ask\` parameter, and the optional \`goal\` parameter:
|
||||
|
||||
\`\`\`
|
||||
GET ${context.linker.toAbsoluteURL(
|
||||
context.linker.toPathForPagePath({
|
||||
path: similarPages[0]?.path ?? 'docs/example',
|
||||
})
|
||||
)}?ask=<question>
|
||||
)}?ask=<question>&goal=<end_goal>
|
||||
\`\`\`
|
||||
|
||||
The question should be specific, self-contained, and written in natural language.
|
||||
\`ask\` is the immediate question: it should be specific, self-contained, and written in natural language.
|
||||
\`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
|
||||
|
||||
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
|
||||
|
||||
### Option 2 — Browse the documentation index
|
||||
@@ -98,24 +100,28 @@ function renderAskFooter(
|
||||
context: GitBookSiteContext,
|
||||
pageLookup: ResolvedPagePath<RevisionPageDocument | RevisionPageGroup>
|
||||
) {
|
||||
const pageUrl = context.linker.toAbsoluteURL(
|
||||
context.linker.toPathForPage({
|
||||
page: pageLookup.page,
|
||||
pages: context.revision.pages,
|
||||
})
|
||||
);
|
||||
|
||||
return `\n\n---\n\n# Agent Instructions
|
||||
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
|
||||
|
||||
## Querying This Documentation
|
||||
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
|
||||
|
||||
Perform an HTTP GET request on the current page URL with the \`ask\` query parameter:
|
||||
Perform an HTTP GET request on the current page URL with the \`ask\` query parameter, and the optional \`goal\` query parameter:
|
||||
|
||||
\`\`\`
|
||||
GET ${context.linker.toAbsoluteURL(
|
||||
context.linker.toPathForPage({
|
||||
page: pageLookup.page,
|
||||
pages: context.revision.pages,
|
||||
})
|
||||
)}?ask=<question>
|
||||
GET ${pageUrl}?ask=<question>&goal=<endgoal>
|
||||
\`\`\`
|
||||
|
||||
The question should be specific, self-contained, and written in natural language.
|
||||
\`ask\` is the immediate question: it should be specific, self-contained, and written in natural language.
|
||||
\`goal\` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
|
||||
|
||||
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
|
||||
|
||||
Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
|
||||
|
||||
Reference in New Issue
Block a user