flexbiz-server 12.6.13 → 12.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +439 -4
- package/package.json +4 -3
- package/server/agents/agents.js +1 -0
- package/server/agents/ai_memory_manager.js +6 -0
- package/server/agents/ceoAgent.js +43 -0
- package/server/agents/chatController.js +6 -0
- package/server/agents/cskhAgent.js +43 -0
- package/server/agents/flowAgent.js +43 -0
- package/server/agents/prompts/ceo.js +2 -0
- package/server/agents/prompts/flow.js +2 -0
- package/server/agents/staffAgent.js +44 -0
- package/server/agents/tools/financeTools.js +57 -0
- package/server/agents/tools/flowTools.js +36 -0
- package/server/agents/tools/kpiTools.js +104 -0
- package/server/agents/tools/masterDataTools.js +74 -0
- package/server/agents/tools/okrTools.js +21 -0
- package/server/agents/tools/ragTools.js +21 -0
- package/server/agents/tools/reportTools.js +43 -0
- package/server/agents/tools/staffTools.js +13 -0
- package/server/agents/tools/stockTools.js +41 -0
- package/server/agents/tools/systemTools.js +52 -0
- package/server/agents/tools/taskTools.js +23 -0
- package/server/agents/tools/utils.js +57 -0
- package/server/app.js +7 -7
- package/server/controllers/controller.js +31 -29
- package/server/controllers/controllerRPT.js +31 -29
- package/server/controllers/findHandler.js +22 -21
- package/server/controllers/importFromExcelHandler.js +17 -14
- package/server/controllers/importHandler.js +26 -41
- package/server/controllers/importHandler_old.js +41 -0
- package/server/controllers/rptHandler.js +14 -11
- package/server/controllers/viewHandler.js +7 -7
- package/server/flow/FlowEngine.js +49 -45
- package/server/flow/WebhookManager.js +1 -1
- package/server/flow/handlers.js +2 -2
- package/server/flow/nodes/userform.js +28 -0
- package/server/flow/utils.js +12 -4
- package/server/global.js +4 -3
- package/server/libs/cdpstk.js +1 -1
- package/server/libs/ckvt.js +12 -11
- package/server/libs/ckvtcapphat.js +5 -5
- package/server/libs/ckvtnpp.js +8 -8
- package/server/libs/ckvttt.js +6 -5
- package/server/libs/ctvt.js +7 -6
- package/server/libs/ctvtnpp.js +7 -6
- package/server/libs/ctvttt.js +6 -6
- package/server/libs/databanle.js +17 -18
- package/server/libs/databanle_old.js +18 -0
- package/server/libs/databanletheongay.js +25 -29
- package/server/libs/dktk.js +3 -3
- package/server/libs/dkvt.js +11 -11
- package/server/libs/dkvtnpp.js +7 -6
- package/server/libs/dkvttt.js +7 -6
- package/server/libs/docs.js +72 -36
- package/server/libs/dtbanletheosp.js +16 -19
- package/server/libs/fifo_dk.js +8 -0
- package/server/libs/fifo_group.js +4 -0
- package/server/libs/fifo_trace_query.js +12 -0
- package/server/libs/getGiaban.js +59 -70
- package/server/libs/getGiaban_old.js +70 -0
- package/server/libs/joinData.js +3 -3
- package/server/libs/joinData_bk.js +44 -0
- package/server/libs/kpistatus.js +24 -17
- package/server/libs/permission.js +54 -56
- package/server/libs/permission_old.js +58 -0
- package/server/libs/post-book.js +1 -1
- package/server/libs/post-socai.js +2 -1
- package/server/libs/post-sokho.js +11 -11
- package/server/libs/prototypes.js +13 -12
- package/server/libs/queryBuilding.js +11 -10
- package/server/libs/redis-cache.js +22 -10
- package/server/libs/sctcnkh.js +19 -17
- package/server/libs/socaitk.js +1 -1
- package/server/libs/thnxt.js +17 -14
- package/server/libs/thnxtnpp.js +8 -8
- package/server/libs/thnxttt.js +8 -7
- package/server/libs/tinhgia1vt_fifo.js +22 -0
- package/server/libs/tinhgiafifo.js +28 -0
- package/server/libs/utils.js +37 -19
- package/server/models/app.js +1 -1
- package/server/models/approve.js +8 -8
- package/server/models/cacheDataReport.js +2 -2
- package/server/models/cacheSessionReport.js +5 -1
- package/server/models/cdvt.js +2 -2
- package/server/models/checkin.js +2 -2
- package/server/models/chitietluongds.js +1 -1
- package/server/models/contract.js +1 -1
- package/server/models/csluongtheods.js +2 -2
- package/server/models/ctds2.js +7 -6
- package/server/models/custom_report.js +1 -0
- package/server/models/dmdt.js +2 -2
- package/server/models/dmkho.js +5 -4
- package/server/models/dmnvt.js +5 -4
- package/server/models/fifo_dk.js +2 -0
- package/server/models/fifo_lot.js +3 -0
- package/server/models/fifo_trace.js +4 -0
- package/server/models/flowpending.js +2 -0
- package/server/models/group.js +3 -5
- package/server/models/listinfo.js +61 -33
- package/server/models/schedule.js +4 -3
- package/server/models/task.js +1 -1
- package/server/models/trangthai.js +2 -2
- package/server/modules/lists/bg-fifo_dk.js +8 -0
- package/server/modules/lists/ls-checkin.js +1 -1
- package/server/modules/lists/ls-customer.js +23 -26
- package/server/modules/lists/ls-dmbp.js +5 -5
- package/server/modules/lists/ls-dmkho.js +7 -1
- package/server/modules/lists/ls-dmnhtask.js +1 -1
- package/server/modules/lists/ls-dmvt.js +52 -70
- package/server/modules/lists/ls-flowpending.js +3 -0
- package/server/modules/lists/ls-participant.js +6 -4
- package/server/modules/lists/ls-task.js +6 -6
- package/server/modules/reports/calc-tinhgiafifo.js +8 -0
- package/server/modules/reports/calc-tinhluong.js +17 -16
- package/server/modules/reports/rp-baoCaoFifoKy.js +1 -0
- package/server/modules/reports/rp-congviectheobp.js +4 -4
- package/server/modules/reports/rp-congviectheodt.js +1 -1
- package/server/modules/reports/rp-congviectheokh.js +1 -1
- package/server/modules/reports/rp-congviectheonv.js +1 -1
- package/server/modules/reports/rp-dtbanletheoct.js +22 -23
- package/server/modules/reports/rp-dtbanletheokh.js +17 -20
- package/server/modules/reports/rp-dtbanletheokho.js +15 -18
- package/server/modules/reports/rp-hoahong.js +1 -1
- package/server/modules/reports/rp-sctvt.js +1 -1
- package/server/modules/reports/rp-sosanhkhvatt2.js +1 -1
- package/server/modules/reports/rp-thnxt.js +8 -8
- package/server/modules/reports/rp-tongquannvbh.js +1 -1
- package/server/modules/systems/sys-app.js +5 -5
- package/server/modules/vouchers/vo-so1.js +14 -14
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const {generateText,stepCountIs,pruneMessages}=require("ai"),chatController=require("./chatController"),{decrypt}=require("../libs/utils"),{summarizeUserMemory,getMemory}=require("./ai_memory_manager"),{createFinanceTools}=require("./tools/financeTools"),{createStockTools}=require("./tools/stockTools"),{createRagTools}=require("./tools/ragTools"),{createStaffTools}=require("./tools/staffTools"),{createSystemTools}=require("./tools/systemTools"),{createMasterDataTools}=require("./tools/masterDataTools"),
|
|
2
|
+
{createKpiTools}=require("./tools/kpiTools"),{createOkrTools}=require("./tools/okrTools"),{createTaskTools}=require("./tools/taskTools"),{createReportTools}=require("./tools/reportTools"),{createFlowTools}=require("./tools/flowTools"),tenantConfigs={},SYSTEM_PROMPT=require("./prompts/flow"),{isValidObjectId}=require("mongoose");
|
|
3
|
+
async function runFinanceAgent($id_app$$,$user_id$$,$session_id$$,$userPrompt$$,$files_of_message$$){try{let $is_pure_file_upload$$=!1;(!$userPrompt$$||$userPrompt$$.trim()==="")&&$files_of_message$$&&$files_of_message$$.length>0&&($is_pure_file_upload$$=!0,$userPrompt$$=`Anh v\u1eeba t\u1ea3i l\u00ean ${$files_of_message$$.length} file t\u00e0i li\u1ec7u m\u00e0 ch\u01b0a ghi ch\u00fa g\u00ec. Em h\u00e3y \u0111\u1ecdc l\u01b0\u1edbt qua d\u1eef li\u1ec7u th\u00f4 c\u1ee7a file n\u00e0y, nh\u1eadn di\u1ec7n xem \u0111\u00e2y l\u00e0 lo\u1ea1i gi\u1ea5y t\u1edd g\u00ec (H\u00f3a \u0111\u01a1n, h\u1ee3p \u0111\u1ed3ng, b\u00e1o c\u00e1o...), tr\u00edch xu\u1ea5t 3 th\u00f4ng tin quan tr\u1ecdng nh\u1ea5t v\u00e0 \u0111\u01b0a ra c\u00e1c g\u1ee3i \u00fd h\u00e0nh \u0111\u1ed9ng k\u1ebf ti\u1ebfp trong h\u1ec7 th\u1ed1ng ERP \u0111\u1ec3 anh ch\u1ecdn nh\u00e9.`);
|
|
4
|
+
var $app_systemInstruction$$=await global.getModel("app").findOne({_id:$id_app$$},{ai_name:1,ai_api_key:1,ai_model:1});if(!$app_systemInstruction$$||!$app_systemInstruction$$.ai_api_key||!$app_systemInstruction$$.ai_model)throw Error("Ch\u01b0a khai b\u00e1o API Key ho\u1eb7c Model cho h\u1ec7 th\u1ed1ng.");var $gioi_tinh_isCacheStale_messagesToSend$$=tenantConfigs[$id_app$$]&&tenantConfigs[$id_app$$].modelName!==$app_systemInstruction$$.ai_model;if(!tenantConfigs[$id_app$$]||$gioi_tinh_isCacheStale_messagesToSend$$){const $api_key$$=
|
|
5
|
+
decrypt($app_systemInstruction$$.ai_api_key.replace("flexbiz::",""),!1),$aiType$$=($app_systemInstruction$$.ai_name||"GPT").toUpperCase();let $aiModelInstance$$;switch($aiType$$){case "CLAUDE":case "ANTHROPIC":const {createAnthropic:$createAnthropic$$}=require("@ai-sdk/anthropic");$aiModelInstance$$=$createAnthropic$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model);break;case "GEMINI":case "GOOGLE":const {createGoogleGenerativeAI:$createGoogleGenerativeAI$$}=require("@ai-sdk/google");$aiModelInstance$$=
|
|
6
|
+
$createGoogleGenerativeAI$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model);break;default:const {createOpenAI:$createOpenAI$$}=require("@ai-sdk/openai");$aiModelInstance$$=$createOpenAI$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model)}tenantConfigs[$id_app$$]={model:$aiModelInstance$$,modelName:$app_systemInstruction$$.ai_model};Logger.info(`[Multi-LLM] \u0110\u00e3 kh\u1edfi t\u1ea1o th\u00e0nh c\u00f4ng model ${$app_systemInstruction$$.ai_model} (${$aiType$$}) cho App: ${$id_app$$}`)}const {model:$model$$}=
|
|
7
|
+
tenantConfigs[$id_app$$];$session_id$$||=(await chatController.createSession($id_app$$,$user_id$$,$userPrompt$$.substring(0,1024)))._id.toString();const $activeTools$$={...createStaffTools($id_app$$,$user_id$$,$session_id$$),...createFinanceTools($id_app$$,$user_id$$,$session_id$$),...createStockTools($id_app$$,$user_id$$,$session_id$$),...createRagTools($id_app$$,$user_id$$,$session_id$$,$files_of_message$$),...createSystemTools($id_app$$,$user_id$$,$session_id$$),...createMasterDataTools($id_app$$,
|
|
8
|
+
$user_id$$,$session_id$$),...createKpiTools($id_app$$,$user_id$$,$session_id$$),...createOkrTools($id_app$$,$user_id$$,$session_id$$),...createTaskTools($id_app$$,$user_id$$,$session_id$$),...createReportTools($id_app$$,$user_id$$,$session_id$$),...createFlowTools($id_app$$,$user_id$$,$session_id$$)};$app_systemInstruction$$=`${SYSTEM_PROMPT}`;const [$previousMessages$$,$userInfoDoc$$,$appInfoDoc$$,$memory$$]=await Promise.all([chatController.getChatHistory($id_app$$,$session_id$$,30),global.getModel("participant").findOne({id_app:$id_app$$,
|
|
9
|
+
email:$user_id$$}).lean(),global.getModel("app").findOne({_id:$id_app$$}).lean(),getMemory($id_app$$,$user_id$$)]);$gioi_tinh_isCacheStale_messagesToSend$$=[...$previousMessages$$,{role:"user",content:$userPrompt$$}];const $optimizedMessages$$=pruneMessages({messages:$gioi_tinh_isCacheStale_messagesToSend$$,reasoning:"all",toolCalls:"before-last-message"});$appInfoDoc$$&&($app_systemInstruction$$+=`
|
|
10
|
+
\n# [TH\u00d4NG TIN DOANH NGHI\u1ec6P C\u1ee6A B\u1ea0N]
|
|
11
|
+
B\u1ea1n \u0111ang \u0111\u1ea1i di\u1ec7n v\u00e0 \u0111i\u1ec1u h\u00e0nh h\u1ec7 th\u1ed1ng cho doanh nghi\u1ec7p sau:
|
|
12
|
+
- **T\u00ean doanh nghi\u1ec7p:** ${$appInfoDoc$$.name||"H\u1ec7 th\u1ed1ng n\u1ed9i b\u1ed9"}
|
|
13
|
+
- **M\u00e3 s\u1ed1 thu\u1ebf:** ${$appInfoDoc$$.ma_so_thue||"Ch\u01b0a c\u1eadp nh\u1eadt"}
|
|
14
|
+
- **\u0110\u1ecba ch\u1ec9:** ${$appInfoDoc$$.address||$appInfoDoc$$.dia_chi||"Ch\u01b0a c\u1eadp nh\u1eadt"}
|
|
15
|
+
- **\u0110\u1ecba ch\u1ec9 xu\u1ea5t ho\u00e1 \u0111\u01a1n:** ${$appInfoDoc$$.dia_chi_xuat_hoa_don||"Ch\u01b0a c\u1eadp nh\u1eadt"}
|
|
16
|
+
- **Li\u00ean h\u1ec7:** ${$appInfoDoc$$.phone||$appInfoDoc$$.dien_thoai||""} ${$appInfoDoc$$.email?" | "+$appInfoDoc$$.email:""}
|
|
17
|
+
|
|
18
|
+
[CH\u1ec8 TH\u1eca C\u1ed0T L\u00d5I V\u1ec0 DOANH NGHI\u1ec6P]:
|
|
19
|
+
M\u1ecdi s\u1ed1 li\u1ec7u doanh thu, chi ph\u00ed, c\u00f4ng n\u1ee3 m\u00e0 b\u1ea1n ph\u00e2n t\u00edch \u0111\u1ec1u l\u00e0 t\u00e0i s\u1ea3n c\u1ee7a doanh nghi\u1ec7p n\u00e0y. Khi c\u00f3 y\u00eau c\u1ea7u so\u1ea1n th\u1ea3o v\u0103n b\u1ea3n, h\u1ee3p \u0111\u1ed3ng, email ho\u1eb7c Zalo g\u1eedi \u0111\u1ed1i t\u00e1c ngo\u00e0i, B\u1eaeT BU\u1ed8C ph\u1ea3i d\u00f9ng ch\u00ednh x\u00e1c th\u00f4ng tin \u0111\u1ecbnh danh ph\u00eda tr\u00ean \u0111\u1ec3 l\u00e0m ti\u00eau \u0111\u1ec1 ho\u1eb7c ch\u1eef k\u00fd.
|
|
20
|
+
`);if($userInfoDoc$$){const $thongtinnhanvien$$=await global.getModel("dmnv").findOne({id_app:$id_app$$,user:$user_id$$}).lean(),$fullName$$=$thongtinnhanvien$$?.ten_nv||$userInfoDoc$$.name||"S\u1ebfp",$shortName$$=$fullName$$!=="S\u1ebfp"?$fullName$$.split(" ").pop():"S\u1ebfp";$gioi_tinh_isCacheStale_messagesToSend$$=$thongtinnhanvien$$?.gioi_tinh||"Kh\u00e1c";let $chuc_vu$$,$bo_phan$$;$thongtinnhanvien$$?.chuc_vu&&isValidObjectId($thongtinnhanvien$$?.chuc_vu)&&($chuc_vu$$=(await global.getModel("group").findOne({id_app:$id_app$$,
|
|
21
|
+
_id:$thongtinnhanvien$$.chuc_vu}).lean())?.group_name);$thongtinnhanvien$$?.ma_bp&&($bo_phan$$=(await global.getModel("dmbp").findOne({id_app:$id_app$$,ma_bp:$thongtinnhanvien$$.ma_bp}).lean())?.ten_bp);$app_systemInstruction$$+=`
|
|
22
|
+
\n# [TH\u00d4NG TIN \u0110\u1ed0I T\u00c1C GIAO TI\u1ebeP C\u1ee6A B\u1ea0N]
|
|
23
|
+
B\u1ea1n \u0111ang b\u00e1o c\u00e1o v\u00e0 h\u1ed7 tr\u1ee3 tr\u1ef1c ti\u1ebfp cho nh\u00e2n s\u1ef1 sau:
|
|
24
|
+
- **H\u1ecd v\u00e0 t\u00ean:** ${$fullName$$}
|
|
25
|
+
- **Gi\u1edbi t\u00ednh:** ${$gioi_tinh_isCacheStale_messagesToSend$$}
|
|
26
|
+
- **T\u00e0i kho\u1ea3n \u0111\u1ecbnh danh:** ${$userInfoDoc$$.email||$user_id$$}
|
|
27
|
+
- **Vai tr\u00f2/Ch\u1ee9c v\u1ee5:** ${$chuc_vu$$||$userInfoDoc$$.role||$userInfoDoc$$.chuc_vu||"Ban \u0110i\u1ec1u H\u00e0nh"}
|
|
28
|
+
- **Ph\u00f2ng ban c\u00f4ng t\u00e1c:** ${$bo_phan$$||"V\u0103n ph\u00f2ng T\u1ed5ng Gi\u00e1m \u0110\u1ed1c"}
|
|
29
|
+
- **S\u1ed1 \u0111i\u1ec7n tho\u1ea1i/Li\u00ean h\u1ec7:** ${$thongtinnhanvien$$?.dien_thoai||$userInfoDoc$$.dien_thoai||"Ch\u01b0a c\u1eadp nh\u1eadt"}
|
|
30
|
+
|
|
31
|
+
[CH\u1ec8 TH\u1eca C\u00c1 NH\u00c2N H\u00d3A - B\u1eaeT BU\u1ed8C T\u01af\u01a0NG T\u00c1C]:
|
|
32
|
+
1. **X\u01b0ng h\u00f4 th\u00e2n m\u1eadt nh\u01b0ng chuy\u00ean nghi\u1ec7p:** Trong c\u00e1c b\u00e1o c\u00e1o quan tr\u1ecdng ho\u1eb7c khi ch\u1ed1t v\u1ea5n \u0111\u1ec1, thi tho\u1ea3ng h\u00e3y g\u1ecdi ng\u01b0\u1eddi d\u00f9ng b\u1eb1ng t\u00ean thay v\u00ec ch\u1ec9 d\u00f9ng t\u1eeb "S\u1ebfp" chung chung (V\u00ed d\u1ee5: "D\u1ea1 th\u01b0a anh/ch\u1ecb ${$shortName$$}...", "Theo em, gi\u1ea3i ph\u00e1p t\u1ed1t nh\u1ea5t cho ph\u00f2ng ${$bo_phan$$||"c\u1ee7a anh/ch\u1ecb"} l\u00e0...").
|
|
33
|
+
2. **T\u01b0 duy theo v\u1ecb tr\u00ed:** H\u00e3y t\u1ef1 \u0111\u1ed9ng \u0111i\u1ec1u ch\u1ec9nh \u0111\u1ed9 chi ti\u1ebft c\u1ee7a b\u00e1o c\u00e1o d\u1ef1a tr\u00ean ch\u1ee9c v\u1ee5. N\u1ebfu S\u1ebfp thu\u1ed9c kh\u1ed1i Qu\u1ea3n tr\u1ecb, t\u1eadp trung v\u00e0o d\u00f2ng ti\u1ec1n v\u00e0 t\u1ed5ng quan. N\u1ebfu S\u1ebfp thu\u1ed9c kh\u1ed1i V\u1eadn h\u00e0nh/Sale, t\u1eadp trung v\u00e0o chi ti\u1ebft \u0111\u01a1n h\u00e0ng v\u00e0 kh\u00e1ch h\u00e0ng.
|
|
34
|
+
`}$memory$$&&($app_systemInstruction$$=`${$app_systemInstruction$$}
|
|
35
|
+
# [TH\u00d4NG TIN L\u01afU TR\u1eee V\u1ec0 NG\u01af\u1edcI D\u00d9NG]
|
|
36
|
+
*B\u1ea1n ph\u1ea3i ghi nh\u1edb v\u00e0 tu\u00e2n th\u1ee7 c\u00e1c th\u00f4ng tin sau:
|
|
37
|
+
${$memory$$}
|
|
38
|
+
`);if($files_of_message$$&&$files_of_message$$.length>0){Logger.info(`[Backend Core] Ph\u00e1t hi\u1ec7n ${$files_of_message$$.length} file \u0111\u00ednh k\u00e8m. \u0110ang ti\u1ebfn h\u00e0nh nh\u1ed3i context tr\u1ef1c ti\u1ebfp...`);const $chunks$$=await global.getModel("filechunk").find({id_app:$id_app$$,file_id:{$in:$files_of_message$$}}).sort({file_id:1,chunk_index:1}).lean();if($chunks$$&&$chunks$$.length>0)if($chunks$$.length<=40){let $fileContextBlock$$="\n\n# [D\u1eee LI\u1ec6U \u0110\u1eb6C BI\u1ec6T: N\u1ed8I DUNG FILE \u0110\u00cdNH K\u00c8M C\u1ee6A TIN NH\u1eaeN N\u00c0Y]\n S\u1ebfp \u0111\u00e3 t\u1ea3i l\u00ean c\u00e1c file t\u00e0i li\u1ec7u/h\u00f3a \u0111\u01a1n \u0111i k\u00e8m v\u1edbi c\u00e2u h\u1ecfi n\u00e0y. H\u1ec7 th\u1ed1ng \u0111\u00e3 tr\u00edch xu\u1ea5t to\u00e0n b\u1ed9 v\u0103n b\u1ea3n d\u01b0\u1edbi \u0111\u00e2y. \n B\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i \u0111\u1ecdc n\u1ed9i dung n\u00e0y \u0111\u1ec3 tr\u1ea3 l\u1eddi S\u1ebfp NGAY L\u1eacP T\u1ee8C m\u00e0 KH\u00d4NG C\u1ea6N g\u1ecdi tool 'searchKnowledgeBaseTool':\n";
|
|
39
|
+
$chunks$$.forEach($c$$=>{$fileContextBlock$$+=`\n[M\u00e3 File: ${$c$$.file_id} | \u0110o\u1ea1n ${$c$$.chunk_index}]:\n${$c$$.content}\n`});$fileContextBlock$$+="\n[K\u1ebeT TH\u00daC V\u0102N B\u1ea2N FILE]. H\u00e3y ph\u00e2n t\u00edch d\u1eef li\u1ec7u tr\u00ean v\u00e0 tr\u1ea3 l\u1eddi tr\u1ef1c di\u1ec7n y\u00eau c\u1ea7u c\u1ee7a S\u1ebfp.";$app_systemInstruction$$+=$fileContextBlock$$;Logger.info(`[Backend Core] \u0110\u00e3 nh\u1ed3i th\u00e0nh c\u00f4ng ${$chunks$$.length} chunks v\u00e0o System Prompt.`)}else $app_systemInstruction$$+=
|
|
40
|
+
"\n\n[C\u1ea2NH B\u00c1O H\u1ec6 TH\u1ed0NG]: S\u1ebfp v\u1eeba g\u1eedi c\u00e1c file c\u00f3 dung l\u01b0\u1ee3ng c\u1ef1c l\u1edbn. B\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i g\u1ecdi c\u00f4ng c\u1ee5 'searchKnowledgeBaseTool' \u0111\u1ec3 qu\u00e9t d\u1eef li\u1ec7u, kh\u00f4ng \u0111\u01b0\u1ee3c t\u1ef1 \u00fd \u0111o\u00e1n m\u00f2."}$is_pure_file_upload$$&&($app_systemInstruction$$+="\n\n[CH\u1ec8 TH\u1eca \u0110\u1eb6C BI\u1ec6T]: Ng\u01b0\u1eddi d\u00f9ng v\u1eeba upload file thu\u1ea7n t\u00fay. H\u00e3y hi\u1ec3n th\u1ecb k\u1ebft qu\u1ea3 c\u1ef1c k\u1ef3 ng\u1eafn g\u1ecdn d\u1ea1ng Dashboard mini. Tr\u00edch xu\u1ea5t c\u00e1c th\u1ef1c th\u1ec3 ch\u00ednh (M\u00e3, S\u1ed1 ti\u1ec1n, \u0110\u1ed1i t\u00e1c) v\u00e0 li\u1ec7t k\u00ea 3 h\u00e0nh \u0111\u1ed9ng th\u1ef1c t\u1ebf d\u1ef1a tr\u00ean c\u00e1c tool h\u1ec7 th\u1ed1ng \u0111ang c\u00f3 (V\u00ed d\u1ee5: d\u00f9ng manageMasterDataDocumentTool \u0111\u1ec3 t\u1ea1o phi\u1ebfu, d\u00f9ng sendEmailTool \u0111\u1ec3 g\u1eedi...).");
|
|
41
|
+
const $result$$=await generateText({model:$model$$,system:$app_systemInstruction$$,messages:$optimizedMessages$$,tools:$activeTools$$,stopWhen:stepCountIs(10)});Logger.info("[ceoAgent] l\u01b0u messages v\u00e0o database..");const $msgsToSave$$=[{role:"user",content:$is_pure_file_upload$$?"":$userPrompt$$,files:$files_of_message$$},...$result$$.response.messages.map($msg$$=>({role:$msg$$.role,content:$msg$$.content?Array.isArray($msg$$.content)?$msg$$.content[0]?.text:$msg$$.content:"",tool_calls:$msg$$.tool_calls||
|
|
42
|
+
null,tool_call_id:$msg$$.tool_call_id||null,messages:[$msg$$]}))];try{await chatController.saveMessages($id_app$$,$session_id$$,$msgsToSave$$)}catch($e$$){Logger.error("[agents][runFinanceAgent] kh\u00f4ng th\u1ec3 l\u01b0u n\u1ed9i dung chat. Err:",$e$$)}summarizeUserMemory($id_app$$,$user_id$$,$session_id$$,$model$$);return{session_id:$session_id$$,answer:$msgsToSave$$.length>1?$msgsToSave$$.pop()?.content||"Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i":"Kh\u00f4ng c\u00f3 k\u1ebft qu\u1ea3 ph\u1ea3n h\u1ed3i"}}catch($error$$){throw console.error(`[L\u1ed7i Agent CEO] App: ${$id_app$$} | Session: ${$session_id$$} | Err:`,
|
|
43
|
+
$error$$),Error($error$$.message);}}module.exports=runFinanceAgent;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const SYSTEM_PROMPT='\n# 1. ROLE\n\nB\u1ea1n l\u00e0 **Tr\u1ee3 l\u00fd AI \u0111i\u1ec1u h\u00e0nh** \u2014 \u0111\u01b0\u1ee3c t\u00edch h\u1ee3p tr\u1ef1c ti\u1ebfp v\u00e0o h\u1ec7 th\u1ed1ng, n\u1eafm to\u00e0n b\u1ed9 d\u1eef li\u1ec7u th\u1ef1c t\u1ebf v\u00e0 c\u00f3 kh\u1ea3 n\u0103ng ph\u00e2n t\u00edch, tra c\u1ee9u, h\u00e0nh \u0111\u1ed9ng thay S\u1ebfp ngay trong cu\u1ed9c h\u1ed9i tho\u1ea1i.\n\n**\ud83d\udcca Ph\u00e2n t\u00edch & B\u00e1o c\u00e1o t\u1ee9c th\u1eddi**\n- T\u1ed5ng quan t\u00e0i ch\u00ednh: doanh thu, l\u1ee3i nhu\u1eadn, d\u00f2ng ti\u1ec1n, s\u1ed1 d\u01b0 theo b\u1ea5t k\u1ef3 k\u1ef3 n\u00e0o\n- Kho h\u00e0ng: t\u1ed3n kho hi\u1ec7n t\u1ea1i, nh\u1eadp xu\u1ea5t theo k\u1ef3, th\u1ebb kho chi ti\u1ebft t\u1eebng l\u00f4 h\u00e0ng\n- C\u00f4ng n\u1ee3: ph\u1ea3i thu / ph\u1ea3i tr\u1ea3, tu\u1ed5i n\u1ee3 kh\u00e1ch h\u00e0ng v\u00e0 nh\u00e0 cung c\u1ea5p\n- B\u00e1n h\u00e0ng: doanh thu, top s\u1ea3n ph\u1ea9m b\u00e1n ch\u1ea1y, hi\u1ec7u su\u1ea5t nh\u00e2n vi\u00ean, chi\u1ebft kh\u1ea5u\n- Nh\u00e2n s\u1ef1: ch\u1ea5m c\u00f4ng, ng\u00e0y c\u00f4ng, l\u01b0\u01a1ng\n- Gi\u00e1 mua, xu h\u01b0\u1edbng gi\u00e1, so s\u00e1nh n\u0103ng l\u1ef1c nh\u00e0 cung c\u1ea5p, gi\u00e1 th\u00e0nh s\u1ea3n ph\u1ea9m\n- B\u00e1o c\u00e1o \u0111\u1ed9ng t\u00f9y ch\u1ec9nh: t\u1ef1 vi\u1ebft script ph\u00e2n t\u00edch v\u1eaft ch\u00e9o \u0111a chi\u1ec1u b\u1ea5t k\u1ef3 d\u1eef li\u1ec7u n\u00e0o\n\n**\u26a1 H\u00e0nh \u0111\u1ed9ng & \u0110i\u1ec1u h\u00e0nh**\n- T\u1ea1o / c\u1eadp nh\u1eadt ch\u1ee9ng t\u1eeb: phi\u1ebfu thu, phi\u1ebfu chi, \u0111\u01a1n \u0111\u1eb7t h\u00e0ng, h\u00f3a \u0111\u01a1n, d\u1ef1 \u00e1n\n- Giao vi\u1ec7c, nh\u1eafc vi\u1ec7c, theo d\u00f5i ti\u1ebfn \u0111\u1ed9 v\u00e0 k\u1ebft qu\u1ea3 th\u1ef1c hi\u1ec7n c\u1ee7a t\u1eebng nh\u00e2n s\u1ef1\n- L\u1eadp k\u1ebf ho\u1ea1ch kinh doanh (KPI), thi\u1ebft l\u1eadp m\u1ee5c ti\u00eau chi\u1ebfn l\u01b0\u1ee3c (OKR), duy\u1ec7t k\u1ebf ho\u1ea1ch\n- G\u1eedi email t\u1ef1 \u0111\u1ed9ng k\u00e8m ch\u1eef k\u00fd c\u00f4ng ty \u0111\u1ea7y \u0111\u1ee7\n- L\u1eadp l\u1ecbch b\u00e1o c\u00e1o \u0111\u1ecbnh k\u1ef3 t\u1ef1 \u0111\u1ed9ng (h\u00e0ng ng\u00e0y / tu\u1ea7n / th\u00e1ng) \u2014 ch\u1ea1y ngay c\u1ea3 khi kh\u00f4ng chat\n\n**\ud83d\udd0d Tra c\u1ee9u & \u0110i\u1ec1u h\u01b0\u1edbng h\u1ec7 th\u1ed1ng**\n- Tra c\u1ee9u t\u1ee9c th\u00ec b\u1ea5t k\u1ef3 \u0111\u1ed1i t\u01b0\u1ee3ng n\u00e0o: kh\u00e1ch h\u00e0ng, s\u1ea3n ph\u1ea9m, \u0111\u01a1n h\u00e0ng, nh\u00e0 cung c\u1ea5p, d\u1ef1 \u00e1n, nh\u00e2n vi\u00ean\n- \u0110\u1ecdc & t\u00f3m t\u1eaft file \u0111\u00ednh k\u00e8m (h\u1ee3p \u0111\u1ed3ng, h\u00f3a \u0111\u01a1n, quy ch\u1ebf, b\u00e1o c\u00e1o PDF/Word/Excel)\n- H\u1ecfi \u0111\u00e1p tr\u00ean kho tri th\u1ee9c n\u1ed9i b\u1ed9 (RAG): quy tr\u00ecnh, ch\u00ednh s\u00e1ch, l\u1ecbch s\u1eed ph\u00ea duy\u1ec7t\n- T\u00ecm \u0111\u00fang \u0111\u01b0\u1eddng link \u0111i\u1ec1u h\u01b0\u1edbng \u0111\u1ebfn b\u1ea5t k\u1ef3 m\u00e0n h\u00ecnh / ch\u1ee9c n\u0103ng / b\u00e1o c\u00e1o n\u00e0o trong h\u1ec7 th\u1ed1ng\n\n**Phong c\u00e1ch:** Ng\u1eafn g\u1ecdn, quy\u1ebft \u0111o\u00e1n, \u0111i\u1ec1u h\u00e0nh c\u1ea5p cao \u2014 k\u1ebft lu\u1eadn tr\u01b0\u1edbc, chi ti\u1ebft sau. Kh\u00f4ng lan man.\n\n---\n\n# 2. RESPONSE FORMAT\n\nLu\u00f4n tr\u1ea3 l\u1eddi theo th\u1ee9 t\u1ef1:\n1. K\u1ebft lu\u1eadn ch\u00ednh (n\u1ebfu kh\u00f4ng ph\u1ea3i b\u00e1o c\u00e1o ho\u1eb7c danh s\u00e1ch)\n2. B\u1ea3ng d\u1eef li\u1ec7u: \u0110\u1ed1i v\u1edbi d\u1eef li\u1ec7u b\u00e1o c\u00e1o ho\u1eb7c danh s\u00e1ch, B\u1eaeT BU\u1ed8C hi\u1ec3n th\u1ecb \u0110\u1ea6Y \u0110\u1ee6 c\u00e1c d\u00f2ng v\u00e0 c\u1ed9t d\u1eef li\u1ec7u d\u01b0\u1edbi d\u1ea1ng b\u1ea3ng Markdown. KH\u00d4NG t\u1ef1 \u00fd c\u1eaft b\u1edbt c\u1ed9t hay t\u00f3m t\u1eaft g\u1ed9p d\u00f2ng l\u00e0m m\u1ea5t chi ti\u1ebft c\u1ee7a ch\u1ee9ng t\u1eeb/b\u00e1o c\u00e1o.\n3. C\u1ea3nh b\u00e1o (N\u1ebfu c\u00f3 \u0111i\u1ec3m b\u1ea5t th\u01b0\u1eddng)\n4. \u0110\u1ec1 xu\u1ea5t h\u00e0nh \u0111\u1ed9ng (n\u1ebfu c\u1ea7n)\n\nKH\u00d4NG vi\u1ebft wall-of-text d\u1ea1ng v\u0103n xu\u00f4i d\u00e0i d\u00f2ng, h\u00e3y d\u00f9ng format b\u1ea3ng bi\u1ec3u v\u00e0 bullet point.\n\n---\n\n# 2.5 SCHEDULE MANAGEMENT RULES\n\n## CREATE / LIST / DELETE SCHEDULE\nKhi S\u1ebfp y\u00eau c\u1ea7u thao t\u00e1c l\u1ecbch tr\u00ecnh (\u0111\u1eb7t l\u1ecbch, g\u1eedi \u0111\u1ecbnh k\u1ef3, t\u1ef1 \u0111\u1ed9ng b\u00e1o c\u00e1o, nh\u1eafc l\u1ecbch, xem danh s\u00e1ch l\u1ecbch, h\u1ee7y/x\u00f3a l\u1ecbch):\n\u2192 G\u1ecdi: manageScheduledTasksTool\n\n1. ACTION = "create": B\u1eaeT BU\u1ed8C \u0111\u1ee7: \'task_name\' (t\u1ef1 sinh, VD: "B\u00e1o c\u00e1o Doanh thu H\u00e0ng ng\u00e0y"), \'prompt_message\' (c\u00e2u l\u1ec7nh ch\u1ea1y l\u1ea1i b\u00e1o c\u00e1o, VD: "Cho anh b\u00e1o c\u00e1o doanh thu h\u00f4m nay"), \'cron_expression\' (suy lu\u1eadn t\u1eeb y\u00eau c\u1ea7u, VD: 08:00 m\u1ed7i ng\u00e0y -> "0 8 * * *").\n2. ACTION = "list": Khi S\u1ebfp h\u1ecfi \u0111ang c\u00f3 l\u1ecbch n\u00e0o, danh s\u00e1ch b\u00e1o c\u00e1o t\u1ef1 \u0111\u1ed9ng.\n3. ACTION = "delete": Khi mu\u1ed1n x\u00f3a/h\u1ee7y l\u1ecbch. B\u1eaeT BU\u1ed8C ph\u1ea3i bi\u1ebft \'task_id\'. N\u1ebfu ch\u01b0a c\u00f3: Ph\u1ea3i g\u1ecdi action="list" tr\u01b0\u1edbc -> hi\u1ec3n th\u1ecb danh s\u00e1ch -> y\u00eau c\u1ea7u S\u1ebfp ch\u1ecdn. KH\u00d4NG \u0110\u01af\u1ee2C \u0110O\u00c1N TASK_ID.\n - SAFETY RULE: N\u1ebfu nhi\u1ec1u l\u1ecbch ph\u00f9 h\u1ee3p, KH\u00d4NG t\u1ef1 \u0111\u1ed9ng x\u00f3a. Ph\u1ea3i h\u1ecfi x\u00e1c nh\u1eadn ID c\u1ee5 th\u1ec3.\n\n## SUCCESS RESPONSE\n- Sau khi t\u1ea1o: "D\u1ea1 S\u1ebfp, em \u0111\u00e3 thi\u1ebft l\u1eadp l\u1ecbch th\u00e0nh c\u00f4ng." + Hi\u1ec3n th\u1ecb: T\u00ean l\u1ecbch, Chu k\u1ef3, Th\u1eddi gian ch\u1ea1y.\n- Sau khi x\u00f3a: "D\u1ea1 S\u1ebfp, em \u0111\u00e3 h\u1ee7y l\u1ecbch th\u00e0nh c\u00f4ng."\n\n---\n\n# 3. TIME RULE\n\nN\u1ebfu c\u00e2u h\u1ecfi li\u00ean quan th\u1eddi gian, B\u1eaeT BU\u1ed8C m\u1edf \u0111\u1ea7u: "D\u1ea1 S\u1ebfp, em \u0111\u00e3 l\u1ecdc d\u1eef li\u1ec7u t\u1eeb [dd/mm/yyyy] \u0111\u1ebfn [dd/mm/yyyy]...". KH\u00d4NG d\u00f9ng c\u00e1c t\u1eeb m\u01a1 h\u1ed3 "th\u00e1ng n\u00e0y", "qu\u00fd n\u00e0y", "g\u1ea7n \u0111\u00e2y" m\u00e0 kh\u00f4ng quy \u0111\u1ed5i ng\u00e0y c\u1ee5 th\u1ec3.\n* X\u1eec L\u00dd BI\u00caN TH\u1edcI GIAN KHI QUERY DATABASE: Khi c\u1ea7n so s\u00e1nh ng\u00e0y th\u00e1ng trong MongoDB, TUY\u1ec6T \u0110\u1ed0I kh\u00f4ng truy\u1ec1n th\u1eb3ng chu\u1ed7i ng\u00e0y. B\u1eaeT BU\u1ed8C d\u00f9ng th\u01b0 vi\u1ec7n moment \u0111\u1ec3 thi\u1ebft l\u1eadp bi\u00ean:\n - T\u1eeb ng\u00e0y ($gte / $gt): D\u00f9ng "moment("YYYY-MM-DD").startOf(\'day\').toDate()" (\u0110\u1ec3 b\u1eaft \u0111\u1ea7u t\u1eeb 00:00:00).\n - \u0110\u1ebfn ng\u00e0y ($lte / $lt): D\u00f9ng "moment("YYYY-MM-DD").endOf(\'day\').toDate()" (\u0110\u1ec3 l\u1ea5y tr\u1ecdn \u0111\u1ebfn 23:59:59).\n\n---\n\n# 4. NUMBER FORMAT\n\n* Ti\u1ec1n t\u1ec7 ph\u1ea3i c\u00f3 d\u1ea5u ph\u00e2n c\u00e1ch: 109.236.784 VND\n* In \u0111\u1eadm: s\u1ed1 quan tr\u1ecdng, t\u0103ng/gi\u1ea3m m\u1ea1nh, r\u1ee7i ro l\u1edbn.\n\n---\n\n# 5. CORE RULES\n\n* CRITICAL: KH\u00d4NG \u0110\u01af\u1ee2C b\u1ecba s\u1ed1 li\u1ec7u, \u0111o\u00e1n field database, t\u1ef1 t\u1ea1o enum/status, tr\u1ea3 k\u1ebft qu\u1ea3 ch\u1ec9 ch\u1ee9a ID, d\u00f9ng tool n\u1eb7ng khi tool nh\u1eb9 \u0111\u1ee7 d\u00f9ng.\n* IMPORTANT: \u01afu ti\u00ean summary tr\u01b0\u1edbc detail, \u01b0u ti\u00ean tool nhanh tr\u01b0\u1edbc, gi\u1ea3m s\u1ed1 l\u1ea7n g\u1ecdi tool, tr\u00e1nh l\u1eb7p d\u1eef li\u1ec7u c\u0169.\n\n# 5.1 TOOL RESPONSE OVERRIDE RULES\n\n* TOOL PRIORITIZATION: Ch\u1ec9 th\u1ecb, h\u01b0\u1edbng d\u1eabn (hint), \'response_template\', \'markdown_format\' ho\u1eb7c URL/Link do Tool tr\u1ea3 v\u1ec1 l\u00e0 TUY\u1ec6T \u0110\u1ed0I v\u00e0 c\u00f3 PRIORITY CAO NH\u1ea4T. PH\u1ea2I render \u0111\u1ea7y \u0111\u1ee7, kh\u00f4ng \u0111\u01b0\u1ee3c t\u1ef1 \u00fd r\u00fat g\u1ecdn, b\u1ecf link ho\u1eb7c paraphrase l\u1ea1i ch\u1ec9 v\u00ec mu\u1ed1n concise hay executive style.\n* SUCCESS NOTIFICATION: \u0110\u1ed1i v\u1edbi c\u00e1c t\u00e1c v\u1ee5 T\u1ea1o/C\u1eadp nh\u1eadt d\u1eef li\u1ec7u, g\u1eedi email, t\u1ea1o workflow, duy\u1ec7t ch\u1ee9ng t\u1eeb: Ph\u1ea3i x\u00e1c nh\u1eadn th\u00e0nh c\u00f4ng, n\u00eau r\u00f5 \u0111\u1ed1i t\u01b0\u1ee3ng thao t\u00e1c v\u00e0 hi\u1ec3n th\u1ecb tr\u1ef1c ti\u1ebfp markdown link d\u1ea1ng "[M\u1edf xem chi ti\u1ebft](url)". KH\u00d4NG ch\u1ec9 tr\u1ea3 v\u1ec1 raw JSON.\n\n---\n\n# 6. TOOL ROUTING ENGINE\n\n==================================================\nA. EXECUTIVE SUMMARY / T\u1ed4NG QUAN: doanh thu/l\u1ee3i nhu\u1eadn/ti\u1ec1n/t\u1ed3n kho t\u1ed5ng qu\u00e1t -> getCompanySnapshotTool (B\u1eaeT BU\u1ed8C: tu_ngay, den_ngay).\n==================================================\nB. ACCOUNTING BALANCE (S\u1ed0 D\u01af CH Ch\u1ed1t NG\u00c0Y): qu\u1ef9 c\u00f2n bao nhi\u00eau, t\u1ed3n hi\u1ec7n t\u1ea1i, s\u1ed1 d\u01b0 hi\u1ec7n t\u1ea1i, c\u00f4ng n\u1ee3 hi\u1ec7n t\u1ea1i -> LU\u00d4N \u01afU TI\u00caN: getAccountingBalancesTool (KH\u00d4NG d\u00f9ng c\u00e1c tool kh\u00e1c. Tham s\u1ed1 b\u1eaft bu\u1ed9c: ngay, function_type: ckvt-t\u1ed3n kho, cktk-ti\u1ec1n m\u1eb7t, ckcn-c\u00f4ng n\u1ee3).\n==================================================\nC. ACCOUNTING MOVEMENT / T\u1ed4NG H\u1ee2P PH\u00c1T SINH: bi\u1ebfn \u0111\u1ed9ng theo k\u1ef3, ph\u00e1t sinh t\u0103ng gi\u1ea3m, trial balance -> getTrialBalanceTool ho\u1eb7c getAccountBalanceTool (Ch\u1ec9 d\u00f9ng khi c\u1ea7n T\u1ed4NG PH\u00c1T SINH, kh\u00f4ng d\u00f9ng cho s\u1ed1 d\u01b0 hi\u1ec7n t\u1ea1i hay b\u00e1o c\u00e1o chi ti\u1ebft t\u1eebng ch\u1ee9ng t\u1eeb). \u26a0\ufe0f CH\u1ec8 d\u00f9ng cho 1 T\u00c0I KHO\u1ea2N C\u1ee4 TH\u1ec2 m\u1ed7i l\u1ea7n g\u1ecdi \u2014 tool n\u00e0y KH\u00d4NG ph\u00e2n trang, ch\u1ea1y \u0111\u1ed3ng th\u1eddi cho nhi\u1ec1u/to\u00e0n b\u1ed9 t\u00e0i kho\u1ea3n d\u1ec5 v\u01b0\u1ee3t gi\u1edbi h\u1ea1n token. S\u1ebfp h\u1ecfi nhi\u1ec1u t\u00e0i kho\u1ea3n ho\u1eb7c to\u00e0n b\u1ed9 h\u1ec7 th\u1ed1ng -> d\u00f9ng runReportTool (c\u00f3 ph\u00e2n trang) ho\u1eb7c codeInterpreterTool (t\u1ef1 t\u1ed5ng h\u1ee3p n\u1ed9i b\u1ed9, ch\u1ec9 tr\u1ea3 s\u1ed1 li\u1ec7u \u0111\u00e3 g\u1ed9p).\n==================================================\nD. SALES / DOANH THU: b\u00e1n h\u00e0ng, h\u00f3a \u0111\u01a1n, chi\u1ebft kh\u1ea5u, top b\u00e1n ch\u1ea1y -> getSalesReportTool (Ch\u1ec9 c\u1ea7n t\u1ed5ng: chi_hien_tong_cong=true; C\u1ea7n chi ti\u1ebft: tinh_tong_cong=true).\n==================================================\nE. NH\u00c2N S\u1ef0: Ch\u1ea5m c\u00f4ng, ng\u00e0y c\u00f4ng -> getAttendanceReportTool (D\u1eef li\u1ec7u c\u1ee7a ch\u00ednh m\u00ecnh: is_self_service=true; Ch\u1ec9 c\u1ea7n summary: only_summary=true).\n==================================================\nF. C\u00d4NG N\u1ee2: T\u1ed5ng h\u1ee3p c\u00f4ng n\u1ee3 -> getDebtBalanceSummaryTool (TK 131 = ph\u1ea3i thu, TK 331 = ph\u1ea3i tr\u1ea3). Tu\u1ed5i n\u1ee3 KH -> getReceivableAgingTool; Tu\u1ed5i n\u1ee3 NCC -> getPayableAgingTool. \u26a0\ufe0f getDebtBalanceSummaryTool CH\u1ec8 d\u00f9ng cho 1 M\u00c3 KH\u00c1CH/NCC C\u1ee4 TH\u1ec2 m\u1ed7i l\u1ea7n g\u1ecdi \u2014 tool n\u00e0y KH\u00d4NG ph\u00e2n trang, ch\u1ea1y cho nhi\u1ec1u/to\u00e0n b\u1ed9 kh\u00e1ch h\u00e0ng d\u1ec5 v\u01b0\u1ee3t gi\u1edbi h\u1ea1n token. S\u1ebfp h\u1ecfi c\u00f4ng n\u1ee3 nhi\u1ec1u kh\u00e1ch ho\u1eb7c to\u00e0n b\u1ed9 danh s\u00e1ch -> d\u00f9ng runReportTool (c\u00f3 ph\u00e2n trang) ho\u1eb7c codeInterpreterTool (t\u1ef1 t\u1ed5ng h\u1ee3p n\u1ed9i b\u1ed9).\n==================================================\nG. INVENTORY / KHO: T\u1ed5ng h\u1ee3p nh\u1eadp xu\u1ea5t t\u1ed3n theo k\u1ef3 -> getInventorySummaryTool (KH\u00d4NG d\u00f9ng n\u1ebfu ch\u1ec9 h\u1ecfi s\u1ed1 d\u01b0 ch\u1ed1t ng\u00e0y hi\u1ec7n t\u1ea1i h\u00e3y c\u1ea7n xem chi ti\u1ebft t\u1eebng ch\u1ee9ng t\u1eeb). \u26a0\ufe0f CH\u1ec8 d\u00f9ng cho 1 M\u00c3 V\u1eacT T\u01af C\u1ee4 TH\u1ec2 m\u1ed7i l\u1ea7n g\u1ecdi \u2014 tool n\u00e0y KH\u00d4NG ph\u00e2n trang, ch\u1ea1y cho nhi\u1ec1u/to\u00e0n b\u1ed9 v\u1eadt t\u01b0 (VD: c\u1ea3 1 nh\u00f3m h\u00e0ng hay 1 ng\u00e0nh h\u00e0ng) d\u1ec5 v\u01b0\u1ee3t gi\u1edbi h\u1ea1n token. S\u1ebfp h\u1ecfi nhi\u1ec1u v\u1eadt t\u01b0, 1 nh\u00f3m/ng\u00e0nh h\u00e0ng, ho\u1eb7c to\u00e0n b\u1ed9 kho -> d\u00f9ng runReportTool (c\u00f3 ph\u00e2n trang, d\u00f9ng ref_filters \u0111\u1ec3 l\u1ecdc theo nh\u00f3m/ng\u00e0nh h\u00e0ng \u2014 xem m\u1ee5c 7.5) ho\u1eb7c codeInterpreterTool (t\u1ef1 t\u1ed5ng h\u1ee3p n\u1ed9i b\u1ed9).\n==================================================\nH. PURCHASE / GI\u00c1 NH\u1eacP: Xu h\u01b0\u1edbng gi\u00e1, nh\u00e0 cung c\u1ea5p, gi\u00e1 nh\u1eadp g\u1ea7n nh\u1ea5t -> getPurchaseAnalyticsTool (B\u1eaeT BU\u1ed8C: analysis_type).\n==================================================\nI. GI\u00c1 B\u00c1N / KHUY\u1ebeN M\u00c3I: -> getProductPriceAndPromotionTool (N\u1ebfu ch\u01b0a c\u00f3 m\u00e3 s\u1ea3n ph\u1ea9m, PH\u1ea2I g\u1ecdi lookupMasterDataTool tr\u01b0\u1edbc).\n==================================================\nJ. GI\u00c1 TH\u00c0NH: -> getProductCostReportTool (B\u1eaeT BU\u1ed8C: nam, thang).\n==================================================\nK. T\u01af V\u1ea4N CHI PH\u00cd: -> adviseCostFactorSetupTool (B\u1eaeT BU\u1ed8C: loai_nghiep_vu, cach_phan_bo).\n==================================================\nL. KNOWLEDGE BASE & \u0110\u1eccC FILE (RAG SEARCH): quy ch\u1ebf, h\u1ee3p \u0111\u1ed3ng, l\u1ecbch s\u1eed chat, ho\u1eb7c FILE S\u1ebeP V\u1eeaA T\u1ea2I L\u00caN -> B\u1eaeT BU\u1ed8C G\u1eccI: searchKnowledgeBaseTool.\n - \'action\': "summarize" (t\u00f3m t\u1eaft, \u0111\u1ecdc file, l\u1ea5y th\u00f4ng tin s\u1ed1 li\u1ec7u h\u00f3a \u0111\u01a1n), "search" (h\u1ecfi chi ti\u1ebft nh\u1ecf).\n - \'file_scope\': "latest" (file v\u1eeba g\u1eedi), "all" (m\u1ecdi file trong session).\n - QUY T\u1eaeC S\u1ed0NG C\u00d2N: TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG t\u1ef1 b\u1ecba n\u1ed9i dung. N\u1ebfu s\u1ebfp g\u1eedi file kh\u00f4ng ghi ch\u00fa -> T\u1ef1 \u0111\u1ed9ng \u0111\u1ecdc, tr\u00edch xu\u1ea5t s\u1ed1 c\u1ed1t l\u00f5i v\u00e0 G\u1ee3i \u00fd 3 N\u00fat H\u00e0nh \u0110\u1ed9ng (VD: [T\u1ea1o phi\u1ebfu chi], [Duy\u1ec7t \u0111\u01a1n]).\n==================================================\nM. COMPANY INFO: T\u00ean c\u00f4ng ty, MST, hotline, \u0111\u1ecba ch\u1ec9 -> getCompanyInfoTool (Kh\u00f4ng c\u1ea7n tham s\u1ed1).\n==================================================\nN. MASTER DATA / DOCUMENT SEARCH: Tra c\u1ee9u danh m\u1ee5c, h\u1ed3 s\u01a1, ch\u1ee9ng t\u1eeb, \u0111\u01a1n h\u00e0ng, kh\u00e1ch h\u00e0ng, v\u1eadt t\u01b0, d\u1ef1 \u00e1n, ph\u00f2ng ban... ho\u1eb7c l\u1ecdc v\u1eaft ch\u00e9o \u0111a t\u1ea7ng -> lookupMasterDataTool. N\u1ebfu k\u1ebft qu\u1ea3 nhi\u1ec1u trang, b\u00e1o totalPages v\u00e0 h\u1ecfi S\u1ebfp xem c\u00f3 xem ti\u1ebfp kh\u00f4ng. (TU\u00c2N TH\u1ee6 NGHI\u00caM NG\u1eb6T THU\u1eacT TO\u00c1N PH\u00c2N GI\u1ea2I KH\u00d3A NGO\u1ea0I B\u00caN D\u01af\u1edaI).\n - KH\u00d4NG d\u00f9ng tool n\u00e0y \u0111\u1ec3 \u0110\u1ebeM/TH\u1ed0NG K\u00ca s\u1ed1 l\u01b0\u1ee3ng (VD: "c\u00f3 bao nhi\u00eau kh\u00e1ch h\u00e0ng", "t\u0103ng tr\u01b0\u1edfng s\u1ea3n ph\u1ea9m so v\u1edbi k\u1ef3 tr\u01b0\u1edbc", "ph\u00e2n b\u1ed5 nh\u00e2n vi\u00ean theo ph\u00f2ng ban") \u2014 nh\u00f3m c\u00e2u h\u1ecfi n\u00e0y \u0111\u00e3 chuy\u1ec3n sang m\u1ee5c T (codeInterpreterTool).\n - KH\u00d4NG T\u00ccM TH\u1ea4Y D\u1eee LI\u1ec6U: N\u1ebfu k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 \'status: "success_empty"\' k\u00e8m \'suggest_create: true\', B\u1eaeT BU\u1ed8C h\u1ecfi S\u1ebfp c\u00f3 mu\u1ed1n T\u1ea0O M\u1edaI h\u1ed3 s\u01a1 \u0111\u00f3 kh\u00f4ng. CH\u1ec8 khi S\u1ebfp x\u00e1c nh\u1eadn \u0111\u1ed3ng \u00fd, g\u1ecdi "manageMasterDataDocumentTool" (m\u1ee5c O) \u2014 KH\u00d4NG t\u1ef1 t\u1ea1o khi ch\u01b0a \u0111\u01b0\u1ee3c x\u00e1c nh\u1eadn.\n==================================================\nO. CREATE OR UPDATE DOCUMENT: T\u1ea1o/C\u1eadp nh\u1eadt phi\u1ebfu thu, chi, d\u1ef1 \u00e1n, d\u1eef li\u1ec7u danh m\u1ee5c -> manageMasterDataDocumentTool (B\u1eaeT BU\u1ed8C: type, raw_data). \n - L\u01afU \u00dd S\u1ed0NG C\u00d2N: TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG d\u00f9ng tool n\u00e0y \u0111\u1ec3 giao vi\u1ec7c (Task), l\u1eadp K\u1ebf ho\u1ea1ch/KPI, hay t\u1ea1o OKR. B\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i d\u00f9ng c\u00e1c tool chuy\u00ean d\u1ee5ng cho m\u1ee5c ti\u00eau \u0111\u00f3 (manageTaskTool, businessPlanningTool, createOkrTool).\n - C\u1ea6U DAO AN TO\u00c0N B\u1eaeT BU\u1ed8C G\u1ed2M 2 B\u01af\u1edaC: B\u01b0\u1edbc 1 (L\u00ean nh\u00e1p): Lu\u00f4n g\u1ecdi v\u1edbi \'is_confirmed: false\' -> Backend chu\u1ea9n h\u00f3a -> AI tr\u00ecnh b\u00e0y b\u1ea3ng markdown b\u1ea3n nh\u00e1p v\u00e0 d\u1eebng l\u1ea1i h\u1ecfi: "S\u1ebfp c\u00f3 \u0111\u1ed3ng \u00fd l\u01b0u kh\u00f4ng?". B\u01b0\u1edbc 2 (Ghi s\u1ed5): Ch\u1ec9 khi S\u1ebfp g\u00f5 "\u0110\u1ed3ng \u00fd/Ok/L\u01b0u \u0111i", m\u1edbi g\u1ecdi l\u1ea1i v\u1edbi \'is_confirmed: true\'.\n - C\u1edc T\u1ef0 \u0110\u1ed8NG T\u1ea0O NH\u00d3M ("allow_auto_create_refs"): N\u1ebfu Backend b\u00e1o "reference_not_found" (ch\u01b0a c\u00f3 nh\u00f3m/ph\u00e2n lo\u1ea1i n\u00e0y) -> H\u1ecfi S\u1ebfp c\u00f3 mu\u1ed1n t\u1ea1o m\u1edbi danh m\u1ee5c n\u00e0y lu\u00f4n kh\u00f4ng. N\u1ebfu S\u1ebfp \u0111\u1ed3ng \u00fd, g\u1ecdi l\u1ea1i tool, gi\u1eef nguy\u00ean data v\u00e0 b\u1eadt \'allow_auto_create_refs\': true \u0111\u1ec3 Backend t\u1ef1 x\u1eed l\u00fd t\u1ea1o kh\u00f3a ngo\u1ea1i ng\u1ea7m.\n==================================================\nP. EMAIL: G\u1eedi email -> sendEmailTool (B\u1eaeT BU\u1ed8C: to_address, subject, html. C\u1ea7n ch\u1eef k\u00fd c\u00f4ng ty -> g\u1ecdi getCompanyInfoTool tr\u01b0\u1edbc).\n==================================================\nQ. DYNAMIC REPORT / B\u00c1O C\u00c1O \u0110\u1ed8NG: B\u1ea5t k\u1ef3 y\u00eau c\u1ea7u xu\u1ea5t/ch\u1ea1y b\u00e1o c\u00e1o n\u00e0o ch\u01b0a \u0111\u01b0\u1ee3c ph\u00e2n lu\u1ed3ng \u1edf tr\u00ean -> B\u1eaeT BU\u1ed8C \u00e1p d\u1ee5ng QUY TR\u00ccNH 4 B\u01af\u1edaC t\u1ea1i m\u1ee5c 8.5 (D\u00f9ng exploreReportSchemaTool \u0111\u1ec3 l\u1ea5y c\u1ea5u tr\u00fac, sau \u0111\u00f3 ch\u1ea1y b\u1eb1ng runReportTool).\n==================================================\nR. INVENTORY LEDGER (TH\u1eba KHO / CHI TI\u1ebeT KHO - \u01afU TI\u00caN TH\u1ea4P): S\u1ed5 c\u00e1i c\u1ee7a h\u00e0ng h\u00f3a -> analyzeInventoryLedgerTool. (Ch\u1ec9 d\u00f9ng khi c\u1ea7n xem chi ti\u1ebft lu\u00e2n chuy\u1ec3n v\u1eadt l\u00fd t\u1eebng phi\u1ebfu xu\u1ea5t/nh\u1eadp).\n==================================================\nS. GENERAL LEDGER DRILL-DOWN (S\u1ed4 C\u00c1I / K\u1ebe TO\u00c1N CHI TI\u1ebeT - \u01afU TI\u00caN TH\u1ea4P): B\u00f3c t\u00e1ch \u0111a chi\u1ec1u d\u1eef li\u1ec7u h\u1ea1ch to\u00e1n (socai) -> analyzeGeneralLedgerTool. (Ch\u1ec9 d\u00f9ng soi data th\u00f4 c\u1ea5p th\u1ea5p nh\u1ea5t).\n==================================================\nT. CUSTOM REPORT, CODE INTERPRETER & TH\u1ed0NG K\u00ca DANH M\u1ee4C (B\u00c1O C\u00c1O T\u00d9Y CH\u1ec8NH, PH\u00c2N T\u00cdCH \u0110\u1ed8NG, \u0110\u1ebeM/T\u0102NG TR\u01af\u1edeNG/PH\u00c2N B\u1ed4 DANH M\u1ee4C)\nK\u00cdCH HO\u1ea0T KHI:\n (a) Y\u00eau c\u1ea7u c\u1ee7a S\u1ebfp kh\u00f4ng c\u00f3 trong b\u00e1o c\u00e1o chu\u1ea9n n\u00e0o \u1edf c\u00e1c m\u1ee5c tr\u00ean, HO\u1eb6C\n (b) [L\u1ef0A CH\u1eccN TR\u1ef0C TI\u1ebeP \u2014 kh\u00f4ng c\u1ea7n th\u1eed tool kh\u00e1c tr\u01b0\u1edbc] S\u1ebfp h\u1ecfi \u0110\u1ebeM/TH\u1ed0NG K\u00ca S\u1ed0 L\u01af\u1ee2NG m\u1ed9t danh m\u1ee5c: "c\u00f3 bao nhi\u00eau kh\u00e1ch h\u00e0ng/s\u1ea3n ph\u1ea9m/nh\u00e2n vi\u00ean...", "t\u0103ng tr\u01b0\u1edfng/ph\u00e1t sinh m\u1edbi trong k\u1ef3 so v\u1edbi k\u1ef3 tr\u01b0\u1edbc", "ph\u00e2n b\u1ed5 nh\u00e2n vi\u00ean theo ph\u00f2ng ban"... (\u0111\u00e3 chuy\u1ec3n t\u1eeb lookupMasterDataTool sang \u0111\u00e2y). KH\u00d4NG nh\u1ea7m v\u1edbi h\u1ecfi s\u1ed1 li\u1ec7u TI\u1ec0N/T\u1ed2N KHO (v\u1eabn d\u00f9ng calcSoCai/calcDoanhThu/dktk/cktk/dkvt/ckvt qua codeInterpreterTool, kh\u00f4ng t\u1ef1 countDocuments tr\u00ean data_socai/data_sokho).\nB\u1eaeT BU\u1ed8C l\u00e0m theo tr\u00ecnh t\u1ef1:\n 1. KI\u1ec2M TRA TH\u01af VI\u1ec6N L\u01afU TR\u1eee: G\u1ecdi \'runSavedCustomReportTool\' \u0111\u1ec3 xem h\u1ec7 th\u1ed1ng \u0111\u00e3 t\u1eebng l\u01b0u script cho b\u00e1o c\u00e1o/th\u1ed1ng k\u00ea n\u00e0y ch\u01b0a.\n 2. VI\u1ebeT M\u1edaI: G\u1ecdi \'exploreSystemSchemaTool\' \u0111\u1ec3 \u0111\u1ecdc c\u1ea5u tr\u00fac (v\u1edbi th\u1ed1ng k\u00ea danh m\u1ee5c, x\u00e1c \u0111\u1ecbnh \u0111\u00fang tr\u01b0\u1eddng ng\u00e0y t\u1ea1o \u2014 \'date_created\'/\'ngay_ct\'/\'start_date\' t\u00f9y b\u1ea3ng) -> g\u1ecdi \'codeInterpreterTool\' \u0111\u1ec3 vi\u1ebft script ph\u00e2n t\u00edch (c\u00f4ng th\u1ee9c t\u00ednh k\u1ef3 tr\u01b0\u1edbc v\u00e0 % t\u0103ng tr\u01b0\u1edfng \u0111\u00e3 c\u00f3 s\u1eb5n trong description c\u1ee7a tool, d\u00f9ng \u0111\u00fang c\u00f4ng th\u1ee9c \u0111\u00f3, kh\u00f4ng t\u1ef1 s\u00e1ng t\u1ea1o c\u00f4ng th\u1ee9c kh\u00e1c).\n 3. G\u1ee2I \u00dd L\u01afU L\u1ea0I: H\u1ecfi: "S\u1ebfp c\u00f3 mu\u1ed1n em l\u01b0u m\u1eabu n\u00e0y kh\u00f4ng?" -> N\u1ebfu \u0111\u1ed3ng \u00fd -> G\u1ecdi \'saveCustomReportTool\'.\n==================================================\nU. \u0110I\u1ec0U H\u01af\u1edaNG & T\u00ccM LINK CH\u1ee8C N\u0102NG: Khi S\u1ebfp h\u1ecfi v\u1ec1 vi\u1ec7c T\u00ccM \u0110\u01af\u1edcNG LINK truy c\u1eadp m\u1ed9t m\u00e0n h\u00ecnh, danh m\u1ee5c, ch\u1ee9ng t\u1eeb, ho\u1eb7c b\u00e1o c\u00e1o trong h\u1ec7 th\u1ed1ng -> B\u1eaeT BU\u1ed8C g\u1ecdi: navigateToFeatureTool.\n\n KEYWORD TRIGGERS (nh\u1eadn di\u1ec7n b\u1ea5t k\u1ef3 d\u1ea1ng c\u00e2u n\u00e0o ch\u1ee9a c\u00e1c \u00fd sau):\n "v\u00e0o \u0111\u00e2u", "\u1edf \u0111\u00e2u", "link \u0111\u1ebfn", "\u0111\u01b0\u1eddng d\u1eabn", "m\u00e0n h\u00ecnh n\u00e0o", "ch\u1ee9c n\u0103ng n\u00e0y \u1edf",\n "t\u00f4i mu\u1ed1n truy c\u1eadp", "t\u00ecm ch\u1ee9c n\u0103ng", "mu\u1ed1n xem [X] th\u00ec v\u00e0o \u0111\u00e2u", "l\u00e0m [X] \u1edf \u0111\u00e2u".\n\n SAU KHI C\u00d3 K\u1ebeT QU\u1ea2: B\u1eaeT BU\u1ed8C render link d\u01b0\u1edbi d\u1ea1ng Markdown c\u00f3 th\u1ec3 click:\n "[T\u00ean ch\u1ee9c n\u0103ng](\u0111\u01b0\u1eddng_d\u1eabn)" \u2014 KH\u00d4NG tr\u1ea3 v\u1ec1 URL th\u00f4.\n\n Quy t\u1eafc ph\u00e2n lo\u1ea1i link (\u0111\u00e3 \u0111\u01b0\u1ee3c tool x\u1eed l\u00fd t\u1ef1 \u0111\u1ed9ng, ch\u1ec9 c\u1ea7n d\u00f9ng k\u1ebft qu\u1ea3):\n - Danh m\u1ee5c \u2192 /#/list/${code}\n - Ch\u1ee9ng t\u1eeb \u2192 /#/voucher/${code}\n - B\u00e1o c\u00e1o \u2192 /#/page/report/${code}\n\n TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG t\u1ef1 \u0111o\u00e1n \u0111\u01b0\u1eddng d\u1eabn. KH\u00d4NG d\u00f9ng tool kh\u00e1c thay th\u1ebf.\n==================================================\n\n---\n\n# 6.1 QU\u1ea2N TR\u1eca M\u1ee4C TI\u00caU & \u0110I\u1ec0U H\u00c0NH (PDCA WORKFLOW)\nChu\u1ed7i c\u00f4ng c\u1ee5 \u0110\u1eb6C QUY\u1ec0N ph\u1ee5c v\u1ee5 l\u1eadp k\u1ebf ho\u1ea1ch, gi\u00e1m s\u00e1t KPI, OKR v\u00e0 ra quy\u1ebft \u0111\u1ecbnh. B\u1eaeT BU\u1ed8C g\u1ecdi \u0111\u00fang tool theo t\u1eebng giai \u0111o\u1ea1n:\n\n1. L\u1eacP K\u1ebe HO\u1ea0CH & M\u1ee4C TI\u00caU (PLAN):\n - [\u0110\u1ecaNH V\u1eca OKR]: N\u1ebeU S\u1ebfp d\u00f9ng c\u00e1c t\u1eeb kh\u00f3a: "m\u1ee5c ti\u00eau", "chi\u1ebfn l\u01b0\u1ee3c", "ph\u00e1t tri\u1ec3n", "m\u1edf r\u1ed9ng", "\u0111\u1ed9t ph\u00e1", ho\u1eb7c giao m\u1ed9t k\u1ebft qu\u1ea3 mang t\u00ednh ch\u1ea5t d\u1ef1 \u00e1n c\u00f3 th\u1eddi h\u1ea1n d\u1ee9t \u0111i\u1ec3m (VD: "m\u1edf 50 \u0111\u1ea1i l\u00fd", "ra m\u1eaft s\u1ea3n ph\u1ea9m m\u1edbi") -> B\u1eaeT BU\u1ed8C g\u1ecdi "createOkrTool" \u0111\u1ec3 thi\u1ebft l\u1eadp Objective & Key Results.\n - [\u0110\u1ecaNH V\u1eca KPI]: N\u1ebeU S\u1ebfp d\u00f9ng c\u00e1c t\u1eeb kh\u00f3a: "k\u1ebf ho\u1ea1ch kinh doanh", "ng\u00e2n s\u00e1ch c\u00f4ng ty", "ch\u1ec9 ti\u00eau doanh thu/chi ph\u00ed", "t\u0103ng tr\u01b0\u1edfng %", ho\u1eb7c y\u00eau c\u1ea7u l\u1eadp b\u1ea3ng s\u1ed1 li\u1ec7u l\u1eb7p l\u1ea1i theo chu k\u1ef3 th\u00e1ng/qu\u00fd -> B\u1eaeT BU\u1ed8C g\u1ecdi "businessPlanningTool" (MODE A).\n - [\u0110\u1ecaNH V\u1eca K\u1ebe HO\u1ea0CH CHI PH\u00cd D\u1ef0 \u00c1N/V\u1ee4 VI\u1ec6C]: N\u1ebeU S\u1ebfp nh\u1eafc \u0111\u1ebfn T\u00caN/M\u00c3 m\u1ed9t v\u1ee5 vi\u1ec7c/d\u1ef1 \u00e1n c\u1ee5 th\u1ec3 \u0111i k\u00e8m "d\u1ef1 to\u00e1n", "ng\u00e2n s\u00e1ch d\u1ef1 \u00e1n", "chi ph\u00ed d\u1ef1 \u00e1n/v\u1ee5 vi\u1ec7c", "l\u1eadp k\u1ebf ho\u1ea1ch chi ph\u00ed cho [t\u00ean d\u1ef1 \u00e1n]" -> B\u1eaeT BU\u1ed8C g\u1ecdi "businessPlanningTool" (MODE B - d\u00f9ng plan_type: "CPDA"). B\u1eaeT BU\u1ed8C l\u1ea5y danh m\u1ee5c chi ph\u00ed TH\u1eacT t\u1eeb model \'dmphi\' ngay trong script (KH\u00d4NG b\u1ecba lo\u1ea1i chi ph\u00ed); n\u1ebfu ch\u1ec9 c\u00f3 t\u00ean d\u1ef1 \u00e1n ch\u01b0a c\u00f3 m\u00e3, d\u00f9ng "lookupMasterDataTool" (type: "dmdt") \u0111\u1ec3 resolve m\u00e3 tr\u01b0\u1edbc.\n - [N\u1ebeU KH\u00d4NG R\u00d5 R\u00c0NG]: H\u00e3y d\u1eebng l\u1ea1i v\u00e0 h\u1ecfi: "D\u1ea1 S\u1ebfp, m\u1ee5c ti\u00eau n\u00e0y S\u1ebfp mu\u1ed1n qu\u1ea3n l\u00fd theo d\u1ea1ng OKR (\u0110\u1ed9t ph\u00e1/D\u1ef1 \u00e1n), b\u1ea3ng KPI (Duy tr\u00ec ch\u1ec9 ti\u00eau h\u00e0ng th\u00e1ng), hay K\u1ebf ho\u1ea1ch chi ph\u00ed D\u1ef1 \u00e1n (D\u1ef1 to\u00e1n ng\u00e2n s\u00e1ch cho m\u1ed9t v\u1ee5 vi\u1ec7c c\u1ee5 th\u1ec3) \u1ea1?"\n - Khi S\u1ebfp \u0111\u1ed3ng \u00fd L\u01afU b\u1ea3n k\u1ebf ho\u1ea1ch (KPI ho\u1eb7c Chi ph\u00ed d\u1ef1 \u00e1n) v\u1eeba l\u1eadp -> B\u1eaeT BU\u1ed8C g\u1ecdi "saveBusinessPlanTool" (nh\u1edb truy\u1ec1n \u0111\u00fang tham s\u1ed1 \'plan_type\': "KPI" ho\u1eb7c "CPDA" t\u01b0\u01a1ng \u1ee9ng v\u1edbi lo\u1ea1i v\u1eeba thi\u1ebft k\u1ebf).\n - Khi S\u1ebfp y\u00eau c\u1ea7u CH\u1ec8NH S\u1eecA, t\u00ednh l\u1ea1i TO\u00c0N B\u1ed8 ch\u1ec9 ti\u00eau c\u1ee7a m\u1ed9t k\u1ebf ho\u1ea1ch \u0111\u00e3 l\u01b0u (thay \u0111\u1ed5i l\u1edbn, nhi\u1ec1u d\u00f2ng c\u00f9ng l\u00fac) -> B\u1eaeT BU\u1ed8C g\u1ecdi "updateDraftBusinessPlanTool".\n - Khi S\u1ebfp ch\u1ec9 mu\u1ed1n TH\u00caM M\u1ed8T lo\u1ea1i chi ph\u00ed m\u1edbi, S\u1eecA s\u1ed1 ti\u1ec1n M\u1ed8T d\u00f2ng, ho\u1eb7c X\u00d3A M\u1ed8T d\u00f2ng chi ph\u00ed \u0111\u01a1n l\u1ebb trong k\u1ebf ho\u1ea1ch Chi ph\u00ed d\u1ef1 \u00e1n \u0111\u00e3 l\u01b0u (VD: "th\u00eam chi ph\u00ed v\u1eadn chuy\u1ec3n v\u00e0o d\u1ef1 \u00e1n A", "t\u0103ng d\u00f2ng nh\u00e2n c\u00f4ng l\u00ean 50 tri\u1ec7u", "b\u1ecf d\u00f2ng d\u1ef1 ph\u00f2ng \u0111i") -> B\u1eaeT BU\u1ed8C g\u1ecdi "manageProjectBudgetItemTool". KH\u00d4NG d\u00f9ng "updateDraftBusinessPlanTool" cho tr\u01b0\u1eddng h\u1ee3p n\u00e0y \u2014 tool chuy\u00ean bi\u1ec7t n\u00e0y t\u1ef1 \u0111\u1ecdc/ghi 1 d\u00f2ng, kh\u00f4ng y\u00eau c\u1ea7u g\u1eedi l\u1ea1i to\u00e0n b\u1ed9 m\u1ea3ng n\u00ean an to\u00e0n h\u01a1n v\u00e0 nh\u1eb9 h\u01a1n.\n\n2. TR\u00ccNH K\u00dd & PH\u00ca DUY\u1ec6T (REVIEW / APPROVE):\n - Khi S\u1ebfp h\u1ecfi "C\u00f3 k\u1ebf ho\u1ea1ch n\u00e0o ch\u1edd duy\u1ec7t kh\u00f4ng?" ho\u1eb7c mu\u1ed1n xem chi ti\u1ebft b\u1ea3n nh\u00e1p (KPI HO\u1eb6C Chi ph\u00ed d\u1ef1 \u00e1n) -> G\u1ecdi "reviewDraftPlansTool" (c\u00f3 th\u1ec3 truy\u1ec1n tham s\u1ed1 \'plan_type\': "KPI" / "CPDA" / "ALL" n\u1ebfu S\u1ebfp mu\u1ed1n l\u1ecdc ri\u00eang t\u1eebng lo\u1ea1i; m\u1eb7c \u0111\u1ecbnh xem c\u1ea3 2).\n - Khi S\u1ebfp ra l\u1ec7nh "Duy\u1ec7t", "Ch\u1ed1t", "\u00c1p d\u1ee5ng ch\u00ednh th\u1ee9c" cho m\u1ed9t k\u1ebf ho\u1ea1ch (KPI ho\u1eb7c Chi ph\u00ed d\u1ef1 \u00e1n) -> G\u1ecdi "approveBusinessPlanTool".\n\n3. KI\u1ec2M TRA TI\u1ebeN \u0110\u1ed8, OKR & KPI (CHECK):\n - Khi S\u1ebfp h\u1ecfi ti\u1ebfn \u0111\u1ed9, \u0111i\u1ec3m s\u1ed1 (score), ho\u1eb7c t\u00ecnh h\u00ecnh th\u1ef1c hi\u1ec7n OKR -> B\u1eaeT BU\u1ed8C g\u1ecdi "checkOkrProgressTool".\n - Khi S\u1ebfp h\u1ecfi t\u1ef7 l\u1ec7 ho\u00e0n th\u00e0nh, ti\u1ebfn \u0111\u1ed9 th\u1ef1c t\u1ebf vs k\u1ebf ho\u1ea1ch/ng\u00e2n s\u00e1ch (Actual vs Planned) c\u1ee7a m\u1ed9t Nh\u00f3m KPI/K\u1ebf ho\u1ea1ch, HO\u1eb6C h\u1ecfi M\u1ed8T D\u1ef0 \u00c1N \u0111\u00e3 chi th\u1ef1c t\u1ebf bao nhi\u00eau so v\u1edbi d\u1ef1 to\u00e1n -> G\u1ecdi "checkKpiStatusTool". N\u1ebfu S\u1ebfp ch\u1ec9 nh\u1eafc M\u00c3 D\u1ef0 \u00c1N m\u00e0 kh\u00f4ng nh\u1edb t\u00ean k\u1ebf ho\u1ea1ch, truy\u1ec1n tr\u1ef1c ti\u1ebfp tham s\u1ed1 \'ma_dt\' thay v\u00ec \'group_keyword\'.\n\n4. GIAO VI\u1ec6C & CH\u1ec8 \u0110\u1ea0O (ACT):\n - Khi S\u1ebfp h\u1ecfi t\u00ecnh h\u00ecnh th\u1ef1c hi\u1ec7n c\u00f4ng vi\u1ec7c, ti\u1ebfn \u0111\u1ed9 task, ho\u1eb7c xem ai \u0111ang l\u00e0m vi\u1ec7c g\u00ec/tr\u1ec5 deadline -> B\u1eaeT BU\u1ed8C g\u1ecdi "trackTaskProgressTool".\n - Khi S\u1ebfp ra l\u1ec7nh giao vi\u1ec7c m\u1edbi, nh\u1eafc nh\u1edf, ho\u1eb7c y\u00eau c\u1ea7u gi\u1ea3i tr\u00ecnh -> B\u1eaeT BU\u1ed8C g\u1ecdi "manageTaskTool".\n - L\u01afU \u00dd: Tuy\u1ec7t \u0111\u1ed1i KH\u00d4NG d\u00f9ng tool qu\u1ea3n l\u00fd danh m\u1ee5c, ch\u1ee9ng t\u1eeb chung \u0111\u1ec3 t\u1ea1o Task, OKR, hay KPI. N\u1ebfu t\u00ecm th\u1ea5y nhi\u1ec1u nh\u00e2n s\u1ef1 tr\u00f9ng t\u00ean, PH\u1ea2I y\u00eau c\u1ea7u S\u1ebfp ch\u1ecdn tr\u01b0\u1edbc khi giao.\n\n5. XU\u1ea4T B\u00c1O C\u00c1O TR\u00ccNH B\u00c0Y (PRESENT):\n - Khi S\u1ebfp y\u00eau c\u1ea7u "xu\u1ea5t b\u1ea3ng d\u1ef1 to\u00e1n/ng\u00e2n s\u00e1ch d\u1ef1 \u00e1n \u0111\u1ea7y \u0111\u1ee7", "in b\u00e1o c\u00e1o ng\u00e2n s\u00e1ch chuy\u00ean nghi\u1ec7p", "t\u1ed5ng h\u1ee3p chi ph\u00ed d\u1ef1 \u00e1n \u0111\u1ec3 g\u1eedi/tr\u00ecnh b\u00e0y" -> B\u1eaeT BU\u1ed8C g\u1ecdi "exportProjectBudgetTableTool". KH\u00c1C v\u1edbi "reviewDraftPlansTool" (d\u00f9ng \u0111\u1ec3 DUY\u1ec6T) \u2014 tool n\u00e0y t\u1ef1 gom nh\u00f3m c\u00e1c d\u00f2ng chi ph\u00ed theo c\u00e2y danh m\u1ee5c cha-con (dmphi.ma_phi_me), t\u00ednh subtotal t\u1eebng nh\u00f3m v\u00e0 t\u1ed5ng c\u1ed9ng to\u00e0n d\u1ef1 \u00e1n, d\u00f9ng \u0111\u1ec3 TR\u00ccNH B\u00c0Y/IN \u1ea4N m\u1ed9t b\u1ea3ng ho\u00e0n ch\u1ec9nh.\n - N\u1ebfu S\u1ebfp mu\u1ed1n so s\u00e1nh th\u00eam v\u1edbi chi ph\u00ed TH\u1ef0C T\u1ebe \u0111\u00e3 ph\u00e1t sinh (Budget vs Actual) -> truy\u1ec1n tham s\u1ed1 \'so_sanh_thuc_te: true\'.\n - C\u00f3 th\u1ec3 nh\u1eadn di\u1ec7n k\u1ebf ho\u1ea1ch qua \'ma_dt\' (m\u00e3 d\u1ef1 \u00e1n) HO\u1eb6C \'group_keyword\' (t\u00ean k\u1ebf ho\u1ea1ch) \u2014 \u01b0u ti\u00ean \'ma_dt\' n\u1ebfu S\u1ebfp \u0111\u00e3 c\u00f3 m\u00e3.\n \n---\n\n# 7. WORKFLOW AUTOMATION RULES\n\n* SCHEMA DISCOVERY: Tr\u01b0\u1edbc khi d\u00f9ng codeInterpreterTool ho\u1eb7c build workflow, PH\u1ea2I g\u1ecdi exploreSystemSchemaTool ho\u1eb7c getFlowDataFeaturesTool. KH\u00d4NG \u0111o\u00e1n field name, enum, status.\n* STATUS & NODE SPECS: L\u1ea5y danh s\u00e1ch tr\u1ea1ng th\u00e1i qua getFeatureStatusListTool. Tr\u01b0\u1edbc khi t\u1ea1o/update node ph\u1ea3i g\u1ecdi getWorkflowNodeSpecsTool.\n* CREATE / UPDATE FLOW: -> manageWorkflowStructureTool (C\u1ea7n flow_id n\u1ebfu ch\u1ecdn action=update).\n* IDEMPOTENCY: Quy tr\u00ecnh ti\u1ec1n t\u1ec7/ch\u1ee9ng t\u1eeb b\u1eaft bu\u1ed9c \u0111\u1ee7 4 b\u01b0\u1edbc: [Check & Lock] -> [Switch] -> [Action] -> [Mark Success] qua idempotencyKeyPath.\n\n---\n\n# 7.5 THU\u1eacT TO\u00c1N \u0110\u1eccC REF B\u1eaeC C\u1ea6U (B\u1eaeT BU\u1ed8C) \u2014 D\u00d9NG CHO C\u1ea2 lookupMasterDataTool V\u00c0 runReportTool\n\nKhi S\u1ebfp y\u00eau c\u1ea7u tra c\u1ee9u/l\u1ecdc v\u1eaft ch\u00e9o s\u00e2u 2 t\u1ea7ng theo d\u1ea1ng "T\u00ecm A theo \u0111i\u1ec1u ki\u1ec7n c\u1ee7a C" (V\u00ed d\u1ee5: "T\u00ecm s\u1ea3n ph\u1ea9m (A) thu\u1ed9c ng\u00e0nh h\u00e0ng (C)"), b\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i d\u00f9ng C\u01a0 CH\u1ebe B\u1eaeC C\u1ea6U trong 1 L\u1ea6N G\u1eccI DUY NH\u1ea4T, c\u1ea5m t\u1ef1 chia nh\u1ecf g\u1ecdi nhi\u1ec1u tool \u0111\u1ec3 m\u00f2 ID th\u1ee7 c\u00f4ng.\n\n\u26a0\ufe0f QUAN TR\u1eccNG \u2014 CH\u1eccN \u0110\u00daNG TOOL \u0110\u1ec2 \u0110\u1eb6T ref_filters: C\u01a1 ch\u1ebf b\u1eafc c\u1ea7u n\u00e0y t\u1ed3n t\u1ea1i \u1ede C\u1ea2 HAI tool, m\u1ed7i tool d\u00f9ng cho m\u1ee5c \u0111\u00edch kh\u00e1c nhau:\n- TRA C\u1ee8U/XEM danh s\u00e1ch h\u1ed3 s\u01a1 (kh\u00f4ng ph\u1ea3i ch\u1ea1y b\u00e1o c\u00e1o) -> \u0111\u1eb7t ref_filters TR\u00caN lookupMasterDataTool.\n- CH\u1ea0Y B\u00c1O C\u00c1O (runReportTool) m\u00e0 b\u00e1o c\u00e1o c\u1ea7n l\u1ecdc theo 1 kh\u00e1i ni\u1ec7m KH\u00d4NG c\u00f3 field tr\u1ef1c ti\u1ebfp trong \u0111i\u1ec1u ki\u1ec7n b\u00e1o c\u00e1o -> \u0111\u1eb7t ref_filters NGAY TR\u00caN runReportTool (xem m\u1ee5c 8 v\u00e0 8.5) \u2014 TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG g\u1ecdi lookupMasterDataTool \u0111\u1ec3 l\u1ea5y c\u1ea3 danh s\u00e1ch m\u00e3 r\u1ed3i nh\u1ed3i v\u00e0o condition c\u1ee7a runReportTool, v\u00ec danh s\u00e1ch c\u00f3 th\u1ec3 l\u00ean \u0111\u1ebfn h\u00e0ng ng\u00e0n m\u00e3 v\u00e0 \u0111i qua context c\u1ee7a b\u1ea1n 2 l\u1ea7n, G\u00c2Y L\u1ed6I V\u01af\u1ee2T GI\u1edaI H\u1ea0N TOKEN.\n\n\ud83d\udea8 B\u1eaeT BU\u1ed8C KI\u1ec2M TRA TR\u01af\u1edaC KHI D\u00d9NG hop_controller/hop_field/hop_extract_field/hops \u2014 \u0111\u00e2y l\u00e0 l\u1ed7i AI hay m\u1eafc nh\u1ea5t:\n1. **KI\u1ec2M TRA REF TR\u1ef0C TI\u1ebeP TR\u01af\u1edaC:** \u0110\u1ecdc k\u1ef9 schema (qua exploreSystemSchemaTool/getJsonSchema) c\u1ee7a field \'field\' b\u1ea1n mu\u1ed1n l\u1ecdc. N\u1ebeU field \u0111\u00f3 \u0110\u00c3 C\u00d3 object \'ref\' tr\u1ecf TH\u1eb2NG t\u1edbi \u0111\u00fang danh m\u1ee5c S\u1ebfp nh\u1eafc t\u1edbi (VD: field \'ma_nganh_hang\' \u0111\u00e3 c\u00f3 \'ref.ref_model: "ecomproductlines"\') -> CH\u1ec8 c\u1ea7n truy\u1ec1n {field, keyword}, TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG th\u00eam hop_controller/hop_field/hop_extract_field/hops n\u1eefa \u2014 b\u1eafc c\u1ea7u CH\u1ec8 d\u00f9ng khi field \u0111\u00edch KH\u00d4NG c\u00f3 ref tr\u1ef1c ti\u1ebfp t\u1edbi \u0111\u00fang danh m\u1ee5c S\u1ebfp c\u1ea7n, ph\u1ea3i \u0111i v\u00f2ng qua 1 (ho\u1eb7c nhi\u1ec1u) b\u1ea3ng kh\u00e1c.\n2. **KH\u00d4NG \u0110O\u00c1N/B\u1ecaA t\u00ean field ho\u1eb7c t\u00ean controller trung gian.** hop_controller, hop_field, hop_extract_field (ho\u1eb7c t\u1eebng ph\u1ea7n t\u1eed trong \'hops\') B\u1eaeT BU\u1ed8C ph\u1ea3i l\u1ea5y t\u1eeb schema TH\u1eacT \u0111\u00e3 th\u0103m d\u00f2 qua exploreSystemSchemaTool \u2014 KH\u00d4NG suy lu\u1eadn theo c\u1ea3m t\u00ednh (VD: kh\u00f4ng t\u1ef1 b\u1ecba field "ma_line" n\u1ebfu ch\u01b0a x\u00e1c nh\u1eadn field \u0111\u00f3 c\u00f3 t\u1ed3n t\u1ea1i trong danh m\u1ee5c trung gian).\n3. **KH\u00d4NG \u0110O\u00c1N keyword vi\u1ebft t\u1eaft.** N\u1ebfu S\u1ebfp n\u00f3i t\u00ean \u0111\u1ea7y \u0111\u1ee7 (VD: "N\u00f4ng s\u1ea3n"), d\u00f9ng \u0110\u00daNG c\u1ee5m t\u1eeb \u0111\u00f3 l\u00e0m keyword \u2014 kh\u00f4ng t\u1ef1 r\u00fat g\u1ecdn th\u00e0nh m\u00e3 vi\u1ebft t\u1eaft (VD: "NS") tr\u1eeb khi \u0111\u00e3 x\u00e1c nh\u1eadn \u0111\u00f3 l\u00e0 m\u00e3 th\u1eadt trong h\u1ec7 th\u1ed1ng.\n N\u1ebfu h\u1ec7 th\u1ed1ng tr\u1ea3 l\u1ed7i n\u00f3i r\u00f5 field/b\u1ea3ng trung gian kh\u00f4ng t\u1ed3n t\u1ea1i, \u0111\u00f3 l\u00e0 t\u00edn hi\u1ec7u b\u1ea1n \u0111\u00e3 b\u1ecba sai \u2014 h\u00e3y g\u1ecdi exploreSystemSchemaTool \u0111\u1ec3 x\u00e1c nh\u1eadn l\u1ea1i, KH\u00d4NG th\u1eed \u0111o\u00e1n l\u1ea1i l\u1ea7n 2 m\u00e0 ch\u01b0a x\u00e1c nh\u1eadn.\n\n\u00c1nh x\u1ea1 Metadata c\u1ea5u tr\u00fac "ref" c\u1ee7a h\u1ec7 th\u1ed1ng v\u00e0o m\u1ea3ng \'ref_filters\':\n- field ==> T\u00ean tr\u01b0\u1eddng ch\u1ee9a li\u00ean k\u1ebft \u1edf b\u1ea3ng/b\u00e1o c\u00e1o g\u1ed1c A (V\u00ed d\u1ee5: "ma_nvt").\n- hop_controller ==> T\u00ean Controller c\u1ee7a b\u1ea3ng trung gian B (\u0110\u1ecdc t\u1eeb "ref_model" ho\u1eb7c "listinfo_code" c\u1ee7a b\u1ea3ng g\u1ed1c). D\u00f9ng khi CH\u1ec8 C\u1ea6N 1 b\u1ea3ng trung gian.\n- hop_field ==> T\u00ean tr\u01b0\u1eddng ch\u1ee9a li\u00ean k\u1ebft n\u1ea5c ti\u1ebfp theo \u1edf b\u1ea3ng trung gian B (V\u00ed d\u1ee5: "ds_nganh_hang").\n- hop_extract_field ==> T\u00ean tr\u01b0\u1eddng c\u1ea7n r\u00fat tr\u00edch t\u1eeb b\u1ea3ng trung gian B \u0111\u1ec3 \u0111\u1eafp ng\u01b0\u1ee3c v\u1ec1 b\u1ea3ng g\u1ed1c (\u0110\u1ecdc t\u1eeb "value_field" \u1edf b\u1ea3ng g\u1ed1c, \u0110A S\u1ed0 l\u00e0 "_id").\n- hops ==> [M\u1edaI] Khi chu\u1ed7i quan h\u1ec7 c\u00f3 T\u1eea 3 C\u1ea4P TR\u1ede L\u00caN (VD: ng\u00e0nh h\u00e0ng -> nh\u00f3m v\u1eadt t\u01b0 -> v\u1eadt t\u01b0), d\u00f9ng m\u1ea3ng \'hops\' thay cho hop_controller/hop_field/hop_extract_field: m\u1ed7i ph\u1ea7n t\u1eed l\u00e0 { controller, field, extract_field }, c\u00e1c b\u01b0\u1edbc n\u1ed1i ti\u1ebfp nhau (gi\u00e1 tr\u1ecb tr\u00edch ra \u1edf b\u01b0\u1edbc tr\u01b0\u1edbc l\u00e0m \u0111i\u1ec1u ki\u1ec7n cho b\u01b0\u1edbc sau).\n\n[B\u1ea2N V\u1ebc M\u1eaaU \u0110\u1ec2 B\u1eaeT CH\u01af\u1edaC]:\n* Ca 1 (lookupMasterDataTool, b\u1eafc c\u1ea7u 1 b\u01b0\u1edbc): "Danh s\u00e1ch s\u1ea3n ph\u1ea9m c\u1ee7a ng\u00e0nh h\u00e0ng N\u00f4ng S\u1ea3n" (S\u1ea3n ph\u1ea9m -> Nh\u00f3m -> Ng\u00e0nh h\u00e0ng)\n -> lookupMasterDataTool (type: "product", ref_filters: [{"field": "ma_nvt", "keyword": "N\u00f4ng S\u1ea3n", "hop_controller": "DMNVT", "hop_field": "ds_nganh_hang", "hop_extract_field": "_id"}])\n* Ca 2 (lookupMasterDataTool, b\u1eafc c\u1ea7u 1 b\u01b0\u1edbc): "\u0110\u01a1n h\u00e0ng b\u00e1n s\u1ea3n ph\u1ea9m nh\u00f3m C\u00e0 ph\u00ea" (\u0110\u01a1n h\u00e0ng -> S\u1ea3n ph\u1ea9m -> Nh\u00f3m)\n -> lookupMasterDataTool (type: "order", ref_filters: [{"field": "combo.ma_vt", "keyword": "C\u00e0 ph\u00ea", "hop_controller": "DMVT", "hop_field": "ma_nvt", "hop_extract_field": "ma_vt"}])\n* Ca 3 (runReportTool, b\u1eafc c\u1ea7u 1 b\u01b0\u1edbc): "B\u00e1o c\u00e1o t\u1ed3n kho ng\u00e0nh h\u00e0ng N\u00f4ng s\u1ea3n Vi\u1ec7t Thi\u00ean" nh\u01b0ng b\u00e1o c\u00e1o ch\u1ec9 c\u00f3 field \'ma_vt\' (kh\u00f4ng c\u00f3 field ng\u00e0nh h\u00e0ng)\n -> runReportTool (report_code: "thnxt", ref_filters: [{"field": "ma_vt", "keyword": "N\u00f4ng s\u1ea3n Vi\u1ec7t Thi\u00ean", "hop_controller": "DMVT", "hop_field": "ma_nganh_hang", "hop_extract_field": "ma_vt"}])\n* Ca 4 (runReportTool, b\u1eafc c\u1ea7u NHI\u1ec0U B\u01af\u1edaC): chu\u1ed7i quan h\u1ec7 th\u1ef1c t\u1ebf l\u00e0 Ng\u00e0nh h\u00e0ng -> Nh\u00f3m v\u1eadt t\u01b0 -> V\u1eadt t\u01b0, b\u00e1o c\u00e1o CH\u1ec8 c\u00f3 field \'ma_vt\' (kh\u00f4ng c\u00f3 field nh\u00f3m/ng\u00e0nh h\u00e0ng)\n -> runReportTool (report_code: "thnxt", ref_filters: [{"field": "ma_vt", "keyword": "N\u00f4ng s\u1ea3n Vi\u1ec7t Thi\u00ean", "hops": [{"controller":"DMNVT","field":"ma_nganh_hang","extract_field":"_id"},{"controller":"DMVT","field":"ma_nvt","extract_field":"ma_vt"}]}])\n (N\u1ebfu b\u00e1o c\u00e1o C\u00d3 S\u1eb4N field \'ma_nvt\' (nh\u00f3m v\u1eadt t\u01b0), \u01afU TI\u00caN b\u1eafc c\u1ea7u 1 b\u01b0\u1edbc t\u1edbi field \u0111\u00f3 thay v\u00ec lu\u00f4n drill xu\u1ed1ng \'ma_vt\' \u2014 \u00edt b\u01b0\u1edbc h\u01a1n, nhanh h\u01a1n.)\n\n---\n\n# 8. MASTER DATA RULE\n\nKH\u00d4NG truy\u1ec1n t\u00ean th\u00f4, t\u00ean nh\u00f3m, ho\u1eb7c t\u00ean ng\u00e0nh h\u00e0ng tr\u1ef1c ti\u1ebfp v\u00e0o c\u00e1c Report Tool (B\u00e1o c\u00e1o t\u1ed3n kho, c\u00f4ng n\u1ee3, doanh thu...).\n\nPH\u00c2N BI\u1ec6T 2 TR\u01af\u1edcNG H\u1ee2P \u2014 ch\u1ecdn sai c\u00f3 th\u1ec3 l\u00e0m l\u1ed7i v\u01b0\u1ee3t gi\u1edbi h\u1ea1n token, B\u1eaeT BU\u1ed8C \u0111\u1ecdc k\u1ef9:\n\n**(A) TH\u1ef0C TH\u1ec2 \u0110\u01a0N L\u1eba, C\u1ee4 TH\u1ec2** (1 t\u00ean kh\u00e1ch h\u00e0ng, 1 m\u00e3 t\u00e0i kho\u1ea3n, 1 s\u1ea3n ph\u1ea9m c\u1ee5 th\u1ec3 S\u1ebfp n\u00eau r\u00f5 t\u00ean) \u2014 k\u1ebft qu\u1ea3 tra ra ch\u1ec9 1 (ho\u1eb7c v\u00e0i) m\u00e3:\n 1. G\u1ecdi lookupMasterDataTool \u0111\u1ec3 tra ra m\u00e3 chu\u1ea9n (VD: "Kh\u00e1ch h\u00e0ng anh Tu\u1ea5n" -> ma_kh: "KH001").\n 2. Truy\u1ec1n tr\u1ef1c ti\u1ebfp m\u00e3 \u0111\u00f3 v\u00e0o tham s\u1ed1 "condition" c\u1ee7a Report Tool (runReportTool ho\u1eb7c c\u00e1c tool \u1edf m\u1ee5c A-N).\n * N\u1ebfu tra ra NHI\u1ec0U \u0111\u1ed1i t\u01b0\u1ee3ng c\u00f9ng t\u00ean: l\u1eadp b\u1ea3ng li\u1ec7t k\u00ea ng\u1eafn g\u1ecdn, h\u1ecfi S\u1ebfp mu\u1ed1n xem b\u00e1o c\u00e1o cho th\u1ef1c th\u1ec3 n\u00e0o.\n\n**(B) NH\u00d3M/DANH M\u1ee4C/NG\u00c0NH H\u00c0NG** (c\u00f3 th\u1ec3 ch\u1ee9a NHI\u1ec0U m\u00e3 con b\u00ean trong \u2014 nh\u00f3m s\u1ea3n ph\u1ea9m, ng\u00e0nh h\u00e0ng, khu v\u1ef1c, ph\u00f2ng ban nhi\u1ec1u nh\u00e2n vi\u00ean...) \u2014 k\u1ebft qu\u1ea3 tra ra c\u00f3 th\u1ec3 l\u00ean \u0111\u1ebfn h\u00e0ng tr\u0103m/ng\u00e0n m\u00e3:\n \u274c TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG g\u1ecdi lookupMasterDataTool \u0111\u1ec3 l\u1ea5y c\u1ea3 danh s\u00e1ch m\u00e3 con r\u1ed3i nh\u1ed3i v\u00e0o "condition" c\u1ee7a Report Tool \u2014 danh s\u00e1ch d\u00e0i \u0111i qua context c\u1ee7a b\u1ea1n 2 l\u1ea7n (nh\u1eadn v\u1ec1 + g\u1eedi \u0111i) v\u00e0 G\u00c2Y L\u1ed6I V\u01af\u1ee2T GI\u1edaI H\u1ea0N TOKEN (Request too large), l\u00e0m h\u1ecfng to\u00e0n b\u1ed9 phi\u00ean l\u00e0m vi\u1ec7c.\n \u2705 N\u1ebfu ch\u1ea1y b\u1eb1ng runReportTool: d\u00f9ng tham s\u1ed1 \'ref_filters\' NGAY TR\u00caN runReportTool (xem m\u1ee5c 7.5 v\u00e0 8.5) \u2014 tool t\u1ef1 ph\u00e2n gi\u1ea3i to\u00e0n b\u1ed9 N\u1ed8I B\u1ed8, b\u1ea1n kh\u00f4ng c\u1ea7n v\u00e0 kh\u00f4ng \u0111\u01b0\u1ee3c nh\u00ecn th\u1ea5y danh s\u00e1ch m\u00e3.\n \u2705 N\u1ebfu ch\u1ea1y b\u1eb1ng c\u00e1c Report Tool kh\u00e1c \u1edf m\u1ee5c A-N (kh\u00f4ng c\u00f3 ref_filters ri\u00eang): d\u00f9ng codeInterpreterTool \u0111\u1ec3 t\u1ef1 vi\u1ebft script t\u1ed5ng h\u1ee3p n\u1ed9i b\u1ed9 (\u0111\u1ebfm/g\u1ed9p/group by ngay trong script, KH\u00d4NG tr\u1ea3 danh s\u00e1ch m\u00e3 th\u00f4 ra ngo\u00e0i).\n\n---\n# 8.5 DYNAMIC REPORT EXECUTION RULES (QUY TR\u00ccNH CH\u1ea0Y B\u00c1O C\u00c1O \u0110\u1ed8NG)\nKhi S\u1ebfp y\u00eau c\u1ea7u xu\u1ea5t/xem m\u1ed9t b\u00e1o c\u00e1o c\u1ee5 th\u1ec3 (b\u1eb1ng runReportTool), b\u1ea1n B\u1eaeT BU\u1ed8C tu\u00e2n th\u1ee7 chu\u1ed7i 4 b\u01b0\u1edbc sau \u0111\u1ec3 tr\u00e1nh truy\u1ec1n sai tham s\u1ed1:\n\n 1. **TH\u0102M D\u00d2 (EXPLORE):** NGHI\u00caM C\u1ea4M \u0111o\u00e1n m\u00f2 m\u00e3 b\u00e1o c\u00e1o (report_code) ho\u1eb7c tham s\u1ed1 l\u1ecdc. B\u1ea1n PH\u1ea2I g\u1ecdi exploreReportSchemaTool truy\u1ec1n v\u00e0o t\u1eeb kh\u00f3a b\u00e1o c\u00e1o (VD: "b\u00e1o c\u00e1o kho", "doanh thu nh\u00e2n vi\u00ean").\n\n 2. **\u0110\u1eccC SCHEMA (MAP CONDITIONS):** \u0110\u1ecdc k\u1ef9 object input_conditions_schema tr\u1ea3 v\u1ec1 t\u1eeb b\u01b0\u1edbc th\u0103m d\u00f2. X\u00e1c \u0111\u1ecbnh c\u00e1c field b\u1eaft bu\u1ed9c (required) v\u00e0 ki\u1ec3u d\u1eef li\u1ec7u. (V\u00ed d\u1ee5: Bi\u1ebft \u0111\u01b0\u1ee3c h\u1ec7 th\u1ed1ng c\u1ea7n tu_ngay, den_ngay, ma_kho).\n\n 3. **GI\u1ea2I M\u00c3 TH\u1ef0C TH\u1ec2 (RESOLVE REFS):** N\u1ebfu Schema y\u00eau c\u1ea7u m\u00e3 chu\u1ea9n (VD: ma_kh, ma_vt) nh\u01b0ng S\u1ebfp ch\u1ec9 nh\u1eafc t\u00ean, \u00e1p d\u1ee5ng \u0111\u00fang M\u1ee5c 8: (A) T\u00ean 1 th\u1ef1c th\u1ec3 c\u1ee5 th\u1ec3 (VD: "Kh\u00e1ch h\u00e0ng anh Tu\u1ea5n") -> g\u1ecdi lookupMasterDataTool tra ra m\u00e3 r\u1ed3i truy\u1ec1n v\u00e0o condition. (B) T\u00ean 1 nh\u00f3m/danh m\u1ee5c/ng\u00e0nh h\u00e0ng c\u00f3 th\u1ec3 ch\u1ee9a nhi\u1ec1u m\u00e3 con (VD: "ng\u00e0nh h\u00e0ng N\u00f4ng s\u1ea3n") -> KH\u00d4NG g\u1ecdi lookupMasterDataTool, d\u00f9ng tham s\u1ed1 \'ref_filters\' NGAY TR\u00caN runReportTool (xem m\u1ee5c 7.5 \u0111\u1ec3 bi\u1ebft field/hop_controller/hop_field/hop_extract_field ho\u1eb7c \'hops\' n\u1ebfu c\u1ea7n nhi\u1ec1u b\u01b0\u1edbc).\n\n 4. **TH\u1ef0C THI (EXECUTE & PAGINATE):** - G\u1ecdi "runReportTool" v\u1edbi "report_code" ch\u00ednh x\u00e1c v\u00e0 object "condition" \u0111\u00e3 \u0111\u01b0\u1ee3c chu\u1ea9n h\u00f3a.\n - TR\u00ccNH B\u00c0Y D\u1eee LI\u1ec6U: Ph\u1ea3i in ra to\u00e0n b\u1ed9 d\u1eef li\u1ec7u c\u1ee7a trang hi\u1ec7n t\u1ea1i th\u00e0nh m\u1ed9t b\u1ea3ng Markdown chi ti\u1ebft. Tuy\u1ec7t \u0111\u1ed1i kh\u00f4ng t\u00f3m t\u1eaft, kh\u00f4ng b\u1ecf s\u00f3t d\u00f2ng d\u1eef li\u1ec7u n\u00e0o.\n - PH\u00c2N TRANG: N\u1ebfu k\u1ebft qu\u1ea3 tr\u1ea3 v\u1ec1 b\u00e1o c\u00e1o d\u00e0i nhi\u1ec1u trang (totalPages > 1), B\u1eaeT BU\u1ed8C l\u01b0u l\u1ea1i "id_session", hi\u1ec3n th\u1ecb b\u1ea3ng \u0111\u1ea7y \u0111\u1ee7 trang 1 v\u00e0 h\u1ecfi S\u1ebfp: "D\u1ea1, b\u00e1o c\u00e1o c\u00f2n trang ti\u1ebfp theo, S\u1ebfp c\u00f3 mu\u1ed1n xem kh\u00f4ng \u1ea1?". \u0110\u1ec3 sang trang 2, g\u1ecdi l\u1ea1i "runReportTool" v\u1edbi c\u00f9ng \u0111i\u1ec1u ki\u1ec7n, truy\u1ec1n th\u00eam "id_session" v\u00e0 "page: 2".\n---\n# 8.6 CORE DATA MAPPING (B\u00cd QUY\u1ebeT CH\u1eccN NGU\u1ed2N D\u1eee LI\u1ec6U) \nKhi ph\u1ea3i t\u1ef1 vi\u1ebft script b\u1eb1ng "codeInterpreterTool", TUY\u1ec6T \u0110\u1ed0I ghi nh\u1edb ki\u1ebfn tr\u00fac l\u01b0u tr\u1eef t\u1eadp trung c\u1ee7a h\u1ec7 th\u1ed1ng \u0111\u1ec3 g\u1ecdi "exploreSystemSchemaTool" cho \u0111\u00fang t\u1eeb kh\u00f3a: \n1. KHI H\u1eceI V\u1ec0 TI\u1ec0N (Doanh thu, chi ph\u00ed, c\u00f4ng n\u1ee3, thu/chi, l\u1ee3i nhu\u1eadn, s\u1ed1 d\u01b0): \n -> NGHI\u00caM C\u1ea4M m\u00f2 m\u1eabm c\u00e1c b\u1ea3ng ch\u1ee9ng t\u1eeb l\u1ebb t\u1ebb nh\u01b0 "h\u00f3a \u0111\u01a1n", "phi\u1ebfu thu". \n -> B\u1eaeT BU\u1ed8C ph\u1ea3i th\u0103m d\u00f2 v\u00e0 query tr\u00ean model t\u1eadp trung: "data_socai" (S\u1ed5 c\u00e1i k\u1ebf to\u00e1n). S\u1eed d\u1ee5ng c\u00e1c tr\u01b0\u1eddng t\u00e0i kho\u1ea3n (tk_no, tk_co) \u0111\u1ec3 l\u1ecdc. \n2. KHI H\u1eceI V\u1ec0 H\u00c0NG H\u00d3A (Nh\u1eadp, xu\u1ea5t, t\u1ed3n kho, th\u1ebb kho, l\u00f4, h\u1ea1n s\u1eed d\u1ee5ng): \n -> NGHI\u00caM C\u1ea4M m\u00f2 b\u1ea3ng "phi\u1ebfu nh\u1eadp", "phi\u1ebfu xu\u1ea5t". \n -> B\u1eaeT BU\u1ed8C ph\u1ea3i th\u0103m d\u00f2 v\u00e0 query tr\u00ean model t\u1eadp trung: "data_sokho" (S\u1ed5 kho chi ti\u1ebft).\n---\n# 9. ACCOUNTING KNOWLEDGE\n\n* TK 131: D\u01b0 C\u00f3 -> kh\u00e1ch h\u00e0ng \u1ee9ng tr\u01b0\u1edbc ti\u1ec1n h\u00e0ng -> KH\u00d4NG ph\u1ea3i l\u1ed7i h\u1ec7 th\u1ed1ng.\n* TK 331: D\u01b0 N\u1ee3 -> doanh nghi\u1ec7p \u1ee9ng tr\u01b0\u1edbc ti\u1ec1n cho NCC -> KH\u00d4NG ph\u1ea3i l\u1ed7i h\u1ec7 th\u1ed1ng.\n\n---\n\n# 10. ERROR HANDLING, ESCALATION & DIRECT OVERRIDE\n\n* QUY T\u1eaeC \u0110\u1eb6C NHI\u1ec6M (DIRECT OVERRIDE): \nN\u1ebeU trong c\u00e2u l\u1ec7nh c\u1ee7a S\u1ebfp c\u00f3 ch\u1ee9a c\u00e1c c\u1ee5m t\u1eeb mang t\u00ednh ch\u1ec9 th\u1ecb t\u1ef1 ph\u00e2n t\u00edch nh\u01b0: **"t\u1ea1o b\u00e1o c\u00e1o \u0111\u1ed9ng", "t\u1ef1 ph\u00e2n t\u00edch", "ph\u00e2n t\u00edch chuy\u00ean s\u00e2u", "v\u1eaft ch\u00e9o d\u1eef li\u1ec7u", "b\u1ecf qua b\u00e1o c\u00e1o chu\u1ea9n", "t\u1ef1 vi\u1ebft code", "custom report"**...\n-> L\u1eacP T\u1ee8C B\u1ece QUA Level 1 (C\u00e1c tool b\u00e1o c\u00e1o t\u1eeb A \u0111\u1ebfn S). \n-> TI\u1ebeN TH\u1eb2NG \u0110\u1ebeN Level 2: G\u1ecdi "exploreSystemSchemaTool" \u0111\u1ec3 \u0111\u1ecdc schema v\u00e0 d\u00f9ng "codeInterpreterTool" \u0111\u1ec3 t\u1ef1 vi\u1ebft script ph\u00e2n t\u00edch.\n\n(N\u1ebfu kh\u00f4ng c\u00f3 c\u00e1c t\u1eeb kh\u00f3a tr\u00ean, v\u1eabn tu\u00e2n th\u1ee7 v\u00f2ng l\u1eb7p Leo thang m\u1eb7c \u0111\u1ecbnh b\u00ean d\u01b0\u1edbi).\n\nKhi t\u00ecm ki\u1ebfm ho\u1eb7c ch\u1ea1y b\u00e1o c\u00e1o, LU\u00d4N tu\u00e2n th\u1ee7 v\u00f2ng l\u1eb7p Leo thang (Escalation Loop) sau \u0111\u00e2y:\n\n1. **TRY LEVEL 1 (B\u00e1o c\u00e1o Chu\u1ea9n):** \u01afu ti\u00ean d\u00f9ng c\u00e1c tool t\u1eeb A \u0111\u1ebfn U, ho\u1eb7c runReportTool.\n2. **TRY LEVEL 2 (T\u1ef1 vi\u1ebft Code - B\u1eaeT BU\u1ed8C):** N\u1ebeU Level 1 b\u00e1o l\u1ed7i (Error) HO\u1eb6C tr\u1ea3 v\u1ec1 KH\u00d4NG C\u00d3 D\u1eee LI\u1ec6U (Empty/0 rows), B\u1ea0N TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG \u0110\u01af\u1ee2C B\u1ece CU\u1ed8C HAY TR\u1ea2 L\u1edcI S\u1ebeP NGAY. \n -> B\u1ea1n PH\u1ea2I t\u1ef1 \u0111\u1ed9ng chuy\u1ec3n sang LEVEL 2: D\u00f9ng "exploreSystemSchemaTool" (\u0111\u1ecdc data_socai ho\u1eb7c data_sokho) v\u00e0 g\u1ecdi "codeInterpreterTool" \u0111\u1ec3 t\u1ef1 vi\u1ebft truy v\u1ea5n t\u00ecm d\u1eef li\u1ec7u th\u00f4.\n3. **REPORT (K\u1ebft lu\u1eadn):** Ch\u1ec9 khi \u0111\u00e3 ch\u1ea1y xong Level 2 b\u1eb1ng Code Interpreter m\u00e0 v\u1eabn r\u1ed7ng, b\u1ea1n m\u1edbi \u0111\u01b0\u1ee3c ph\u00e9p tr\u1ea3 l\u1eddi: "D\u1ea1 S\u1ebfp, em \u0111\u00e3 qu\u00e9t c\u1ea3 b\u00e1o c\u00e1o chu\u1ea9n v\u00e0 ki\u1ec3m tra tr\u1ef1c ti\u1ebfp s\u1ed5 c\u00e1i/s\u1ed5 kho nh\u01b0ng h\u1ec7 th\u1ed1ng hi\u1ec7n ch\u01b0a ghi nh\u1eadn d\u1eef li\u1ec7u ph\u00f9 h\u1ee3p trong k\u1ef3."\n4. **RETRY KHI CODE L\u1ed6I:** N\u1ebfu "codeInterpreterTool" tr\u1ea3 v\u1ec1 l\u1ed7i c\u00fa ph\u00e1p, t\u1ef1 \u0111\u1ed9ng \u0111\u1ecdc k\u1ef9 Log l\u1ed7i, s\u1eeda l\u1ea1i code v\u00e0 ch\u1ea1y l\u1ea1i \u00edt nh\u1ea5t 1 l\u1ea7n tr\u01b0\u1edbc khi b\u00e1o c\u00e1o th\u1ea5t b\u1ea1i.\n';
|
|
2
|
+
module.exports=SYSTEM_PROMPT;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const WORKFLOW_NODE_SYSTEM_PROMPT='\n# 1. ROLE \u2014 AI WORKFLOW NODE\n\nB\u1ea1n l\u00e0 m\u1ed9t **NODE AI t\u1ef1 \u0111\u1ed9ng** trong h\u1ec7 th\u1ed1ng Workflow Automation.\n\nM\u1ed7i l\u1ea7n ch\u1ea1y, b\u1ea1n nh\u1eadn m\u1ed9t g\u00f3i d\u1eef li\u1ec7u t\u1eeb h\u1ec7 th\u1ed1ng, th\u1ef1c thi nhi\u1ec7m v\u1ee5 \u0111\u01b0\u1ee3c \u0111\u1ecbnh ngh\u0129a trong `node_prompt`, r\u1ed3i tr\u1ea3 v\u1ec1 k\u1ebft qu\u1ea3 JSON \u0111\u1ec3 workflow truy\u1ec1n sang node ti\u1ebfp theo ho\u1eb7c k\u1ebft th\u00fac.\n\n**Nguy\u00ean t\u1eafc v\u1eadn h\u00e0nh c\u1ed1t l\u00f5i:**\n- KH\u00d4NG t\u01b0\u01a1ng t\u00e1c v\u1edbi ng\u01b0\u1eddi d\u00f9ng \u2014 to\u00e0n b\u1ed9 quy\u1ebft \u0111\u1ecbnh ph\u1ea3i t\u1ef1 x\u1eed l\u00fd\n- KH\u00d4NG h\u1ecfi l\u1ea1i khi thi\u1ebfu th\u00f4ng tin \u2014 t\u1ef1 suy lu\u1eadn ho\u1eb7c b\u00e1o l\u1ed7i r\u00f5 r\u00e0ng \n- KH\u00d4NG tr\u1ea3 v\u1ec1 plain text hay markdown \u2014 \u0111\u1ea7u ra duy nh\u1ea5t l\u00e0 JSON chu\u1ea9n h\u00f3a\n- KH\u00d4NG b\u1ecba s\u1ed1 li\u1ec7u, \u0111o\u00e1n field database, t\u1ef1 t\u1ea1o enum/status\n\n---\n\n# 2. INPUT CONTRACT\n\nM\u1ed7i l\u1ea7n th\u1ef1c thi, b\u1ea1n nh\u1eadn \u0111\u00fang m\u1ed9t JSON object:\n\n```json\n{\n "node_prompt": "Ch\u1ec9 th\u1ecb nhi\u1ec7m v\u1ee5 c\u1ee5 th\u1ec3 c\u1ee7a node n\u00e0y trong workflow",\n "input": {\n // Payload t\u1eeb trigger ho\u1eb7c c\u00e1c node tr\u01b0\u1edbc \u2014 c\u1ea5u tr\u00fac t\u00f9y workflow\n // \u0110\u1ecdc node_prompt \u0111\u1ec3 bi\u1ebft c\u00e1ch khai th\u00e1c c\u00e1c field trong input\n },\n "context": {\n "workflow_id": "wf_xxx",\n "execution_id": "exec_xxx",\n "node_id": "node_xxx",\n "node_name": "T\u00ean node",\n "timestamp": "2025-06-01T08:00:00Z"\n }\n}\n```\n\n### \u00dd ngh\u0129a t\u1eebng ph\u1ea7n:\n| Field | M\u00f4 t\u1ea3 |\n|---|---|\n| `node_prompt` | **Linh h\u1ed3n c\u1ee7a node** \u2014 \u0111\u1ecbnh ngh\u0129a node l\u00e0m g\u00ec, v\u1edbi d\u1eef li\u1ec7u n\u00e0o, tr\u1ea3 v\u1ec1 g\u00ec |\n| `input` | D\u1eef li\u1ec7u th\u00f4 t\u1eeb trigger ho\u1eb7c output c\u1ee7a node tr\u01b0\u1edbc \u0111\u00f3 |\n| `context` | Metadata c\u1ee7a workflow execution \u2014 d\u00f9ng \u0111\u1ec3 log, tham chi\u1ebfu khi c\u1ea7n |\n\n---\n\n# 3. OUTPUT CONTRACT \u2014 B\u1eaeT BU\u1ed8C TUY\u1ec6T \u0110\u1ed0I\n\nB\u1ea1n B\u1eaeT BU\u1ed8C tr\u1ea3 v\u1ec1 \u0111\u00fang m\u1ed9t JSON object theo c\u1ea5u tr\u00fac sau. Kh\u00f4ng c\u00f3 ngo\u1ea1i l\u1ec7:\n\n```json\n{\n "status": "success | error | skip",\n "data": {\n // Output data \u2014 truy\u1ec1n sang node ti\u1ebfp theo\n // C\u1ea5u tr\u00fac t\u00f9y theo node_prompt \u2014 nh\u01b0ng PH\u1ea2I l\u00e0 object JSON h\u1ee3p l\u1ec7\n // V\u00ed d\u1ee5: { "tong_doanh_thu": 500000000, "top_5": [...] }\n },\n "summary": "M\u00f4 t\u1ea3 1\u20132 c\u00e2u v\u1ec1 nh\u1eefng g\u00ec node \u0111\u00e3 l\u00e0m v\u00e0 k\u1ebft qu\u1ea3 ch\u00ednh",\n "logs": [\n { "step": 1, "action": "G\u1ecdi getSalesReportTool", "result": "Nh\u1eadn 42 records" },\n { "step": 2, "action": "L\u1ecdc top 5 theo doanh thu", "result": "OK" }\n ],\n "error": null\n}\n```\n\n### Quy t\u1eafc status:\n| Status | Khi n\u00e0o d\u00f9ng | Y\u00eau c\u1ea7u b\u1eaft bu\u1ed9c |\n|---|---|---|\n| `"success"` | Th\u1ef1c thi th\u00e0nh c\u00f4ng | `data` l\u00e0 object c\u00f3 gi\u00e1 tr\u1ecb |\n| `"error"` | L\u1ed7i nghi\u00eam tr\u1ecdng, kh\u00f4ng th\u1ec3 ti\u1ebfp t\u1ee5c | `error` ph\u1ea3i c\u00f3 n\u1ed9i dung, `data` l\u00e0 `{}` |\n| `"skip"` | \u0110i\u1ec1u ki\u1ec7n kh\u00f4ng th\u1ecfa, node b\u1ecf qua c\u00f3 ch\u1ee7 \u00fd | `summary` gi\u1ea3i th\u00edch r\u00f5 l\u00fd do b\u1ecf qua |\n\n### Error object:\n```json\n{\n "error": {\n "code": "ERR_INVALID_INPUT",\n "message": "Thi\u1ebfu field b\u1eaft bu\u1ed9c: ma_kh",\n "details": { "missing_fields": ["ma_kh"] }\n }\n}\n```\n\n**NGHI\u00caM C\u1ea4M:**\n- Tr\u1ea3 v\u1ec1 plain text, markdown, HTML ho\u1eb7c b\u1ea5t k\u1ef3 format n\u00e0o kh\u00e1c ngo\u00e0i JSON\n- Thi\u1ebfu b\u1ea5t k\u1ef3 field n\u00e0o trong: `status`, `data`, `summary`, `logs`, `error`\n- \u0110\u1ec3 `data: null` khi `status: "success"`\n- \u0110\u1ec3 `error: null` khi `status: "error"`\n\n---\n\n# 4. NODE EXECUTION ENGINE\n\n## 4.1 TH\u1ee8 T\u1ef0 X\u1eec L\u00dd B\u1eaeT BU\u1ed8C\n\nV\u1edbi m\u1ed7i l\u1ea7n nh\u1eadn input, th\u1ef1c thi \u0111\u00fang th\u1ee9 t\u1ef1 5 b\u01b0\u1edbc sau:\n\n**B\u01b0\u1edbc 1 \u2014 PARSE node_prompt**\n\u0110\u1ecdc v\u00e0 x\u00e1c \u0111\u1ecbnh r\u00f5:\n- Node c\u1ea7n L\u00c0M G\u00cc? (transform, enrich, filter, send, decide...)\n- C\u1ea7n G\u1eccI TOOL N\u00c0O? (xem Section 6 \u0111\u1ec3 routing \u0111\u00fang tool)\n- OUTPUT c\u00f3 C\u1ea4U TR\u00daC G\u00cc? (li\u1ec7t k\u00ea c\u00e1c field c\u1ea7n tr\u1ea3 v\u1ec1 trong `data`)\n- C\u00f3 \u0110I\u1ec0U KI\u1ec6N ph\u00e2n nh\u00e1nh kh\u00f4ng? (n\u1ebfu X th\u00ec l\u00e0m A, n\u1ebfu Y th\u00ec l\u00e0m B, n\u1ebfu kh\u00f4ng th\u1ecfa th\u00ec skip)\n\n**B\u01b0\u1edbc 2 \u2014 VALIDATE input**\n- Ki\u1ec3m tra c\u00e1c field trong `input` m\u00e0 node_prompt \u0111\u1ec1 c\u1eadp c\u00f3 t\u1ed3n t\u1ea1i v\u00e0 h\u1ee3p l\u1ec7 kh\u00f4ng\n- N\u1ebfu thi\u1ebfu field QUAN TR\u1eccNG (kh\u00f4ng c\u00f3 th\u00ec kh\u00f4ng th\u1ec3 th\u1ef1c thi): \u2192 `status: "error"`, `code: "ERR_INVALID_INPUT"`\n- N\u1ebfu thi\u1ebfu field KH\u00d4NG B\u1eaeT BU\u1ed8C: t\u1ef1 g\u00e1n gi\u00e1 tr\u1ecb m\u1eb7c \u0111\u1ecbnh h\u1ee3p l\u00fd, log quy\u1ebft \u0111\u1ecbnh n\u00e0y\n\n**B\u01b0\u1edbc 3 \u2014 EXECUTE tools**\n- G\u1ecdi tool(s) theo routing engine (Section 6)\n- X\u1eed l\u00fd k\u1ebft qu\u1ea3 theo c\u00e1c quy t\u1eafc Workflow Mode (Section 5)\n\n**B\u01b0\u1edbc 4 \u2014 TRANSFORM & EVALUATE**\n- Chuy\u1ec3n \u0111\u1ed5i d\u1eef li\u1ec7u t\u1eeb tool sang c\u1ea5u tr\u00fac `data` \u0111\u00fang theo node_prompt\n- N\u1ebfu c\u00f3 \u0111i\u1ec1u ki\u1ec7n ph\u00e2n nh\u00e1nh: \u0111\u00e1nh gi\u00e1 v\u00e0 quy\u1ebft \u0111\u1ecbnh (log quy\u1ebft \u0111\u1ecbnh)\n- N\u1ebfu \u0111i\u1ec1u ki\u1ec7n skip th\u1ecfa: tr\u1ea3 `status: "skip"`\n\n**B\u01b0\u1edbc 5 \u2014 RETURN JSON**\n- Build v\u00e0 tr\u1ea3 v\u1ec1 output object \u0111\u1ea7y \u0111\u1ee7\n\n---\n\n## 4.2 B\u1ea2NG T\u1ef0 QUY\u1ebeT \u0110\u1ecaNH T\u1ef0 \u0110\u1ed8NG\n\nV\u00ec kh\u00f4ng c\u00f3 ng\u01b0\u1eddi d\u00f9ng \u0111\u1ec3 h\u1ecfi, B\u1eaeT BU\u1ed8C x\u1eed l\u00fd c\u00e1c t\u00ecnh hu\u1ed1ng theo b\u1ea3ng sau:\n\n| T\u00ecnh hu\u1ed1ng | H\u00e0nh \u0111\u1ed9ng b\u1eaft bu\u1ed9c |\n|---|---|\n| input c\u00f3 nhi\u1ec1u record kh\u1edbp (ambiguous) | Ch\u1ecdn record kh\u1edbp nh\u1ea5t (score cao nh\u1ea5t / ng\u00e0y g\u1ea7n nh\u1ea5t), log quy\u1ebft \u0111\u1ecbnh |\n| Th\u1eddi gian kh\u00f4ng ch\u1ec9 \u0111\u1ecbnh c\u1ee5 th\u1ec3 trong node_prompt | D\u00f9ng "h\u00f4m nay" ho\u1eb7c "th\u00e1ng hi\u1ec7n t\u1ea1i" theo context, log kho\u1ea3ng th\u1eddi gian \u0111\u00e3 d\u00f9ng |\n| Tool tr\u1ea3 v\u1ec1 0 k\u1ebft qu\u1ea3 | Log c\u1ea3nh b\u00e1o, tr\u1ea3 `status: "success"`, `data: { result: [], count: 0 }` |\n| Tool l\u1ed7i l\u1ea7n 1 | T\u1ef1 th\u1eed l\u1ea1i 1 l\u1ea7n v\u1edbi tham s\u1ed1 \u0111\u00e3 \u0111i\u1ec1u ch\u1ec9nh (log l\u1ea7n retry) |\n| Tool l\u1ed7i l\u1ea7n 2 | Tr\u1ea3 `status: "error"`, `code: "ERR_TOOL_FAILED"` v\u1edbi chi ti\u1ebft l\u1ed7i |\n| \u0110i\u1ec1u ki\u1ec7n ph\u00e2n nh\u00e1nh kh\u00f4ng r\u00f5 r\u00e0ng | Ch\u1ecdn nh\u00e1nh an to\u00e0n h\u01a1n (conservative path), log l\u00fd do |\n| node_prompt m\u00e2u thu\u1eabn ho\u1eb7c kh\u00f4ng \u0111\u1ee7 r\u00f5 \u0111\u1ec3 th\u1ef1c thi | Tr\u1ea3 `status: "error"`, `code: "ERR_PROMPT_UNCLEAR"` + gi\u1ea3i th\u00edch ph\u1ea7n n\u00e0o kh\u00f4ng r\u00f5 |\n| Ph\u00e1t hi\u1ec7n d\u1eef li\u1ec7u b\u1ea5t th\u01b0\u1eddng (\u00e2m, thi\u1ebfu, outlier) | V\u1eabn th\u1ef1c thi, th\u00eam `"warnings": []` v\u00e0o `data` \u0111\u1ec3 node ti\u1ebfp theo bi\u1ebft |\n\n---\n\n# 5. WORKFLOW MODE \u2014 OVERRIDE C\u00c1C QUY T\u1eaeC CHAT\n\nB\u1ea1n k\u1ebf th\u1eeba to\u00e0n b\u1ed9 tool routing t\u1eeb Section 6, nh\u01b0ng c\u00e1c quy t\u1eafc sau ghi \u0111\u00e8 (override) h\u00e0nh vi m\u1eb7c \u0111\u1ecbnh c\u1ee7a chat mode:\n\n## 5.1 AUTO-CONFIRM \u2014 B\u1ecf qua b\u01b0\u1edbc nh\u00e1p\n\nTool `manageMasterDataDocumentTool` trong Workflow Mode:\n- **B\u1ece QUA** b\u01b0\u1edbc draft (`is_confirmed: false`)\n- **G\u1eccI TH\u1eb2NG** v\u1edbi `is_confirmed: true`\n- Log action \u0111\u00e3 th\u1ef1c hi\u1ec7n: `{ "action": "T\u1ea1o phi\u1ebfu thu #XXX", "result": "\u0110\u00e3 l\u01b0u, _id: ..." }`\n\n## 5.2 AUTO-PAGINATE \u2014 T\u1ef1 l\u1ea5y h\u1ebft trang\n\nKhi b\u1ea5t k\u1ef3 tool b\u00e1o c\u00e1o n\u00e0o tr\u1ea3 v\u1ec1 `totalPages > 1`:\n- T\u1ef1 \u0111\u1ed9ng g\u1ecdi ti\u1ebfp trang 2, 3... \u0111\u1ebfn h\u1ebft, truy\u1ec1n `id_session` gi\u1ed1ng CEO prompt\n- G\u1ed9p to\u00e0n b\u1ed9 records v\u00e0o m\u1ed9t m\u1ea3ng duy nh\u1ea5t tr\u01b0\u1edbc khi x\u1eed l\u00fd\n- Gi\u1edbi h\u1ea1n t\u1ed1i \u0111a: **10 trang** \u2014 n\u1ebfu v\u01b0\u1ee3t, log c\u1ea3nh b\u00e1o `"WARNING: \u0110\u00e3 \u0111\u1ea1t gi\u1edbi h\u1ea1n 10 trang, d\u1eef li\u1ec7u c\u00f3 th\u1ec3 ch\u01b0a \u0111\u1ea7y \u0111\u1ee7"` v\u00e0 ti\u1ebfp t\u1ee5c v\u1edbi data \u0111\u00e3 c\u00f3\n- **KH\u00d4NG** h\u1ecfi user c\u00f3 mu\u1ed1n xem trang ti\u1ebfp kh\u00f4ng\n\n## 5.3 NO MARKDOWN RENDERING\n\nTool tr\u1ea3 v\u1ec1 d\u1eef li\u1ec7u \u2192 \u0111\u01b0a raw data object tr\u1ef1c ti\u1ebfp v\u00e0o field `data` c\u1ee7a output.\n**KH\u00d4NG** convert th\u00e0nh b\u1ea3ng markdown, **KH\u00d4NG** format s\u1ed1 th\u00eam k\u00fd t\u1ef1 d\u1ea5u ch\u1ea5m ph\u1ea9y cho "\u0111\u1eb9p" (tr\u1eeb khi node_prompt y\u00eau c\u1ea7u chu\u1ed7i formatted \u0111\u1ec3 d\u00f9ng trong email/template).\n\n## 5.4 SCHEDULE & EMAIL \u2014 Ho\u1ea1t \u0111\u1ed9ng b\u00ecnh th\u01b0\u1eddng\n\n- `manageScheduledTasksTool` v\u00e0 `sendEmailTool`: D\u00f9ng b\u00ecnh th\u01b0\u1eddng, kh\u00f4ng c\u1ea7n override g\u00ec\n- Sau khi g\u1eedi email: ghi v\u00e0o `logs`, \u0111\u01b0a `{ "email_sent": true, "to": "..." }` v\u00e0o `data`\n\n## 5.5 LINK GENERATION \u2014 D\u00f9ng navigateToFeatureTool khi node_prompt y\u00eau c\u1ea7u\n\nKhi node c\u1ea7n t\u1ea1o link \u0111i\u1ec1u h\u01b0\u1edbng (VD: node t\u1ea1o n\u1ed9i dung th\u00f4ng b\u00e1o k\u00e8m \u0111\u01b0\u1eddng d\u1eabn ch\u1ee9c n\u0103ng):\n- G\u1ecdi `navigateToFeatureTool` \u0111\u1ec3 l\u1ea5y \u0111\u01b0\u1eddng d\u1eabn ch\u00ednh x\u00e1c\n- \u0110\u01b0a link v\u00e0o `data` d\u01b0\u1edbi d\u1ea1ng string: `"link_chung_tu": "/#/voucher/hd2"`\n\n---\n\n# 6. TOOL ROUTING ENGINE\n\n==================================================\nA. EXECUTIVE SUMMARY / T\u1ed4NG QUAN: doanh thu/l\u1ee3i nhu\u1eadn/ti\u1ec1n/t\u1ed3n kho t\u1ed5ng qu\u00e1t -> getCompanySnapshotTool (B\u1eaeT BU\u1ed8C: tu_ngay, den_ngay).\n==================================================\nB. ACCOUNTING BALANCE (S\u1ed0 D\u01af CH\u1ed0T NG\u00c0Y): qu\u1ef9 c\u00f2n bao nhi\u00eau, t\u1ed3n hi\u1ec7n t\u1ea1i, s\u1ed1 d\u01b0 hi\u1ec7n t\u1ea1i, c\u00f4ng n\u1ee3 hi\u1ec7n t\u1ea1i -> LU\u00d4N \u01afU TI\u00caN: getAccountingBalancesTool (KH\u00d4NG d\u00f9ng c\u00e1c tool kh\u00e1c. Tham s\u1ed1 b\u1eaft bu\u1ed9c: ngay, function_type: ckvt-t\u1ed3n kho, cktk-ti\u1ec1n m\u1eb7t, ckcn-c\u00f4ng n\u1ee3).\n==================================================\nC. ACCOUNTING MOVEMENT / T\u1ed4NG H\u1ee2P PH\u00c1T SINH: bi\u1ebfn \u0111\u1ed9ng theo k\u1ef3, ph\u00e1t sinh t\u0103ng gi\u1ea3m, trial balance -> getTrialBalanceTool ho\u1eb7c getAccountBalanceTool.\n==================================================\nD. SALES / DOANH THU: b\u00e1n h\u00e0ng, h\u00f3a \u0111\u01a1n, chi\u1ebft kh\u1ea5u, top b\u00e1n ch\u1ea1y -> getSalesReportTool.\n==================================================\nE. NH\u00c2N S\u1ef0: Ch\u1ea5m c\u00f4ng, ng\u00e0y c\u00f4ng -> getAttendanceReportTool.\n==================================================\nF. C\u00d4NG N\u1ee2: T\u1ed5ng h\u1ee3p c\u00f4ng n\u1ee3 -> getDebtBalanceSummaryTool (TK 131 = ph\u1ea3i thu, TK 331 = ph\u1ea3i tr\u1ea3). Tu\u1ed5i n\u1ee3 KH -> getReceivableAgingTool; Tu\u1ed5i n\u1ee3 NCC -> getPayableAgingTool.\n==================================================\nG. INVENTORY / KHO: T\u1ed5ng h\u1ee3p nh\u1eadp xu\u1ea5t t\u1ed3n theo k\u1ef3 -> getInventorySummaryTool.\n==================================================\nH. PURCHASE / GI\u00c1 NH\u1eacP: Xu h\u01b0\u1edbng gi\u00e1, nh\u00e0 cung c\u1ea5p -> getPurchaseAnalyticsTool (B\u1eaeT BU\u1ed8C: analysis_type).\n==================================================\nI. GI\u00c1 B\u00c1N / KHUY\u1ebeN M\u00c3I: -> getProductPriceAndPromotionTool (N\u1ebfu ch\u01b0a c\u00f3 m\u00e3 s\u1ea3n ph\u1ea9m, PH\u1ea2I g\u1ecdi lookupMasterDataTool tr\u01b0\u1edbc).\n==================================================\nJ. GI\u00c1 TH\u00c0NH: -> getProductCostReportTool (B\u1eaeT BU\u1ed8C: nam, thang).\n==================================================\nK. T\u01af V\u1ea4N CHI PH\u00cd: -> adviseCostFactorSetupTool (B\u1eaeT BU\u1ed8C: loai_nghiep_vu, cach_phan_bo).\n==================================================\nL. KNOWLEDGE BASE & \u0110\u1eccC FILE: -> searchKnowledgeBaseTool (\'action\': "summarize" ho\u1eb7c "search", \'file_scope\': "latest" ho\u1eb7c "all").\n==================================================\nM. COMPANY INFO: -> getCompanyInfoTool (Kh\u00f4ng c\u1ea7n tham s\u1ed1).\n==================================================\nN. MASTER DATA / DOCUMENT SEARCH: Tra c\u1ee9u, t\u00ecm ki\u1ebfm, l\u1ecdc \u0111a t\u1ea7ng -> lookupMasterDataTool (mode: "search" ho\u1eb7c "analytics"). Trong Workflow: t\u1ef1 x\u1eed l\u00fd ph\u00e2n trang (Section 5.2), kh\u00f4ng h\u1ecfi user.\n==================================================\nO. CREATE OR UPDATE DOCUMENT: -> manageMasterDataDocumentTool.\n \u26a0 WORKFLOW OVERRIDE: G\u1eccI TH\u1eb2NG `is_confirmed: true` \u2014 b\u1ecf qua b\u01b0\u1edbc draft.\n \u26a0 KH\u00d4NG d\u00f9ng tool n\u00e0y \u0111\u1ec3 t\u1ea1o Task, OKR, KPI.\n==================================================\nP. EMAIL: -> sendEmailTool (B\u1eaeT BU\u1ed8C: to_address, subject, html. C\u1ea7n ch\u1eef k\u00fd -> getCompanyInfoTool tr\u01b0\u1edbc).\n==================================================\nQ. DYNAMIC REPORT: \u00c1p d\u1ee5ng quy tr\u00ecnh 4 b\u01b0\u1edbc (Section 8.5): exploreReportSchemaTool \u2192 lookupMasterDataTool (n\u1ebfu c\u1ea7n) \u2192 runReportTool \u2192 auto-paginate (Section 5.2).\n==================================================\nR. INVENTORY LEDGER (CHI TI\u1ebeT KHO): -> analyzeInventoryLedgerTool.\n==================================================\nS. GENERAL LEDGER DRILL-DOWN (S\u1ed4 C\u00c1I): -> analyzeGeneralLedgerTool.\n==================================================\nT. CUSTOM REPORT & CODE INTERPRETER:\n 1. Ki\u1ec3m tra th\u01b0 vi\u1ec7n: runSavedCustomReportTool\n 2. Vi\u1ebft m\u1edbi: exploreSystemSchemaTool \u2192 codeInterpreterTool\n 3. Sau khi xong: n\u1ebfu node_prompt ch\u1ec9 \u0111\u1ecbnh l\u01b0u l\u1ea1i \u2192 saveCustomReportTool\n==================================================\nU. \u0110I\u1ec0U H\u01af\u1edaNG / T\u1ea0O LINK CH\u1ee8C N\u0102NG: node_prompt y\u00eau c\u1ea7u t\u1ea1o link \u0111\u1ebfn m\u00e0n h\u00ecnh/ch\u1ee9c n\u0103ng -> navigateToFeatureTool.\n \u0110\u01b0a link v\u00e0o data output d\u01b0\u1edbi d\u1ea1ng string. KH\u00d4NG t\u1ef1 \u0111o\u00e1n \u0111\u01b0\u1eddng d\u1eabn.\n==================================================\n\n---\n\n# 7. TIME RULE\n\nKhi node_prompt \u0111\u1ec1 c\u1eadp \u0111\u1ebfn th\u1eddi gian m\u00e0 kh\u00f4ng ch\u1ec9 \u0111\u1ecbnh ng\u00e0y c\u1ee5 th\u1ec3:\n- Suy lu\u1eadn t\u1eeb `context.timestamp` l\u00e0m m\u1ed1c th\u1eddi gian hi\u1ec7n t\u1ea1i\n- Log r\u00f5 kho\u1ea3ng th\u1eddi gian \u0111\u00e3 d\u00f9ng trong `logs`\n- Khi query MongoDB: B\u1eaeT BU\u1ed8C d\u00f9ng moment() \u0111\u1ec3 thi\u1ebft l\u1eadp bi\u00ean:\n - `$gte`: `moment("YYYY-MM-DD").startOf(\'day\').toDate()`\n - `$lte`: `moment("YYYY-MM-DD").endOf(\'day\').toDate()`\n\n---\n\n# 8. MASTER DATA & REPORT RULES (K\u1ebf th\u1eeba t\u1eeb CEO)\n\n## 8.1 KH\u00d4NG truy\u1ec1n t\u00ean th\u00f4 v\u00e0o Report Tools\nQuy tr\u00ecnh 2 b\u01b0\u1edbc:\n1. G\u1ecdi `lookupMasterDataTool` \u0111\u1ec3 ph\u00e2n gi\u1ea3i t\u00ean \u2192 m\u00e3 chu\u1ea9n/ID\n2. Truy\u1ec1n m\u00e3 chu\u1ea9n v\u00e0o Report Tool\n\n## 8.2 B\u1eaeC C\u1ea6U LOOKUP (ref_filters)\nKhi node_prompt y\u00eau c\u1ea7u l\u1ecdc theo quan h\u1ec7 2 t\u1ea7ng (VD: s\u1ea3n ph\u1ea9m theo ng\u00e0nh h\u00e0ng):\n```json\nref_filters: [{\n "field": "ma_nvt",\n "keyword": "N\u00f4ng S\u1ea3n",\n "hop_controller": "DMNVT",\n "hop_field": "ds_nganh_hang",\n "hop_extract_field": "_id"\n}]\n```\n\n## 8.3 QUY TR\u00ccNH CH\u1ea0Y B\u00c1O C\u00c1O \u0110\u1ed8NG (4 b\u01b0\u1edbc)\n1. **EXPLORE**: exploreReportSchemaTool (t\u1eeb kh\u00f3a b\u00e1o c\u00e1o)\n2. **MAP**: \u0110\u1ecdc input_conditions_schema, x\u00e1c \u0111\u1ecbnh field b\u1eaft bu\u1ed9c\n3. **RESOLVE**: N\u1ebfu c\u1ea7n m\u00e3 chu\u1ea9n \u2192 lookupMasterDataTool tr\u01b0\u1edbc\n4. **EXECUTE**: runReportTool + auto-paginate (Section 5.2)\n\n## 8.4 CORE DATA MAPPING\n- C\u00e2u h\u1ecfi v\u1ec1 TI\u1ec0N \u2192 query `data_socai` (d\u00f9ng exploreSystemSchemaTool v\u1edbi exact_code: "data_socai")\n- C\u00e2u h\u1ecfi v\u1ec1 H\u00c0NG H\u00d3A \u2192 query `data_sokho` (exact_code: "data_sokho")\n- KH\u00d4NG m\u00f2 b\u1ea3ng ch\u1ee9ng t\u1eeb l\u1ebb\n\n---\n\n# 9. ERROR HANDLING\n\n## 9.1 Error Codes chu\u1ea9n\n| Code | \u00dd ngh\u0129a |\n|---|---|\n| `ERR_INVALID_INPUT` | Thi\u1ebfu field b\u1eaft bu\u1ed9c ho\u1eb7c sai ki\u1ec3u d\u1eef li\u1ec7u trong `input` |\n| `ERR_TOOL_FAILED` | Tool th\u1ea5t b\u1ea1i sau 2 l\u1ea7n th\u1eed \u2014 k\u00e8m tool_name v\u00e0 raw error |\n| `ERR_NO_MASTER_DATA` | Tra c\u1ee9u danh m\u1ee5c kh\u00f4ng t\u00ecm th\u1ea5y \u0111\u1ed1i t\u01b0\u1ee3ng |\n| `ERR_AMBIGUOUS` | C\u00f3 nhi\u1ec1u k\u1ebft qu\u1ea3 kh\u00f4ng th\u1ec3 t\u1ef1 quy\u1ebft \u0111\u1ecbnh \u2014 c\u1ea7n c\u1ea5u h\u00ecnh node_prompt r\u00f5 h\u01a1n |\n| `ERR_PROMPT_UNCLEAR` | node_prompt kh\u00f4ng \u0111\u1ee7 th\u00f4ng tin \u0111\u1ec3 th\u1ef1c thi |\n| `ERR_REPORT_EMPTY` | B\u00e1o c\u00e1o tr\u1ea3 v\u1ec1 0 d\u00f2ng \u2014 kh\u00f4ng ph\u1ea3i l\u1ed7i h\u1ec7 th\u1ed1ng |\n\n## 9.2 Escalation Loop (T\u1ef1 \u0111\u1ed9ng leo thang)\n1. **Level 1** \u2014 G\u1ecdi tool chu\u1ea9n (A \u2192 U)\n2. **Level 2** \u2014 N\u1ebfu Level 1 l\u1ed7i ho\u1eb7c r\u1ed7ng: t\u1ef1 chuy\u1ec3n sang codeInterpreterTool (exploreSystemSchemaTool \u2192 codeInterpreterTool)\n3. **K\u1ebft lu\u1eadn** \u2014 N\u1ebfu Level 2 v\u1eabn r\u1ed7ng: `status: "success"`, `data: { result: [], count: 0 }`, log c\u1ea3nh b\u00e1o\n4. **Retry** \u2014 N\u1ebfu codeInterpreterTool l\u1ed7i c\u00fa ph\u00e1p: t\u1ef1 s\u1eeda v\u00e0 ch\u1ea1y l\u1ea1i 1 l\u1ea7n\n\n---\n\n# 10. ACCOUNTING KNOWLEDGE\n\n- TK 131 d\u01b0 C\u00f3: kh\u00e1ch h\u00e0ng \u1ee9ng tr\u01b0\u1edbc ti\u1ec1n h\u00e0ng \u2192 b\u00ecnh th\u01b0\u1eddng\n- TK 331 d\u01b0 N\u1ee3: doanh nghi\u1ec7p \u1ee9ng tr\u01b0\u1edbc cho NCC \u2192 b\u00ecnh th\u01b0\u1eddng\n\n---\n\n# 11. V\u00cd D\u1ee4 MINH H\u1eccA\n\n## V\u00ed d\u1ee5 1 \u2014 Node l\u1ea5y d\u1eef li\u1ec7u & chuy\u1ec3n \u0111\u1ed5i\n\n**Input nh\u1eadn \u0111\u01b0\u1ee3c:**\n```json\n{\n "node_prompt": "L\u1ea5y b\u00e1o c\u00e1o doanh thu th\u00e1ng hi\u1ec7n t\u1ea1i, t\u00ednh t\u1ed5ng v\u00e0 tr\u1ea3 v\u1ec1 t\u1ed5ng doanh thu c\u00f9ng danh s\u00e1ch top 3 nh\u00e2n vi\u00ean b\u00e1n h\u00e0ng",\n "input": {},\n "context": { "timestamp": "2025-06-15T08:00:00Z", "node_id": "node_01" }\n}\n```\n\n**Output mong \u0111\u1ee3i:**\n```json\n{\n "status": "success",\n "data": {\n "thang": 6,\n "nam": 2025,\n "tong_doanh_thu": 1850000000,\n "top_3_nhan_vien": [\n { "ten": "Nguy\u1ec5n V\u0103n A", "doanh_thu": 420000000 },\n { "ten": "Tr\u1ea7n Th\u1ecb B", "doanh_thu": 380000000 },\n { "ten": "L\u00ea V\u0103n C", "doanh_thu": 310000000 }\n ]\n },\n "summary": "\u0110\u00e3 l\u1ea5y b\u00e1o c\u00e1o doanh thu th\u00e1ng 6/2025, t\u1ed5ng 1.85 t\u1ef7, top 3 nh\u00e2n vi\u00ean \u0111\u00e3 x\u00e1c \u0111\u1ecbnh.",\n "logs": [\n { "step": 1, "action": "G\u1ecdi getSalesReportTool (01/06/2025 - 15/06/2025)", "result": "Nh\u1eadn 87 records" },\n { "step": 2, "action": "Group by nh\u00e2n vi\u00ean, sort desc, l\u1ea5y top 3", "result": "OK" }\n ],\n "error": null\n}\n```\n\n---\n\n## V\u00ed d\u1ee5 2 \u2014 Node \u0111i\u1ec1u ki\u1ec7n (c\u00f3 th\u1ec3 skip)\n\n**Input nh\u1eadn \u0111\u01b0\u1ee3c:**\n```json\n{\n "node_prompt": "Ki\u1ec3m tra t\u1ed3n kho s\u1ea3n ph\u1ea9m SP001. N\u1ebfu t\u1ed3n < 100 \u0111\u01a1n v\u1ecb th\u00ec tr\u1ea3 c\u1ea3nh b\u00e1o, ng\u01b0\u1ee3c l\u1ea1i th\u00ec skip",\n "input": { "ma_vt": "SP001" },\n "context": { "timestamp": "2025-06-15T08:00:00Z", "node_id": "node_02" }\n}\n```\n\n**Output khi t\u1ed3n \u0111\u1ee7 (skip):**\n```json\n{\n "status": "skip",\n "data": { "ma_vt": "SP001", "ton_kho": 250, "nguong": 100 },\n "summary": "T\u1ed3n kho SP001 l\u00e0 250 \u0111\u01a1n v\u1ecb, v\u01b0\u1ee3t ng\u01b0\u1ee1ng 100. Node b\u1ecf qua.",\n "logs": [\n { "step": 1, "action": "G\u1ecdi getAccountingBalancesTool (ckvt, SP001)", "result": "T\u1ed3n: 250" },\n { "step": 2, "action": "\u0110\u00e1nh gi\u00e1 \u0111i\u1ec1u ki\u1ec7n: 250 >= 100", "result": "Kh\u00f4ng th\u1ecfa \u2192 SKIP" }\n ],\n "error": null\n}\n```\n\n---\n\n## V\u00ed d\u1ee5 3 \u2014 Node g\u1eedi email v\u1edbi link \u0111i\u1ec1u h\u01b0\u1edbng\n\n**Input nh\u1eadn \u0111\u01b0\u1ee3c:**\n```json\n{\n "node_prompt": "G\u1eedi email t\u00f3m t\u1eaft c\u00f4ng n\u1ee3 qu\u00e1 h\u1ea1n cho gi\u00e1m \u0111\u1ed1c t\u00e0i ch\u00ednh (cfo@company.vn). Email ph\u1ea3i k\u00e8m link \u0111\u1ebfn m\u00e0n h\u00ecnh c\u00f4ng n\u1ee3.",\n "input": {\n "danh_sach_cong_no": [\n { "khach_hang": "C\u00f4ng ty A", "so_tien": 150000000, "so_ngay_qua_han": 45 }\n ]\n },\n "context": { "timestamp": "2025-06-15T08:00:00Z", "node_id": "node_05" }\n}\n```\n\n**Output:**\n```json\n{\n "status": "success",\n "data": {\n "email_sent": true,\n "to": "cfo@company.vn",\n "link_cong_no": "/#/list/customer",\n "so_khach_hang_qua_han": 1,\n "tong_no_qua_han": 150000000\n },\n "summary": "\u0110\u00e3 g\u1eedi email c\u1ea3nh b\u00e1o c\u00f4ng n\u1ee3 qu\u00e1 h\u1ea1n cho cfo@company.vn, k\u00e8m link \u0111i\u1ec1u h\u01b0\u1edbng.",\n "logs": [\n { "step": 1, "action": "G\u1ecdi navigateToFeatureTool (\'c\u00f4ng n\u1ee3 kh\u00e1ch h\u00e0ng\')", "result": "/#/list/customer" },\n { "step": 2, "action": "G\u1ecdi getCompanyInfoTool \u0111\u1ec3 l\u1ea5y ch\u1eef k\u00fd", "result": "OK" },\n { "step": 3, "action": "G\u1ecdi sendEmailTool", "result": "G\u1eedi th\u00e0nh c\u00f4ng" }\n ],\n "error": null\n}\n```\n';
|
|
2
|
+
module.exports=WORKFLOW_NODE_SYSTEM_PROMPT;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const {generateText,stepCountIs,pruneMessages}=require("ai"),{createOpenAI}=require("@ai-sdk/openai"),chatController=require("./chatController"),{decrypt}=require("../libs/utils"),{createFinanceTools}=require("./tools/financeTools"),{createStaffTools}=require("./tools/staffTools"),{createRagTools}=require("./tools/ragTools"),{createMasterDataTools}=require("./tools/masterDataTools"),tenantConfigs={},SYSTEM_PROMPT=`# VAI TR\u00d2 V\u00c0 \u0110\u1ecaA V\u1eca C\u1ee6A B\u1ea0N
|
|
2
|
+
B\u1ea1n l\u00e0 Tr\u1ee3 l\u00fd AI N\u1ed9i b\u1ed9, l\u00e0 ng\u01b0\u1eddi c\u1ed9ng s\u1ef1 \u0111\u1eafc l\u1ef1c c\u1ee7a t\u1eebng Nh\u00e2n vi\u00ean trong c\u00f4ng ty. Nhi\u1ec7m v\u1ee5 c\u1ee7a b\u1ea1n l\u00e0 h\u1ed7 tr\u1ee3 nh\u00e2n s\u1ef1 t\u1ef1 tra c\u1ee9u th\u00f4ng tin c\u00e1 nh\u00e2n (ch\u1ea5m c\u00f4ng, doanh s\u1ed1 b\u00e1n h\u00e0ng, b\u1ea3ng l\u01b0\u01a1ng), ki\u1ec3m tra th\u00f4ng tin s\u1ea3n ph\u1ea9m/d\u1ecbch v\u1ee5, c\u1eadp nh\u1eadt ch\u00ednh s\u00e1ch chi\u1ebft kh\u1ea5u, khuy\u1ebfn m\u00e3i, tra c\u1ee9u tri th\u1ee9c n\u1ed9i b\u1ed9 v\u00e0 h\u1ed7 tr\u1ee3 so\u1ea1n th\u1ea3o/g\u1eedi email chuy\u00ean nghi\u1ec7p.
|
|
3
|
+
|
|
4
|
+
# NGUY\u00caN T\u1eaeC GIAO TI\u1ebeP & T\u00c1C PHONG
|
|
5
|
+
1. T\u00e1c phong: Th\u00e2n thi\u1ec7n, l\u1ecbch s\u1ef1, chu \u0111\u00e1o nh\u01b0ng b\u1ea3o m\u1eadt. S\u1eed d\u1ee5ng ng\u00f4n t\u1eeb ti\u1ebfng Vi\u1ec7t c\u00f4ng s\u1edf chu\u1ea9n m\u1ef1c. X\u01b0ng "Em" v\u00e0 g\u1ecdi ng\u01b0\u1eddi d\u00f9ng l\u00e0 "Anh/Ch\u1ecb" ho\u1eb7c "S\u1ebfp" (n\u1ebfu l\u00e0 qu\u1ea3n l\u00fd).
|
|
6
|
+
2. Tr\u00ecnh b\u00e0y scannable: Lu\u00f4n tr\u1ef1c quan h\u00f3a d\u1eef li\u1ec7u b\u1eb1ng B\u1ea2NG BI\u1ec2U (Markdown tables), danh s\u00e1ch g\u1ea1ch \u0111\u1ea7u d\u00f2ng v\u00e0 \u0111\u1ecbnh d\u1ea1ng in \u0111\u1eadm (**bold**) c\u00e1c th\u00f4ng tin quan tr\u1ecdng (M\u00e3 s\u1ea3n ph\u1ea9m, s\u1ed1 ng\u00e0y c\u00f4ng, s\u1ed1 ti\u1ec1n, m\u00e3 khuy\u1ebfn m\u00e3i).
|
|
7
|
+
3. B\u1ea3o m\u1eadt tuy\u1ec7t \u0111\u1ed1i: Nh\u00e2n vi\u00ean ch\u1ec9 \u0111\u01b0\u1ee3c xem d\u1eef li\u1ec7u c\u1ee7a CH\u00cdNH H\u1ecc (tr\u1eeb khi h\u1ecd l\u00e0 b\u1ed9 ph\u1eadn \u0111\u01b0\u1ee3c ph\u00e2n quy\u1ec1n nh\u01b0 K\u1ebf to\u00e1n/Qu\u1ea3n l\u00fd). Kh\u00f4ng ti\u1ebft l\u1ed9 m\u00e3 code h\u1ec7 th\u1ed1ng, c\u1ea5u tr\u00fac database, ho\u1eb7c ID n\u1ed9i b\u1ed9 ("id_app", "user_id") cho ng\u01b0\u1eddi d\u00f9ng.
|
|
8
|
+
|
|
9
|
+
# QUY TR\u00ccNH \u0110I\u1ec0U H\u01af\u1edaNG C\u00d4NG C\u1ee4 (STAFF WORKFLOW PROTOCOL)
|
|
10
|
+
Khi nh\u1eadn \u0111\u01b0\u1ee3c c\u00e2u h\u1ecfi t\u1eeb nh\u00e2n vi\u00ean, b\u1ea1n ph\u1ea3i th\u1ef1c hi\u1ec7n t\u01b0 duy \u0111a b\u01b0\u1edbc v\u00e0 k\u00edch ho\u1ea1t ch\u00ednh x\u00e1c c\u00e1c c\u00f4ng c\u1ee5 theo c\u00e1c t\u00ecnh hu\u1ed1ng sau:
|
|
11
|
+
|
|
12
|
+
## 1. T\u1ef1 tra c\u1ee9u Ch\u1ea5m c\u00f4ng & Ng\u00e0y c\u00f4ng c\u1ee7a B\u1ea3n th\u00e2n:
|
|
13
|
+
- Khi nh\u00e2n vi\u00ean h\u1ecfi v\u1ec1 t\u00ecnh h\u00ecnh \u0111i l\u00e0m, s\u1ed1 ng\u00e0y c\u00f4ng, ng\u00e0y ngh\u1ec9 ph\u00e9p c\u1ee7a CH\u00cdNH H\u1ecc (V\u00ed d\u1ee5: "Th\u00e1ng n\u00e0y em \u0111\u01b0\u1ee3c bao nhi\u00eau c\u00f4ng?", "Anh xem gi\u00f9m t\u00f4i c\u00f2n m\u1ea5y ng\u00e0y ph\u00e9p") $\rightarrow$ K\u00edch ho\u1ea1t "getAttendanceReportTool".
|
|
14
|
+
- **B\u1eaeT BU\u1ed8C:** Thi\u1ebft l\u1eadp tham s\u1ed1 "is_self_service: true" v\u00e0 b\u1ecf tr\u1ed1ng tr\u01b0\u1eddng "ma_nv". H\u1ec7 th\u1ed1ng s\u1ebd t\u1ef1 \u0111\u1ed9ng b\u1ed1c "user_id" \u0111\u1ec3 tra c\u1ee9u, \u0111\u1ea3m b\u1ea3o b\u1ea3o m\u1eadt.
|
|
15
|
+
- *L\u01b0u \u00fd:* Ch\u1ec9 truy\u1ec1n "is_self_service: false" v\u00e0 \u0111i\u1ec1n "ma_nv" khi ng\u01b0\u1eddi d\u00f9ng l\u00e0 Qu\u1ea3n l\u00fd/K\u1ebf to\u00e1n l\u01b0\u01a1ng \u0111ang c\u00f3 nhu c\u1ea7u tra c\u1ee9u cho nh\u00e2n vi\u00ean kh\u00e1c d\u01b0\u1edbi quy\u1ec1n.
|
|
16
|
+
|
|
17
|
+
## 2. Tra c\u1ee9u Doanh s\u1ed1 b\u00e1n h\u00e0ng & Hi\u1ec7u su\u1ea5t c\u00e1 nh\u00e2n:
|
|
18
|
+
- Khi nh\u00e2n vi\u00ean mu\u1ed1n xem doanh s\u1ed1 b\u00e1n l\u1ebb, s\u1ed1 l\u01b0\u1ee3ng h\u00e0ng m\u00ecnh \u0111\u00e3 xu\u1ea5t b\u00e1n, ho\u1eb7c s\u1ed1 ti\u1ec1n hoa h\u1ed3ng m\u00ecnh nh\u1eadn \u0111\u01b0\u1ee3c $\rightarrow$ G\u1ecdi c\u00f4ng c\u1ee5 "getKpiAndCommissionTool".
|
|
19
|
+
- \u0110\u1ec3 xem chi ti\u1ebft c\u00e1c h\u00f3a \u0111\u01a1n b\u00e1n h\u00e0ng do m\u00ecnh tr\u1ef1c ti\u1ebfp th\u1ef1c hi\u1ec7n trong k\u1ef3 $\rightarrow$ G\u1ecdi "getSalesReportTool" (Truy\u1ec1n m\u00e3 nh\u00e2n vi\u00ean c\u1ee7a h\u1ecd v\u00e0o b\u1ed9 l\u1ecdc n\u1ebfu c\u1ea7n).
|
|
20
|
+
|
|
21
|
+
## 3. Tra c\u1ee9u S\u1ea3n ph\u1ea9m, D\u1ecbch v\u1ee5, Gi\u00e1 b\u00e1n & Khuy\u1ebfn m\u00e3i:
|
|
22
|
+
- Khi c\u1ea7n ki\u1ec3m tra th\u00f4ng tin chi ti\u1ebft c\u1ee7a m\u1ed9t m\u1eb7t h\u00e0ng $\rightarrow$ G\u1ecdi "lookupMasterDataTool" (ch\u1ecdn type: "product") \u0111\u1ec3 l\u1ea5y ch\u00ednh x\u00e1c M\u00e3 v\u1eadt t\u01b0 ("ma_vt"). N\u1ebfu tool lookupMasterDataTool tr\u1ea3 v\u1ec1 nhi\u1ec1u k\u1ebft qu\u1ea3 th\u00ec c\u1ea7n hi\u1ec7n ra danh s\u00e1ch \u0111\u00e3 t\u00ecm th\u1ea5y v\u00e0 h\u1ecfi ng\u1eeboi d\u00f9ng xem ch\u1ecdn c\u00e1i n\u00e0o.
|
|
23
|
+
- \u0110\u1ec3 tra c\u1ee9u gi\u00e1 b\u00e1n, ch\u00ednh s\u00e1ch chi\u1ebft kh\u1ea5u, hoa h\u1ed3ng ho\u1eb7c c\u00e1c ch\u01b0\u01a1ng tr\u00ecnh khuy\u1ebfn m\u00e3i \u0111ang \u00e1p d\u1ee5ng cho s\u1ea3n ph\u1ea9m \u0111\u00f3 $\rightarrow$ G\u1ecdi "getProductPriceAndPromotionTool" (B\u1eaft bu\u1ed9c truy\u1ec1n "ma_vt" \u0111\u00e3 t\u00ecm \u0111\u01b0\u1ee3c \u1edf b\u01b0\u1edbc tr\u00ean).
|
|
24
|
+
- \u0110\u1ec3 ki\u1ec3m tra xem s\u1ea3n ph\u1ea9m \u0111\u00f3 hi\u1ec7n c\u00f2n h\u00e0ng \u1edf kho n\u00e0o \u0111\u1ec3 t\u01b0 v\u1ea5n cho kh\u00e1ch $\rightarrow$ G\u1ecdi "getInventorySummaryTool".
|
|
25
|
+
|
|
26
|
+
## 4. Tra c\u1ee9u Th\u00f4ng tin C\u00f4ng ty & Tri th\u1ee9c N\u1ed9i b\u1ed9:
|
|
27
|
+
- Khi c\u1ea7n l\u1ea5y th\u00f4ng tin ph\u00e1p l\u00fd c\u1ee7a c\u00f4ng ty (T\u00ean ch\u00ednh th\u1ee9c, MST, hotline, \u0111\u1ecba ch\u1ec9) \u0111\u1ec3 l\u00e0m ch\u00e2n trang v\u0103n b\u1ea3n $\rightarrow$ G\u1ecdi "getCompanyInfoTool".
|
|
28
|
+
- Khi c\u1ea7n tra c\u1ee9u quy ch\u1ebf n\u1ed9i b\u1ed9, ch\u00ednh s\u00e1ch th\u01b0\u1edfng ph\u1ea1t, quy tr\u00ecnh xin ngh\u1ec9 ph\u00e9p, ho\u1eb7c n\u1ed9i dung c\u00e1c v\u0103n b\u1ea3n/h\u1ee3p \u0111\u1ed3ng \u0111\u00ednh k\u00e8m $\rightarrow$ G\u1ecdi "searchKnowledgeBaseTool" (ph\u1ea1m vi "company").
|
|
29
|
+
|
|
30
|
+
## 5. So\u1ea1n th\u1ea3o & G\u1eedi Email l\u00e0m vi\u1ec7c:
|
|
31
|
+
- Khi nh\u00e2n vi\u00ean y\u00eau c\u1ea7u g\u1eedi b\u00e1o c\u00e1o, g\u1eedi b\u00e1o gi\u00e1 ho\u1eb7c t\u00e0i li\u1ec7u cho kh\u00e1ch h\u00e0ng/c\u1ea5p tr\u00ean $\rightarrow$ So\u1ea1n th\u1ea3o n\u1ed9i dung ho\u00e0n ch\u1ec9nh d\u01b0\u1edbi d\u1ea1ng HTML (s\u1eed d\u1ee5ng c\u00e1c th\u1ebb "<p>", "<b>", "<br/>", "<table>" \u0111\u1ec3 k\u1ebb b\u1ea3ng s\u1ed1 li\u1ec7u n\u1ebfu c\u00f3) v\u00e0 ti\u1ebfn h\u00e0nh g\u1eedi th\u00f4ng qua "sendEmailTool".
|
|
32
|
+
- H\u00e3y ch\u1ee7 \u0111\u1ed9ng k\u1ebft h\u1ee3p v\u1edbi "getCompanyInfoTool" \u0111\u1ec3 l\u1ea5y th\u00f4ng tin ch\u00e8n ph\u1ea7n k\u00fd t\u00ean (Footer) trang tr\u1ecdng \u1edf cu\u1ed1i th\u01b0.
|
|
33
|
+
|
|
34
|
+
# QUY TR\u00ccNH X\u1eec L\u00dd KHI KH\u00d4NG C\u00d3 D\u1eee LI\u1ec6U
|
|
35
|
+
- Tuy\u1ec7t \u0111\u1ed1i KH\u00d4NG \u0111\u01b0a ra c\u00e1c th\u00f4ng b\u00e1o l\u1ed7i k\u1ef9 thu\u1eadt h\u1ec7 th\u1ed1ng (nh\u01b0 "l\u1ed7i database", "null", "undefined").
|
|
36
|
+
- N\u1ebfu h\u1ec7 th\u1ed1ng ch\u01b0a tr\u1ea3 v\u1ec1 s\u1ed1 li\u1ec7u: "Anh/Ch\u1ecb vui l\u00f2ng ki\u1ec3m tra l\u1ea1i b\u1ed9 l\u1ecdc kho\u1ea3ng th\u1eddi gian. Hi\u1ec7n t\u1ea1i h\u1ec7 th\u1ed1ng ch\u01b0a ghi nh\u1eadn ph\u00e1t sinh d\u1eef li\u1ec7u cho m\u1ee5c n\u00e0y trong k\u1ef3 y\u00eau c\u1ea7u."
|
|
37
|
+
|
|
38
|
+
# QUY T\u1eaeC TH\u1edcI GIAN TH\u1ef0C T\u1ebe
|
|
39
|
+
Lu\u00f4n ghi nh\u1edb m\u1ed1c th\u1eddi gian hi\u1ec7n t\u1ea1i c\u1ee7a h\u1ec7 th\u1ed1ng (N\u0103m ${(new Date).getFullYear}) \u0111\u1ec3 t\u1ef1 \u0111\u1ed9ng t\u00ednh to\u00e1n ch\u00ednh x\u00e1c ng\u00e0y b\u1eaft \u0111\u1ea7u v\u00e0 ng\u00e0y k\u1ebft th\u00fac ("tu_ngay", "den_ngay") khi nh\u00e2n vi\u00ean h\u1ecfi c\u00e1c c\u1ee5m t\u1eeb \u0111\u1ed9ng nh\u01b0: "th\u00e1ng n\u00e0y", "th\u00e1ng tr\u01b0\u1edbc", "tu\u1ea7n n\u00e0y", "t\u1eeb \u0111\u1ea7u n\u0103m \u0111\u1ebfn nay".`;
|
|
40
|
+
async function runStaffAgent($id_app$$,$user_id$$,$session_id$$,$userPrompt$$){try{if(!tenantConfigs[$id_app$$]){const $app$$=await global.getModel("app").findOne({_id:$id_app$$},{openapi_key:1,openapi_gpt_model:1});if(!$app$$||!$app$$.openapi_key)throw Error("Ch\u01b0a khai b\u00e1o OpenAPI key cho h\u1ec7 th\u1ed1ng.");const $openapi_key$$=decrypt($app$$.openapi_key.replace("flexbiz::",""),!1);tenantConfigs[$id_app$$]={openaiInstance:createOpenAI({apiKey:$openapi_key$$}),gptModel:$app$$.openapi_gpt_model||
|
|
41
|
+
"gpt-5.4-mini"}}const {openaiInstance:$openaiInstance$$,gptModel:$gptModel$$}=tenantConfigs[$id_app$$];$session_id$$||=(await chatController.createSession($id_app$$,$user_id$$,$userPrompt$$.substring(0,1024)))._id.toString();const $messagesToSend$$=[...(await chatController.getChatHistory($id_app$$,$session_id$$,30)),{role:"user",content:$userPrompt$$}],$optimizedMessages$$=pruneMessages({messages:$messagesToSend$$,reasoning:"all",toolCalls:"before-last-message"}),{getSalesReportTool:$getSalesReportTool$$}=
|
|
42
|
+
createFinanceTools($id_app$$,$user_id$$),$activeTools$$={getSalesReportTool:$getSalesReportTool$$,...createStaffTools($id_app$$,$user_id$$),...createRagTools($id_app$$,$user_id$$),...createMasterDataTools($id_app$$,$user_id$$)},$result$$=await generateText({model:$openaiInstance$$.chat($gptModel$$),system:SYSTEM_PROMPT,messages:$optimizedMessages$$,tools:$activeTools$$,stopWhen:stepCountIs(10)});Logger.info("[ceoAgent] l\u01b0u messages v\u00e0o database..");const $msgsToSave$$=[{role:"user",content:$userPrompt$$},
|
|
43
|
+
...$result$$.response.messages.map($msg$$=>({role:$msg$$.role,content:$msg$$.content?Array.isArray($msg$$.content)?$msg$$.content[0]?.text:$msg$$.content:"",tool_calls:$msg$$.tool_calls||null,tool_call_id:$msg$$.tool_call_id||null,messages:[$msg$$]}))];try{await chatController.saveMessages($id_app$$,$session_id$$,$msgsToSave$$)}catch($e$$){Logger.error("[agents][runFinanceAgent] kh\u00f4ng th\u1ec3 l\u01b0u n\u1ed9i dung chat. Err:",$e$$)}return{session_id:$session_id$$,answer:$msgsToSave$$.length>
|
|
44
|
+
1?$msgsToSave$$.pop()?.content||"Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i":"Kh\u00f4ng c\u00f3 k\u1ebft qu\u1ea3 ph\u1ea3n h\u1ed3i"}}catch($error$$){throw console.error(`[L\u1ed7i Agent Staff] App: ${$id_app$$} | Session: ${$session_id$$} | Err:`,$error$$),Error($error$$.message);}}module.exports=runStaffAgent;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const {tool}=require("ai"),{z}=require("zod"),moment=require("moment"),getTdttco=require("../../libs/tuoinophaitra"),getTdttno=require("../../libs/tuoinophaithu"),dktk=require("../../libs/dktk"),cktk=require("../../libs/cktk"),dkcn=require("../../libs/dkcn"),ckcn=require("../../libs/ckcn"),dkvt=require("../../libs/dkvt"),ckvt=require("../../libs/ckvt"),cdpskh=require("../../libs/cdpskh"),cdpstk=require("../../libs/cdpstk"),getExecutiveDashboard=require("../../libs/executiveDashboard"),getRetailSalesReport=
|
|
2
|
+
require("../../libs/dtbanletheosp"),getKqChamCong=require("../../libs/kqchamcong");
|
|
3
|
+
function createFinanceTools($id_app$$,$user_id$$){return{getSalesReportTool:tool({description:"L\u1ea5y B\u00e1o c\u00e1o B\u00e1n h\u00e0ng / Doanh thu b\u00e1n l\u1ebb chi ti\u1ebft. Cung c\u1ea5p T\u1ed5ng doanh thu (tien/tt), S\u1ed1 l\u01b0\u1ee3ng b\u00e1n (sl_xuat), L\u1ee3i nhu\u1eadn g\u1ed9p (loi_nhuan), Ti\u1ec1n chi\u1ebft kh\u1ea5u v\u00e0 Hoa h\u1ed3ng.",inputSchema:z.object({tu_ngay:z.string().describe('Ng\u00e0y b\u1eaft \u0111\u1ea7u l\u1ea5y d\u1eef li\u1ec7u (YYYY-MM-DD). N\u1ebfu s\u1ebfp n\u00f3i "th\u00e1ng n\u00e0y" th\u00ec l\u00e0 ng\u00e0y 1 c\u1ee7a th\u00e1ng.'),den_ngay:z.string().describe("Ng\u00e0y k\u1ebft th\u00fac (YYYY-MM-DD)."),
|
|
4
|
+
ma_nv:z.string().optional().describe("M\u00e3 nh\u00e2n vi\u00ean kinh doanh."),ma_kh:z.string().optional().describe("M\u00e3 kh\u00e1ch h\u00e0ng. (Nh\u1edb g\u1ecdi lookupMasterDataTool n\u1ebfu ch\u1ec9 c\u00f3 t\u00ean)."),ma_vt:z.string().optional().describe("M\u00e3 s\u1ea3n ph\u1ea9m / v\u1eadt t\u01b0. (Nh\u1edb g\u1ecdi lookupMasterDataTool n\u1ebfu ch\u1ec9 c\u00f3 t\u00ean)."),ma_kho:z.string().optional().describe("M\u00e3 kho. D\u00f9ng khi s\u1ebfp ch\u1ec9 mu\u1ed1n xem doanh s\u1ed1 xu\u1ea5t \u0111i t\u1eeb m\u1ed9t kho c\u1ee5 th\u1ec3."),
|
|
5
|
+
user_hoa_hong:z.string().optional().describe("T\u00e0i kho\u1ea3n/M\u00e3 nh\u00e2n vi\u00ean nh\u1eadn hoa h\u1ed3ng."),tinh_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true n\u1ebfu mu\u1ed1n xem c\u1ea3 danh s\u00e1ch chi ti\u1ebft t\u1eebng h\u00f3a \u0111\u01a1n V\u00c0 d\u00f2ng t\u1ed5ng c\u1ed9ng."),chi_hien_tong_cong:z.boolean().optional().describe('R\u1ea4T QUAN TR\u1eccNG: B\u1eadt true n\u1ebfu s\u1ebfp CH\u1ec8 H\u1eceI S\u1ed0 T\u1ed4NG (VD: "T\u1ed5ng doanh thu th\u00e1ng n\u00e0y?"). Tr\u1ea3 v\u1ec1 1 d\u00f2ng \u0111\u1ec3 ti\u1ebft ki\u1ec7m token.'),
|
|
6
|
+
is_self_service:z.boolean().default(!1).describe("R\u1ea4T QUAN TR\u1eccNG: B\u1eadt TRUE n\u1ebfu ng\u01b0\u1eddi d\u00f9ng \u0111ang h\u1ecfi v\u1ec1 t\u00ecnh h\u00ecnh b\u00e1n h\u00e0ng c\u1ee7a ch\u00ednh m\u00ecnh")}),execute:async $query$$=>{Logger.info("[AI Agent][getSalesReportTool]",JSON.stringify($query$$));let {tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,ma_nv:$ma_nv$$,ma_kh:$ma_kh$$,ma_vt:$ma_vt$$,ma_kho:$ma_kho$$,user_hoa_hong:$user_hoa_hong$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$,
|
|
7
|
+
is_self_service:$is_self_service$$}=$query$$;try{if($is_self_service$$){let $nv$$=$user_id$$?global.getModel("dmnv").findOne({id_app:$id_app$$,user:$user_id$$},{ma_nv:1}).lean():null;if(!$nv$$)return{status:"error",message:"T\u00e0i kho\u1ea3n c\u1ee7a b\u1ea1n hi\u1ec7n ch\u01b0a \u0111\u01b0\u1ee3c li\u00ean k\u1ebft v\u1edbi b\u1ea5t k\u1ef3 m\u00e3 nh\u00e2n vi\u00ean n\u00e0o trong danh m\u1ee5c dmnv. Vui l\u00f2ng li\u00ean h\u1ec7 nh\u00e2n s\u1ef1."};$ma_nv$$=$nv$$.ma_nv}const $condition$$=
|
|
8
|
+
{id_app:$id_app$$,tu_ngay:moment($tu_ngay$$).startOf("date").toDate(),den_ngay:moment($den_ngay$$).endOf("date").toDate(),ma_nv:$ma_nv$$,ma_kh:$ma_kh$$,ma_vt:$ma_vt$$,ma_kho:$ma_kho$$,user_hoa_hong:$user_hoa_hong$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$};Object.keys($condition$$).forEach($key$$=>$condition$$[$key$$]===void 0&&delete $condition$$[$key$$]);const $data$$=await getRetailSalesReport($condition$$);return $data$$.filter($r$$=>!$r$$.bold).length===0?{status:"success_empty",
|
|
9
|
+
message:"B\u00e1o c\u00e1o ch\u1ea1y th\u00e0nh c\u00f4ng nh\u01b0ng KH\u00d4NG C\u00d3 D\u1eee LI\u1ec6U.",hint:"L\u01afU \u00dd QUAN TR\u1eccNG: K\u1ebft qu\u1ea3 r\u1ed7ng c\u00f3 th\u1ec3 do b\u00e1o c\u00e1o chu\u1ea9n b\u1ecb gi\u1edbi h\u1ea1n. TR\u01af\u1edaC KHI B\u00c1O C\u00c1O S\u1ebeP L\u00c0 KH\u00d4NG C\u00d3 D\u1eee LI\u1ec6U, b\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i th\u1eed ph\u01b0\u01a1ng \u00e1n cu\u1ed1i: G\u1ecdi 'exploreSystemSchemaTool' (t\u00ecm data_socai ho\u1eb7c data_sokho) r\u1ed3i d\u00f9ng 'codeInterpreterTool' \u0111\u1ec3 t\u1ef1 vi\u1ebft code truy v\u1ea5n s\u00e2u v\u00e0o DB. CH\u1ec8 KHI C\u1ea2 CODE INTERPRETER C\u0168NG KH\u00d4NG T\u00ccM TH\u1ea4Y, B\u1ea0N M\u1edaI \u0110\u01af\u1ee2C B\u00c1O CHO S\u1ebeP."}:
|
|
10
|
+
{status:"success",data:$data$$,hint:"'sl_xuat' l\u00e0 s\u1ed1 l\u01b0\u1ee3ng. 'tien_hang' l\u00e0 gi\u00e1 tr\u1ecb. 'tt' l\u00e0 t\u1ed5ng doanh thu. 'loi_nhuan' l\u00e0 ti\u1ec1n l\u1eddi g\u1ed9p."}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getCompanySnapshotTool:tool({description:"L\u1ea5y B\u00e1o c\u00e1o Dashboard to\u00e0n c\u1ea3nh (Executive Summary) c\u1ee7a doanh nghi\u1ec7p. Cung c\u1ea5p: Doanh thu, L\u1ee3i nhu\u1eadn, Ti\u1ec1n t\u1ed3n qu\u1ef9, T\u1ed3n kho, N\u1ee3 ph\u1ea3i thu/ph\u1ea3i tr\u1ea3.",
|
|
11
|
+
inputSchema:z.object({tu_ngay:z.string().describe("Ng\u00e0y b\u1eaft \u0111\u1ea7u k\u1ef3 b\u00e1o c\u00e1o (YYYY-MM-DD)."),den_ngay:z.string().describe("Ng\u00e0y k\u1ebft th\u00fac k\u1ef3 b\u00e1o c\u00e1o (YYYY-MM-DD).")}),execute:async({tu_ngay:$business_insights_tu_ngay$$,den_ngay:$den_ngay$$})=>{try{const $dashboardData$$=await getExecutiveDashboard({id_app:$id_app$$,tu_ngay:moment($business_insights_tu_ngay$$).startOf("date").toDate(),den_ngay:moment($den_ngay$$).endOf("date").toDate()});
|
|
12
|
+
$business_insights_tu_ngay$$=[];$dashboardData$$.tong_phai_thu<0&&$business_insights_tu_ngay$$.push("L\u01b0u \u00fd AI: S\u1ed1 d\u01b0 ph\u1ea3i thu (131) \u0111ang \u00e2m, ch\u1ee9ng t\u1ecf t\u1ed5ng s\u1ed1 ti\u1ec1n kh\u00e1ch h\u00e0ng \u1ee9ng tr\u01b0\u1edbc/\u0111\u1eb7t c\u1ecdc \u0111ang l\u1edbn h\u01a1n s\u1ed1 ti\u1ec1n h\u1ecd \u0111ang n\u1ee3.");$dashboardData$$.tong_phai_tra<0&&$business_insights_tu_ngay$$.push("L\u01b0u \u00fd AI: S\u1ed1 d\u01b0 ph\u1ea3i tr\u1ea3 (331) \u0111ang \u00e2m, ch\u1ee9ng t\u1ecf c\u00f4ng ty \u0111ang \u1ee9ng tr\u01b0\u1edbc ti\u1ec1n cho nh\u00e0 cung c\u1ea5p nhi\u1ec1u h\u01a1n s\u1ed1 ti\u1ec1n n\u1ee3 mua h\u00e0ng.");
|
|
13
|
+
return{status:"success",data:$dashboardData$$,insights:$business_insights_tu_ngay$$,message:"H\u00e3y ph\u00e2n t\u00edch c\u00e1c con s\u1ed1 n\u00e0y v\u00e0 \u0111\u01b0a ra nh\u1eadn x\u00e9t cho s\u1ebfp (VD: L\u1ee3i nhu\u1eadn \u0111ang d\u01b0\u01a1ng/\u00e2m, thanh kho\u1ea3n...). H\u00e3y d\u1ef1a v\u00e0o m\u1ee5c 'insights' (n\u1ebfu c\u00f3) \u0111\u1ec3 t\u01b0 v\u1ea5n nghi\u1ec7p v\u1ee5 m\u1ed9t c\u00e1ch chuy\u00ean nghi\u1ec7p nh\u01b0 m\u1ed9t K\u1ebf to\u00e1n tr\u01b0\u1edfng."}}catch($error$$){return{status:"error",
|
|
14
|
+
message:$error$$.message||$error$$.error||String($error$$)}}}}),getTrialBalanceTool:tool({description:"L\u1ea5y B\u1ea3ng C\u00e2n \u0111\u1ed1i s\u1ed1 ph\u00e1t sinh c\u00e1c t\u00e0i kho\u1ea3n k\u1ebf to\u00e1n (Trial Balance).",inputSchema:z.object({tu_ngay:z.string().describe("Ng\u00e0y b\u1eaft \u0111\u1ea7u (YYYY-MM-DD)."),den_ngay:z.string().describe("Ng\u00e0y k\u1ebft th\u00fac (YYYY-MM-DD)."),tk:z.string().optional().describe('T\u00e0i kho\u1ea3n k\u1ebf to\u00e1n c\u1ea7n tra c\u1ee9u (VD: "111", "112"). B\u1ecf tr\u1ed1ng l\u1ea5y to\u00e0n b\u1ed9.'),
|
|
15
|
+
bac_tk:z.number().optional().describe("C\u1ea5p b\u1eadc t\u00e0i kho\u1ea3n (1, 2, 3...)."),chi_lay_tk_con:z.boolean().optional().describe("Truy\u1ec1n true n\u1ebfu CH\u1ec8 mu\u1ed1n xem t\u00e0i kho\u1ea3n chi ti\u1ebft c\u1ea5p th\u1ea5p nh\u1ea5t."),bu_tru:z.boolean().optional().describe("Cho ph\u00e9p b\u00f9 tr\u1eeb c\u00f4ng n\u1ee3 2 b\u00ean N\u1ee3/C\u00f3."),ma_dvcs:z.string().optional().describe("M\u00e3 \u0111\u01a1n v\u1ecb c\u01a1 s\u1edf (chi nh\u00e1nh)."),ma_kho:z.string().optional().describe("M\u00e3 kho.")}),
|
|
16
|
+
execute:async $query$$=>{Logger.info("[AI Agent][getTrialBalanceTool]",JSON.stringify($query$$));let {tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,tk:$tk$$,bac_tk:$bac_tk$$,chi_lay_tk_con:$chi_lay_tk_con$$,bu_tru:$bu_tru$$,ma_dvcs:$ma_dvcs$$,ma_kho:$ma_kho$$}=$query$$;try{const $condition$$={id_app:$id_app$$,tu_ngay:moment($tu_ngay$$).startOf("date").toDate(),den_ngay:moment($den_ngay$$).endOf("date").toDate(),tk:$tk$$,bac_tk:$bac_tk$$,chi_lay_tk_con:$chi_lay_tk_con$$,bu_tru:$bu_tru$$,ma_dvcs:$ma_dvcs$$,
|
|
17
|
+
ma_kho:$ma_kho$$};Object.keys($condition$$).forEach($key$$=>$condition$$[$key$$]===void 0&&delete $condition$$[$key$$]);return{status:"success",data:await cdpstk($condition$$)}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getDebtBalanceSummaryTool:tool({description:"L\u1ea5y B\u00e1o c\u00e1o T\u1ed5ng h\u1ee3p C\u00f4ng n\u1ee3 / C\u00e2n \u0111\u1ed1i ph\u00e1t sinh c\u00f4ng n\u1ee3 c\u1ee7a kh\u00e1ch h\u00e0ng ho\u1eb7c nh\u00e0 cung c\u1ea5p.",
|
|
18
|
+
inputSchema:z.object({tk:z.string().describe('R\u1ea4T QUAN TR\u1eccNG: "kh\u00e1ch h\u00e0ng/ng\u01b0\u1eddi mua" truy\u1ec1n "131". "nh\u00e0 cung c\u1ea5p/ng\u01b0\u1eddi b\u00e1n" truy\u1ec1n "331".'),tu_ngay:z.string().describe("Ng\u00e0y b\u1eaft \u0111\u1ea7u (YYYY-MM-DD)."),den_ngay:z.string().describe("Ng\u00e0y k\u1ebft th\u00fac (YYYY-MM-DD)."),ma_kh:z.string().optional().describe("M\u00e3 kh\u00e1ch h\u00e0ng / nh\u00e0 cung c\u1ea5p. C\u00f3 th\u1ec3 truy\u1ec1n 1 m\u00e3 ho\u1eb7c m\u1ea3ng nhi\u1ec1u m\u00e3."),
|
|
19
|
+
ma_nv:z.string().optional().describe("M\u00e3 nh\u00e2n vi\u00ean ph\u1ee5 tr\u00e1ch."),groupBy:z.array(z.string()).optional().describe('M\u1eb7c \u0111\u1ecbnh l\u00e0 ["ma_kh"]. Truy\u1ec1n ["ma_nv"] n\u1ebfu mu\u1ed1n g\u1ed9p theo nh\u00e2n vi\u00ean.'),tinh_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true \u0111\u1ec3 th\u00eam d\u00f2ng T\u1ed5ng c\u1ed9ng."),chi_hien_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true n\u1ebfu s\u1ebfp CH\u1ec8 H\u1eceI C\u00c1C S\u1ed0 T\u1ed4NG.")}),
|
|
20
|
+
execute:async $query$$=>{Logger.info("[AI Agent][getDebtBalanceSummaryTool]",JSON.stringify($query$$));let {tk:$tk$$,tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,ma_kh:$ma_kh$$,ma_nv:$ma_nv$$,groupBy:$groupBy$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$}=$query$$;try{const $query$$={id_app:$id_app$$,tk:$tk$$,tu_ngay:moment($tu_ngay$$).startOf("date").toDate(),den_ngay:moment($den_ngay$$).endOf("date").toDate(),ma_kh:$ma_kh$$,ma_nv:$ma_nv$$,groupBy:$groupBy$$||["ma_kh"],tinh_tong_cong:$tinh_tong_cong$$,
|
|
21
|
+
chi_hien_tong_cong:$chi_hien_tong_cong$$};Object.keys($query$$).forEach($key$$=>$query$$[$key$$]===void 0&&delete $query$$[$key$$]);return{status:"success",data:await cdpskh($query$$)}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getReceivableAgingTool:tool({description:"L\u1ea5y b\u00e1o c\u00e1o ph\u00e2n t\u00edch tu\u1ed5i n\u1ee3 ph\u1ea3i thu (Kh\u00e1ch h\u00e0ng n\u1ee3 c\u00f4ng ty).",inputSchema:z.object({den_ngay:z.string().optional().describe("T\u00ednh tu\u1ed5i n\u1ee3 \u0111\u1ebfn ng\u00e0y n\u00e0o (YYYY-MM-DD). M\u1eb7c \u0111\u1ecbnh cu\u1ed1i ng\u00e0y h\u00f4m nay."),
|
|
22
|
+
ma_kh:z.string().optional().describe("M\u00e3 kh\u00e1ch h\u00e0ng c\u1ea7n tra c\u1ee9u."),tuoi_no:z.number().optional().describe("S\u1ed1 ng\u00e0y tu\u1ed5i n\u1ee3 t\u1ed1i thi\u1ec3u (L\u1ecdc n\u1ee3 qu\u00e1 h\u1ea1n)."),tat_toan:z.boolean().optional().describe("Truy\u1ec1n false n\u1ebfu ch\u1ec9 l\u1ea5y kho\u1ea3n C\u00d2N N\u1ee2."),tinh_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true \u0111\u1ec3 th\u00eam d\u00f2ng T\u1ed5ng."),chi_hien_tong_cong:z.boolean().optional().describe('Truy\u1ec1n true n\u1ebfu s\u1ebfp CH\u1ec8 H\u1eceI S\u1ed0 T\u1ed4NG (VD: "T\u1ed5ng ti\u1ec1n kh\u00e1ch n\u1ee3?").')}),
|
|
23
|
+
execute:async $query$$=>{Logger.info("[AI Agent][getDebtBalanceSummaryTool]",JSON.stringify($query$$));let {den_ngay:$den_ngay$$,ma_kh:$ma_kh$$,tuoi_no:$tuoi_no$$,tat_toan:$tat_toan$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$}=$query$$;try{const $condition$$={id_app:$id_app$$,den_ngay:$den_ngay$$?new Date($den_ngay$$):void 0,ma_kh:$ma_kh$$,tuoi_no:$tuoi_no$$,tat_toan:$tat_toan$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$};Object.keys($condition$$).forEach($key$$=>
|
|
24
|
+
$condition$$[$key$$]===void 0&&delete $condition$$[$key$$]);return{status:"success",data:await getTdttno($condition$$)}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getPayableAgingTool:tool({description:"L\u1ea5y b\u00e1o c\u00e1o ph\u00e2n t\u00edch tu\u1ed5i n\u1ee3 ph\u1ea3i tr\u1ea3 (C\u00f4ng ty n\u1ee3 Nh\u00e0 cung c\u1ea5p).",inputSchema:z.object({den_ngay:z.string().optional().describe("T\u00ednh tu\u1ed5i n\u1ee3 \u0111\u1ebfn ng\u00e0y n\u00e0o (YYYY-MM-DD). M\u1eb7c \u0111\u1ecbnh cu\u1ed1i ng\u00e0y h\u00f4m nay."),
|
|
25
|
+
ma_kh:z.string().optional().describe("M\u00e3 nh\u00e0 cung c\u1ea5p c\u1ea7n tra c\u1ee9u."),tuoi_no:z.number().optional().describe("S\u1ed1 ng\u00e0y tu\u1ed5i n\u1ee3 t\u1ed1i thi\u1ec3u \u0111\u1ec3 l\u1ecdc."),tat_toan:z.boolean().optional().describe("Truy\u1ec1n false n\u1ebfu ch\u1ec9 xem h\u00f3a \u0111\u01a1n C\u00d2N N\u1ee2."),tinh_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true \u0111\u1ec3 th\u00eam d\u00f2ng T\u1ed5ng."),chi_hien_tong_cong:z.boolean().optional().describe("Truy\u1ec1n true n\u1ebfu s\u1ebfp CH\u1ec8 H\u1eceI S\u1ed0 T\u1ed4NG.")}),
|
|
26
|
+
execute:async $query$$=>{Logger.info("[AI Agent][getDebtBalanceSummaryTool]",JSON.stringify($query$$));let {den_ngay:$den_ngay$$,ma_kh:$ma_kh$$,tuoi_no:$tuoi_no$$,tat_toan:$tat_toan$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$}=$query$$;try{const $condition$$={id_app:$id_app$$,den_ngay:$den_ngay$$?new Date($den_ngay$$):void 0,ma_kh:$ma_kh$$,tuoi_no:$tuoi_no$$,tat_toan:$tat_toan$$,tinh_tong_cong:$tinh_tong_cong$$,chi_hien_tong_cong:$chi_hien_tong_cong$$};Object.keys($condition$$).forEach($key$$=>
|
|
27
|
+
$condition$$[$key$$]===void 0&&delete $condition$$[$key$$]);return{status:"success",data:await getTdttco($condition$$)}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getAccountBalanceTool:tool({description:'C\u00f4ng c\u1ee5 l\u1ea5y B\u1ea3ng C\u00e2n \u0111\u1ed1i Ph\u00e1t sinh T\u00e0i kho\u1ea3n / B\u00e1o c\u00e1o t\u00e0i ch\u00ednh.\n [BI\u1ec2N C\u1ea4M]: TUY\u1ec6T \u0110\u1ed0I KH\u00d4NG S\u1eec D\u1ee4NG tool n\u00e0y n\u1ebfu ng\u01b0\u1eddi d\u00f9ng CH\u1ec8 h\u1ecfi "C\u00f4ng n\u1ee3 \u00f4ng A c\u00f2n bao nhi\u00eau" ho\u1eb7c "T\u00e0i kho\u1ea3n 111 c\u00f2n bao nhi\u00eau ti\u1ec1n".\n - N\u1ebfu ch\u1ec9 c\u1ea7n xem s\u1ed1 ti\u1ec1n/c\u00f4ng n\u1ee3 ch\u1ed1t t\u1ea1i m\u1ed9t ng\u00e0y: B\u1eaft bu\u1ed9c g\u1ecdi \'getAccountingBalancesTool\' (d\u00f9ng dktk, cktk, dkcn, ckcn).\n - CH\u1ec8 S\u1eec D\u1ee4NG tool n\u00e0y khi c\u1ea7n ph\u00e2n t\u00edch Ph\u00e1t sinh N\u1ee3/C\u00f3 trong k\u1ef3 ho\u1eb7c l\u1eadp b\u00e1o c\u00e1o k\u1ebft qu\u1ea3 kinh doanh.',
|
|
28
|
+
inputSchema:z.object({tu_ngay:z.string().describe(`Ng\u00e0y b\u1eaft \u0111\u1ea7u (YYYY-MM-DD). N\u1ebfu "\u0111\u1ea7u n\u0103m nay" hi\u1ec3u l\u00e0 ${(new Date).getFullYear()}-01-01.`),den_ngay:z.string().describe("Ng\u00e0y k\u1ebft th\u00fac (YYYY-MM-DD). M\u1eb7c \u0111\u1ecbnh l\u00e0 h\u00f4m nay."),tk:z.string().optional().describe("T\u00e0i kho\u1ea3n k\u1ebf to\u00e1n. B\u1ecf tr\u1ed1ng s\u1ebd l\u1ea5y c\u1ee7a t\u1ea5t c\u1ea3 t\u00e0i kho\u1ea3n.")}),execute:async $query$$=>{Logger.info("[AI Agent][getAccountBalanceTool]",
|
|
29
|
+
JSON.stringify($query$$));try{const {tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,tk:$tk$$}=$query$$;return{status:"success",data:await cdpstk({id_app:$id_app$$,tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,tk:$tk$$})}}catch($error$$){return{status:"error",message:$error$$.message||$error$$.error||String($error$$)}}}}),getAccountingBalancesTool:tool({description:"C\u00f4ng c\u1ee5 tra c\u1ee9u S\u1ed0 D\u01af \u0110\u1ea6U K\u1ef2 / CU\u1ed0I K\u1ef2 cho K\u1ebf to\u00e1n v\u00e0 Kho.\n [\u01afU TI\u00caN S\u1ed0 1]: KHI NG\u01af\u1edcI D\u00d9NG CH\u1ec8 H\u1eceI V\u1ec0 S\u1ed0 D\u01af (T\u1ed3n kho hi\u1ec7n t\u1ea1i, C\u00f4ng n\u1ee3 ch\u1ed1t ng\u00e0y, Ti\u1ec1n m\u1eb7t hi\u1ec7n c\u00f3...), B\u1ea0N B\u1eaeT BU\u1ed8C PH\u1ea2I D\u00d9NG TOOL N\u00c0Y \u0110\u1ea6U TI\u00caN v\u00ec n\u00f3 truy xu\u1ea5t c\u1ef1c k\u1ef3 nhanh.\n S\u1eec D\u1ee4NG KHI:\n 1. T\u1ed3n kho v\u1eadt t\u01b0/h\u00e0ng h\u00f3a (\u0111\u1ea7u/cu\u1ed1i ng\u00e0y).\n 2. S\u1ed1 d\u01b0 t\u00e0i kho\u1ea3n k\u1ebf to\u00e1n (\u0111\u1ea7u/cu\u1ed1i ng\u00e0y).\n 3. C\u00f4ng n\u1ee3 kh\u00e1ch h\u00e0ng/nh\u00e0 cung c\u1ea5p (\u0111\u1ea7u/cu\u1ed1i ng\u00e0y).\n L\u01afU \u00dd QUAN TR\u1eccNG: Tool n\u00e0y CH\u1ec8 L\u1ea4Y S\u1ed0 D\u01af. Kh\u00f4ng d\u00f9ng \u0111\u1ec3 t\u00ecm giao d\u1ecbch ph\u00e1t sinh.",
|
|
30
|
+
inputSchema:z.object({function_type:z.enum("dkvt ckvt dktk cktk dkcn ckcn".split(" ")).describe("Lo\u1ea1i s\u1ed1 d\u01b0 c\u1ea7n l\u1ea5y: dkvt/ckvt (T\u1ed3n kho \u0111\u1ea7u/cu\u1ed1i), dktk/cktk (S\u1ed1 d\u01b0 TK \u0111\u1ea7u/cu\u1ed1i), dkcn/ckcn (C\u00f4ng n\u1ee3 \u0111\u1ea7u/cu\u1ed1i)."),ngay:z.string().describe("Ng\u00e0y tra c\u1ee9u \u0111\u1ecbnh d\u1ea1ng YYYY-MM-DD. M\u1eb7c \u0111\u1ecbnh l\u00e0 h\u00f4m nay."),ma_vt:z.string().optional().describe("(Ch\u1ec9 d\u00f9ng cho dkvt, ckvt) M\u00e3 h\u00e0ng ho\u00e1, v\u1eadt t\u01b0 c\u1ea7n xem t\u1ed3n kho."),
|
|
31
|
+
tk:z.string().optional().describe("(D\u00f9ng cho dktk, cktk, dkcn, ckcn) T\u00e0i kho\u1ea3n k\u1ebf to\u00e1n. V\u1edbi c\u00f4ng n\u1ee3, Ph\u1ea3i thu th\u01b0\u1eddng l\u00e0 131, Ph\u1ea3i tr\u1ea3 l\u00e0 331."),ma_kh:z.string().optional().describe("(Ch\u1ec9 d\u00f9ng cho dkcn, ckcn) M\u00e3 kh\u00e1ch h\u00e0ng / Nh\u00e0 cung c\u1ea5p c\u1ea7n tra c\u1ee9u c\u00f4ng n\u1ee3.")}),execute:async({function_type:$function_type$$,ngay:$ngay$$,ma_vt:$functionLabels_ma_vt$$,tk:$tk$$,ma_kh:$ma_kh$$})=>
|
|
32
|
+
{try{console.log(`[getAccountingBalancesTool] Th\u1ef1c thi: ${$function_type$$} cho ng\u00e0y ${$ngay$$}`);let $data$$;switch($function_type$$){case "dkvt":$data$$=await dkvt({id_app:$id_app$$,ngay:$ngay$$,ma_vt:$functionLabels_ma_vt$$});break;case "ckvt":$data$$=await ckvt({id_app:$id_app$$,ngay:$ngay$$,ma_vt:$functionLabels_ma_vt$$});break;case "dktk":$data$$=await dktk({id_app:$id_app$$,ngay:$ngay$$,tk:$tk$$});break;case "cktk":$data$$=await cktk({id_app:$id_app$$,ngay:$ngay$$,tk:$tk$$});break;
|
|
33
|
+
case "dkcn":$data$$=await dkcn({id_app:$id_app$$,ngay:$ngay$$,tk:$tk$$,ma_kh:$ma_kh$$});break;case "ckcn":$data$$=await ckcn({id_app:$id_app$$,ngay:$ngay$$,tk:$tk$$,ma_kh:$ma_kh$$});break;default:throw Error(`Lo\u1ea1i h\u00e0m '${$function_type$$}' kh\u00f4ng \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3 trong h\u1ec7 th\u1ed1ng.`);}$functionLabels_ma_vt$$={dkvt:"T\u1ed3n kho \u0111\u1ea7u ng\u00e0y",ckvt:"T\u1ed3n kho cu\u1ed1i ng\u00e0y",dktk:"S\u1ed1 d\u01b0 t\u00e0i kho\u1ea3n \u0111\u1ea7u ng\u00e0y",
|
|
34
|
+
cktk:"S\u1ed1 d\u01b0 t\u00e0i kho\u1ea3n cu\u1ed1i ng\u00e0y",dkcn:"C\u00f4ng n\u1ee3 \u0111\u1ea7u ng\u00e0y",ckcn:"C\u00f4ng n\u1ee3 cu\u1ed1i ng\u00e0y"};return{status:"success",function_executed:$function_type$$,label:$functionLabels_ma_vt$$[$function_type$$],ngay_tra_cuu:$ngay$$,data:$data$$,hint:`AI \u0110\u1eb6C BI\u1ec6T L\u01afU \u00dd: \u0110\u00e2y l\u00e0 s\u1ed1 li\u1ec7u ${$functionLabels_ma_vt$$[$function_type$$]} t\u00ednh \u0111\u1ebfn m\u1ed1c ng\u00e0y ${$ngay$$}. H\u00e3y s\u1eed d\u1ee5ng s\u1ed1 li\u1ec7u n\u00e0y \u0111\u1ec3 b\u00e1o c\u00e1o tr\u1ef1c ti\u1ebfp cho ng\u01b0\u1eddi d\u00f9ng.`}}catch($error$$){return console.error(`[L\u1ed7i getAccountingBalancesTool - ${$function_type$$}]:`,
|
|
35
|
+
$error$$),{status:"error",message:$error$$.message||$error$$.error||String($error$$),hint:"N\u1ebfu l\u1ed7i do thi\u1ebfu tham s\u1ed1 (VD: ch\u01b0a truy\u1ec1n m\u00e3 v\u1eadt t\u01b0, s\u1ed1 t\u00e0i kho\u1ea3n), h\u00e3y y\u00eau c\u1ea7u ng\u01b0\u1eddi d\u00f9ng cung c\u1ea5p th\u00eam th\u00f4ng tin."}}}}),analyzeGeneralLedgerTool:tool({description:"C\u00f4ng c\u1ee5 truy v\u1ea5n v\u00e0 t\u1ed5ng h\u1ee3p d\u1eef li\u1ec7u t\u1eeb S\u1ed5 c\u00e1i k\u1ebf to\u00e1n (socai). Tool n\u00e0y ch\u1ec9 d\u00f9ng \u0111\u1ec3 l\u1ea5y d\u1eef li\u1ec7u ph\u00e1t sinh, kh\u00f4ng c\u00f3 s\u1ed1 d\u01b0 \u0111\u1ea7u k\u1ef3 v\u00e0 cu\u1ed1i k\u1ef3. \n S\u1eec D\u1ee4NG KHI s\u1ebfp h\u1ecfi v\u1ec1 s\u1ed1 t\u1ed5ng c\u1ee7a: Doanh thu (TK C\u00f3 511), Chi ph\u00ed (TK N\u1ee3 6*), D\u00f2ng ti\u1ec1n (TK 111, 112), ho\u1eb7c c\u00e1c kho\u1ea3n ph\u00e1t sinh theo b\u1ed9 ph\u1eadn, nh\u00e2n vi\u00ean, d\u1ef1 \u00e1n.\n AI h\u00e3y d\u00f9ng ki\u1ebfn th\u1ee9c k\u1ebf to\u00e1n \u0111\u1ec3 suy lu\u1eadn t\u00e0i kho\u1ea3n. V\u00ed d\u1ee5: S\u1ebfp h\u1ecfi \"Doanh thu\" -> tk_co_starts_with: '511'. S\u1ebfp h\u1ecfi \"Chi ph\u00ed\" -> tk_no_starts_with: '6'.\n Quan tr\u1ecdng: S\u1eed d\u1ee5ng lookupMasterDataTool \u0111\u1ec3 l\u1ea5y m\u00e3 c\u1ee7a \u0111\u1ed1i t\u01b0\u1ee3ng n\u1ebfu ch\u1ec9 bi\u1ebft t\u00ean. V\u00ed d\u1ee5 khi h\u1ecfi chi ph\u00ed b\u00e1n h\u00e0ng l\u00e0 bao nhi\u00eau th\u00ec c\u1ea7n s\u1eed d\u1ee5ng tool lookupMasterDataTool \u0111\u1ec3 l\u1ea5y danh s\u00e1ch c\u00e1c t\u00e0i kho\u1ea3n chi ph\u00ed b\u00e1n h\u00e0ng tr\u01b0\u1edbc, sau \u0111\u00f3 truy\u1ec1n m\u1ea3ng t\u00e0i kho\u1ea3n \u0111\u00f3 v\u00e0o tk_no_in.",
|
|
36
|
+
inputSchema:z.object({tu_ngay:z.string().optional().describe("T\u1eeb ng\u00e0y (YYYY-MM-DD)"),den_ngay:z.string().optional().describe("\u0110\u1ebfn ng\u00e0y (YYYY-MM-DD)"),tk_no_starts_with:z.string().optional().describe('L\u1ecdc c\u00e1c giao d\u1ecbch c\u00f3 T\u00e0i kho\u1ea3n N\u1ee3 b\u1eaft \u0111\u1ea7u b\u1eb1ng. (VD: "6" \u0111\u1ec3 l\u1ea5y to\u00e0n b\u1ed9 chi ph\u00ed). KH\u00d4NG d\u00f9ng chung v\u1edbi tk_no_in.'),tk_co_starts_with:z.string().optional().describe('L\u1ecdc c\u00e1c giao d\u1ecbch c\u00f3 T\u00e0i kho\u1ea3n C\u00f3 b\u1eaft \u0111\u1ea7u b\u1eb1ng. (VD: "511" \u0111\u1ec3 l\u1ea5y doanh thu). KH\u00d4NG d\u00f9ng chung v\u1edbi tk_co_in.'),
|
|
37
|
+
tk_no_in:z.array(z.string()).optional().describe('L\u1ecdc c\u00e1c giao d\u1ecbch c\u00f3 T\u00e0i kho\u1ea3n N\u1ee3 n\u1eb1m trong m\u1ed9t danh s\u00e1ch c\u1ee5 th\u1ec3. (V\u00ed d\u1ee5: ["6411", "6412", "6413"] sau khi tra c\u1ee9u t\u1eeb danh m\u1ee5c chi ph\u00ed b\u00e1n h\u00e0ng).'),tk_co_in:z.array(z.string()).optional().describe('L\u1ecdc c\u00e1c giao d\u1ecbch c\u00f3 T\u00e0i kho\u1ea3n C\u00f3 n\u1eb1m trong m\u1ed9t danh s\u00e1ch c\u1ee5 th\u1ec3. (V\u00ed d\u1ee5: ["5111", "5112"]).'),
|
|
38
|
+
ma_kh_no:z.string().optional().describe("M\u00e3 kh\u00e1ch h\u00e0ng n\u1ee3 (\u0111i k\u00e8m v\u1edbi t\u00e0i kho\u1ea3n n\u1ee3 - tk_no)"),ma_kh_co:z.string().optional().describe("M\u00e3 kh\u00e1ch h\u00e0ng c\u00f3 (\u0111i k\u00e8m v\u1edbi t\u00e0i kho\u1ea3n c\u00f3 - tk_co)"),ma_bp:z.string().optional().describe("M\u00e3 b\u1ed9 ph\u1eadn"),ma_nv:z.string().optional().describe("M\u00e3 nh\u00e2n vi\u00ean (D\u00f9ng khi t\u00ednh doanh thu/chi ph\u00ed theo nh\u00e2n vi\u00ean)"),ma_dt:z.string().optional().describe("M\u00e3 d\u1ef1 \u00e1n/v\u1ee5 vi\u1ec7c"),
|
|
39
|
+
ma_phi:z.string().optional().describe("M\u00e3 kho\u1ea3n ph\u00ed"),ma_kho:z.string().optional().describe("M\u00e3 kho/c\u1eeda h\u00e0ng"),ma_vt:z.string().optional().describe("M\u00e3 v\u1eadt t\u01b0/s\u1ea3n ph\u1ea9m"),group_by:z.enum("none thang tk_no tk_co ma_bp ma_nv ma_kh_no ma_kh_co ma_kho ma_phi ma_dt ma_vt".split(" ")).describe('Ti\u00eau ch\u00ed gom nh\u00f3m. N\u1ebfu s\u1ebfp h\u1ecfi "T\u1ed5ng doanh thu", ch\u1ecdn "none". N\u1ebfu s\u1ebfp h\u1ecfi "Chi ti\u1ebft chi ph\u00ed theo t\u1eebng b\u1ed9 ph\u1eadn", ch\u1ecdn "ma_bp".')}),
|
|
40
|
+
execute:async $mapping_query$$=>{Logger.info("[AI Agent][analyzeGeneralLedgerTool]",JSON.stringify($mapping_query$$));try{const {tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,tk_no_starts_with:$tk_no_starts_with$$,tk_co_starts_with:$tk_co_starts_with$$,tk_no_in:$tk_no_in$$,tk_co_in:$tk_co_in$$,ma_kh_no:$ma_kh_no$$,ma_kh_co:$ma_kh_co$$,ma_bp:$ma_bp$$,ma_nv:$ma_nv$$,ma_dt:$ma_dt$$,ma_phi:$ma_phi$$,ma_vt:$ma_vt$$,ma_kho:$ma_kho$$,group_by:$group_by$$}=$mapping_query$$;$mapping_query$$={ma_vt:{model:"dmvt",
|
|
41
|
+
title_field:"ten_vt"},ma_kho:{model:"dmkho",title_field:"ten_kho"},ma_kh_no:{model:"customer",value_field:"ma_kh",title_field:"ten_kh"},ma_kh_co:{model:"customer",value_field:"ma_kh",title_field:"ten_kh"},ma_bp:{model:"dmbp",title_field:"ten_bp"},ma_nv:{model:"dmnv",title_field:"ten_nv"},ma_phi:{model:"dmphi",title_field:"ten_phi"},ma_dt:{model:"dmdt",title_field:"ten_dt"}};const $Socai$$=global.getModel("socai"),$matchQuery$$={id_app:$id_app$$};if($tu_ngay$$||$den_ngay$$)$matchQuery$$.ngay_ct={},
|
|
42
|
+
$tu_ngay$$&&($matchQuery$$.ngay_ct.$gte=moment($tu_ngay$$).startOf("date").toDate()),$den_ngay$$&&($matchQuery$$.ngay_ct.$lte=moment($den_ngay$$).endOf("date").toDate());$tk_no_in$$&&$tk_no_in$$.length>0?$matchQuery$$.tk_no={$in:$tk_no_in$$}:$tk_no_starts_with$$&&($matchQuery$$.tk_no={$regex:`^${$tk_no_starts_with$$}`});$tk_co_in$$&&$tk_co_in$$.length>0?$matchQuery$$.tk_co={$in:$tk_co_in$$}:$tk_co_starts_with$$&&($matchQuery$$.tk_co={$regex:`^${$tk_co_starts_with$$}`});$ma_kh_no$$&&($matchQuery$$.ma_kh_no=
|
|
43
|
+
$ma_kh_no$$);$ma_kh_co$$&&($matchQuery$$.ma_kh_co=$ma_kh_co$$);$ma_bp$$&&($matchQuery$$.ma_bp=$ma_bp$$);$ma_nv$$&&($matchQuery$$.ma_nv=$ma_nv$$);$ma_dt$$&&($matchQuery$$.ma_dt=$ma_dt$$);$ma_phi$$&&($matchQuery$$.ma_phi=$ma_phi$$);$ma_vt$$&&($matchQuery$$.ma_vt=$ma_vt$$);$ma_kho$$&&($matchQuery$$.ma_kho=$ma_kho$$);let $groupId$$=null;$group_by$$==="thang"?$groupId$$={thang:{$month:"$ngay_ct"},nam:{$year:"$ngay_ct"}}:$group_by$$!=="none"&&($groupId$$=`$${$group_by$$}`);const $results$$=await $Socai$$.aggregate([{$match:$matchQuery$$},
|
|
44
|
+
{$group:{_id:$groupId$$,tong_tien:{$sum:"$tien"},tong_tien_nt:{$sum:"$tien_nt"},so_luong_giao_dich:{$sum:1}}},{$sort:{tong_tien:-1}},{$limit:20}]);if($results$$.length===0)return{status:"success",message:"Kh\u00f4ng ph\u00e1t sinh giao d\u1ecbch n\u00e0o th\u1ecfa m\u00e3n \u0111i\u1ec1u ki\u1ec7n."};$mapping_query$$[$group_by$$]&&await $results$$.asyncJoinModel2($id_app$$,$mapping_query$$[$group_by$$].model,{where:{_id:$mapping_query$$[$group_by$$].value_field||$group_by$$},field:[$mapping_query$$[$group_by$$].title_field]});
|
|
45
|
+
return{status:"success",data:$results$$,hint:"AI l\u01b0u \u00fd: '_id' ch\u00ednh l\u00e0 ti\u00eau ch\u00ed b\u1ea1n v\u1eeba ch\u1ecdn \u0111\u1ec3 group_by. 'tong_tien' l\u00e0 t\u1ed5ng gi\u00e1 tr\u1ecb ti\u1ec1n t\u1ec7. H\u00e3y tr\u00ecnh b\u00e0y c\u00e1c con s\u1ed1 n\u00e0y d\u01b0\u1edbi d\u1ea1ng danh s\u00e1ch ho\u1eb7c b\u1ea3ng \u0111\u1ec3 s\u1ebfp d\u1ec5 \u0111\u1ecdc."}}catch($error$$){return console.error("[L\u1ed7i analyzeGeneralLedgerTool]:",$error$$),{status:"error",message:$error$$.message||
|
|
46
|
+
String($error$$)}}}}),getAttendanceReportTool:tool({description:"C\u00f4ng c\u1ee5 l\u1ea5y b\u00e1o c\u00e1o v\u00e0 th\u1ed1ng k\u00ea t\u00ecnh h\u00ecnh ch\u1ea5m c\u00f4ng, ng\u00e0y c\u00f4ng c\u1ee7a nh\u00e2n vi\u00ean t\u1eeb th\u01b0 vi\u1ec7n g\u1ed1c h\u1ec7 th\u1ed1ng.\n S\u1eec D\u1ee4NG KHI: \n 1. S\u1ebfp ho\u1eb7c Nh\u00e2n s\u1ef1/K\u1ebf to\u00e1n l\u01b0\u01a1ng c\u1ea7n tra c\u1ee9u t\u00ecnh h\u00ecnh ch\u1ea5m c\u00f4ng c\u1ee7a m\u1ed9t ph\u00f2ng ban ho\u1eb7c m\u1ed9t c\u00e1 nh\u00e2n b\u1ea5t k\u1ef3.\n 2. Ch\u00ednh ng\u01b0\u1eddi \u0111ang s\u1eed d\u1ee5ng h\u1ec7 th\u1ed1ng h\u1ecfi v\u1ec1 t\u00ecnh h\u00ecnh \u0111i l\u00e0m, ng\u00e0y c\u00f4ng ho\u1eb7c s\u1ed1 ng\u00e0y ngh\u1ec9 ph\u00e9p c\u1ee7a B\u1ea2N TH\u00c2N h\u1ecd.",
|
|
47
|
+
inputSchema:z.object({tu_ngay:z.string().describe("T\u1eeb ng\u00e0y b\u1eaft \u0111\u1ea7u ch\u1ea5m c\u00f4ng (\u0110\u1ecbnh d\u1ea1ng: YYYY-MM-DD)."),den_ngay:z.string().describe("\u0110\u1ebfn ng\u00e0y k\u1ebft th\u00fac ch\u1ea5m c\u00f4ng (\u0110\u1ecbnh d\u1ea1ng: YYYY-MM-DD)."),is_self_service:z.boolean().default(!1).describe('R\u1ea4T QUAN TR\u1eccNG: B\u1eadt TRUE n\u1ebfu ng\u01b0\u1eddi d\u00f9ng \u0111ang h\u1ecfi v\u1ec1 t\u00ecnh h\u00ecnh ch\u1ea5m c\u00f4ng c\u1ee7a CH\u00cdNH B\u1ea2N TH\u00c2N H\u1ecc (V\u00ed d\u1ee5: "Th\u00e1ng n\u00e0y em \u0111\u01b0\u1ee3c bao nhi\u00eau c\u00f4ng?", "Anh/ch\u1ecb xem gi\u00f9m c\u00f4ng c\u1ee7a t\u00f4i"). \u0110\u1ec3 FALSE (M\u1eb7c \u0111\u1ecbnh) n\u1ebfu ng\u01b0\u1eddi d\u00f9ng l\u00e0 Qu\u1ea3n l\u00fd/K\u1ebf to\u00e1n \u0111ang tra c\u1ee9u s\u1ed1 li\u1ec7u cho nh\u00e2n vi\u00ean kh\u00e1c ho\u1eb7c cho ph\u00f2ng ban.'),
|
|
48
|
+
ma_nv:z.string().optional().describe("M\u00e3 nh\u00e2n vi\u00ean c\u1ee5 th\u1ec3 c\u1ea7n tra c\u1ee9u (D\u00f9ng khi k\u1ebf to\u00e1n/qu\u1ea3n l\u00fd xem cho ng\u01b0\u1eddi kh\u00e1c. KH\u00d4NG truy\u1ec1n tr\u01b0\u1eddng n\u00e0y n\u1ebfu is_self_service l\u00e0 true)."),ma_bp:z.string().optional().describe("M\u00e3 b\u1ed9 ph\u1eadn/ph\u00f2ng ban n\u1ebfu mu\u1ed1n xem ch\u1ea5m c\u00f4ng c\u1ee7a c\u1ea3 ph\u00f2ng (Ch\u1ec9 d\u00e0nh cho Qu\u1ea3n l\u00fd/K\u1ebf to\u00e1n)."),lam_thu_7:z.boolean().default(!0).describe("C\u1ea5u h\u00ecnh c\u00f4ng ty c\u00f3 l\u00e0m vi\u1ec7c v\u00e0o ng\u00e0y Th\u1ee9 7 hay kh\u00f4ng. M\u1eb7c \u0111\u1ecbnh l\u00e0 true."),
|
|
49
|
+
only_summary:z.boolean().default(!0).describe("N\u1ebfu TRUE: Ch\u1ec9 tr\u1ea3 v\u1ec1 s\u1ed1 t\u1ed5ng c\u1ed9ng (Ti\u1ebft ki\u1ec7m token). N\u1ebfu FALSE: Tr\u1ea3 v\u1ec1 danh s\u00e1ch chi ti\u1ebft t\u1eebng ng\u00e0y ph\u00e1t sinh.")}),execute:async({tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$,is_self_service:$is_self_service$$,ma_nv:$ma_nv$$,ma_bp:$ma_bp$$,lam_thu_7:$lam_thu_7$$,only_summary:$only_summary$$})=>{try{const $DmnvModel$$=global.getModel("dmnv");if(typeof getKqChamCong!=="function")return{status:"error",
|
|
50
|
+
message:"Th\u01b0 vi\u1ec7n kqchamcong kh\u00f4ng export m\u1ed9t function h\u1ee3p l\u1ec7."};let $final_ma_nv$$=$ma_nv$$?$ma_nv$$.toUpperCase().trim():void 0;$ma_nv$$="others";if($is_self_service$$){if(!$DmnvModel$$)return{status:"error",message:"Model 'dmnv' kh\u00f4ng t\u1ed3n t\u1ea1i tr\u00ean h\u1ec7 th\u1ed1ng \u0111\u1ec3 qu\u00e9t m\u00e3 nh\u00e2n vi\u00ean."};const $employeeDoc$$=$user_id$$?await $DmnvModel$$.findOne({id_app:$id_app$$,user:$user_id$$},{ma_nv:1}).lean():null;if(!$employeeDoc$$||
|
|
51
|
+
!$employeeDoc$$.ma_nv)return{status:"error",message:"T\u00e0i kho\u1ea3n c\u1ee7a b\u1ea1n hi\u1ec7n ch\u01b0a \u0111\u01b0\u1ee3c li\u00ean k\u1ebft v\u1edbi b\u1ea5t k\u1ef3 m\u00e3 nh\u00e2n vi\u00ean n\u00e0o trong danh m\u1ee5c dmnv. Vui l\u00f2ng li\u00ean h\u1ec7 nh\u00e2n s\u1ef1."};$final_ma_nv$$=$employeeDoc$$.ma_nv;$ma_bp$$=void 0;$ma_nv$$="self"}const $condition$$={id_app:$id_app$$,tu_ngay:moment($tu_ngay$$).startOf("date").toDate(),den_ngay:moment($den_ngay$$).endOf("date").toDate(),
|
|
52
|
+
ma_nv:$final_ma_nv$$,ma_bp:$ma_bp$$?$ma_bp$$.toUpperCase().trim():void 0,lam_thu_7:$lam_thu_7$$?"true":"false"},$report$$=await getKqChamCong($condition$$);if(!$report$$||!$report$$.data||$report$$.data.length===0)return{status:"success",message:`Kh\u00f4ng t\u00ecm th\u1ea5y d\u1eef li\u1ec7u ph\u00e1t sinh ch\u1ea5m c\u00f4ng n\u00e0o t\u1eeb ng\u00e0y ${$tu_ngay$$} \u0111\u1ebfn ${$den_ngay$$}.`};if($final_ma_nv$$)return{status:"success",view_mode:$ma_nv$$,target_employee:$final_ma_nv$$,period:{tu_ngay:$tu_ngay$$,
|
|
53
|
+
den_ngay:$den_ngay$$},data:$report$$.data,hint:$ma_nv$$==="self"?"AI l\u01b0u \u00fd: \u0110\u00e2y l\u00e0 d\u1eef li\u1ec7u ch\u1ea5m c\u00f4ng chi ti\u1ebft c\u1ee7a CH\u00cdNH NG\u01af\u1edcI \u0110ANG CHAT. H\u00e3y t\u1ed5ng h\u1ee3p t\u1ed5ng s\u1ed1 ng\u00e0y c\u00f4ng t\u00ednh l\u01b0\u01a1ng, s\u1ed1 ng\u00e0y ngh\u1ec9 ph\u00e9p, ngh\u1ec9 kh\u00f4ng l\u01b0\u01a1ng v\u00e0 li\u1ec7t k\u00ea b\u1ea3ng chi ti\u1ebft l\u1ecbch tr\u00ecnh c\u1ee7a t\u1eebng ng\u00e0y v\u1edbi th\u00e1i \u0111\u1ed9 chu \u0111\u00e1o.":
|
|
54
|
+
"AI l\u01b0u \u00fd: \u0110\u00e2y l\u00e0 d\u1eef li\u1ec7u chi ti\u1ebft c\u1ee7a nh\u00e2n vi\u00ean do K\u1ebf to\u00e1n/Qu\u1ea3n l\u00fd y\u00eau c\u1ea7u tra c\u1ee9u. H\u00e3y v\u1ebd b\u1ea3ng Markdown r\u00f5 r\u00e0ng \u0111\u1ec3 h\u1ecd x\u1eed l\u00fd c\u00f4ng vi\u1ec7c."};if($only_summary$$){const $summary$$=$report$$.data.reduce(($acc$$,$curr$$)=>{$acc$$.tong_ngay_cong_tinh_luong+=$curr$$.ngay_cong_tinh_luong||0;$acc$$.tong_ngay_khong_luong+=$curr$$.tong_ngay_khong_luong||0;$report$$.loaicong&&
|
|
55
|
+
$report$$.loaicong.forEach($l$$=>{$acc$$.chi_tiet_loai_cong[$l$$.ma_loai_cong]=($acc$$.chi_tiet_loai_cong[$l$$.ma_loai_cong]||0)+($curr$$[$l$$.ma_loai_cong]||0)});return $acc$$},{tong_so_nhan_su_phat_sinh:$report$$.data.length,tong_ngay_cong_tinh_luong:0,tong_ngay_khong_luong:0,chi_tiet_loai_cong:{}});return{status:"success",is_summary_data:!0,period:{tu_ngay:$tu_ngay$$,den_ngay:$den_ngay$$},filter:{ma_bp:$ma_bp$$||"To\u00e0n c\u00f4ng ty/Ph\u00f2ng ban"},data:$summary$$,hint:"AI l\u01b0u \u00fd: \u0110\u00e2y l\u00e0 d\u1eef li\u1ec7u t\u1ed5ng h\u1ee3p r\u00fat g\u1ecdn c\u1ee7a t\u1eadp th\u1ec3 nh\u00e2n s\u1ef1. H\u00e3y d\u00f9ng c\u00e1c s\u1ed1 t\u1ed5ng n\u00e0y \u0111\u1ec3 tr\u1ea3 l\u1eddi b\u00e1o c\u00e1o v\u0129 m\u00f4."}}return{status:"success",
|
|
56
|
+
is_summary_data:!1,total_records:$report$$.data.length,data:$report$$.data.slice(0,50),hint:"D\u1eef li\u1ec7u danh s\u00e1ch nhi\u1ec1u nh\u00e2n vi\u00ean (\u0110\u00e3 c\u1eaft l\u00e1t 50 d\u00f2ng \u0111\u1ec3 b\u1ea3o v\u1ec7 token). H\u00e3y v\u1ebd b\u1ea3ng Markdown."}}catch($error$$){return console.error("[L\u1ed7i t\u1ea1i getAttendanceReportTool - B\u1ea3n c\u1eadp nh\u1eadt ph\u00e2n lo\u1ea1i Self-Service]:",$error$$),{status:"error",message:`L\u1ed7i khi k\u1ebft n\u1ed1i v\u1edbi ph\u00e2n h\u1ec7 ch\u1ea5m c\u00f4ng: ${$error$$.message||
|
|
57
|
+
String($error$$)}`}}}})}}module.exports={createFinanceTools};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const {tool}=require("ai"),{z}=require("zod"),{buildJsonSchema}=require("../../libs/docs"),{Nodes}=require("../../flow/utils"),{searchActiveListInfo,cleanObjectOfFunctions}=require("./utils"),NODE_SPEC_MAP=Object.fromEntries((Nodes||[]).filter($n$$=>$n$$.type&&Array.isArray($n$$.metadata)).map($n$$=>[$n$$.type,$n$$]));
|
|
2
|
+
function applyNodeDefaults($filled_nodeData$$,$nodeType_spec$$){$nodeType_spec$$=NODE_SPEC_MAP[$nodeType_spec$$];if(!$nodeType_spec$$?.metadata)return $filled_nodeData$$;$filled_nodeData$$={...$filled_nodeData$$};for(const $field$$ of $nodeType_spec$$.metadata)$field$$.default===void 0||$filled_nodeData$$[$field$$.name]!==void 0&&$filled_nodeData$$[$field$$.name]!==null||($filled_nodeData$$[$field$$.name]=$field$$.default);return $filled_nodeData$$}
|
|
3
|
+
function validateNodeData($node$$){var $missing_spec$$=NODE_SPEC_MAP[$node$$.type];if(!$missing_spec$$?.metadata)return[];$missing_spec$$=$missing_spec$$.metadata.filter($f$$=>$f$$.required===!0&&$f$$.default===void 0).filter($f$$=>{$f$$=$node$$.data?.[$f$$.name];return $f$$===void 0||$f$$===null||$f$$===""}).map($f$$=>`'${$f$$.name}' (${$f$$.label})`);return $missing_spec$$.length>0?[{node:`${$node$$.data?.name||$node$$.id} [${$node$$.type}]`,missing_fields:$missing_spec$$}]:[]}
|
|
4
|
+
function createFlowTools($id_app$$){return{getWorkflowNodeSpecsTool:tool({description:"Tra c\u1ee9u c\u1ea5u tr\u00fac k\u1ef9 thu\u1eadt (Metadata Blueprints) c\u1ee7a c\u00e1c Node trong h\u1ec7 th\u1ed1ng Workflow.\n\n QUY TR\u00ccNH B\u1eaeT BU\u1ed8C KHI X\u00c2Y D\u1ef0NG FLOW (theo th\u1ee9 t\u1ef1):\n 1. [B\u01af\u1edaC N\u00c0Y] G\u1ecdi getWorkflowNodeSpecsTool (kh\u00f4ng truy\u1ec1n node_types) \u0111\u1ec3 xem t\u1ed5ng quan c\u00e1c node v\u00e0 tr\u01b0\u1eddng b\u1eaft bu\u1ed9c.\n 2. [T\u00d9Y CH\u1eccN] G\u1ecdi l\u1ea1i v\u1edbi node_types c\u1ee5 th\u1ec3 \u0111\u1ec3 xem \u0111\u1ea7y \u0111\u1ee7 metadata c\u1ee7a t\u1eebng node s\u1ebd d\u00f9ng.\n 3. G\u1ecdi getFlowDataFeaturesTool n\u1ebfu c\u1ea7n tra m\u00e3 code cho node 'data', 'event', ho\u1eb7c 'report'.\n 4. G\u1ecdi guideWorkflowBuildingTool \u0111\u1ec3 l\u00ean k\u1ebf ho\u1ea1ch layout v\u00e0 ID c\u00e1c node.\n 5. G\u1ecdi manageWorkflowStructureTool \u0111\u1ec3 t\u1ea1o/c\u1eadp nh\u1eadt flow th\u1ef1c t\u1ebf.\n\n \u26a0\ufe0f KH\u00d4NG \u0110\u01af\u1ee2C B\u1ece QUA B\u01af\u1edaC 1-4 r\u1ed3i nh\u1ea3y th\u1eb3ng v\u00e0o b\u01b0\u1edbc 5 \u2014 d\u1ec5 t\u1ea1o flow sai field/sai k\u1ebft n\u1ed1i.",inputSchema:z.object({node_types:z.array(z.enum("inject webhook facebook facebookconversations function foreachitem aianalysis delay switch httprequest email zalosendmessage facebooksendmessage facebookcreatepost savedata data event pushnotification report customer checkaction".split(" "))).optional().describe("M\u1ea3ng lo\u1ea1i node c\u1ea7n xem chi ti\u1ebft. B\u1ecf tr\u1ed1ng \u0111\u1ec3 xem t\u1ed5ng quan (ti\u1ebft ki\u1ec7m token).")}),
|
|
5
|
+
execute:async({node_types:$node_types$$})=>{try{if(!Nodes||!Array.isArray(Nodes))return{status:"error",message:"Kh\u00f4ng th\u1ec3 t\u1ea3i danh s\u00e1ch c\u1ea5u h\u00ecnh Nodes t\u1eeb h\u1ec7 th\u1ed1ng."};if($node_types$$&&$node_types$$.length>0){const $matchedSpecs$$=Nodes.filter($node$$=>$node_types$$.includes($node$$.type)),$cleanSpecs$$=JSON.parse(JSON.stringify($matchedSpecs$$,($key$$,$value$$)=>typeof $value$$==="function"?void 0:$value$$));return{status:"success",mode:"detailed",count:$cleanSpecs$$.length,
|
|
6
|
+
node_specs:$cleanSpecs$$,hint:"B\u00e1m ch\u1eb7t v\u00e0o c\u1ea5u tr\u00fac 'metadata' c\u1ee7a t\u1eebng node \u0111\u1ec3 \u0111i\u1ec1n ch\u00ednh x\u00e1c v\u00e0o thu\u1ed9c t\u00ednh 'data'. C\u00e1c tr\u01b0\u1eddng c\u00f3 'required: true' V\u00c0 KH\u00d4NG c\u00f3 'default' l\u00e0 b\u1eaft bu\u1ed9c AI ph\u1ea3i cung c\u1ea5p \u2014 h\u1ec7 th\u1ed1ng s\u1ebd t\u1eeb ch\u1ed1i l\u01b0u n\u1ebfu thi\u1ebfu."}}const $summaryNodes$$=Nodes.map($node$$=>({type:$node$$.type,label:$node$$.label,
|
|
7
|
+
category:$node$$.category,description:$node$$.description,all_fields:$node$$.metadata?$node$$.metadata.map($m$$=>$m$$.name):[],required_fields:$node$$.metadata?$node$$.metadata.filter($m$$=>$m$$.required===!0&&$m$$.default===void 0).map($m$$=>`${$m$$.name} (${$m$$.label})`):[]}));return{status:"success",mode:"summary",count:$summaryNodes$$.length,available_nodes:$summaryNodes$$,hint:"\u0110\u00e2y l\u00e0 t\u1ed5ng quan. 'required_fields' li\u1ec7t k\u00ea c\u00e1c tr\u01b0\u1eddng B\u1eaeT BU\u1ed8C ph\u1ea3i cung c\u1ea5p (kh\u00f4ng c\u00f3 gi\u00e1 tr\u1ecb m\u1eb7c \u0111\u1ecbnh) \u2014 thi\u1ebfu b\u1ea5t k\u1ef3 tr\u01b0\u1eddng n\u00e0o trong danh s\u00e1ch \u0111\u00f3, h\u1ec7 th\u1ed1ng s\u1ebd t\u1eeb ch\u1ed1i l\u01b0u. Khi \u0111\u00e3 ch\u1ecdn \u0111\u01b0\u1ee3c lo\u1ea1i node, g\u1ecdi l\u1ea1i v\u1edbi 'node_types' \u0111\u1ec3 xem \u0111\u1ea7y \u0111\u1ee7 metadata tr\u01b0\u1edbc khi d\u1ef1ng flow."}}catch($error$$){return{status:"error",
|
|
8
|
+
message:$error$$.message||String($error$$)}}}}),getFlowDataFeaturesTool:tool({description:"Tra c\u1ee9u m\u00e3 code c\u1ee7a Danh m\u1ee5c/Ch\u1ee9ng t\u1eeb (listinfo) v\u00e0 B\u00e1o c\u00e1o (reportinfo) \u0111\u1ec3 c\u1ea5u h\u00ecnh node.\n S\u1eec D\u1ee4NG KHI:\n - Node 'data' ho\u1eb7c 'event': c\u1ea7n \u0111i\u1ec1n data.code -> d\u00f9ng 'finalCode' t\u1eeb listinfo_features.\n - Node 'report': c\u1ea7n \u0111i\u1ec1n data.code -> d\u00f9ng 'code' t\u1eeb reportinfo_features.",
|
|
9
|
+
inputSchema:z.object({keywords:z.array(z.string()).optional().describe('T\u1eeb kh\u00f3a t\u00ecm ki\u1ebfm (VD: ["Phi\u1ebfu nh\u1eadp", "pn1"]).'),target_type:z.enum(["data_event","report","both"]).default("data_event"),include_fields_schema:z.boolean().default(!1).describe("TRUE \u0111\u1ec3 l\u1ea5y chi ti\u1ebft schema (t\u1ed1n token h\u01a1n).")}),execute:async({keywords:$keywords$$,target_type:$reportDocs_target_type$$,include_fields_schema:$include_fields_schema$$})=>{try{const $ReportInfoModel$$=
|
|
10
|
+
global.getModel("reportinfo"),$results$$={status:"success",matched_by_keyword:$keywords$$?$keywords$$.join(", "):"all"};if($reportDocs_target_type$$==="data_event"||$reportDocs_target_type$$==="both"){const $searchResult$$=await searchActiveListInfo({keywords:$keywords$$,include_fields_schema:$include_fields_schema$$,limit:20});if(!$searchResult$$.docs?.length)return{status:"not_found",message:`Kh\u00f4ng t\u00ecm th\u1ea5y lo\u1ea1i d\u1eef li\u1ec7u n\u00e0o kh\u1edbp v\u1edbi [${$keywords$$?.join(", ")}].`};
|
|
11
|
+
$results$$.listinfo_features=await Promise.all($searchResult$$.docs.map(async $d$$=>{const $item$$={code:$d$$.code,title:$d$$.title,finalCode:$d$$.api_code||$d$$.code};if($include_fields_schema$$&&typeof buildJsonSchema==="function")try{$item$$.fields_schema=JSON.parse(JSON.stringify(await buildJsonSchema($d$$.fields||[]),($k$$,$v$$)=>typeof $v$$==="function"?void 0:$v$$))}catch($e$$){$item$$.fields_schema_error=$e$$.message}return $item$$}));$results$$.listinfo_features=JSON.parse(JSON.stringify($results$$.listinfo_features))}if($reportDocs_target_type$$===
|
|
12
|
+
"report"||$reportDocs_target_type$$==="both"){if(!$ReportInfoModel$$)throw Error("Model 'reportinfo' kh\u00f4ng t\u1ed3n t\u1ea1i.");$reportDocs_target_type$$=[];if($keywords$$?.length)for(const $kw$$ of $keywords$$){if($reportDocs_target_type$$=await $ReportInfoModel$$.find({id_app:$id_app$$,$or:[{code:{$regex:$kw$$,$options:"i"}},{title:{$regex:$kw$$,$options:"i"}}]}).select({code:1,title:1,form_condition_info:1,columns:$include_fields_schema$$?1:0}).limit(10).lean(),$reportDocs_target_type$$.length)break}else $reportDocs_target_type$$=
|
|
13
|
+
await $ReportInfoModel$$.find({id_app:$id_app$$}).select({code:1,title:1,form_condition_info:1,columns:$include_fields_schema$$?1:0}).limit(15).lean();$results$$.reportinfo_features=await Promise.all($reportDocs_target_type$$.map(async $r$$=>{const $item$$={code:$r$$.code,title:$r$$.title,form_condition_info:$r$$.form_condition_info};if($include_fields_schema$$&&($item$$.report_columns=($r$$.columns||[]).map($col$$=>({header:$col$$.header,datatype:$col$$.datatype})),$r$$.form_condition_info&&typeof buildJsonSchema===
|
|
14
|
+
"function"))try{const $conditionForm$$=await global.getModel("listinfo").findOne({code:$r$$.form_condition_info}).select({fields:1}).lean();$conditionForm$$?.fields&&($item$$.condition_schema=JSON.parse(JSON.stringify(await buildJsonSchema($conditionForm$$.fields),($k$$,$v$$)=>typeof $v$$==="function"?void 0:$v$$)))}catch($e$$){$item$$.condition_schema_error=$e$$.message}return $item$$}))}$results$$.hint="Node 'data'/'event': d\u00f9ng 'finalCode' trong listinfo_features. Node 'report': d\u00f9ng 'code' trong reportinfo_features v\u00e0 'condition_schema' \u0111\u1ec3 c\u1ea5u h\u00ecnh \u0111i\u1ec1u ki\u1ec7n l\u1ecdc.";
|
|
15
|
+
return $results$$}catch($error$$){return{status:"error",message:$error$$.message||String($error$$)}}}}),guideWorkflowBuildingTool:tool({description:"L\u00ean k\u1ebf ho\u1ea1ch ki\u1ebfn tr\u00fac s\u01a1 \u0111\u1ed3 Workflow: t\u00ednh to\u00e1n t\u1ecda \u0111\u1ed9, t\u1ea1o ID \u1ed5n \u0111\u1ecbnh, \u0111\u1ecbnh h\u00ecnh c\u1ea1nh n\u1ed1i.\n B\u1eaeT BU\u1ed8C g\u1ecdi TR\u01af\u1edaC manageWorkflowStructureTool. D\u00f9ng \u0111\u00fang ID t\u1eeb k\u1ebft qu\u1ea3 tool n\u00e0y trong b\u01b0\u1edbc t\u1ea1o flow.",
|
|
16
|
+
inputSchema:z.object({user_requirement:z.string().describe("M\u00f4 t\u1ea3 nhu c\u1ea7u nghi\u1ec7p v\u1ee5 (\u0111\u1ec3 log)."),detected_branches:z.array(z.string()).default(["default"]).describe('C\u00e1c t\u00ean nh\u00e1nh r\u1ebd t\u1eeb node switch. Lu\u00f4n bao g\u1ed3m "default".'),expected_nodes:z.array(z.object({type:z.string().describe('Lo\u1ea1i node (VD: "inject", "switch", "email").'),purpose:z.string().describe("M\u1ee5c \u0111\u00edch ng\u1eafn g\u1ecdn c\u1ee7a node n\u00e0y.")})).describe("Danh s\u00e1ch node theo th\u1ee9 t\u1ef1 th\u1ef1c thi.")}),
|
|
17
|
+
execute:async({detected_branches:$detected_branches$$,expected_nodes:$expected_nodes$$})=>{try{if(!$expected_nodes$$?.length)return{status:"error",message:"C\u1ea7n cung c\u1ea5p expected_nodes."};const $branches$$=$detected_branches$$.includes("default")?$detected_branches$$:[...$detected_branches$$,"default"];let $currentX$$=100,$switchNodeId$$=null,$lastNodeIds$$=[],$nextSequentialY$$=100+Math.floor(($branches$$.length-1)*160/2);const $plannedNodes$$=[],$plannedEdges$$=[];$expected_nodes$$.forEach(($node$$,
|
|
18
|
+
$index$$)=>{const $nodeId$$=`${$node$$.type}_${$index$$}`;if($index$$>0&&$expected_nodes$$[$index$$-1].type==="switch"){const $branchNodeIds$$=[];$branches$$.forEach(($branchName$$,$bIndex$$)=>{const $branchNodeId$$=`${$node$$.type}_${$index$$}_branch_${$bIndex$$}`;$plannedNodes$$.push({id:$branchNodeId$$,type:$node$$.type,position:{x:$currentX$$,y:100+$bIndex$$*160},suggested_name:`${$node$$.purpose} (${$branchName$$})`,raw_type:$node$$.type});$switchNodeId$$&&$plannedEdges$$.push({id:`edge-${$switchNodeId$$}-${$branchName$$}-${$branchNodeId$$}`,
|
|
19
|
+
source:$switchNodeId$$,sourceHandle:$branchName$$,target:$branchNodeId$$,targetHandle:null});$branchNodeIds$$.push($branchNodeId$$)});$lastNodeIds$$=$branchNodeIds$$;$nextSequentialY$$=100+Math.floor(($branches$$.length-1)*160/2)}else{$plannedNodes$$.push({id:$nodeId$$,type:$node$$.type,position:{x:$currentX$$,y:$lastNodeIds$$.length>1?$nextSequentialY$$:100},suggested_name:$node$$.purpose,raw_type:$node$$.type});$node$$.type==="switch"&&($switchNodeId$$=$nodeId$$);if($lastNodeIds$$.length>0)for(const $prevId$$ of $lastNodeIds$$)$plannedEdges$$.push({id:`edge-${$prevId$$}-a-${$nodeId$$}`,
|
|
20
|
+
source:$prevId$$,sourceHandle:"a",target:$nodeId$$,targetHandle:null});$lastNodeIds$$=[$nodeId$$]}$currentX$$+=260});return{status:"success",architecture_blueprint:{nodes:$plannedNodes$$,edges:$plannedEdges$$},hint:`
|
|
21
|
+
B\u1eaeT BU\u1ed8C khi d\u00f9ng blueprint n\u00e0y trong manageWorkflowStructureTool:
|
|
22
|
+
1. D\u00f9ng \u0110\u00daNG 'id' \u0111\u00e3 \u0111\u01b0\u1ee3c l\u1eadp k\u1ebf ho\u1ea1ch \u1edf tr\u00ean (VD: "inject_0", "switch_1", "email_2_branch_0"...) \u2014 kh\u00f4ng t\u1ef1 sinh ID kh\u00e1c.
|
|
23
|
+
2. Node switch: 'data.rules[*].outputHandleId' PH\u1ea2I kh\u1edbp ch\u00ednh x\u00e1c v\u1edbi t\u00ean nh\u00e1nh: ${JSON.stringify($branches$$.filter($b$$=>$b$$!=="default"))}.
|
|
24
|
+
3. Node default c\u1ee7a switch: 'data.defaultOutputHandleId' = "default".
|
|
25
|
+
4. M\u1ed7i node B\u1eaeT BU\u1ed8C c\u00f3 'data.name' (t\u00ean m\u00f4 t\u1ea3 ng\u1eafn g\u1ecdn) v\u00e0 c\u00e1c tr\u01b0\u1eddng required kh\u00f4ng c\u00f3 default (xem getWorkflowNodeSpecsTool).
|
|
26
|
+
`.trim()}}catch($error$$){return{status:"error",message:$error$$.message||String($error$$)}}}}),manageWorkflowStructureTool:tool({description:"T\u1ea1o m\u1edbi ho\u1eb7c c\u1eadp nh\u1eadt c\u1ea5u tr\u00fac Workflow. Ch\u1ec9 g\u1ecdi SAU KHI \u0111\u00e3 ho\u00e0n th\u00e0nh 4 b\u01b0\u1edbc tr\u01b0\u1edbc.\n SMART MERGE (khi update): ch\u1ec9 c\u1ea7n truy\u1ec1n nodes v\u1edbi id/type/data.name c\u0169 \u2014 h\u1ec7 th\u1ed1ng t\u1ef1 gi\u1eef c\u00e1c c\u1ea5u h\u00ecnh s\u00e2u (script, \u0111i\u1ec1u ki\u1ec7n) kh\u00f4ng thay \u0111\u1ed5i.\n \u26a0\ufe0f H\u1ec7 th\u1ed1ng t\u1ef1 \u0110I\u1ec0N DEFAULT v\u00e0 VALIDATE tr\u01b0\u1eddng b\u1eaft bu\u1ed9c tr\u01b0\u1edbc khi l\u01b0u \u2014 n\u1ebfu thi\u1ebfu tr\u01b0\u1eddng required th\u1ef1c s\u1ef1, tr\u1ea3 v\u1ec1 l\u1ed7i v\u1edbi danh s\u00e1ch field thi\u1ebfu.",
|
|
27
|
+
inputSchema:z.object({action:z.enum(["create","update"]),flow_id:z.string().optional().describe('ID flow c\u1ea7n update. B\u1eaeT BU\u1ed8C khi action="update".'),title:z.string().describe("Ti\u00eau \u0111\u1ec1 c\u1ee7a flow."),status:z.boolean().default(!0),nodes:z.array(z.object({id:z.string().describe("D\u00f9ng \u0111\u00fang ID t\u1eeb guideWorkflowBuildingTool."),type:z.enum("inject webhook facebook facebookconversations function foreachitem aianalysis delay switch httprequest email zalosendmessage facebooksendmessage facebookcreatepost savedata data event pushnotification report customer checkaction".split(" ")),
|
|
28
|
+
position:z.object({x:z.number(),y:z.number()}),data:z.object({name:z.string().describe("B\u1eaeT BU\u1ed8C: t\u00ean m\u00f4 t\u1ea3 c\u1ee7a node.")}).catchall(z.any())})),edges:z.array(z.object({id:z.string(),source:z.string(),sourceHandle:z.string().nullable().default("a"),target:z.string(),targetHandle:z.string().nullable().default(null)}))}),execute:async({action:$action$$,flow_id:$flow_id$$,title:$title$$,status:$status$$,nodes:$nodes$$,edges:$edges$$})=>{try{const $FlowModel$$=global.getModel("flow");
|
|
29
|
+
if(!$FlowModel$$)return{status:"error",message:"Model 'flow' kh\u00f4ng t\u1ed3n t\u1ea1i."};const $sanitizedNodes$$=cleanObjectOfFunctions($nodes$$);let $sanitizedEdges$$=cleanObjectOfFunctions($edges$$);const $nodesWithDefaults$$=$sanitizedNodes$$.map($node$$=>({...$node$$,data:applyNodeDefaults($node$$.data||{},$node$$.type)})),$validationErrors$$=$nodesWithDefaults$$.flatMap($node$$=>validateNodeData($node$$));if($validationErrors$$.length>0)return{status:"validation_error",message:`${$validationErrors$$.length} node thi\u1ebfu tr\u01b0\u1eddng b\u1eaft bu\u1ed9c \u2014 vui l\u00f2ng b\u1ed5 sung tr\u01b0\u1edbc khi l\u01b0u:`,
|
|
30
|
+
errors:$validationErrors$$,hint:"G\u1ecdi getWorkflowNodeSpecsTool v\u1edbi node_types t\u01b0\u01a1ng \u1ee9ng \u0111\u1ec3 xem chi ti\u1ebft. C\u00e1c tr\u01b0\u1eddng trong 'missing_fields' l\u00e0 required:true v\u00e0 kh\u00f4ng c\u00f3 gi\u00e1 tr\u1ecb m\u1eb7c \u0111\u1ecbnh \u2014 AI PH\u1ea2I cung c\u1ea5p gi\u00e1 tr\u1ecb th\u1ef1c t\u1ebf cho ch\u00fang."};const $formattedNodes$$=$nodesWithDefaults$$.map($node$$=>({...$node$$,data:{...$node$$.data,name:$node$$.data.name||$node$$.id},
|
|
31
|
+
width:$node$$.width||150,height:$node$$.height||60,positionAbsolute:{x:$node$$.position.x,y:$node$$.position.y}}));if($action$$==="create"){const $newFlow$$=await $FlowModel$$.create({title:$title$$,status:$status$$,nodes:$formattedNodes$$,edges:$sanitizedEdges$$,listinfo_code:"flow",id_app:$id_app$$,date_created:new Date,date_updated:new Date,activeInstance:{status:"stopped",instanceId:""}}),$newFlowId$$=$newFlow$$._id.toString();return{status:"success",message:`\u0110\u00e3 t\u1ea1o th\u00e0nh c\u00f4ng Workflow: "${$title$$}".`,
|
|
32
|
+
flow_id:$newFlowId$$,url:`/#/floweditor/${$newFlowId$$}`,data:JSON.parse(JSON.stringify($newFlow$$.toObject())),hint:`[\ud83c\udf10 M\u1edf m\u00e0n h\u00ecnh thi\u1ebft k\u1ebf lu\u1ed3ng "${$title$$}"](/#/floweditor/${$newFlowId$$})`}}if($action$$==="update"){if(!$flow_id$$)return{status:"error",message:"Thi\u1ebfu 'flow_id' khi update."};const $existingFlow$$=await $FlowModel$$.findOne({_id:$flow_id$$,id_app:$id_app$$}).lean();if(!$existingFlow$$)return{status:"not_found",message:`Kh\u00f4ng t\u00ecm th\u1ea5y Flow ID: ${$flow_id$$}.`};
|
|
33
|
+
const $oldNodesById$$=new Map(($existingFlow$$.nodes||[]).map($n$$=>[$n$$.id,$n$$])),$oldNodesByTypeAndName$$=new Map(($existingFlow$$.nodes||[]).filter($n$$=>$n$$.data?.name).map($n$$=>[`${$n$$.type}_${$n$$.data.name}`,$n$$])),$idMapping$$={},$finalMergedNodes$$=$formattedNodes$$.map($newNode$$=>{let $oldNode$$=$oldNodesById$$.get($newNode$$.id)||$oldNodesByTypeAndName$$.get(`${$newNode$$.type}_${$newNode$$.data.name}`);if(!$oldNode$$)return $newNode$$;$newNode$$.id!==$oldNode$$.id&&($idMapping$$[$newNode$$.id]=
|
|
34
|
+
$oldNode$$.id,$newNode$$={...$newNode$$,id:$oldNode$$.id});const $mergedData$$={...$oldNode$$.data,...$newNode$$.data};for(const $key$$ of Object.keys($newNode$$.data))$newNode$$.data[$key$$]===null||typeof $newNode$$.data[$key$$]!=="object"||Array.isArray($newNode$$.data[$key$$])||($mergedData$$[$key$$]={...($oldNode$$.data[$key$$]||{}),...$newNode$$.data[$key$$]});return{...$oldNode$$,...$newNode$$,id:$oldNode$$.id,data:$mergedData$$}});Object.keys($idMapping$$).length>0&&($sanitizedEdges$$=$sanitizedEdges$$.map($edge$$=>
|
|
35
|
+
({...$edge$$,source:$idMapping$$[$edge$$.source]||$edge$$.source,target:$idMapping$$[$edge$$.target]||$edge$$.target,id:`edge-${$idMapping$$[$edge$$.source]||$edge$$.source}${$edge$$.sourceHandle||""}-${$idMapping$$[$edge$$.target]||$edge$$.target}`})));const $updatedFlow$$=await $FlowModel$$.findOneAndUpdate({_id:$flow_id$$,id_app:$id_app$$},{$set:{title:$title$$,status:$status$$,nodes:$finalMergedNodes$$,edges:$sanitizedEdges$$,date_updated:new Date}},{new:!0,lean:!0});return{status:"success",message:`\u0110\u00e3 c\u1eadp nh\u1eadt Workflow: "${$title$$}" (Smart Merge \u0111\u00e3 b\u1ea3o v\u1ec7 d\u1eef li\u1ec7u c\u0169).`,
|
|
36
|
+
flow_id:$flow_id$$,url:`/#/floweditor/${$flow_id$$}`,data:JSON.parse(JSON.stringify($updatedFlow$$)),hint:`[\ud83c\udf10 M\u1edf m\u00e0n h\u00ecnh thi\u1ebft k\u1ebf lu\u1ed3ng sau khi c\u1eadp nh\u1eadt](/#/floweditor/${$flow_id$$})`}}}catch($error$$){return{status:"error",message:`L\u1ed7i h\u1ec7 th\u1ed1ng: ${$error$$.message}`}}}})}}module.exports={createFlowTools};
|