fix(knowledge-fs): constrain research graph planning

This commit is contained in:
Jyong 2026-08-20 02:30:59 -04:00
parent 8096e61133
commit 63cfebbef3
3 changed files with 40 additions and 3 deletions

View File

@ -1,6 +1,6 @@
{
"schemaVersion": 5,
"subtreeTree": "0739e3faf3861c45e78beb693edcd7e9d6550c1f",
"subtreeTree": "cd6c4687eaafa5e178c79aaa52c88869666f36b4",
"openapiSha256": "2cf348c68bbe65dd51bbde9a0a4f91398beeebd79e89e9288c9386b26ae09796",
"capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7",
"capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3",

View File

@ -83,11 +83,23 @@ describe("Research evidence reasoning", () => {
subqueries: ["renewal terms", "termination terms"],
});
expect(generate).toHaveBeenCalledTimes(2);
expect(generate.mock.calls[1]?.[0]).toMatchObject({
structuredOutputSchema: {
properties: {
useGraph: { enum: ["false", "true"], type: "string" },
},
},
});
});
it.each([
{ expected: false, providerValue: "false" },
{
expected: false,
providerValue: "不使用;这是按主题拆分的概览检索,不需要关系图或多跳推理。",
},
{ expected: true, providerValue: "true" },
{ expected: true, providerValue: "需要使用关系图完成多跳推理。" },
])(
"normalizes the structured-output string boolean useGraph=$providerValue",
async ({ expected, providerValue }) => {

View File

@ -403,7 +403,9 @@ function zodJsonSchema(schema: z.ZodTypeAny): Readonly<Record<string, unknown>>
evidenceDimensions: { items: { type: "string" }, maxItems: 6, type: "array" },
intent: { enum: ["comparison", "direct", "multi-hop", "overview"], type: "string" },
subqueries: { items: { type: "string" }, maxItems: 3, type: "array" },
useGraph: { type: "boolean" },
// Dify's structured-output compatibility layer converts JSON Schema booleans to strings.
// Constrain the string values here so the model cannot replace a boolean with prose.
useGraph: { enum: ["false", "true"], type: "string" },
},
required: ["evidenceDimensions", "intent", "subqueries", "useGraph"],
type: "object",
@ -444,7 +446,7 @@ function parseQueryPlan(text: string): z.infer<typeof QueryPlanSchema> {
// Dify's structured-output compatibility layer represents JSON Schema booleans as strings
// for providers that do not accept native boolean fields. Normalize that transport detail
// before applying the strict domain schema, just as the evidence judge does below.
const useGraph = normalizeBooleanValue(record.useGraph);
const useGraph = normalizeUseGraphValue(record.useGraph);
if (useGraph !== undefined) {
value = { ...record, useGraph };
}
@ -542,6 +544,29 @@ function normalizeBooleanValue(value: unknown): boolean | undefined {
return undefined;
}
function normalizeUseGraphValue(value: unknown): boolean | undefined {
const normalizedBoolean = normalizeBooleanValue(value);
if (normalizedBoolean !== undefined || typeof value !== "string") {
return normalizedBoolean;
}
const normalized = value.trim().toLocaleLowerCase();
if (
/^(?:do not use|don't use|not needed|unnecessary|使|使|||)(?:\s||||[.,:;]|$)/u.test(
normalized,
)
) {
return false;
}
if (
/^(?:use|should use|need to use|enabled|使|使|使||)(?:\s||||[.,:;]|$)/u.test(
normalized,
)
) {
return true;
}
return undefined;
}
function modelFailureIsRetryable(error: unknown): boolean {
if (error && typeof error === "object" && "retryable" in error) {
return (error as { readonly retryable?: unknown }).retryable === true;