portal AI 問答:/portal/data/chat 端點+搜尋頁問答面板

端點:requirePortalUser 閘(同 search);in-process 執行 tenant 的 rag_chat
workflow(與 graph_neighbors 同機制),input {question},回 {answer, sources,
graph_facts};workflow 不存在 → 誠實 404「此實例未安裝問答 workflow」。
前端:搜尋框下方問答列,loading→純文字 answer(pre-wrap 保留換行、esc 防
XSS)+sources 列表(mode+page,點 page 帶回搜尋框自行驗證)。設計哲學:
AI 檢索=用戶手動搜尋同一套,AI 只代查再作答,不多開任何權限。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BH7LdhuCdVUHfHXbM7N8r5
This commit is contained in:
Claude
2026-07-17 06:20:27 +00:00
parent 204cd5bff3
commit c01c6c4d02
2 changed files with 98 additions and 0 deletions
+37
View File
@@ -199,6 +199,43 @@ portalDataRouter.get('/portal/data/graph/neighbors/:name', (c) =>
}),
);
// GET /portal/data/chat?question=... — AI 問答(portal-demo-suite)。
// 設計哲學:AI 檢索=用戶手動搜尋同一套——同 search 的 requirePortalUser 閘、同一個租戶資料面,
// 只是把「人下關鍵字」換成「workflow 代查再作答」;前端不因走 AI 多拿任何權限。
// 機制同 graph_neighborsin-process 執行 tenant 的 rag_chat workflowexecuteWebhookGraph
// 絕不 fetch 自己 hostname);workflow 不存在 → 誠實 404,不假裝這實例有問答能力。
portalDataRouter.get('/portal/data/chat', (c) =>
run(c, async () => {
const auth = await requirePortalUser(c);
if (!auth.ok) return auth.res;
const question = c.req.query('question');
if (!question) return c.json({ error: 'question 必填' }, 400);
const wfGraph = await getTenantWorkflowGraph(c.env, 'rag_chat');
if (!wfGraph) return c.json({ error: '此實例未安裝問答 workflow' }, 404);
const result = await executeWebhookGraph(
c.env,
wfGraph,
{ question },
'rag_chat',
portalTenant(c.env),
c.executionCtx,
);
if (!result.success) {
// workflow 執行失敗 → 誠實 502(不把錯誤編成答案)
return c.json({ error: `rag_chat workflow 執行失敗:${result.error ?? '未知錯誤'}` }, 502);
}
// 回 workflow 回應內層 data{answer, sources, graph_facts}(缺欄位誠實回空,不編造)
const inner = unwrapWorkflowData(result.data, 'answer');
return c.json({
answer: typeof inner.answer === 'string' ? inner.answer : '',
sources: Array.isArray(inner.sources) ? inner.sources : [],
graph_facts: inner.graph_facts ?? null,
});
}),
);
// GET /portal/data/workflows — 工作流顯示(D-8):唯讀 list+每條的最近一次執行,**不開 trigger**
//trigger 是 owner/console 的事;回應也不含 webhook_url,不給可打的把手)。
// 可見性:PORTAL_SHOW_WORKFLOWS=admin(預設,role 閘 403/ all / off(整頁不存在 → 404)。