From 89f6944e65967df225c38442d8435437fc8edc96 Mon Sep 17 00:00:00 2001 From: BBIT-Kai <2911862937@qq.com> Date: Fri, 17 Jul 2026 09:53:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bbit_ai/app/config/cubeReport.py | 2 +- vue2/apps/web-antd/src/api/llm/report-cube.ts | 41 +- .../llm/report/report-cube-chat/index.vue | 162 +- 数据库助手v3.12.yml | 2716 +++++++++++++++++ 4 files changed, 2835 insertions(+), 86 deletions(-) create mode 100644 数据库助手v3.12.yml diff --git a/bbit_ai/app/config/cubeReport.py b/bbit_ai/app/config/cubeReport.py index a8c3046..d8b73c2 100644 --- a/bbit_ai/app/config/cubeReport.py +++ b/bbit_ai/app/config/cubeReport.py @@ -20,7 +20,7 @@ def get_cube_report_settings() -> CubeReportSettings: "DIFY_DATABASE_ASSISTANT_API_BASE", "https://chat.bbitcn.net/v1" ).rstrip("/"), dify_api_key=os.getenv( - "DIFY_DATABASE_ASSISTANT_API_KEY", "app-uibWo8ZEpqHCsWXREPTCBDH6" + "DIFY_DATABASE_ASSISTANT_API_KEY", "app-Ojd3zT2wBsO2Bop575ZiHQOD" ), cube_api_base=os.getenv( "CUBE_API_BASE_URL", "http://10.10.12.101:4001/cubejs-api/v1" diff --git a/vue2/apps/web-antd/src/api/llm/report-cube.ts b/vue2/apps/web-antd/src/api/llm/report-cube.ts index db08764..9751ee7 100644 --- a/vue2/apps/web-antd/src/api/llm/report-cube.ts +++ b/vue2/apps/web-antd/src/api/llm/report-cube.ts @@ -138,6 +138,32 @@ function getFetchConfig() { }; } +async function authorizedFetch(path: string, init: RequestInit) { + const request = () => { + const { authorization, baseURL } = getFetchConfig(); + const headers = new Headers(init.headers); + if (authorization) { + headers.set('Authorization', authorization); + } else { + headers.delete('Authorization'); + } + return fetch(`${baseURL}${path}`, { ...init, headers }); + }; + + let response = await request(); + if (response.status !== 401) return response; + + try { + // 标准请求客户端会使用项目现有的 refresh token 机制更新令牌。 + await getCubeReportScope(); + } catch { + return response; + } + await response.body?.cancel(); + response = await request(); + return response; +} + async function responseError(response: Response) { const text = await response.text(); try { @@ -272,12 +298,10 @@ export async function stopCubeReportTask(taskId: string) { } export async function uploadCubeReportFile(file: File) { - const { authorization, baseURL } = getFetchConfig(); const form = new FormData(); form.append('file', file); - const response = await fetch(`${baseURL}/llm/cube-report/files/upload`, { + const response = await authorizedFetch('/llm/cube-report/files/upload', { method: 'POST', - headers: { Authorization: authorization }, body: form, }); if (!response.ok) throw new Error(await responseError(response)); @@ -291,14 +315,13 @@ export async function downloadCubeReportFile( conversationId: string, file: CubeReportFile, ) { - const { authorization, baseURL } = getFetchConfig(); const params = new URLSearchParams({ conversationId, asAttachment: 'true', }); - const response = await fetch( - `${baseURL}/llm/cube-report/files/${file.id}/preview?${params}`, - { headers: { Authorization: authorization } }, + const response = await authorizedFetch( + `/llm/cube-report/files/${file.id}/preview?${params}`, + {}, ); if (!response.ok) throw new Error(await responseError(response)); const blob = await response.blob(); @@ -314,12 +337,10 @@ export async function streamCubeReportMessage( payload: SendCubeReportMessage, onEvent: (event: CubeReportStreamEvent) => void, ) { - const { authorization, baseURL } = getFetchConfig(); - const response = await fetch(`${baseURL}/llm/cube-report/messages/stream`, { + const response = await authorizedFetch('/llm/cube-report/messages/stream', { method: 'POST', headers: { Accept: 'text/event-stream', - Authorization: authorization, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), diff --git a/vue2/apps/web-antd/src/views/llm/report/report-cube-chat/index.vue b/vue2/apps/web-antd/src/views/llm/report/report-cube-chat/index.vue index 68a3576..2dda306 100644 --- a/vue2/apps/web-antd/src/views/llm/report/report-cube-chat/index.vue +++ b/vue2/apps/web-antd/src/views/llm/report/report-cube-chat/index.vue @@ -381,49 +381,64 @@ async function sendMessage() { let completedConversationId = currentSessionId.value; let completedWithData = false; try { - await api.streamCubeReportMessage( - { - content, - conversationId: currentSessionId.value, - files: sentFiles, - tenantId: currentSession.value?.tenantId || selectedCompany.value?.id, - tenantName: - currentSession.value?.tenantName || selectedCompany.value?.name, - }, - (event: CubeReportStreamEvent) => { - switch (event.type) { - case 'complete': { - completedConversationId = - event.conversationId || completedConversationId; - completedWithData = event.hasData; - assistantMessage.status = 'completed'; - if (event.hasData) activeView.value = 'split'; - break; + try { + await api.streamCubeReportMessage( + { + content, + conversationId: currentSessionId.value, + files: sentFiles, + tenantId: currentSession.value?.tenantId || selectedCompany.value?.id, + tenantName: + currentSession.value?.tenantName || selectedCompany.value?.name, + }, + (event: CubeReportStreamEvent) => { + switch (event.type) { + case 'complete': { + completedConversationId = + event.conversationId || completedConversationId; + completedWithData = event.hasData; + assistantMessage.status = 'completed'; + if (event.hasData) activeView.value = 'split'; + break; + } + case 'message_delta': { + assistantMessage.content += event.content; + void scrollToBottom(); + break; + } + case 'message_replace': { + assistantMessage.content = event.content; + break; + } + case 'status': { + streamStatus.value = event.text; + break; + } + case 'task': { + currentTaskId.value = event.taskId; + break; + } + case 'error': { + throw new Error(event.message); + } + // No default } - case 'message_delta': { - assistantMessage.content += event.content; - void scrollToBottom(); - break; - } - case 'message_replace': { - assistantMessage.content = event.content; - break; - } - case 'status': { - streamStatus.value = event.text; - break; - } - case 'task': { - currentTaskId.value = event.taskId; - break; - } - case 'error': { - throw new Error(event.message); - } - // No default - } - }, - ); + }, + ); + } catch (error) { + const text = + error instanceof Error ? error.message : '请求失败,请稍后重试'; + assistantMessage.status = stopRequested.value ? 'completed' : 'failed'; + assistantMessage.content = stopRequested.value + ? assistantMessage.content || '响应已停止。' + : `请求失败:${text}`; + if (!stopRequested.value) { + inputMessage.value = content; + pendingFiles.value = sentFiles; + message.error(text); + } + return; + } if (!assistantMessage.content) { assistantMessage.content = stopRequested.value @@ -431,30 +446,24 @@ async function sendMessage() { : '查询已完成。'; } if (completedConversationId) { - currentSessionId.value = completedConversationId; - const detail = await api.getCubeReportSession(completedConversationId); - currentSession.value = detail.session; - messages.value = detail.messages; - if (currentReportId.value) { - currentReport.value = await api.getCubeReport(currentReportId.value); + try { + currentSessionId.value = completedConversationId; + const detail = await api.getCubeReportSession(completedConversationId); + currentSession.value = detail.session; + messages.value = detail.messages; + if (currentReportId.value) { + currentReport.value = await api.getCubeReport(currentReportId.value); + } + await reloadNavigation(); + if (completedWithData || detail.session.hasData) { + activeView.value = 'split'; + await loadData(1); + } + } catch (error) { + const text = + error instanceof Error ? error.message : '会话和数据刷新失败'; + message.warning(`回复已完成,但数据刷新失败:${text}`); } - await reloadNavigation(); - if (completedWithData || detail.session.hasData) { - activeView.value = 'split'; - await loadData(1); - } - } - } catch (error) { - const text = - error instanceof Error ? error.message : '请求失败,请稍后重试'; - assistantMessage.status = stopRequested.value ? 'completed' : 'failed'; - assistantMessage.content = stopRequested.value - ? assistantMessage.content || '响应已停止。' - : `请求失败:${text}`; - if (!stopRequested.value) { - inputMessage.value = content; - pendingFiles.value = sentFiles; - message.error(text); } } finally { sending.value = false; @@ -465,6 +474,12 @@ async function sendMessage() { } } +function sendSuggestedQuestion(question: string) { + if (sending.value) return; + inputMessage.value = question; + void sendMessage(); +} + async function stopResponse() { if (!currentTaskId.value || stopping.value) return; stopping.value = true; @@ -874,11 +889,11 @@ onBeforeUnmount(() => { v-for="session in sessions" :key="session.id" type="button" - class="session-item" - :class="{ - 'session-item-active': - !currentReportId && currentSessionId === session.id, - }" + class="session-item" + :class="{ + 'session-item-active': + !currentReportId && currentSessionId === session.id, + }" @click="loadConversation(session.id)" >
@@ -1183,17 +1198,14 @@ onBeforeUnmount(() => { v-for="(question, index) in suggestedQuestions" :key="question" type="button" - @click="inputMessage = question" + :disabled="sending" + @click="sendSuggestedQuestion(question)" > {{ question }} -
diff --git a/数据库助手v3.12.yml b/数据库助手v3.12.yml new file mode 100644 index 0000000..b2a37da --- /dev/null +++ b/数据库助手v3.12.yml @@ -0,0 +1,2716 @@ +app: + description: F8数据库业务助手-接入AI实验室(去除历史问答记录) + icon: face_with_hand_over_mouth + icon_background: '#ECE9FE' + icon_type: emoji + mode: advanced-chat + name: 数据库助手v3.12 + use_icon_as_answer_icon: true +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/tongyi:0.2.4@72bdf0d70a10b1b9f64746ccd1bd792397598f4536872b310e77532af02ad6a6 + version: null +kind: app +version: 0.6.0 +workflow: + conversation_variables: + - description: 最后一次生成 + id: ab2439a1-9b4d-4ede-87b6-a4613f095a78 + name: latest_load + selector: + - conversation + - latest_load + value: '' + value_type: string + - description: 当前蚕季ID + id: d541aee5-cedd-44f4-8434-5b3845e97a1e + name: canjiId + selector: + - conversation + - canjiId + value: 未指定 + value_type: string + - description: 当前蚕季名 + id: f8c87483-4d91-4828-a4f4-ab4dec0634a7 + name: canjiName + selector: + - conversation + - canjiName + value: 未指定 + value_type: string + - description: Cube表join关系 + id: 4d28225d-2249-4390-b9c3-ea4e64184188 + name: cube_joins + selector: + - conversation + - cube_joins + value: "{\n \"bus_co_breeding_assessment\": [\n \"poc_tenants\",\n \"cz_gyh\"\ + ,\n \"nonghu_info\",\n \"batch_canji\"\n ],\n \"bus_farmer_operation\"\ + : [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\n ],\n \"bus_farmer_yield\"\ + : [\n \"poc_tenants\",\n \"nonghu_info\",\n \"batch_canji\"\n ],\n\ + \ \"bus_order_delivery\": [\n \"poc_tenants\",\n \"nonghu_info\",\n \ + \ \"batch_canji\",\n \"cbms_rbac_base_departments\"\n ],\n \"bus_payment_transfer\"\ + : [\n \"poc_tenants\",\n \"nonghu_info\",\n \"batch_canji\",\n \"\ + cbms_rbac_base_departments\"\n ],\n \"bus_purchase_bill\": [\n \"poc_tenants\"\ + ,\n \"nonghu_info\",\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"bus_purchase_grade_mode\": [\n \"poc_tenants\",\n \"sg_chengzhong\"\ + ,\n \"nonghu_info\",\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"bus_quality_inspection\": [\n \"poc_tenants\",\n \"yp_bill_kpi_item_fix\"\ + ,\n \"sg_chengzhong\",\n \"nonghu_info\",\n \"batch_canji\"\n ],\n\ + \ \"bus_subsidy_policy\": [\n \"poc_tenants\",\n \"nonghu_info\",\n \ + \ \"batch_canji\"\n ],\n \"bank_pay_base\": [\n \"poc_tenants\",\n \ + \ \"nonghu_info\",\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"batch_canji\": [\n \"poc_tenants\"\n ],\n \"cbms_rbac_assoc_department_users\"\ + : [\n \"poc_tenants\",\n \"cbms_rbac_base_users\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"cbms_rbac_assoc_user_roles\": [\n \"poc_tenants\",\n \"cbms_rbac_base_users\"\ + ,\n \"cbms_rbac_base_roles\"\n ],\n \"cbms_rbac_base_departments\": [\n\ + \ \"poc_tenants\"\n ],\n \"cbms_rbac_base_roles\": [\n \"poc_tenants\"\ + \n ],\n \"cbms_rbac_base_users\": [\n \"poc_tenants\"\n ],\n \"csb_exchange\"\ + : [\n \"poc_tenants\",\n \"nonghu_info\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"cz_config_item\": [\n \"poc_tenants\",\n \"batch_canji\"\n\ + \ ],\n \"cz_dzbill_list\": [\n \"poc_tenants\",\n \"nonghu_info\",\n\ + \ \"batch_canji\",\n \"cz_gyh\",\n \"cbms_rbac_base_departments\"\n\ + \ ],\n \"cz_gyh\": [\n \"poc_tenants\",\n \"nonghu_info\",\n \"batch_canji\"\ + ,\n \"cbms_rbac_base_departments\"\n ],\n \"dep_xianxiangcun_config\":\ + \ [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\n ],\n \"gj_crm_clientinfo\"\ + : [\n \"poc_tenants\"\n ],\n \"gj_cy_item\": [\n \"poc_tenants\",\n\ + \ \"gj_cy_main\"\n ],\n \"gj_cy_main\": [\n \"poc_tenants\",\n \"\ + batch_canji\"\n ],\n \"gj_jianbie\": [\n \"poc_tenants\"\n ],\n \"gj_kc_fbtl_item\"\ + : [\n \"poc_tenants\",\n \"gj_kc_fbtl_plan\"\n ],\n \"gj_kc_fbtl_plan\"\ + : [\n \"poc_tenants\",\n \"batch_canji\",\n \"gj_jianbie\"\n ],\n\ + \ \"gj_kc_fbtl_release\": [\n \"poc_tenants\",\n \"batch_canji\"\n ],\n\ + \ \"gyh_kaohe_check_result_bbit\": [\n \"poc_tenants\",\n \"batch_canji\"\ + ,\n \"cz_gyh\"\n ],\n \"gyh_kaohe_check_result_item_bbit\": [\n \"poc_tenants\"\ + ,\n \"batch_canji\",\n \"gyh_kaohe_check_result_bbit\",\n \"gyh_kaohe_config_bbit\"\ + \n ],\n \"gyh_kaohe_config_bbit\": [\n \"poc_tenants\",\n \"batch_canji\"\ + \n ],\n \"gyh_kpi_result\": [\n \"poc_tenants\",\n \"batch_canji\",\n\ + \ \"cz_gyh\"\n ],\n \"ljtc_nonghu_pinkunhu\": [],\n \"nonghu_extend_con_config\"\ + : [\n \"poc_tenants\"\n ],\n \"nonghu_info\": [\n \"poc_tenants\",\n\ + \ \"cbms_rbac_base_departments\"\n ],\n \"nonghu_info_temp\": [\n \"\ + poc_tenants\",\n \"nonghu_info\",\n \"cbms_rbac_base_departments\"\n \ + \ ],\n \"nsl_pinkunhu_info\": [],\n \"poc_tenants\": [],\n \"sangyuan_manage\"\ + : [\n \"poc_tenants\",\n \"nonghu_info\"\n ],\n \"sc_worker\": [\n \ + \ \"poc_tenants\"\n ],\n \"sc_worker_xianxiangcun_config\": [\n \"poc_tenants\"\ + ,\n \"sc_worker\"\n ],\n \"sg_box_config_bydep\": [\n \"poc_tenants\"\ + ,\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\n ],\n \"sg_butie_param\"\ + : [\n \"poc_tenants\",\n \"batch_canji\"\n ],\n \"sg_canji_config\"\ + : [\n \"poc_tenants\",\n \"batch_canji\"\n ],\n \"sg_canji_config_price\"\ + : [\n \"poc_tenants\",\n \"batch_canji\",\n \"sg_type_config\",\n \ + \ \"cbms_rbac_base_departments\"\n ],\n \"sg_chengzhong\": [\n \"poc_tenants\"\ + ,\n \"nonghu_info\",\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\ + ,\n \"cz_gyh\"\n ],\n \"sg_chengzhong_extention\": [\n \"poc_tenants\"\ + ,\n \"sg_chengzhong\"\n ],\n \"sg_chengzhong_inputdata\": [\n \"poc_tenants\"\ + ,\n \"sg_chengzhong\"\n ],\n \"sg_chengzhong_item_sum\": [\n \"poc_tenants\"\ + ,\n \"sg_chengzhong\",\n \"sg_type_config\"\n ],\n \"sg_datawall_config\"\ + : [\n \"poc_tenants\",\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"sg_fb_dan\": [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"sg_fb_dan_detail\": [\n \"poc_tenants\",\n \"sg_fb_dan\"\n\ + \ ],\n \"sg_type_config\": [\n \"poc_tenants\"\n ],\n \"sys_update_log\"\ + : [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\n ],\n \"user_xianxiangcun_config\"\ + : [\n \"poc_tenants\",\n \"cbms_rbac_base_users\",\n \"cbms_rbac_base_roles\"\ + \n ],\n \"wms_kufang\": [\n \"poc_tenants\"\n ],\n \"wms_wanglaidanwei\"\ + : [\n \"poc_tenants\"\n ],\n \"wms_wuzi\": [\n \"poc_tenants\",\n \ + \ \"wms_wanglaidanwei\"\n ],\n \"wms_wuzikucun\": [\n \"poc_tenants\"\ + ,\n \"wms_wuzi\",\n \"wms_kufang\",\n \"wms_wanglaidanwei\"\n ],\n\ + \ \"worker\": [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\n\ + \ ],\n \"worker_nonghu\": [\n \"poc_tenants\",\n \"worker\",\n \"\ + nonghu_info\"\n ],\n \"worker_record\": [\n \"poc_tenants\",\n \"worker\"\ + ,\n \"batch_canji\",\n \"cbms_rbac_base_departments\"\n ],\n \"wuzi_archives\"\ + : [\n \"poc_tenants\"\n ],\n \"wuzi_commit\": [\n \"poc_tenants\"\n\ + \ ],\n \"wuzi_commit_item\": [\n \"poc_tenants\",\n \"wuzi_commit\"\ + ,\n \"wuzi_archives\"\n ],\n \"wuzi_config\": [\n \"poc_tenants\"\n\ + \ ],\n \"wuzi_inout_bill\": [\n \"poc_tenants\",\n \"nonghu_info\",\n\ + \ \"batch_canji\",\n \"cbms_rbac_base_departments\"\n ],\n \"wuzi_inout_bill_item\"\ + : [\n \"poc_tenants\",\n \"wuzi_inout_bill\",\n \"wuzi_archives\"\n\ + \ ],\n \"wuzi_input\": [\n \"poc_tenants\",\n \"wuzi_warehouse\",\n\ + \ \"wuzi_supplier\"\n ],\n \"wuzi_input_item\": [\n \"poc_tenants\"\ + ,\n \"wuzi_input\",\n \"wuzi_archives\"\n ],\n \"wuzi_inventory\": [\n\ + \ \"poc_tenants\",\n \"wuzi_archives\",\n \"wuzi_warehouse\"\n ],\n\ + \ \"wuzi_out\": [\n \"poc_tenants\",\n \"wuzi_warehouse\",\n \"nonghu_info\"\ + \n ],\n \"wuzi_out_item\": [\n \"poc_tenants\",\n \"wuzi_out\",\n \ + \ \"wuzi_archives\"\n ],\n \"wuzi_supplier\": [\n \"poc_tenants\"\n ],\n\ + \ \"wuzi_warehouse\": [\n \"poc_tenants\",\n \"cbms_rbac_base_departments\"\ + \n ],\n \"yp_bill_kpi_item\": [\n \"poc_tenants\",\n \"sg_chengzhong\"\ + ,\n \"yp_bill_list\"\n ],\n \"yp_bill_kpi_item_fix\": [\n \"poc_tenants\"\ + ,\n \"sg_chengzhong\"\n ],\n \"yp_bill_list\": [\n \"poc_tenants\",\n\ + \ \"sg_chengzhong\",\n \"sg_type_config\"\n ]\n}\n" + value_type: string + - description: 用户提问 + id: c0ec71d5-7fb5-4279-a8fd-95c94e35a837 + name: user_query + selector: + - conversation + - user_query + value: '' + value_type: string + - description: 业务规则 + id: 94f312c3-0a47-4d37-970c-aaa2bfbae2a7 + name: global_rules + selector: + - conversation + - global_rules + value: '【实体与范围】 + + - 蚕季名称优先按 batch_canji.batchname 查询;需要拆分时使用 bathcyears + batchname,不按自然日期年份替代蚕季年度。 + + - 茧站名称或编码先查询 cbms_rbac_base_departments;收购站、订种站、支付站、建档站按各业务表 depsysid 或 departmentsysid + 区分。 + + - 常见蚕季名称:2020春三批、2019年正秋季、2024年晚春季等,在遇到类似名称时,要将其视为蚕季名称。且在查询时最好将用户所述的蚕季名分开查询,例如用户查询2026春一的情况,则需要分解”2026春一“为2026与春一,并限定"batch_canji".batchname + contain ”春一“ 同时 "batch_canji".batchyear equal ”2026“ + + - 常见租户名称/简称,在遇到此类名称时视为租户:鎏金铜蚕、南丝路集团/南丝路、主干信息、会东、嘉联、蓉桑里、嘉风、雅天丝、奥立丝绸、安泰茧丝绸。 + + + 【默认有效口径】 + + - 农户数量默认统计 nonghu_info.flag=1;只有明确要求全部档案或包含冻结时才使用未过滤 count。 + + - 有效收购:sg_chengzhong.flag=1 且 billstate=2。 + + - 有效订种:cz_dzbill_list.flag=1 且 dzsum>0。 + + - 鎏金铜蚕租户特殊订种口径:当租户为鎏金铜蚕或 tenantid=603143556655353856,统计订种张数、领种数量、养蚕张数时,默认只统计支付成功订种,即 + CZ_DZBILL_LIST.dzpaystate=1;使用业务 Cube 时优先选择 bus_order_delivery.paid_order_quantity_sheet,而不是 + bus_order_delivery.order_quantity_sheet。 + + - 有效支付:bank_pay_base.flag>0;支付成功默认 paystate in (3,6,7)。 + + + 【计算与模型选择】 + + - 选择能完整回答问题的最小 Cube;单纯农户档案、数量、年龄使用 nonghu_info,跨蚕季累计经营使用 bus_farmer_operation。 + + - 汇总均价、比率、单产必须使用汇总分子/汇总分母,不平均行级比率。 + + - 默认农户单产=有效售茧净重/全部有效订种张数;排行/Top 类问题默认按目标指标 DESC,大数在前;小于/低于阈值只作为过滤条件,不改变排序方向,只有明确“最低/风险优先”才 + ASC。 + + - 收购新模式使用 sg_chengzhong.sgqudaotype<>0;订种新模式使用 cz_dzbill_list.dzplat<>4,两者不得混用。 + + - 农户年龄使用 nonghu_info.age_years;该维度从身份证出生日期计算周岁,无效身份证返回空值。 + + - 补贴金额均为测算,不代表实际发放;仪评加权指标以最终重量为权重。 + + ' + value_type: string + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .XLSX + - .XLS + allowed_file_types: + - custom + allowed_file_upload_methods: + - local_file + enabled: true + fileUploadConfig: + attachment_image_file_size_limit: 2 + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + file_upload_limit: 20 + image_file_batch_limit: 10 + image_file_size_limit: 10 + single_chunk_attachment_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 1 + opening_statement: 您好,我是F8数据助手,请问有什么可以帮到您的呢?🫡 + retriever_resource: + enabled: false + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: + - 有多少农户 + - 查询农户张松期的手机号码 + - '池河镇2026春一的单产多少 ' + - 查询军民村的李志富的订种记录 + - 统计当前蚕季的收购情况 + - 统计当前蚕季各茧别的收购情况 + - 统计石泉县单产中小于70公斤/张的前十农户排行榜 + - 按农户乡镇统计新模式中蚕季为”2026春一“的蚕茧收购情况 + - 统计农户年龄为小于16岁,大于70岁的农户。 农户名、身份证号 、手机号以及最近一次卖茧时间。 + suggested_questions_after_answer: + enabled: false + model: + completion_params: + echo: false + frequency_penalty: 0 + max_tokens: 0 + presence_penalty: 0 + stop: [] + temperature: 0.7 + top_p: 0 + mode: chat + name: qwen3.5-flash-2026-02-23 + provider: langgenius/tongyi/tongyi + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: true + isInLoop: false + iteration_id: '1781599320305' + sourceType: iteration-start + targetType: knowledge-retrieval + id: 1781599320305start-source-1781599540907-target + selected: false + source: 1781599320305start + sourceHandle: source + target: '1781599540907' + targetHandle: target + type: custom + zIndex: 1002 + - data: + isInLoop: false + sourceType: iteration + targetType: code + id: 1781599320305-source-1781594054454-target + selected: false + source: '1781599320305' + sourceHandle: source + target: '1781594054454' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: true + isInLoop: false + iteration_id: '17817582339680' + sourceType: iteration-start + targetType: knowledge-retrieval + id: 1781758233968start-source-1781758233968017817582339690-target + selected: false + source: 1781758233968start + sourceHandle: source + target: '1781758233968017817582339690' + targetHandle: target + type: custom + zIndex: 1002 + - data: + isInLoop: false + sourceType: iteration + targetType: code + id: 17817582339680-source-17817583423940-target + selected: false + source: '17817582339680' + sourceHandle: source + target: '17817583423940' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: document-extractor + targetType: llm + id: 1782889122238-source-1782889576269-target + selected: false + source: '1782889122238' + sourceHandle: source + target: '1782889576269' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: assigner + id: 1782889576269-source-1782889756496-target + selected: false + source: '1782889576269' + sourceHandle: source + target: '1782889756496' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: document-extractor + id: 1782889131357-true-1782889122238-target + selected: false + source: '1782889131357' + sourceHandle: 'true' + target: '1782889122238' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: assigner + id: 1782889131357-false-17828899879200-target + selected: false + source: '1782889131357' + sourceHandle: 'false' + target: '17828899879200' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: code + id: 1781581099032-source-1783053752709-target + selected: false + source: '1781581099032' + sourceHandle: source + target: '1783053752709' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: code + id: 1783053752709-source-1782400000012-target + selected: false + source: '1783053752709' + sourceHandle: source + target: '1782400000012' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: http-request + targetType: code + id: 17830731532390-source-17830731571350-target + selected: false + source: '17830731532390' + sourceHandle: source + target: '17830731571350' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: assigner + id: 17830731571350-source-17830731406880-target + selected: false + source: '17830731571350' + sourceHandle: source + target: '17830731406880' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: if-else + id: 1784083007146-source-1783073068957-target + selected: false + source: '1784083007146' + sourceHandle: source + target: '1783073068957' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: start + targetType: tool + id: 1780903664367-source-1781232881963-target + selected: false + source: '1780903664367' + sourceHandle: source + target: '1781232881963' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: http-request + id: 1783073068957-true-17830731532390-target + selected: false + source: '1783073068957' + sourceHandle: 'true' + target: '17830731532390' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: assigner + targetType: llm + id: 1782889756496-source-1782351696945-target + selected: false + source: '1782889756496' + sourceHandle: source + target: '1782351696945' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: assigner + targetType: llm + id: 17828899879200-source-1782351696945-target + selected: false + source: '17828899879200' + sourceHandle: source + target: '1782351696945' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: answer + id: 1782400000011-source-1784098833176-target + selected: false + source: '1782400000011' + sourceHandle: source + target: '1784098833176' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: tool + targetType: code + id: 1781232881963-source-1784083007146-target + selected: false + source: '1781232881963' + sourceHandle: source + target: '1784083007146' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: tool + targetType: http-request + id: 1781232881963-source-1781581167522-target + selected: false + source: '1781232881963' + sourceHandle: source + target: '1781581167522' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: tool + targetType: if-else + id: 1781232881963-source-1782889131357-target + selected: false + source: '1781232881963' + sourceHandle: source + target: '1782889131357' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: iteration + id: 1782351696945-source-17817582339680-target + selected: false + source: '1782351696945' + sourceHandle: source + target: '17817582339680' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: iteration + id: 1782351696945-source-1781599320305-target + selected: false + source: '1782351696945' + sourceHandle: source + target: '1781599320305' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: llm + id: 1781594054454-source-1781581099032-target + selected: false + source: '1781594054454' + sourceHandle: source + target: '1781581099032' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: llm + id: 17817583423940-source-1781581099032-target + selected: false + source: '17817583423940' + sourceHandle: source + target: '1781581099032' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: http-request + targetType: code + id: 1781581167522-source-1782400000012-target + selected: false + source: '1781581167522' + sourceHandle: source + target: '1782400000012' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: if-else + id: 1782400000012-source-1784188747804-target + selected: false + source: '1782400000012' + sourceHandle: source + target: '1784188747804' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: llm + id: 1784188747804-true-1782400000011-target + selected: false + source: '1784188747804' + sourceHandle: 'true' + target: '1782400000011' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: assigner + targetType: if-else + id: 17830731406880-source-1784188747804-target + selected: false + source: '17830731406880' + sourceHandle: source + target: '1784188747804' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: llm + id: 1784188747804-false-17841887361130-target + selected: false + source: '1784188747804' + sourceHandle: 'false' + target: '17841887361130' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: code + id: 1782400000011-source-1784188957627-target + selected: false + source: '1782400000011' + sourceHandle: source + target: '1784188957627' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: code + targetType: assigner + id: 1784188957627-source-1784183263188-target + selected: false + source: '1784188957627' + sourceHandle: source + target: '1784183263188' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: code + id: 17841887361130-source-1784188957627-target + selected: false + source: '17841887361130' + sourceHandle: source + target: '1784188957627' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: answer + id: 17841887361130-source-1784098833176-target + source: '17841887361130' + sourceHandle: source + target: '1784098833176' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + selected: false + title: 用户提问 + type: start + variables: + - default: '603143556655353856' + hint: '' + label: 租户ID + max_length: 20 + options: [] + placeholder: '' + required: true + type: text-input + variable: tenant_id + - default: 鎏金铜蚕(石泉)茧丝绸有限公司 + hint: '' + label: 租户名称 + max_length: 20 + options: [] + placeholder: '' + required: true + type: text-input + variable: tenant_name + height: 135 + id: '1780903664367' + position: + x: -11347.926845025962 + y: -296.5887295533155 + positionAbsolute: + x: -11347.926845025962 + y: -296.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + response_format: json_object + mode: chat + name: qwen3.7-plus + provider: langgenius/tongyi/tongyi + prompt_config: + jinja2_variables: [] + prompt_template: + - edition_type: basic + id: 99e91b51-0052-4e93-95b5-62e502798f91 + jinja2_text: '' + role: system + text: '你是 Cube load 查询体生成器。 + + + # 任务 + + 根据参考信息,生成可用于Cube `/cubejs-api/v1/load`接口的请求体的请求,以便获取用户想要的数据。 + + + # 规则: + + - 当查询农户名单、明细、记录或排行榜时,须额外加入当前 Cube 的农户唯一ID维度,如 nhsysid;不要只用农户姓名作为农户粒度。 + + - 如果选择的 measure 已经包含用户要求的业务状态或口径,不要再额外添加同义 filter。 + + + + # 参考知识: + + ## 当前时间:{{#1781232881963.text#}} + + + ## 当前蚕季ID:{{#conversation.canjiId#}} + + (当用户提问当前蚕季时,筛选数据时需要明确筛选蚕季ID) + + + ## 业务规则 + + {{#conversation.global_rules#}} + + + ## Cube数据 + + {{#1782400000012.result#}}' + - edition_type: basic + id: c5f6e041-13bd-48f8-b879-f4c2fbd2f183 + role: user + text: '{{#conversation.user_query#}} + + + 当前租户ID:{{#1780903664367.tenant_id#}} + + **(如果Cube数据中包含类似tenant_id或租户隔离的字段,必须必须必须加此ID筛选)** + + ' + reasoning_format: separated + selected: false + structured_output: + schema: + additionalProperties: false + properties: + limit_source: + description: 如果query中有限制limit的话,此处说明限制来源是系统要求还是用户要求 + enum: + - system + - user + type: string + message: + description: 如果status为false或者无法根据知识完全覆盖用户所需要的字段,则此处说明原因以及缺失的字段,否则填写:“成功生成” + type: string + query: + additionalProperties: false + description: Cube的/v1/load请求 + properties: + dimensions: + description: 维度,负责“按什么看、返回哪些分类/明细字段” 来源是 Cube 元数据里的 dimensions[].name。不可放入measures的name + items: + type: string + type: array + filters: + description: 过滤条件 + items: + additionalProperties: false + properties: + and: + description: “且”条件时使用 + items: + additionalProperties: false + properties: + member: + description: 过滤对象 + type: string + operator: + description: 操作符,用户表达“蚕季为/名称为/包含/叫/查某某名称”,优先使用 operator="contains",不要使用 + equals,除非用户明确说“精确等于/完全等于/ID + enum: + - equals + - notEquals + - contains + - startsWith + - gt + - lt + - set + - notSet + - inDateRange + type: string + values: + description: 过滤值 + items: + type: string + type: array + required: [] + type: object + type: array + or: + description: “或”条件时使用, + items: + additionalProperties: false + properties: + member: + description: 过滤对象 + type: string + operator: + description: 操作符,用户表达“蚕季为/名称为/包含/叫/查某某名称”,优先使用 operator="contains",不要使用 + equals,除非用户明确说“精确等于/完全等于/ID + enum: + - equals + - notEquals + - contains + - startsWith + - gt + - lt + - set + - notSet + - inDateRange + type: string + values: + description: 过滤值 + items: + type: string + type: array + required: [] + type: object + type: array + required: [] + type: object + type: array + limit: + description: 限制返回数量,最大5 + type: number + measures: + description: 指标,负责“算什么数” 来源是 Cube 元数据里的 measures[].name。不可放入dimensions的name + items: + type: string + type: array + offset: + description: 跳过前面多少行,可以结合limit实现分页 + type: number + order: + description: 排序,必须是二维数组,例如 [["bus_farmer_yield.yield_kg_per_sheet", + "asc"]] + items: + maxItems: 2 + minItems: 2 + prefixItems: + - description: 排序字段,例如 bus_farmer_yield.yield_kg_per_sheet + type: string + - description: 排序方向 + enum: + - asc + - desc + type: string + type: array + type: array + segments: + description: 预先在 Cube 模型中定义好的命名过滤条件 + items: + type: string + type: array + timeDimensions: + description: 时间维度过滤 + items: + additionalProperties: false + properties: + dateRange: + items: + type: string + type: array + dimension: + description: 时间维度字段,例如 orders.createdAt + type: string + granularity: + description: 如果不写 granularity,只过滤时间,不按时间分组 + enum: + - year + - quarter + - month + - week + - day + - hour + - minute + - second + type: string + required: [] + type: object + type: array + required: + - limit + - dimensions + type: object + status: + description: 是否可以生成query。缺失字段时仍然返回true + enum: + - 'true' + - 'false' + type: string + required: + - query + - message + - status + - limit_source + type: object + structured_output_enabled: true + title: 生成数据请求 + type: llm + vision: + enabled: false + height: 88 + id: '1782400000011' + position: + x: -7843.845548425899 + y: -275.5887295533155 + positionAbsolute: + x: -7843.845548425899 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + desc: '' + is_team_authorization: true + paramSchemas: + - auto_generate: null + default: '%Y-%m-%d %H:%M:%S' + form: form + human_description: + en_US: Time format in strftime standard. + ja_JP: Time format in strftime standard. + pt_BR: Time format in strftime standard. + zh_Hans: strftime 标准的时间格式。 + label: + en_US: Format + ja_JP: Format + pt_BR: Format + zh_Hans: 格式 + llm_description: null + max: null + min: null + name: format + options: [] + placeholder: null + precision: null + required: false + scope: null + template: null + type: string + - auto_generate: null + default: UTC + form: form + human_description: + en_US: Timezone + ja_JP: Timezone + pt_BR: Timezone + zh_Hans: 时区 + label: + en_US: Timezone + ja_JP: Timezone + pt_BR: Timezone + zh_Hans: 时区 + llm_description: null + max: null + min: null + name: timezone + options: + - icon: null + label: + en_US: UTC + ja_JP: UTC + pt_BR: UTC + zh_Hans: UTC + value: UTC + - icon: null + label: + en_US: America/New_York + ja_JP: America/New_York + pt_BR: America/New_York + zh_Hans: 美洲/纽约 + value: America/New_York + - icon: null + label: + en_US: America/Los_Angeles + ja_JP: America/Los_Angeles + pt_BR: America/Los_Angeles + zh_Hans: 美洲/洛杉矶 + value: America/Los_Angeles + - icon: null + label: + en_US: America/Chicago + ja_JP: America/Chicago + pt_BR: America/Chicago + zh_Hans: 美洲/芝加哥 + value: America/Chicago + - icon: null + label: + en_US: America/Sao_Paulo + ja_JP: America/Sao_Paulo + pt_BR: América/São Paulo + zh_Hans: 美洲/圣保罗 + value: America/Sao_Paulo + - icon: null + label: + en_US: Asia/Shanghai + ja_JP: Asia/Shanghai + pt_BR: Asia/Shanghai + zh_Hans: 亚洲/上海 + value: Asia/Shanghai + - icon: null + label: + en_US: Asia/Ho_Chi_Minh + ja_JP: Asia/Ho_Chi_Minh + pt_BR: Ásia/Ho Chi Minh + zh_Hans: 亚洲/胡志明市 + value: Asia/Ho_Chi_Minh + - icon: null + label: + en_US: Asia/Tokyo + ja_JP: Asia/Tokyo + pt_BR: Asia/Tokyo + zh_Hans: 亚洲/东京 + value: Asia/Tokyo + - icon: null + label: + en_US: Asia/Dubai + ja_JP: Asia/Dubai + pt_BR: Asia/Dubai + zh_Hans: 亚洲/迪拜 + value: Asia/Dubai + - icon: null + label: + en_US: Asia/Kolkata + ja_JP: Asia/Kolkata + pt_BR: Asia/Kolkata + zh_Hans: 亚洲/加尔各答 + value: Asia/Kolkata + - icon: null + label: + en_US: Asia/Seoul + ja_JP: Asia/Seoul + pt_BR: Asia/Seoul + zh_Hans: 亚洲/首尔 + value: Asia/Seoul + - icon: null + label: + en_US: Asia/Singapore + ja_JP: Asia/Singapore + pt_BR: Asia/Singapore + zh_Hans: 亚洲/新加坡 + value: Asia/Singapore + - icon: null + label: + en_US: Europe/London + ja_JP: Europe/London + pt_BR: Europe/London + zh_Hans: 欧洲/伦敦 + value: Europe/London + - icon: null + label: + en_US: Europe/Berlin + ja_JP: Europe/Berlin + pt_BR: Europe/Berlin + zh_Hans: 欧洲/柏林 + value: Europe/Berlin + - icon: null + label: + en_US: Europe/Moscow + ja_JP: Europe/Moscow + pt_BR: Europe/Moscow + zh_Hans: 欧洲/莫斯科 + value: Europe/Moscow + - icon: null + label: + en_US: Australia/Sydney + ja_JP: Australia/Sydney + pt_BR: Australia/Sydney + zh_Hans: 澳大利亚/悉尼 + value: Australia/Sydney + - icon: null + label: + en_US: Pacific/Auckland + ja_JP: Pacific/Auckland + pt_BR: Pacific/Auckland + zh_Hans: 太平洋/奥克兰 + value: Pacific/Auckland + - icon: null + label: + en_US: Africa/Cairo + ja_JP: Africa/Cairo + pt_BR: Africa/Cairo + zh_Hans: 非洲/开罗 + value: Africa/Cairo + placeholder: null + precision: null + required: false + scope: null + template: null + type: select + params: + format: '' + timezone: '' + plugin_id: null + plugin_unique_identifier: '' + provider_icon: http://10.10.12.101:8095/console/api/workspaces/current/tool-provider/builtin/time/icon + provider_id: time + provider_name: time + provider_type: builtin + selected: false + title: 获取当前时间 + tool_configurations: + format: + type: mixed + value: '%Y-%m-%d %H:%M:%S' + timezone: + type: constant + value: Asia/Shanghai + tool_description: 一个用于获取当前时间的工具。 + tool_label: 获取当前时间 + tool_name: current_time + tool_node_version: '2' + tool_parameters: {} + type: tool + height: 114 + id: '1781232881963' + position: + x: -11092.366793451323 + y: -275.5887295533155 + positionAbsolute: + x: -11092.366793451323 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + response_format: json_object + temperature: 0 + top_p: 0.1 + mode: chat + name: qwen3.5-plus + provider: langgenius/tongyi/tongyi + prompt_config: + jinja2_variables: [] + prompt_template: + - edition_type: basic + id: 47a93af5-5c9f-463d-bc6d-009ab0a498c7 + role: system + text: '你是 Cube 核心业务口径识别器。 + + + 根据用户问题和知识库召回结果,识别本次查询最核心的业务 Cube,用于后续扩展可 join 的 Cube Meta。 + + + # 任务 + + 只输出“核心 Cube”,也就是决定本次查询业务口径、数据粒度和主事实来源的 Cube。 + + + # 生成规则 + + - 输出的 name 必须来自业务 Cube CSV 的“业务Cube名”列,或基础 Cube CSV 的“主表”列。 + + - 优先输出 1 个核心 Cube; + + - 不要输出仅用于补充字段的 join 表,例如租户表、农户档案、蚕季表、部门表、用户表等。 + + - 用户问题包含“手机/电话/身份证”时,不要因此输出 nonghu_info;这些是补充字段,由后续可 join 扩展自动判断。 + + + # 业务提示 + + - 看到“订种、领种、养蚕张数、订种模式、订种清单、农户订种明细”,核心 Cube 必须优先输出 bus_order_delivery。 + + - 如果问题是普通农户档案、农户名单、农户手机号、身份证号,且不涉及订种/收购/补助等业务事实,核心 Cube 可输出 nonghu_info。 + + - 如果问题是农户经营画像、累计订种、累计售茧、经营蚕季数,核心 Cube 优先输出 bus_farmer_operation。 + + - 如果问题是售茧、收购、交售、收购金额,核心 Cube 优先输出 bus_purchase_bill。 + + - 如果问题是补助、补贴政策、补贴金额,核心 Cube 优先输出 bus_subsidy_policy。 + + - 如果用户问题包含以下任何一种茧别:茧别、正茧、普茧、普通正茧、方格正茧、口评茧、双宫、双宫茧、黄斑、黄斑茧、血茧、下茧、下足茧、上茧、品类、分类净重、分类金额问题,同时涉及收购、售茧、鲜茧、茧款、重量、金额、均价、收购情况等语义,使用bus_purchase_grade_mode。 + + + # 业务规则 + + {{#conversation.global_rules#}} + + + # 业务逻辑知识库召回结果(CSV结构) + + 业务词或别名,业务含义,业务Cube名,核心成员,可用维度,数据粒度,默认规则,不适用情况,应用场景 + + {{#17817583423940.csv#}} + + + # 基础表知识库召回结果(CSV结构) + + 业务名词或别名,含义解释,主表,涉及的其他表,涉及字段,不适用的情况 + + {{#1781594054454.csv#}}' + - id: 3f574f4c-73ff-4de5-8a00-724830b23ed7 + role: user + text: '{{#conversation.user_query#}}' + reasoning_format: separated + selected: false + structured_output: + schema: + additionalProperties: false + properties: + core_cube: + description: '' + type: string + required: + - core_cube + type: object + structured_output_enabled: true + title: 获取重点表 + type: llm + vision: + enabled: false + height: 88 + id: '1781581099032' + position: + x: -8868.424816665049 + y: -109.81201181221708 + positionAbsolute: + x: -8868.424816665049 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + authorization: + config: null + type: no-auth + body: + data: [] + type: none + headers: '' + method: get + params: '' + retry_config: + max_retries: 3 + retry_enabled: false + retry_interval: 100 + selected: false + ssl_verify: false + timeout: + max_connect_timeout: 0 + max_read_timeout: 0 + max_write_timeout: 0 + title: 获取最新表结构 + type: http-request + url: http://10.10.12.101:4001/cubejs-api/v1/meta + variables: [] + height: 108 + id: '1781581167522' + position: + x: -10836.84627996062 + y: -466.4249608991162 + positionAbsolute: + x: -10836.84627996062 + y: -466.4249608991162 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main({ schema_json, cube_names }) {\n let schema = schema_json;\n\ + \n if (typeof schema_json === \"string\") {\n try {\n schema =\ + \ JSON.parse(schema_json);\n } catch {\n return { result: \"[]\"\ + \ };\n }\n }\n\n const cubes =\n schema && Array.isArray(schema.cubes)\n\ + \ ? schema.cubes\n : [];\n\n const nameSet = parseCubeNameSet(cube_names);\n\ + \n if (nameSet.size === 0) {\n return { result: \"[]\" };\n }\n\n \ + \ // 筛选和转换合并为一次遍历,避免 selectedCubes 中间数组\n const slimCubes = [];\n\n for\ + \ (let i = 0; i < cubes.length; i++) {\n const cube = cubes[i];\n\n \ + \ if (nameSet.has(normalizeName(cube.name))) {\n slimCubes.push(toLLMSchemaCube(cube));\n\ + \ }\n }\n\n return {\n result: JSON.stringify(slimCubes)\n };\n\ + }\n\n/**\n * 直接生成标准化后的 Set,避免:\n * normalizeList → map → filter → new Set\n\ + \ */\nfunction parseCubeNameSet(value, result) {\n const nameSet = result\ + \ || new Set();\n\n if (!value) {\n return nameSet;\n }\n\n if (Array.isArray(value))\ + \ {\n addNamesToSet(value, nameSet);\n return nameSet;\n }\n\n if\ + \ (typeof value === \"string\") {\n const text = value.trim();\n\n \ + \ if (!text) {\n return nameSet;\n }\n\n /*\n * 只有看起来可能是合法\ + \ JSON 时才调用 JSON.parse。\n * 普通的 \"orders,users\" 不再通过异常进入降级路径。\n \ + \ */\n if (looksLikeJson(text)) {\n try {\n const parsed\ + \ = JSON.parse(text);\n\n if (Array.isArray(parsed)) {\n \ + \ addNamesToSet(parsed, nameSet);\n return nameSet;\n }\n\ + \n if (typeof parsed === \"string\") {\n addNameToSet(parsed,\ + \ nameSet);\n return nameSet;\n }\n\n if (parsed\ + \ && typeof parsed === \"object\") {\n parseCubeNamesObject(parsed,\ + \ nameSet);\n }\n\n // JSON 解析成功,但类型不受支持时,与原逻辑一样返回空结果\n \ + \ return nameSet;\n } catch {\n // 解析失败时按逗号字符串处理\n \ + \ }\n }\n\n addCommaSeparatedNames(text, nameSet);\n return nameSet;\n\ + \ }\n\n if (typeof value === \"object\") {\n parseCubeNamesObject(value,\ + \ nameSet);\n }\n\n return nameSet;\n}\n\nfunction parseCubeNamesObject(value,\ + \ nameSet) {\n // 保持原来的判断优先级\n if (Array.isArray(value.cubes)) {\n \ + \ addNamesToSet(value.cubes, nameSet);\n return;\n }\n\n if (Array.isArray(value.cube_names))\ + \ {\n addNamesToSet(value.cube_names, nameSet);\n return;\n }\n\n\ + \ if (\n typeof value.result === \"string\" ||\n Array.isArray(value.result)\n\ + \ ) {\n parseCubeNameSet(value.result, nameSet);\n }\n}\n\nfunction\ + \ addCommaSeparatedNames(text, nameSet) {\n let start = 0;\n\n for (let\ + \ i = 0; i <= text.length; i++) {\n if (i === text.length || text.charCodeAt(i)\ + \ === 44) {\n addNameToSet(text.slice(start, i), nameSet);\n start\ + \ = i + 1;\n }\n }\n}\n\nfunction addNamesToSet(values, nameSet) {\n\ + \ for (let i = 0; i < values.length; i++) {\n addNameToSet(values[i],\ + \ nameSet);\n }\n}\n\nfunction addNameToSet(value, nameSet) {\n if (typeof\ + \ value !== \"string\") {\n return;\n }\n\n const text = value.trim();\n\ + \n if (!text) {\n return;\n }\n\n const key = normalizeName(text);\n\ + \n if (key) {\n nameSet.add(key);\n }\n}\n\nfunction looksLikeJson(text)\ + \ {\n const first = text.charCodeAt(0);\n\n // { [ \" -\n if (\n first\ + \ === 123 ||\n first === 91 ||\n first === 34 ||\n first === 45\n\ + \ ) {\n return true;\n }\n\n // 0-9\n if (first >= 48 && first <=\ + \ 57) {\n return true;\n }\n\n // JSON 原始值\n return (\n text ===\ + \ \"true\" ||\n text === \"false\" ||\n text === \"null\"\n );\n\ + }\n\nfunction normalizeName(name) {\n if (typeof name === \"string\") {\n\ + \ return name.trim().toLowerCase();\n }\n\n return String(name || \"\ + \")\n .trim()\n .toLowerCase();\n}\n\nfunction toLLMSchemaCube(cube)\ + \ {\n const result = {};\n\n assignNonEmpty(result, \"name\", cube.name);\n\ + \ assignNonEmpty(result, \"title\", cube.title);\n assignNonEmpty(result,\ + \ \"description\", cube.description);\n\n if (Array.isArray(cube.joins)\ + \ && cube.joins.length > 0) {\n result.joins = mapArray(cube.joins, toLLMJoin);\n\ + \ }\n\n if (Array.isArray(cube.measures) && cube.measures.length > 0)\ + \ {\n result.measures = mapArray(cube.measures, toLLMMeasure);\n }\n\ + \n if (\n Array.isArray(cube.dimensions) &&\n cube.dimensions.length\ + \ > 0\n ) {\n result.dimensions = mapArray(\n cube.dimensions,\n\ + \ toLLMDimension\n );\n }\n\n return result;\n}\n\nfunction toLLMJoin(join)\ + \ {\n const result = {};\n\n assignNonEmpty(result, \"name\", join.name);\n\ + \ assignNonEmpty(result, \"relationship\", join.relationship);\n\n return\ + \ result;\n}\n\nfunction toLLMMeasure(measure) {\n const result = {};\n\ + \n assignNonEmpty(result, \"name\", measure.name);\n assignNonEmpty(result,\ + \ \"title\", measure.title);\n assignNonEmpty(result, \"description\",\ + \ measure.description);\n assignNonEmpty(result, \"type\", measure.type);\n\ + \ assignNonEmpty(result, \"aggType\", measure.aggType);\n assignNonEmpty(result,\ + \ \"format\", measure.format);\n\n return result;\n}\n\nfunction toLLMDimension(dimension)\ + \ {\n const result = {};\n\n assignNonEmpty(result, \"name\", dimension.name);\n\ + \ assignNonEmpty(result, \"title\", dimension.title);\n assignNonEmpty(result,\ + \ \"description\", dimension.description);\n assignNonEmpty(result, \"\ + type\", dimension.type);\n\n return result;\n}\n\n/**\n * 替代 removeEmpty\ + \ + Object.entries。\n * 保留原 removeEmpty 的判断语义:\n * - 跳过 undefined/null\n\ + \ * - 跳过空白字符串\n * - 跳过空数组\n * - 保留 0、false、NaN、空对象等值\n */\nfunction assignNonEmpty(target,\ + \ key, value) {\n if (value === undefined || value === null) {\n return;\n\ + \ }\n\n if (typeof value === \"string\") {\n if (value.trim() === \"\ + \") {\n return;\n }\n } else if (Array.isArray(value) && value.length\ + \ === 0) {\n return;\n }\n\n target[key] = value;\n}\n\n/**\n * 与 Array.prototype.map\ + \ 一样保留稀疏数组结构。\n */\nfunction mapArray(source, mapper) {\n const result\ + \ = new Array(source.length);\n\n for (let i = 0; i < source.length; i++)\ + \ {\n if (i in source) {\n result[i] = mapper(source[i]);\n }\n\ + \ }\n\n return result;\n}" + code_language: javascript + outputs: + result: + children: null + type: string + selected: false + title: 筛选所需表 + type: code + variables: + - value_selector: + - '1781581167522' + - body + value_type: string + variable: schema_json + - value_selector: + - '1783053752709' + - result + value_type: string + variable: cube_names + height: 52 + id: '1782400000012' + position: + x: -8375.53288280137 + y: -466.4249608991162 + positionAbsolute: + x: -8375.53288280137 + y: -466.4249608991162 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main({ input }) {\n let data = input;\n\n // 如果 input 是字符串,先尝试转\ + \ JSON\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n\ + \ } catch (e) {\n return {\n csv: '',\n count: 0\n\ + \ };\n }\n }\n\n const contents = [];\n const seen = new Set();\n\ + \n // 递归提取所有 content\n function collect(value) {\n if (!value) return;\n\ + \n // 兼容字符串形式的 JSON\n if (typeof value === 'string') {\n const\ + \ text = value.trim();\n if (!text) return;\n\n try {\n \ + \ const parsed = JSON.parse(text);\n collect(parsed);\n } catch\ + \ (e) {\n // 如果本身就是一行 CSV 文本,也允许收集\n addContent(text);\n \ + \ }\n\n return;\n }\n\n // 数组:逐项递归\n if (Array.isArray(value))\ + \ {\n for (const item of value) {\n collect(item);\n }\n\ + \ return;\n }\n\n // 对象:优先处理 content\n if (typeof value ===\ + \ 'object') {\n if (typeof value.content === 'string') {\n addContent(value.content);\n\ + \ }\n\n // 兼容 Dify 常见包装结构\n if (Array.isArray(value.input))\ + \ collect(value.input);\n if (Array.isArray(value.result)) collect(value.result);\n\ + \ if (Array.isArray(value.results)) collect(value.results);\n \ + \ if (Array.isArray(value.data)) collect(value.data);\n if (Array.isArray(value.items))\ + \ collect(value.items);\n if (Array.isArray(value.output)) collect(value.output);\n\ + \ }\n }\n\n function addContent(content) {\n const text = String(content).trim();\n\ + \ if (!text) return;\n\n // 归一化 key:去掉所有空白,降低重复误差\n const key =\ + \ text.replace(/\\s+/g, '');\n\n if (seen.has(key)) return;\n\n seen.add(key);\n\ + \ contents.push(text);\n }\n\n collect(data);\n\n return {\n csv:\ + \ contents.join('\\n')\n };\n}" + code_language: javascript + desc: '' + outputs: + csv: + children: null + type: string + selected: false + title: 格式化基础表知识 + type: code + variables: + - value_selector: + - '1781599320305' + - output + value_type: array[object] + variable: input + height: 52 + id: '1781594054454' + position: + x: -9124.069219455127 + y: -109.81201181221708 + positionAbsolute: + x: -9124.069219455127 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + error_handle_mode: terminated + flatten_output: true + height: 181 + is_parallel: true + iterator_input_type: object + iterator_selector: + - '1782351696945' + - structured_output + - base_queries + output_selector: + - '1781599540907' + - result + output_type: array[object] + parallel_nums: 10 + selected: false + start_node_id: 1781599320305start + title: 深度检索基础表知识库 + type: iteration + width: 387 + height: 181 + id: '1781599320305' + position: + x: -9518.358462826265 + y: -109.81201181221708 + positionAbsolute: + x: -9518.358462826265 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 387 + zIndex: 1 + - data: + desc: '' + isInIteration: true + selected: false + title: '' + type: iteration-start + draggable: false + height: 48 + id: 1781599320305start + parentId: '1781599320305' + position: + x: 24 + y: 68 + positionAbsolute: + x: -9494.358462826265 + y: -41.81201181221708 + selectable: false + selected: false + sourcePosition: right + targetPosition: left + type: custom-iteration-start + width: 44 + zIndex: 1002 + - data: + dataset_ids: + - BCdSJboJ+yZpNXBm4gDUIqxRo7DFaNkkrfR2/ccOpsUknXvLnp5xVzH81WRrWHsR + isInIteration: true + isInLoop: false + iteration_id: '1781599320305' + multiple_retrieval_config: + reranking_enable: true + reranking_mode: reranking_model + reranking_model: + model: qwen3-rerank + provider: langgenius/tongyi/tongyi + score_threshold: null + top_k: 1 + weights: + keyword_setting: + keyword_weight: 0.8 + vector_setting: + embedding_model_name: text-embedding-v4 + embedding_provider_name: langgenius/tongyi/tongyi + vector_weight: 0.2 + weight_type: customized + query_attachment_selector: [] + query_variable_selector: + - '1781599320305' + - item + retrieval_mode: multiple + selected: false + title: 检索基础表知识库 + type: knowledge-retrieval + height: 90 + id: '1781599540907' + parentId: '1781599320305' + position: + x: 16 + y: 65 + positionAbsolute: + x: -9502.358462826265 + y: -44.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + zIndex: 1002 + - data: + desc: '' + error_handle_mode: terminated + flatten_output: true + height: 180 + isInIteration: false + isInLoop: false + is_parallel: true + iterator_input_type: object + iterator_selector: + - '1782351696945' + - structured_output + - business_queries + output_selector: + - '1781758233968017817582339690' + - result + output_type: array[object] + parallel_nums: 10 + selected: false + start_node_id: 1781758233968start + title: 深度检索业务知识库 + type: iteration + width: 389 + height: 180 + id: '17817582339680' + position: + x: -9518.358462826265 + y: 86.07331027256038 + positionAbsolute: + x: -9518.358462826265 + y: 86.07331027256038 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 389 + zIndex: 1 + - data: + dataset_ids: + - vPpPaNv/0AGRcN8EkOHLdghdF945aY9k9WiMl95EPLEl1rvgcTneSjK6S7VhRsmr + desc: '' + isInIteration: true + isInLoop: false + iteration_id: '17817582339680' + multiple_retrieval_config: + reranking_enable: true + reranking_mode: reranking_model + reranking_model: + model: qwen3-rerank + provider: langgenius/tongyi/tongyi + score_threshold: null + top_k: 1 + weights: + keyword_setting: + keyword_weight: 0.8 + vector_setting: + embedding_model_name: text-embedding-v4 + embedding_provider_name: langgenius/tongyi/tongyi + vector_weight: 0.2 + weight_type: customized + query_attachment_selector: [] + query_variable_selector: + - '17817582339680' + - item + retrieval_mode: multiple + selected: false + title: 检索业务知识库 + type: knowledge-retrieval + height: 90 + id: '1781758233968017817582339690' + parentId: '17817582339680' + position: + x: 118 + y: 68 + positionAbsolute: + x: -9400.358462826265 + y: 154.07331027256038 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + zIndex: 1002 + - data: + desc: '' + isInIteration: true + selected: false + title: '' + type: iteration-start + draggable: false + height: 48 + id: 1781758233968start + parentId: '17817582339680' + position: + x: 24 + y: 68 + positionAbsolute: + x: -9494.358462826265 + y: 154.07331027256038 + selectable: false + selected: false + sourcePosition: right + targetPosition: left + type: custom-iteration-start + width: 44 + zIndex: 1002 + - data: + code: "function main({ input }) {\n let data = input;\n\n // 如果 input 是字符串,先尝试转\ + \ JSON\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n\ + \ } catch (e) {\n return {\n csv: '',\n count: 0\n\ + \ };\n }\n }\n\n const contents = [];\n const seen = new Set();\n\ + \n // 递归提取所有 content\n function collect(value) {\n if (!value) return;\n\ + \n // 兼容字符串形式的 JSON\n if (typeof value === 'string') {\n const\ + \ text = value.trim();\n if (!text) return;\n\n try {\n \ + \ const parsed = JSON.parse(text);\n collect(parsed);\n } catch\ + \ (e) {\n // 如果本身就是一行 CSV 文本,也允许收集\n addContent(text);\n \ + \ }\n\n return;\n }\n\n // 数组:逐项递归\n if (Array.isArray(value))\ + \ {\n for (const item of value) {\n collect(item);\n }\n\ + \ return;\n }\n\n // 对象:优先处理 content\n if (typeof value ===\ + \ 'object') {\n if (typeof value.content === 'string') {\n addContent(value.content);\n\ + \ }\n\n // 兼容 Dify 常见包装结构\n if (Array.isArray(value.input))\ + \ collect(value.input);\n if (Array.isArray(value.result)) collect(value.result);\n\ + \ if (Array.isArray(value.results)) collect(value.results);\n \ + \ if (Array.isArray(value.data)) collect(value.data);\n if (Array.isArray(value.items))\ + \ collect(value.items);\n if (Array.isArray(value.output)) collect(value.output);\n\ + \ }\n }\n\n function addContent(content) {\n const text = String(content).trim();\n\ + \ if (!text) return;\n\n // 归一化 key:去掉所有空白,降低重复误差\n const key =\ + \ text.replace(/\\s+/g, '');\n\n if (seen.has(key)) return;\n\n seen.add(key);\n\ + \ contents.push(text);\n }\n\n collect(data);\n\n return {\n csv:\ + \ contents.join('\\n')\n };\n}" + code_language: javascript + desc: '' + isInIteration: false + isInLoop: false + outputs: + csv: + children: null + type: string + selected: false + title: 格式化业务知识 + type: code + variables: + - value_selector: + - '17817582339680' + - output + value_type: array[object] + variable: input + height: 52 + id: '17817583423940' + position: + x: -9124.069219455127 + y: 86.07331027256038 + positionAbsolute: + x: -9124.069219455127 + y: 86.07331027256038 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + model: + completion_params: + response_format: json_object + temperature: 0.7 + mode: chat + name: qwen3.5-plus + provider: langgenius/tongyi/tongyi + prompt_template: + - id: 47b4da58-d911-439e-b970-3e3f2486b74f + role: system + text: "你是业务知识库检索词生成器。\n\n根据用户原始问题,分别生成:\n\n- business_queries:用于检索“业务逻辑知识库”\n\ + - base_queries:用于检索“基础表知识库”\n- qa_queries:用于检索历史问答知识库\n- \n\n业务逻辑知识库主要包含:\n\ + - 业务指标和业务含义\n- 核心成员和可用维度\n- 数据粒度\n- 适用和不适用场景\n\n基础表知识库主要包含:\n- 业务名词和别名\n\ + - 实体所在表\n- 原始字段含义\n- 表关系\n- 状态字段\n- 实体定位方式\n\n生成要求:\n\n1. 每类最多生成2条检索词,简短明确。\n\ + 3. 不要把具体租户名、人名、身份证号、编号、日期或数值放入检索词。\n4. 保留影响业务含义的条件,例如“有效农户”“年龄”“低产”“已支付”。\n\ + 5. business_queries重点描述查询指标、维度和业务场景。\n6. base_queries重点描述实体定位、字段含义和原始数据关系。\n\ + 7. qa_queries 只参考用户原始问题,提取 1~5 个核心业务关键词或短语,用于召回相似历史问题。\n8. 不要生成含义重复的检索词。\n\ + \n其他规则:\n{{#conversation.global_rules#}}" + - id: e83796ca-f021-41d2-85b3-91790d1db984 + role: user + text: '{{#conversation.user_query#}}' + selected: false + structured_output: + schema: + additionalProperties: false + properties: + base_queries: + items: + type: string + type: array + business_queries: + items: + type: string + type: array + qa_queries: + items: + type: string + type: array + required: + - business_queries + - base_queries + - qa_queries + type: object + structured_output_enabled: true + title: 意图理解 + type: llm + vision: + enabled: false + height: 88 + id: '1782351696945' + position: + x: -9793.33434626631 + y: 27.746422531720228 + positionAbsolute: + x: -9793.33434626631 + y: 27.746422531720228 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + is_array_file: true + selected: false + title: 文档解析 + type: document-extractor + variable_selector: + - sys + - files + height: 104 + id: '1782889122238' + position: + x: -10582.764943990636 + y: -109.81201181221708 + positionAbsolute: + x: -10582.764943990636 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + cases: + - case_id: 'true' + conditions: + - comparison_operator: not empty + id: f16c19d2-12b2-4890-b13a-95ab9ff3c102 + sub_variable_condition: + case_id: 596e5c2a-c188-490c-a210-10bf05151805 + conditions: + - comparison_operator: '>' + id: 8278eaf8-c597-4023-8a61-616164ec2fcf + key: size + value: '0' + varType: string + logical_operator: and + value: '' + varType: array[file] + variable_selector: + - sys + - files + id: 'true' + logical_operator: and + selected: false + title: 判断用户文件输入 + type: if-else + height: 124 + id: '1782889131357' + position: + x: -10836.84627996062 + y: -109.81201181221708 + positionAbsolute: + x: -10836.84627996062 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + model: + completion_params: + temperature: 0.7 + mode: chat + name: qwen3.5-flash + provider: langgenius/tongyi/tongyi + prompt_template: + - id: cd5e2e74-1424-4f74-83ba-ee5366f71130 + role: system + text: '你是一个数据查询问题整理助手。请结合用户原始问题和用户提供的表格字段信息,将其整理成一个语义完整、条件明确、可直接用于数据查询或自然语言转 + SQL 的问题。 + + + 整理要求: + + - 保留用户原始问题中的查询对象、范围、筛选条件和业务含义。 + + - 根据表格字段补充查询所需的返回内容,但不要虚构表格中不存在的字段。 + + - 对用户问题中的口语化、模糊或不完整表达进行规范化。 + + - 只输出整理后的完整问题,不要输出分析过程、解释或 SQL。 + + - 不要将示例数据、字段说明或无关内容加入最终问题。' + - id: fddcbfa6-abf2-4030-b331-951a739fdd4a + role: user + text: '用户提问: + + {{#sys.query#}} + + + 用户提供的表格: + + {{#1782889122238.text#}}' + selected: false + title: 整理文件 + type: llm + vision: + enabled: false + height: 88 + id: '1782889576269' + position: + x: -10317.383808423989 + y: -109.81201181221708 + positionAbsolute: + x: -10317.383808423989 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + items: + - input_type: variable + operation: over-write + value: + - '1782889576269' + - text + variable_selector: + - conversation + - user_query + selected: false + title: 整理问题 + type: assigner + version: '2' + height: 84 + id: '1782889756496' + position: + x: -10049.790484827632 + y: -109.81201181221708 + positionAbsolute: + x: -10049.790484827632 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + desc: '' + isInIteration: false + isInLoop: false + items: + - input_type: variable + operation: over-write + value: + - sys + - query + variable_selector: + - conversation + - user_query + selected: false + title: 整理问题 + type: assigner + version: '2' + height: 84 + id: '17828899879200' + position: + x: -10582.764943990636 + y: 27.746422531720228 + positionAbsolute: + x: -10582.764943990636 + y: 27.746422531720228 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main({ cube_joins, core_name }) {\n const joinMap = parseJsonObject(cube_joins);\n\ + \ const coreName = parseCoreName(core_name);\n\n if (!coreName) {\n \ + \ return {\n result: \"[]\"\n };\n }\n\n const normalizedCore\ + \ = normalizeName(coreName);\n const keyMap = buildKeyMap(joinMap);\n\n\ + \ const actualCoreKey = keyMap[normalizedCore] || coreName;\n const joinNames\ + \ = Array.isArray(joinMap[actualCoreKey])\n ? joinMap[actualCoreKey]\n\ + \ : [];\n\n const result = unique([\n actualCoreKey,\n ...joinNames\n\ + \ ]);\n\n return {\n result: JSON.stringify(result)\n };\n}\n\nfunction\ + \ parseJsonObject(value) {\n if (!value) {\n return {};\n }\n\n if\ + \ (typeof value === \"object\" && !Array.isArray(value)) {\n return value;\n\ + \ }\n\n if (typeof value !== \"string\") {\n return {};\n }\n\n try\ + \ {\n const parsed = JSON.parse(value);\n return parsed && typeof\ + \ parsed === \"object\" && !Array.isArray(parsed)\n ? parsed\n \ + \ : {};\n } catch (e) {\n return {};\n }\n}\n\nfunction parseCoreName(value)\ + \ {\n if (!value) {\n return \"\";\n }\n\n if (typeof value === \"\ + string\") {\n const text = value.trim();\n\n if (!text) {\n return\ + \ \"\";\n }\n\n try {\n const parsed = JSON.parse(text);\n\n\ + \ if (typeof parsed === \"string\") {\n return parsed.trim();\n\ + \ }\n\n if (Array.isArray(parsed)) {\n return String(parsed[0]\ + \ || \"\").trim();\n }\n\n if (parsed && Array.isArray(parsed.cubes))\ + \ {\n return String(parsed.cubes[0] || \"\").trim();\n }\n\n\ + \ if (parsed && typeof parsed.core_name === \"string\") {\n \ + \ return parsed.core_name.trim();\n }\n\n if (parsed && typeof\ + \ parsed.name === \"string\") {\n return parsed.name.trim();\n \ + \ }\n } catch (e) {\n return text;\n }\n\n return text;\n\ + \ }\n\n if (Array.isArray(value)) {\n return String(value[0] || \"\"\ + ).trim();\n }\n\n if (typeof value === \"object\") {\n if (Array.isArray(value.cubes))\ + \ {\n return String(value.cubes[0] || \"\").trim();\n }\n\n if\ + \ (typeof value.core_name === \"string\") {\n return value.core_name.trim();\n\ + \ }\n\n if (typeof value.name === \"string\") {\n return value.name.trim();\n\ + \ }\n }\n\n return \"\";\n}\n\nfunction buildKeyMap(object) {\n const\ + \ result = {};\n\n for (const key of Object.keys(object || {})) {\n \ + \ result[normalizeName(key)] = key;\n }\n\n return result;\n}\n\nfunction\ + \ normalizeName(name) {\n return String(name || \"\")\n .trim()\n \ + \ .toLowerCase();\n}\n\nfunction unique(values) {\n const result = [];\n\ + \ const seen = new Set();\n\n for (const value of values) {\n if (typeof\ + \ value !== \"string\") {\n continue;\n }\n\n const text = value.trim();\n\ + \n if (!text) {\n continue;\n }\n\n const key = normalizeName(text);\n\ + \n if (seen.has(key)) {\n continue;\n }\n\n seen.add(key);\n\ + \ result.push(text);\n }\n\n return result;\n}" + code_language: javascript + outputs: + result: + children: null + type: string + selected: false + title: 查询joins + type: code + variables: + - value_selector: + - conversation + - cube_joins + value_type: string + variable: cube_joins + - value_selector: + - '1781581099032' + - structured_output + - core_cube + value_type: object + variable: core_name + height: 52 + id: '1783053752709' + position: + x: -8614.489525771321 + y: -109.81201181221708 + positionAbsolute: + x: -8614.489525771321 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + cases: + - case_id: 'true' + conditions: + - comparison_operator: is + id: d1187a47-b9e2-4637-b3d8-2d93cb55613b + value: 'true' + varType: string + variable_selector: + - '1784083007146' + - need_current_batch + id: 'true' + logical_operator: and + selected: false + title: 是否要求当前蚕季 + type: if-else + height: 148 + id: '1783073068957' + position: + x: -10582.764943990636 + y: -275.5887295533155 + positionAbsolute: + x: -10582.764943990636 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + desc: '' + isInIteration: false + isInLoop: false + items: + - input_type: variable + operation: over-write + value: + - '17830731571350' + - canjiId + variable_selector: + - conversation + - canjiId + - input_type: variable + operation: over-write + value: + - '17830731571350' + - canjiName + variable_selector: + - conversation + - canjiName + selected: false + title: '确定蚕季ID ' + type: assigner + version: '2' + height: 110 + id: '17830731406880' + position: + x: -9793.33434626631 + y: -275.5887295533155 + positionAbsolute: + x: -9793.33434626631 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + authorization: + config: null + type: no-auth + body: + data: + - id: key-value-323 + key: '' + type: text + value: '{ + +   "query": { + +     "dimensions": [ + +       "sg_canji_config.cjsysid", + +       "sg_canji_config.cjname", + +       "sg_canji_config.sgstartdatettime", + +       "sg_canji_config.sgenddatettime" + +     ], + +     "filters": [ + +       { + +         "member": "sg_canji_config.tenantid", + +         "operator": "equals", + +         "values": ["{{#conversation.default_tenant_id#}}"] + +       }, + +       { + +         "member": "sg_canji_config.flag", + +         "operator": "gt", + +         "values": ["0"] + +       }, + +       { + +         "or": [ + +           { + +             "and": [ + +               { + +                 "member": "sg_canji_config.sgstartdatettime", + +                 "operator": "beforeOrOnDate", + +                 "values": ["{{#1781232881963.text#}}"] + +               }, + +               { + +                 "member": "sg_canji_config.sgenddatettime", + +                 "operator": "afterOrOnDate", + +                 "values": ["{{#1781232881963.text#}}"] + +               } + +             ] + +           }, + +           { + +             "member": "sg_canji_config.sgenddatettime", + +             "operator": "beforeDate", + +             "values": ["{{#1781232881963.text#}}"] + +           } + +         ] + +       } + +     ], + +     "order": [ + +       ["sg_canji_config.sgenddatettime", "desc"], + +       ["sg_canji_config.sgstartdatettime", "desc"], + +       ["sg_canji_config.cjsysid", "desc"] + +     ], + +     "limit": 1 + +   } + + }' + type: json + desc: '' + headers: '' + isInIteration: false + isInLoop: false + method: post + params: '' + retry_config: + max_retries: 3 + retry_enabled: false + retry_interval: 100 + selected: false + ssl_verify: false + timeout: + connect: 2 + max_connect_timeout: 0 + max_read_timeout: 0 + max_write_timeout: 0 + read: 10 + write: 10 + title: 查询当前蚕季ID + type: http-request + url: http://10.10.12.101:4001/cubejs-api/v1/load + variables: [] + height: 108 + id: '17830731532390' + position: + x: -10317.383808423989 + y: -275.5887295533155 + positionAbsolute: + x: -10317.383808423989 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main({ input }) {\n let data = input;\n\n if (typeof input\ + \ === \"string\") {\n try {\n data = JSON.parse(input);\n } catch\ + \ (e) {\n return {\n canjiId: \"\",\n canjiName: \"\"\ + \n };\n }\n }\n\n const firstItem = data?.data?.[0] ?? {};\n\n\ + \ return {\n canjiId: firstItem[\"sg_canji_config.cjsysid\"] ?? \"\"\ + ,\n canjiName: firstItem[\"sg_canji_config.cjname\"] ?? \"\"\n };\n}" + code_language: javascript + desc: '' + isInIteration: false + isInLoop: false + outputs: + canjiId: + children: null + type: string + canjiName: + children: null + type: string + selected: false + title: 提取蚕季ID + type: code + variables: + - value_selector: + - '17830731532390' + - body + value_type: string + variable: input + height: 52 + id: '17830731571350' + position: + x: -10049.790484827632 + y: -275.5887295533155 + positionAbsolute: + x: -10049.790484827632 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main(inputs) {\n const question = inputs.question;\n\n return\ + \ {\n need_current_batch: String(\n typeof question === \"string\"\ + \ &&\n question.includes(\"当前蚕季\")\n )\n };\n}" + code_language: javascript + outputs: + need_current_batch: + children: null + type: string + selected: false + title: 判断当前蚕季 + type: code + variables: + - value_selector: + - sys + - query + value_type: string + variable: question + height: 52 + id: '1784083007146' + position: + x: -10836.84627996062 + y: -275.5887295533155 + positionAbsolute: + x: -10836.84627996062 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + answer: '{{#1782400000011.structured_output#}} + + {{#17841887361130.structured_output#}}' + selected: false + title: 返回load请求 + type: answer + variables: [] + height: 122 + id: '1784098833176' + position: + x: -7585.9671110161225 + y: -377.5887295533152 + positionAbsolute: + x: -7585.9671110161225 + y: -377.5887295533152 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + items: + - input_type: variable + operation: over-write + value: + - '1784188957627' + - result + variable_selector: + - conversation + - latest_load + selected: false + title: 保存上次Load + type: assigner + version: '2' + height: 84 + id: '1784183263188' + position: + x: -7296.9671110161225 + y: -109.81201181221708 + positionAbsolute: + x: -7296.9671110161225 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + isInIteration: false + isInLoop: false + model: + completion_params: + response_format: json_object + mode: chat + name: qwen3.7-plus + provider: langgenius/tongyi/tongyi + prompt_config: + jinja2_variables: [] + prompt_template: + - edition_type: basic + id: 99e91b51-0052-4e93-95b5-62e502798f91 + jinja2_text: '' + role: system + text: '你是 Cube load 查询体生成器。 + + + # 任务 + + 根据参考信息,生成可用于Cube `/cubejs-api/v1/load`接口的请求体的请求,以便获取用户想要的数据。 + + + # 规则: + + - 当查询农户名单、明细、记录或排行榜时,须额外加入当前 Cube 的农户唯一ID维度,如 nhsysid;不要只用农户姓名作为农户粒度。 + + - 如果选择的 measure 已经包含用户要求的业务状态或口径,不要再额外添加同义 filter。 + + - 去掉/不要 XXX 为空的"、"排除 XXX 为空"、"只保留 XXX 不为空"均表示修改 filter,不得删除该 dimension。只有用户明确要求"不要显示 + XXX"、"去掉 XXX 列"、"隐藏 XXX"时,才能从 dimensions 中删除字段。 + + + # 参考知识: + + ## 当前时间:{{#1781232881963.text#}} + + + ## 当前蚕季ID:{{#conversation.canjiId#}} + + (当用户提问当前蚕季时,筛选数据时需要明确筛选蚕季ID) + + + ## 业务规则 + + {{#conversation.global_rules#}} + + + ## Cube数据 + + {{#1782400000012.result#}}' + - edition_type: basic + id: c5f6e041-13bd-48f8-b879-f4c2fbd2f183 + role: user + text: '当前的Cube请求: + + {{#conversation.latest_load#}} + + + 用户的新要求: + + {{#conversation.user_query#}} + + + 当前租户ID:{{#1780903664367.tenant_id#}} + + **(如果Cube数据中包含类似tenant_id或租户隔离的字段,必须必须必须加此ID筛选)** + + ' + reasoning_format: separated + selected: false + structured_output: + schema: + additionalProperties: false + properties: + limit_source: + description: 如果query中有限制limit的话,此处说明限制来源是系统要求还是用户要求 + enum: + - system + - user + type: string + message: + description: 如果status为false或者无法根据知识完全覆盖用户所需要的字段,则此处说明原因以及缺失的字段,否则填写:“成功生成” + type: string + query: + additionalProperties: false + description: Cube的/v1/load请求 + properties: + dimensions: + description: 维度,负责“按什么看、返回哪些分类/明细字段” 来源是 Cube 元数据里的 dimensions[].name。不可放入measures的name + items: + type: string + type: array + filters: + description: 过滤条件 + items: + additionalProperties: false + properties: + and: + description: “且”条件时使用 + items: + additionalProperties: false + properties: + member: + description: 过滤对象 + type: string + operator: + description: 操作符,用户表达“蚕季为/名称为/包含/叫/查某某名称”,优先使用 operator="contains",不要使用 + equals,除非用户明确说“精确等于/完全等于/ID + enum: + - equals + - notEquals + - contains + - startsWith + - gt + - lt + - set + - notSet + - inDateRange + type: string + values: + description: 过滤值 + items: + type: string + type: array + required: [] + type: object + type: array + or: + description: “或”条件时使用, + items: + additionalProperties: false + properties: + member: + description: 过滤对象 + type: string + operator: + description: 操作符,用户表达“蚕季为/名称为/包含/叫/查某某名称”,优先使用 operator="contains",不要使用 + equals,除非用户明确说“精确等于/完全等于/ID + enum: + - equals + - notEquals + - contains + - startsWith + - gt + - lt + - set + - notSet + - inDateRange + type: string + values: + description: 过滤值 + items: + type: string + type: array + required: [] + type: object + type: array + required: [] + type: object + type: array + limit: + description: 限制返回数量,最大5 + type: number + measures: + description: 指标,负责“算什么数” 来源是 Cube 元数据里的 measures[].name。不可放入dimensions的name + items: + type: string + type: array + offset: + description: 跳过前面多少行,可以结合limit实现分页 + type: number + order: + description: 排序,必须是二维数组,例如 [["bus_farmer_yield.yield_kg_per_sheet", + "asc"]] + items: + maxItems: 2 + minItems: 2 + prefixItems: + - description: 排序字段,例如 bus_farmer_yield.yield_kg_per_sheet + type: string + - description: 排序方向 + enum: + - asc + - desc + type: string + type: array + type: array + segments: + description: 预先在 Cube 模型中定义好的命名过滤条件 + items: + type: string + type: array + timeDimensions: + description: 时间维度过滤 + items: + additionalProperties: false + properties: + dateRange: + items: + type: string + type: array + dimension: + description: 时间维度字段,例如 orders.createdAt + type: string + granularity: + description: 如果不写 granularity,只过滤时间,不按时间分组 + enum: + - year + - quarter + - month + - week + - day + - hour + - minute + - second + type: string + required: [] + type: object + type: array + required: + - limit + - dimensions + type: object + status: + description: 是否可以生成query。缺失字段时仍然返回true + enum: + - 'true' + - 'false' + type: string + required: + - query + - message + - status + - limit_source + type: object + structured_output_enabled: true + title: 修正数据请求 + type: llm + vision: + enabled: false + height: 88 + id: '17841887361130' + position: + x: -7843.845548425899 + y: -109.81201181221708 + positionAbsolute: + x: -7843.845548425899 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + cases: + - case_id: 'true' + conditions: + - comparison_operator: empty + id: 1bbb3588-583e-43d7-9214-446c406370d6 + value: '' + varType: string + variable_selector: + - conversation + - latest_load + id: 'true' + logical_operator: and + selected: false + title: 条件分支 3 + type: if-else + height: 124 + id: '1784188747804' + position: + x: -8107.816891509001 + y: -275.5887295533155 + positionAbsolute: + x: -8107.816891509001 + y: -275.5887295533155 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + code: "function main({ input }) {\n return {\n result: JSON.stringify(input)\n\ + \ };\n}" + code_language: javascript + outputs: + result: + children: null + type: string + selected: false + title: 代码执行 7 + type: code + variables: + - value_selector: + - '1782400000011' + - structured_output + value_type: object + variable: input + height: 52 + id: '1784188957627' + position: + x: -7576.967111016122 + y: -109.81201181221708 + positionAbsolute: + x: -7576.967111016122 + y: -109.81201181221708 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + viewport: + x: 5449.14149570833 + y: 542.9157509872703 + zoom: 0.6597539553864491 + rag_pipeline_variables: []